1
2// elpelele.saol
3// "El Pelele, E. Granados", MIDI: R. Finley, Piano: E. Scheirer
4// Originally written by Eric Scheirer
5// Modified by John Lazzaro
6// song length: 8 minutes, 50.192 seconds (530.192 seconds)
7//
8
9global {
10  interp 0;
11  srate 44100;
12  krate 1050;
13  ksig rvb_on, distance;
14  outchannels 2;
15  table A0(sample, - 1, "samp_1.aif");
16  table C2(sample, - 1, "samp_3.aif");
17  table F2(sample, - 1, "samp_5.aif");
18  table Ds3(sample, - 1, "samp_7.aif");
19  table Cs4(sample, - 1, "samp_9.aif");
20  table Fs4(sample, - 1, "samp_11.aif");
21  table B4(sample, - 1, "samp_13.aif");
22  route (dry, piano);
23
24// change "large_room" to "small_room" or "schroeder"
25// for alternative reverbs
26
27  send (large_room; 0.650000, 1; dry);
28  route (wet, large_room);
29
30  send (mix;; dry, wet);
31  }
32
33//
34// instr piano
35//
36// sample player of global tables
37//
38
39instr piano (pitch, vel) preset 0 {
40  ivar cps, scale, t, l, b, cut;
41  ksig kamp, localtime, segment, first;
42  asig samp;
43  // selects sample based on midi numbers
44  table pitchmap(step, - 1, 0, 0, 28, 1, 38, 2, 46, 3, 55, 4, 64, 5, 69, 6, 200);
45  // bogus loop points -- samples are long enough w/o looping
46  table loop(data, - 1, 197584, 151848, 137124, 132915, 104285, 59648, 50773);
47  // base frequency for each table
48  table base(data, - 1, 21, 36, 41, 51, 61, 66, 71);
49  imports exports table A0, C2, F2, Ds3, Cs4, Fs4, B4;
50  // pno[] indexed by pitchmap values
51  tablemap pno (A0, C2, F2, Ds3, Cs4, Fs4, B4);
52
53  if (! first) {
54    segment = 1;
55    first = 1;
56    }
57
58  // i-rate -- starting parameters for note
59
60  pitch = pitch - 12;
61  t = tableread (pitchmap, pitch);
62  l = tableread (loop, t);
63  b = cpsmidi (tableread (base, t));
64  cps = cpsmidi (pitch);
65  scale = (vel / 128) * (vel / 128) / 2;
66
67  // k-rate -- note envelope
68
69  localtime = localtime + 1 / k_rate;
70  if (segment == 1) {
71    if (localtime < 0.010000) {
72      kamp = kline (0, 0.010000, scale);
73      }
74    else {
75      segment = 2;
76      }
77    }
78  if (segment == 2) {
79    kamp = scale;
80    if (localtime > 5.000000) {
81      segment = 4;
82      turnoff;
83      }
84    }
85  if (released && segment < 3) {
86    if (MIDIctrl[64] > 15) {
87      segment = 3;
88      extend (5.000000 - localtime);
89      }
90    else {
91      segment = 4;
92      localtime = 0;
93      extend (0.100000);
94      }
95    }
96  if (segment == 3) {
97    if (MIDIctrl[64] <= 15) {
98      segment = 4;
99      localtime = 0;
100      }
101    else {
102      kamp = scale;
103      if (localtime >= 5.000000) {
104        turnoff;
105        }
106      }
107    }
108  if (segment == 4) {
109    kamp = var_kline(scale, 0.100000, 0);
110    if (localtime >= 0.100000) {
111      turnoff;
112      }
113    }
114
115  // a-rate
116
117  samp = kamp *  loscil(pno [ t ], cps, b, l);
118  output (samp * (1 - (pitch / 100)), samp * pitch / 100);
119
120  }
121
122//
123// opcode var_kline -- helper routine for piano
124//
125
126kopcode var_kline(ksig st, ksig t, ksig e) {
127  ksig ltime, val;
128
129  val = ltime / t * (e - st) + st;
130  ltime = ltime + 1 / k_rate;
131  return (val);
132  }
133
134//
135// instr large_room
136//
137// used in default settings
138//
139
140instr large_room (revgain, distance)  {
141  asig first;
142  ivar f, lpf_pos, lpf_cut, d2;
143  asig out[2];
144  table sections (data, - 1, 1, 10, 43, 44, 164, 165, 242);
145  table gains(data, - 1, 0.300000, 0.300000, 0.500000, 0.250000, 0.500000, 0.250000, 0.250000);
146  table lengths (data, - 1, 8, 12, 87, 62, 120, 76, 30);
147  table outs(data, - 1, 26, 161, 290);
148  table outg(data, - 1, 0.340000, 0.140000, 0.140000);
149  ksig num_sec, num_out;
150
151
152// i-rate
153
154  lpf_pos = 295;
155  lpf_cut= 2600;
156  d2 = distance * distance;
157
158// k-rate
159
160  num_sec= ftlen (sections);
161  num_out = ftlen (outs);
162
163// a-rate
164
165  out = gen_allpass(sections, num_sec, gains, lengths, outs, num_out, outg,
166                    lpf_pos, lpf_cut, 1 / 1000, revgain);
167
168  output ((input[0]/ d2 + out[0]) * distance, (input[1]/ d2 + out[1]) * distance);
169  }
170
171//
172// gen_allpass -- where *_room reverb gets computed
173//
174// uses fracdelay as a reverberation engine
175//
176
177aopcode gen_allpass(table sections, ksig num_sec, table gains,
178          table lengths, table outs, ksig num_out,
179         table outg, ivar lpf_pos, ivar lpf_cut, ivar f, ivar revgain) {
180
181  oparray fracdelay[1];
182  asig first, outl, outr, p1, p2, g, tap1, tap2, i, fb, sum;
183
184  // initializes fracdelat as a 300ms tapped line
185
186  if (! first && input[0]) {
187    fracdelay[0](1, 0.300000);
188    first = 1;
189    }
190
191  if (first) {
192
193    // recirculate delay taps through line
194
195    i = 0;
196    while (i < num_sec) {
197      p1 = tableread (sections, i) * f;
198      p2 = floor (p1 + tableread (lengths, i) * f);
199      g = tableread (gains, i);
200      tap1 = fracdelay[0](2, p1);        // read tap
201      fracdelay[0](4, p2, tap1 * - g);   // add back in
202      tap2 = fracdelay[0](2, p2);        // read tap
203      fracdelay[0](4, p1, tap2 * g);     // add back in
204      i = i + 1;
205      }
206
207   // compute new output
208
209    outl = 0;
210    outr = 0;
211    i = 0;
212    while (i < num_out) {
213      p1 = tableread (outs, i) * f;
214      g = tableread (outg, i);
215      outl = outl + fracdelay[0](2, p1) * g;
216      outr = outr + fracdelay[0](2, p1 + pow (- 1, i) * f) * g;
217      i = i + 1;
218      }
219    p1 = lpf_pos * f;
220    tap1 = fracdelay[0](2, p1);
221    fb = lopass (tap1, lpf_cut) * revgain;
222    i = 0;
223
224    // compute new input into line
225
226    sum = 0;
227    while (i < 2) {
228      sum = sum + input [ i ];
229      i = i + 1;
230      }
231
232    // insert new input, then shift
233
234    fracdelay[0](3, 0, sum + fb);
235    fracdelay[0](5);
236    return (outl, outr);
237    }
238  else {
239    return (0, 0);
240    }
241  }
242
243//
244// instr mix
245//
246// mixes reverb and direct sound equally
247//
248
249instr mix () {
250  asig out[2];
251
252  out[0] = input[0]*1 + input[1]*0 + input[2]*1 + input[3]*0;
253  out[1] = input[0]*0 + input[1]*1 + input[2]*0 + input[3]*1;
254  output(out);
255  }
256
257//
258// instr small_room
259//
260// alternative reverb, uses gen_allpass as engine
261//
262
263instr small_room (revgain) {
264  asig first;
265  ivar f, lpf_pos, lpf_cut, d2;
266  asig out[2];
267  table sections (data, - 1, 24, 25, 48, 61, 62);
268  table gains (data, - 1, 0.300000, 0.400000, 0.600000,
269                                    0.100000, 0.400000);
270  table lengths (data, - 1, 35, 22, 8.300000, 66, 30);
271  table outs (data, - 1, 60, 129);
272  table outg (data, - 1, 0.500000, 0.500000);
273  ksig num_sec, num_out;
274
275// i-rate
276
277  lpf_pos = 128;
278  lpf_cut = 4200;
279
280  num_sec= ftlen (sections);
281  num_out = ftlen (outs);
282
283// k-rate
284
285  out = gen_allpass(sections, num_sec, gains, lengths, outs, num_out,
286                    outg, lpf_pos, lpf_cut, 1 / 1000, revgain);
287  output (out);
288  }
289
290//
291// instr schroeder
292//
293// alternative reverb, uses comb and allpass sections
294//
295
296instr schroeder (rt) {
297  ksig first, ki, d2;
298  asig in, ap1, ap2;
299  asig c[4];
300  asig outL, outR;
301  ivar t[4], revgain[4], i;
302  table timeconst (data, 5, 0.030000, 0.034300, 0.039300,
303	                    0.045000, 0.000000);
304  imports exports ksig rvb_on, distance;
305  oparray comb [4];
306
307  in = input[0]+ input[1];
308  if (first == 0) {
309    rvb_on = 1;
310    first = 1;
311    distance = 0.600000;
312    }
313  d2 = distance * distance;
314  i = 0;
315  while (i < 4) {
316    t [ i ] = tableread (timeconst, i);
317    revgain [ i ] = combgain (t [ i ], rt);
318    i = i + 1;
319    }
320  if (rvb_on) {
321    ap1 = allpass (in / distance, 0.001700, 0.700000);
322    ap2 = allpass (ap1, 0.005000, 0.700000);
323    c[0]= comb (ap2, t[0], revgain[0]);
324    c[1]= comb (ap2, t[1], revgain[1]);
325    c[2]= comb (ap2, t[2], revgain[2]);
326    c[3]= comb (ap2, t[3], revgain[3]);
327    outL = (c[0]+ c[1]+ c[2]+ c[3]) / 4;
328    outR = (c[0]- c[1]+ c[2]- c[3]) / 4;
329    output ((input[0]/ d2 + outL) * distance * 0.750000,
330	    (input[1]/ d2 + outR) * distance * 0.750000);
331    }
332  else {
333    output (input);
334    }
335  }
336
337//
338// opcode combgain
339//
340// helper routine for schroeder
341//
342
343opcode combgain (xsig t, xsig rt) {
344  xsig temp;
345
346  temp = exp (log (10) * - 3 * t / rt);
347  return (temp);
348  }
349