Skip to content

Instantly share code, notes, and snippets.

@danstowell
Created February 2, 2016 14:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danstowell/717cb5ad82eb275b54d7 to your computer and use it in GitHub Desktop.
Save danstowell/717cb5ad82eb275b54d7 to your computer and use it in GitHub Desktop.
Generate Schroeder-phase complexes in Supercollider

// schroeder-phase waveforms

s.boot s.scope

// we'll do the simple flat-spectra case // (The paper I saw this version in is "Phase effects on the perceived elevation of complex tones", http://dx.doi.org/10.1121/1.3372753.)

~nb = 5; ~nt = 250; ~f0 = 65; ~nspls = 1800;

/////////////////////////////////////////////////////////////////////////////////////////////////////////// // phase eqns: ~hartmann_schroeder_minus_phase_single = {|n, nb, nt| (-pi * ((n * (n-1)) - (nb * (nb-1)))/(nt - nb + 1)).wrap2(pi) }; // eq (2) from Hartmann, plus wrap to +-pi

( // This function gives the different phase modes explored in these types of experiment ~phasecalc = {|n, nb, nt, mode| mode.switch( \plus, {0 - ~hartmann_schroeder_minus_phase_single.value(n, nb, nt)}, \minus, { ~hartmann_schroeder_minus_phase_single.value(n, nb, nt)}, \sine, {0}, \cosine, {0.5pi}, \rand, {2pi.rand} ) }; )

// 10.collect{|n| ~phasecalc.value(n, 0, 10, \rand)};

/////////////////////////////////////////////////////////////////////////////////////////////////////////// // synthesis

// Direct waveform making in sclang ( ~hartmann_schroeder_direct = {|nspls, f0, nb, nt, mode| nspls.collect{|x| (nb..nt).mean{|n| 0 - sin(2pi * n * f0 * (x/s.sampleRate) + ~phasecalc.value(n, nb, nt, mode) - 0.5pi)} }; // eq (1) from hartmann, translated to sin not cos }; ) [\plus, \minus, \sine, \cosine, \rand].do{|mode| ~hartmann_schroeder_direct.value(~nspls, ~f0, ~nb, ~nt, mode).plot(mode)};

// Now via SinOsc: ( ~hartmann_schroeder_sinosc = { |f0, nb, nt, mode| (nb..nt).mean{|n| 0 - SinOsc.ar(n * f0, ~phasecalc.value(n, nb, nt, mode) - 0.5pi) }.dup }; )

[\plus, \minus, \sine, \cosine, \rand].do{|mode| {~hartmann_schroeder_sinosc.value(~f0, ~nb, ~nt, mode)}.plot(~nspls/s.sampleRate) }

~f0 = 300; ~f0 = 65; ~f0 = 16; ~f0 = 2; x = {~hartmann_schroeder_sinosc.value(~f0, ~nb, ~nt, \sine)}.play x.free; x = {~hartmann_schroeder_sinosc.value(~f0, ~nb, ~nt, \cosine)}.play x.free; x = {~hartmann_schroeder_sinosc.value(~f0, ~nb, ~nt, \plus)}.play x.free; x = {~hartmann_schroeder_sinosc.value(~f0, ~nb, ~nt, \minus)}.play x.free; x = {~hartmann_schroeder_sinosc.value(~f0, ~nb, ~nt, \rand)}.play x.free;

( Task{ loop{ x = { ~hartmann_schroeder_sinosc.value(~f0, ~nb, ~nt, [\plus, \minus, \sine, \cosine, \rand].choose.postln) * EnvGen.ar(Env.linen(0.01, 0.5, 0.01), doneAction:2) }.play; 1.5.wait; } }.play )

( // Let's add some reverb (and stereo) to see how reverb affects the distibguishability of the plus and minus ~verb = { |wet=1, room=0.3, damp=1| ReplaceOut.ar(0, FreeVerb.ar(In.ar(0,1), wet, room:room, damp:damp).dup * wet.linlin(0, 1, 0.75, 2)) }.play(s, addAction:\addToTail); ) ~verb.set(\wet, 0.1) ~verb.set(\wet, 0.75, \room, 0.9, \damp, 0.5) ~verb.free

( Task{ loop{ x = { ~hartmann_schroeder_sinosc.value(~f0, ~nb, ~nt, \minus) * EnvGen.ar(Env.linen(0.01, 0.5, 0.01), doneAction:2) }.play; 2.5.wait; x = { ~hartmann_schroeder_sinosc.value(~f0, ~nb, ~nt, \plus) * EnvGen.ar(Env.linen(0.01, 0.5, 0.01), doneAction:2) }.play; 2.5.wait; } }.play )

s.record s.stopRecording

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment