Mercurial > hg > ishara
diff dsp/synth/sonify.m @ 34:c75bb62b90a9
Imported audio synthesis tools.
author | samer |
---|---|
date | Sun, 20 Jan 2013 19:05:05 +0000 |
parents | |
children | 9e7be347b3a0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dsp/synth/sonify.m Sun Jan 20 19:05:05 2013 +0000 @@ -0,0 +1,83 @@ +function ad=sonify(S,D,varargin) +% sonify - Sonify sequences of durations and pitches +% +% sonify :: +% ([[N]] | seq(real)) ~'sequence of pitches in semitones', +% (natural | seq(real)) ~'sequence of durations', +% options { +% env :: ([[N]]->[[N]]) ~'envelope shaping function', +% transpose :: real /0 ~'add this to all pitches first'; +% bpm :: nonneg /240 ~'number of time units per minute'; +% +% gen :: { 'sine' ~'sine wave', +% 'square' ~'frequency-quantised square wave', +% 'blsquare' ~'bandlimited square wave', +% 'blsquare2' ~'alternative method', +% 'shepard' ~'Shepard tones' +% } / 'sine' ~'determines waveform generated'; +% betap :: [[2]] /[2,8] ~'Beta pdf parameters for Shepard tones' +% +% fs :: real /11025 ~'intended sampling rate'; +% buffer :: noneg /0.125 ~'buffer size in seconds, [] means one per note' +% } +% -> seq([[1,T]]). + + opts=prefs( ... + 'bpm',240,'transpose',0, ... + 'gen','sine','betap',[2,8], ... + 'fs',11025, 'buffer',0.125, ... + 'env',[],'bl_cutoff',[], ... + varargin{:}); + + env=opts.env; + fm=440/opts.fs; + dm=60*opts.fs/opts.bpm; + tr=opts.transpose; + num2hz=@(n)nan2x(0,fm*2.^((n+tr)/12)); + + if isscalar(D), D=dm*D; + else D=fndata(@(d)dm*d,dd(D),'compose',1); % D=dm*D with optimisation + end + C=fndata(@(n)(n+tr)/12,dd(S),'compose',1); % chroma values + F=fndata(num2hz,dd(S),'compose',1); % convert to normalised frequencies + + % cutoff for band-limited waveform generation + if isempty(opts.bl_cutoff) + cutoff=0.21+0.2*repeat(normalise01(cumsum(sample(stable(0,1.4,0),[1,1024]),2),2)); + else + cutoff=opts.bl_cutoff; + end + + switch opts.gen + case 'square' + % square wave with period quantized to sample period + Y=blockdata(square,D,0,repeat(0.5),F); + case 'blsquare' + % cumsum within each buffer: doesn't drift but makes + % audible clicks between buffers. + Y=fndata(@(t)0.5*cumsum(t),blockdata(bpblit,D,0,cutoff,F)); + case 'blsquare2' + % this version does cumsum across the sequence to remove + % discontinuities but requires high-pass filter to stop drift. + [fb,fa]=butter(2,0.001,'high'); + Y=filter(0.5*fb,fa,cumsum(blockdata(bpblit,D,0,cutoff,F),2)); + case 'shepard' + Y=0.04*shepardtone(D,C,opts.betap); + case 'sine2' + Y=blockdata(sine,D,0,F)./(F/0.01); + otherwise + Y=blockdata(sine,D,0,F); + end + + % if supplied apply envelope to shape each note + if nargin>2 && ~isempty(env), + ad=fndata(env,Y,'sizecheck',1); + else + ad=Y; + end + + % rebuffer to constant size + if ~isempty(opts.buffer) + ad=windowdata(ad,ceil(opts.buffer*opts.fs)); + end +