# HG changeset patch # User Chris Cannam # Date 1400589562 -3600 # Node ID f0da2404108caec1d75531bb30360650e99db706 # Parent af8ecd7866cd7e6b595ac4bcf2d920d8e02267de# Parent f9e557d6ac2ff0cc608a2f427a53163cb26fd86c Merge diff -r f9e557d6ac2f -r f0da2404108c src/may/stream/audiofile.yeti --- a/src/may/stream/audiofile.yeti Tue May 13 18:50:27 2014 +0100 +++ b/src/may/stream/audiofile.yeti Tue May 20 13:39:22 2014 +0100 @@ -156,6 +156,8 @@ //!!! no, the header size is included in the return value! n / (channels * bitdepth)); +//!!! todo: io module style openWith + { open, openMono, diff -r f9e557d6ac2f -r f0da2404108c src/may/stream/format.yeti --- a/src/may/stream/format.yeti Tue May 13 18:50:27 2014 +0100 +++ b/src/may/stream/format.yeti Tue May 20 13:39:22 2014 +0100 @@ -29,12 +29,12 @@ done ); -decodeFail () = - throw new UnsupportedAudioFileException("Audio format not supported. Supported formats are 8-bit unsigned PCM, 16-bit signed little-endian PCM, or IEEE float"); +decodeFail f is ~AudioFormat -> () = + throw new UnsupportedAudioFileException("Audio format \"\(f#toString())\" not supported. Supported formats are 8-bit unsigned PCM, 16-bit signed little-endian PCM, or IEEE float."); decode { format is ~AudioFormat } bytes doubles n = (if format#isBigEndian() then - decodeFail() + decodeFail format; else enc = format#getEncoding(); bits = format#getSampleSizeInBits(); @@ -45,7 +45,7 @@ elif bits == 8 and enc == AudioFormat$Encoding#PCM_UNSIGNED then decode8u bytes doubles n; else - decodeFail(); + decodeFail format; fi fi); diff -r f9e557d6ac2f -r f0da2404108c src/may/stream/readall.yeti --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/may/stream/readall.yeti Tue May 20 13:39:22 2014 +0100 @@ -0,0 +1,26 @@ + +module may.stream.readall; + +load may.stream.type; + +mat = load may.matrix; +af = load may.stream.audiofile; + +readAll stream = + case stream.available of + Known n: stream.read n; + _: failWith "Cannot readAll from infinite or unknown-duration stream"; + esac; + +//!!! todo: use openWith (when available!) +readAllFrom filename = + (str = af.open filename; + mat = readAll str; + str.close (); + mat); + +{ + readAll is stream_t -> mat.matrix_t, + readAllFrom is string -> mat.matrix_t, +} + diff -r f9e557d6ac2f -r f0da2404108c src/may/stream/syntheticstream.yeti --- a/src/may/stream/syntheticstream.yeti Tue May 13 18:50:27 2014 +0100 +++ b/src/may/stream/syntheticstream.yeti Tue May 20 13:39:22 2014 +0100 @@ -43,8 +43,20 @@ result); generated sampleRate generator); +/** + * Generate a white noise stream with a maximum amplitude of 0.5. + * + * This is limited to 0.5 rather than 1.0 to help avoid + * situations in which subsequent processing causes hard-to-spot + * clipping. For example, filtering noise to limit its bandwidth + * may increase the levels of some samples: if you do this naively + * and then export the result to an audio file, it will clip and + * the resulting noise will be neither correctly distributed nor + * band-limited. But because individual samples are random, it's + * very easy to miss what is happening. + */ whiteNoise sampleRate = - generated sampleRate \((Math#random() * 2.0) - 1.0); + generated sampleRate \((Math#random() * 1.0) - 0.5); silent sampleRate = generated sampleRate \0; diff -r f9e557d6ac2f -r f0da2404108c src/may/stream/test/test_waves.yeti --- a/src/may/stream/test/test_waves.yeti Tue May 13 18:50:27 2014 +0100 +++ b/src/may/stream/test/test_waves.yeti Tue May 20 13:39:22 2014 +0100 @@ -36,7 +36,7 @@ compare str.sampleRate 8 and compare str.available (Infinite ()) and compare str.finished? false and - compareApprox epsilon (vec.list (mat.getRow 0 (str.read 9))) [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ] and + compareApprox epsilon (vec.list (mat.getRow 0 (str.read 9))) [ 0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5 ] and compare str.position 9 ), @@ -49,7 +49,7 @@ compare str.sampleRate 10 and compare str.available (Infinite ()) and compare str.finished? false and - compareApprox epsilon (vec.list (mat.getRow 0 (str.read 11))) [ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ] and + compareApprox epsilon (vec.list (mat.getRow 0 (str.read 11))) [ 0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0 ] and compare str.position 11 ), @@ -66,7 +66,7 @@ compare str.available (Infinite ()) and compare str.finished? false and compareApprox epsilon (vec.list (mat.getRow 0 (str.read 14))) - [ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0 ] and + [ 0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0 ] and compare str.position 14 */ ), diff -r f9e557d6ac2f -r f0da2404108c src/may/stream/waves.yeti --- a/src/may/stream/waves.yeti Tue May 13 18:50:27 2014 +0100 +++ b/src/may/stream/waves.yeti Tue May 20 13:39:22 2014 +0100 @@ -54,7 +54,8 @@ // Bandlimited impulse train: produce naive pulse train at a frequency // that is an integer fraction of the sample rate (so as to have exact // sample locations), then resample so as to place the impulses at the -// requested frequency +// requested frequency. The maximum sample is 0.5 rather than 1 to +// avoid clipping in further manipulation. impulseTrain sampleRate freq = if freq > sampleRate/2 then failWith "Can't generate impulse train with frequency > half the sample rate (\(freq) > \(sampleRate)/2)" @@ -62,7 +63,7 @@ naiveTrain f = (spacing = int (sampleRate / f); generated sampleRate - do n: if n % spacing == 0 then 1 else 0 fi done); + do n: if n % spacing == 0 then 0.5 else 0 fi done); ratio = sampleRate / freq; if ratio == int ratio then naiveTrain freq