STRUM


STRUM generates plucked-string sounds based on a neat hack developed by Kevin Karplus and Alex Strong. The k-s plucked-string algorithm is a subtractive synthesis system featuring a burst of white noise, a recirculating delay line, a lowpass filter, an allpass filter, and some serious math involving things like the letter sigma which i won't talk about here (see Roads, 1997).

The basic idea is that a burst of noise is pushed through a delay line, which splits its output, sending one half as output and the rest of it back into itself after going through a lowpass and allpass filter setup. The result is a burst of rich sound that gradually loses its higher harmonics as it decays (as does, funnily enough, a plucked string). A number of simple additions to the instrument design have fine-tuned the algorithm so that you can get some pretty realistic (or decidedly weird) effects along the lines of amp-driven guitars or tremelo-picked mandolins.

STRUM, like METAFLUTE, has a few sub-instruments inside of it. The basic instrument, START(), is your generic dry plucked string synthesis. When placed after START() commands, BEND() adds string bending, and FRET() emulates, er, two-hand tapping (?). START1() and its friends, BEND1(), FRET1(), add amplifier distortion, and VSTART1() and VFRET1(), in turn, add vibrato to that.


Syntax:


The syntax for strum is a bit of a mess, but here goes:


regular plucked strings (sounding most like mandolin or guitar:

START(outskip, duration, pitch, funddecay, nyquistdecay, amplitude, squish, stereo-pan, deleteflag)

A couple of notes on some of these:
BEND(start, duration, pitch0, pitch1, glissfunc, funddecay, nyquistdecay, updatefreq, stereo_position) /* important note -- will only work after a START() command */

Now for the parameters with "issues" in BEND:

FRET(start, duration, pitch, funddecay, nyquistdecay, spread) /* the pitch field is the pitch that alternates with the one specified in the preceding START() command */

Similar duration constraints to those listed above apply to this as well. It's a bit simpler, though, as there are no gliss-curves to deal with.


guitars with feedback:

START1(outskip, duration, pitch, funddecay, nyquistdecay, distortiongain, feedbackgain, feedbackpitch, cleanlevel, distortionlevel, amplitude, squish, spread, deleteflag)

BEND1(start, duration, pitch0, pitch1, glissfunc, funddecay, nyquistdecay, distortiongain, feedbackgain, feedbackpitch, cleanlevel, distortionlevel, amplitude, updatefreq, spread)

FRET1(start, duration, pitch, funddecay, nyquistdecay, distortiongain, feedbackgain, feedbackpitch, cleanlevel, distortionlevel, amplitude, spread)


guitars with vibrato and feedback:

for VSTART1 and VFRET1 only:

makegen(2, 10, 1000, p0, p1...) /* waveform for the vibrato */
makegen(3, 24, 1000, t0, a0, t1, a1...) /* envelope for the vibrato, in time/amplitude pairs */

VSTART1(outskip, duration, pitch, funddecay, nyquistdecay, distortiongain, feedbackgain, feedbackpitch, cleanlevel, distortionlevel, amplitude, squish, lowvibratorange, hivibratorange, vibratodepth, randomseed, updatefreq, spread, deleteflag)

VFRET1(outskip, duration, pitch, funddecay, nyquistdecay, distortiongain, feedbackgain, feedbackpitch, cleanlevel, distortionlevel, amplitude, lowvibratorange, hivibratorange, vibratodepth, randomseed, updatefreq, spread)


in comment form:

/* START:
   p0 = start; p1 = dur; p2 = pitch (oct.pc); p3 = fundamental decay time
   p4 = nyquist decay time; p5 = amp, p6 = squish; p7 = stereo spread [optional]
   p8 = flag for deleting pluck arrays (used by FRET, BEND, etc.) [optional]
   assumes makegen 1 is the amplitude envelope

   BEND:
   p0 = start; p1 = dur; p2 = pitch0 (oct.pc); p3 = pitch1 (oct.pc);
   p4 = gliss function; p5 = fundamental decay time; p6 = nyquist decay time;
   p7 = update times/sec; p8 = stereo spread [optional]
   assumes makegen 1 is the amplitude envelope

   FRET:
   p0 = start; p1 = dur; p2 = pitch(oct.pc); p3 = fundamental decay time;
   p4 = nyquist decay time; p5 = stereo spread [optional]
   assumes makegen 1 is the amplitude envelope

   START1:
   p0 = start; p1 = dur; p2 = pitch (oct.pc); p3 = fundamental decay time
   p4 = nyquist decay time; p5 = distortion gain; p6 = feedback gain
   p7 = feedback pitch (oct.pc); p8 = clean signal level
   p9 = distortion signal level; p10 = amp; p11 = squish
   p12 = stereo spread [optional]
   p13 = flag for deleting pluck arrays (used by FRET1, BEND1, etc.) [optional]
   assumes makegen 1 is the amplitude envelope

   BEND1:
   p0 = start; p1 = dur; p2 = pitch0 (oct.pc); p3 = pitch1 (oct.pc)
   p4 = gliss function #; p5 = fundamental decay time
   p6 = nyquist decay time; p7 = distortion gain; p8 = feedback gain
   p9 = feedback pitch (oct.pc); p10 = clean signal level
   p11 = distortion signal level; p12 = amp; p13 = update gliss nsamples
   p14 = stereo spread [optional]
   assumes makegen 1 is the amplitude envelope

   FRET1:
   p0 = start; p1 = dur; p2 = pitch (oct.pc); p3 = fundamental decay time
   p4 = nyquist decay time; p5 = distortion gain; p6 = feedback gain
   p7 = feedback pitch (oct.pc); p8 = clean signal level
   p9 = distortion signal level; p10 = amp; p11 = stereo spread [optional]
   assumes makegen 1 is the amplitude envelope

   VSTART1:
   p0 = start; p1 = dur; p2 = pitch (oct.pc); p3 = fundamental decay time
   p4 = nyquist decay time; p5 = distortion gain; p6 = feedback gain
   p7 = feedback pitch (oct.pc); p8 = clean signal level
   p9 = distortion signal level; p10 = amp; p11 = squish
   p12 = low vibrato freq range; p13 = hi vibrato freq range
   p14 = vibrato freq depth (expressed in cps); p15 = random seed value
   p16 = pitch update (default 200/sec)
   p17 = stereo spread [optional]
   p18 = flag for deleting pluck arrays (used by FRET1, BEND1, etc.) [optional]
   assumes makegen 1 is the amplitude envelope
   assumes makegen 2 is the vibrato function and makegen 3 is the
   vibrato amplitude envelope

   VFRET1:
   p0 = start; p1 = dur; p2 = pitch (oct.pc); p3 = fundamental decay time
   p4 = nyquist decay time; p5 = distortion gain; p6 = feedback gain
   p7 = feedback pitch (oct.pc); p8 = clean signal level
   p9 = distortion signal level; p10 = amp; 
   p11 = low vibrato freq range; p12 = hi vibrato freq range
   p13 = vibrato freq depth (expressed in cps); p14 = random seed value
   p15 = pitch update (default 200/sec)
   p16 = stereo spread [optional]
   assumes makegen 1 is the amplitude envelope
   assumes makegen 2 is the vibrato function and makegen 3 is the
   vibrato amplitude envelope

*/

An example score:

rtsetparams(44100, 2)
load("STRUM")
makegen(1, 24, 1000, 0,1,1,1)
makegen(2, 2, 7, 7, 7.00, 7.02, 7.05, 7.07, 7.10, 8.00, 8.07)


srand(0.314)
for (st = 0; st < 15; st = st + 0.1) {
        pind = random() * 7
        pitch = sampfunc(2, pind)
        START(st, 1.0, pitch, 1.0, 0.1, 10000.0, 1, random())
}

Another score:

rtsetparams(44100, 2)
load("STRUM")
makegen(1, 24, 1000, 0,1,1,1)
makegen(2, 2, 7, 7, 7.00, 7.02, 7.05, 7.07, 7.10, 8.00, 8.07)
 
srand(0.314)
for (st = 0; st < 15; st = st + 0.2) {
        pind = random() * 7
        pitch = sampfunc(2, pind)
        stereo = random()
        START(st, 0.05, pitch, 1.0, 0.1, 10000, 1,  stereo)
        FRET(st+0.05, 0.05, pitch+0.07, 1.0, 0.1, stereo)
        FRET(st+0.1, 0.05, pitch+0.04, 1.0, 0.1, stereo)
        FRET(st+0.15, 0.05, pitch+0.02, 1.0, 0.1, stereo)
}       

A third score:

rtsetparams(44100, 2)
load("STRUM")
makegen(1, 24, 1000, 0,1,1,1)
START1(0, 4, 6.08, 1, 1, 10, 0.05, 7.00, 0, 1, 10000, 2)
makegen(2, 24, 1000, 0, 0, 1, 1, 2, 0)
BEND1(4, 4, 6.08, 7.00, 2, 1, 1, 10, 0.05, 7.00, 0, 1, 10000, 100)