Created
February 2, 2016 14:52
-
-
Save danstowell/717cb5ad82eb275b54d7 to your computer and use it in GitHub Desktop.
Generate Schroeder-phase complexes in Supercollider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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