Sound synthesizer using Manipulate

Sound synthesizers, both digital and analog, often include a parameter section for shaping the amplitude of a sound wave over time. This parameter section is commonly known as an "ADSR EG" (attack, decay sustain, release; envelope generator).

I included a parameter for shaping the curvature of three different lines within the piecewise function describing my envelope generator.

An abstract example of my parameter would look like this:

Linear function $y$

Piecewise function $ADSR\ EG(x)$

$y=(x-x_1)^{curvature}\frac{y_2-y_1}{x_2-x_1}+y_1$

$y\in ADSR\ EG(x)$

where $curvature$ it's the slope shaping parameter I'm referring to, so when $curvature=1$ is a straight line, when $curvature>1$ is a concave curve, when $0<curvature<1$ it's a convex curve.

I attached a file with the mathematical description of the envelope generating function I tried to prototype in Wolfram Mathematica. I also attached a beta version of the prototype as a CDF file for someone here to inspect. The free application Wolfram CDF Player needs to be installed to open the cdf file I attached and analyze the code.

The curvature parameter it's not working as expected in my Mathematica program; the lines with the curvature parameter included are not bending smoothly in place when I drag the corresponding slider and I can't spot the error in typing by myself. Can someone please analyze the attached files and tell me how to fix the problem in Mathematica? The curvature parameters have labels designated "Attack time slope," "Decay time slope" and "Release time slope." The rest of the sliders behave exactly in the manner I expected. Thank you in advance.

Wolfram Language code:

Manipulate[
Plot[
Piecewise[
{{0,
time <= n0*n5},
{(((time - n0*n5)^n8)*((n0 + n1)*n5 - 0)/((n0 + n1)*n5 - n0*n5) + 0)*(n6*Piecewise[{{10^(n11/20), -120 < n11 <= 12}, {0, n11 == -120}}])/((n0 + n1)*n5),
n0*n5 < time <= (n0 + n1)*n5},
{(((time - (n0 + n1)*n5)^n9)*((n0 + n1)*n5*n7 - (n0 + n1)*n5)/((n0 + n1 + n2)*n5 - (n0 + n1)*n5) + (n0 + n1)*n5)*(n6*Piecewise[{{10^(n11/20), -120 < n11 <= 12}, {0, n11 == -120}}])/((n0 + n1)*n5),
(n0 + n1)*n5 < time <= (n0 + n1 + n2)*n5},
{((time - (n0 + n1 + n2)*n5)*((n0 + n1)*n5*n7 - (n0 + n1)*n5*n7)/((n0 + n1 + n2 + n3)*n5 - (n0 + n1 + n2)*n5) + (n0 + n1)*n5*n7)*(n6*Piecewise[{{10^(n11/20), -120 < n11 <= 12}, {0, n11 == -120}}])/((n0 + n1)*n5),
(n0 + n1 + n2)*n5 < time <= (n0 + n1 + n2 + n3)*n5},
{(((time - (n0 + n1 + n2 + n3)*n5)^n10)*(0 - (n0 + n1)*n5*n7)/((n0 + n1 + n2 + n3 + n4)*n5 - (n0 + n1 + n2 + n3)*n5) + (n0 + n1)*n5*n7)*(n6*Piecewise[{{10^(n11/20), -120 < n11 <= 12}, {0, n11 == -120}}])/((n0 + n1)*n5),
(n0 + n1 + n2 + n3)*n5 < time <= (n0 + n1 + n2 + n3 + n4)*n5},
{0,
time > (n0 + n1 + n2 + n3 + n4)*n5}}
],
{time, 0, (n0 + n1 + n2 + n3 + n4)*n5},
AxesLabel -> {"Time", "Amplitude"}
],
{{n0, 500, "Delay time"}, 0, 5000, 1/1000},
{{n1, 500, "Attack time"}, 1/1000, 5000, 1/1000},
{{n2, 500, "Decay time"}, 1/1000, 5000, 1/1000},
{{n3, 500, "Sustain time"}, 1/1000, 5000, 1/1000},
{{n4, 500, "Release time"}, 1/1000, 5000, 1/1000},
{{n5, 1, "Envelope lenght"}, 1/100, 2, 1/100},
{{n6, 1, "Envelope depth"}, -1, 1, 1/100},
{{n7, 1/2, "Sustain amplitude"}, 0, 1, 1/100},
{{n8, 1, "Attack time slope"}, 1/100, 2, 1/100},
{{n9, 1, "Decay time slope"}, 1/100, 2, 1/100},
{{n10, 1, "Release time slope"}, 1/100, 2, 1/100},
{{n11, 0, "Volume"}, -120, 12, 1/10}
]


• Could you please include your code, or perhaps better, a minimal version of it, in the text of your question? Jul 23, 2016 at 5:00
• Yes. Hold on... Jul 23, 2016 at 5:15
• I neatly spaced the entire code to make it intelligible. Please take a second look. Jul 23, 2016 at 5:38
• A graphic representation of what I'm explaining, I'm trying to get the straight lines of the envelope to bend with a slider: zytrax.com/tech/audio/note-adsr.gif samplecraze.com/sites/samplecraze.com/files/images/adsr.png Jul 23, 2016 at 5:53

First let me observe that your coding style makes debugging difficult, I highly recommend breaking giant expressions into manageable pieces.

Second, in the code below I have used a different definition for the segments. Your version:

$y=(x-x_1)^{curvature}\frac{y_2-y_1}{x_2-x_1}+y_1$

does not give an amplitude of $y_2$ at $x=x_2$ if $curvature\neq1$. I don't know if that's intentional but it seemed wrong to me. Instead I have used:

$y=(\frac{x-x_1}{x_2-x_1})^{curvature}(y_2-y_1)+y_1$

Since each segment is defined the same way, I defined a function segment. This takes the start and end times and amplitudes, and the curvature parameter, and returns a {value, condition} suitable for Piecewise. I also pulled out the definition for the volume. Within the Manipulate I used p and s for the peak and sustain amplitudes, and also t1 to t5 to represent the segment boundaries. This helps to make the contents of the Piecewise expression more readable.

segment[t_, t1_, t2_, a1_, a2_, g_] :=
{a1 + (a2 - a1) ((t - t1)/(t2 - t1))^g, t1 < t <= t2}

vol[d_, v_] := d Piecewise[{{10^(v/20), -120 < v <= 12}, {0, v == -120}}]

Manipulate[
Module[{t1, t2, t3, t4, t5, p, s},
{t1, t2, t3, t4, t5} = n5 Accumulate[{n0, n1, n2, n3, n4}];
p = vol[n6, n11];
s = p n7;
Plot[Piecewise[{

{0, time <= t1},
segment[time, t1, t2, 0, p, n8],
segment[time, t2, t3, p, s, n9],
segment[time, t3, t4, s, s, 1],
segment[time, t4, t5, s, 0, n10],
{0, time > t5}

}], {time, 0, t5},
AxesLabel -> {"Time", "Amplitude"}]
],
{{n0, 500, "Delay time"}, 0, 5000, 1/1000},
{{n1, 500, "Attack time"}, 1/1000, 5000, 1/1000},
{{n2, 500, "Decay time"}, 1/1000, 5000, 1/1000},
{{n3, 500, "Sustain time"}, 1/1000, 5000, 1/1000},
{{n4, 500, "Release time"}, 1/1000, 5000, 1/1000},
{{n5, 1, "Envelope lenght"}, 1/100, 2, 1/100},
{{n6, 1, "Envelope depth"}, -1, 1, 1/100},
{{n7, 1/2, "Sustain amplitude"}, 0, 1, 1/100},
{{n8, 1, "Attack time slope"}, 1/100, 2, 1/100},
{{n9, 1, "Decay time slope"}, 1/100, 2, 1/100},
{{n10, 1, "Release time slope"}, 1/100, 2, 1/100},
{{n11, 0, "Volume"}, -120, 12, 1/10}]


• Hahaha, wow. Your segment expression reminds me of the previous version I made for this envelope generator. It worked. But I thought the expression could be optimized. My previous definition was: Linear function $f(x)$ $Domain(curvature)=\{curvature\in \mathbb R| 0 \le curvature < \infty\}$ $g(x)= \begin{cases} f(x), & \text{if$curvature=1$} \\ (y_2-y_1)\frac{curvature^{x-x_1}-1}{curvature^{x_2-x_1}-1}+y_1, & \text{if$curvature\neq1$} \end{cases}$ when $curvature=1$, curve is linear when $0<curvature<1$, curve is convex when $1<curvature$, curve is concave Jul 23, 2016 at 18:59
• Now imagine I multiplied each segment of the Piecewise envelope by the built in function "Sin[ ]"(as "Sin[time]*")...is there any possible way to wrap the modelled sound in "Play[ ]" and then listen to it? Jul 24, 2016 at 5:12
• @RichardSantiago, you could create the sound object using Play and assign it to a variable e.g. snd = Play[...], and in the controls list put Button["Play", EmitSound[snd]] Jul 24, 2016 at 10:48
• I did this: "Plot[Sin[Time]*Piecewise[..." Then "Plot[copy here]," "snd=Play[paste here, except for AxesLabel];", and I placed that snd decalaration after the variable declaration s = p n7. I made the "Play" button appear under the last slider as "Button["Play", EmitSound[snd]]," but still won't emit a sound when I click on it... :( Jul 24, 2016 at 17:54
• @RichardSantiago, did you remember that Play is in seconds not milliseconds? Jul 24, 2016 at 19:41

All credit goes to Simon Woods.

Simple mathematical modelling of a sound with graphical representation and playback. Sound synthesis done in plain Wolfram Language. Wu!

I suggest to leave "Envelope lenght" slider where is at. Don't take the "Frequency" values seriously; my impression is there's something there to be fixed yet.

segment[t_, t1_, t2_, a1_, a2_, g_] := {a1 + (a2 - a1) ((t - t1)/(t2 - t1))^g, t1 < t <= t2}
vol[d_, v_] := d Piecewise[{{10^(v/20), -120 < v <= 12}, {0, v == -120}}]
Manipulate[Module[{t1, t2, t3, t4, t5, p, s}, {t1, t2, t3, t4, t5} = n5 Accumulate[{n0, n1, n2, n3, n4}];
p = vol[n6, n11];
s = p n7;
snd = Play[Sin[n12*time]*Piecewise[{{0, time <= t1}, segment[time, t1, t2, 0, p, n8], segment[time, t2, t3, p, s, n9], segment[time, t3, t4, s, s, 1],segment[time, t4, t5, s, 0, n10], {0, time > t5}}], {time, 0, t5}];
Plot[Sin[n12*time]*Piecewise[{{0, time <= t1}, segment[time, t1, t2, 0, p, n8], segment[time, t2, t3, p, s, n9], segment[time, t3, t4, s, s, 1],segment[time, t4, t5, s, 0, n10], {0, time > t5}}],
{time, 0, t5}, AxesLabel -> {"Time", "Amplitude"}]],
{{n0, 0, "Delay time"},0, 5000, 1/1000},
{{n1, 500, "Attack time"}, 1/1000, 5000, 1/1000},
{{n2, 500, "Decay time"}, 1/1000, 5000, 1/1000},
{{n3, 500, "Sustain time"}, 1/1000, 5000, 1/1000},
{{n4, 500, "Release time"}, 1/1000, 5000, 1/1000},
{{n5, 1/1000, "Envelope lenght"}, 1/1000, 2, 1/100},
{{n6, 1, "Envelope depth"}, -1, 1, 1/100},
{{n7, 1/2, "Sustain amplitude"}, 0, 1, 1/100},
{{n8, 1, "Attack time curvature"}, 1/100, 10, 1/100},
{{n9, 1, "Decay time curvature"}, 1/100, 10, 1/100},
{{n10, 1, "Release time curvature"}, 1/100, 10, 1/100},
{{n11, 0, "Volume"}, -120, 12, 1/10},
{{n12, 2000, "Frequency"}, 20, 21000, 1},
Button["Play", EmitSound[snd]]]