Skip to content

Instantly share code, notes, and snippets.

@danstowell
Created February 2, 2016 14:52
Show Gist options
  • 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