// // SAOL Resonator-Based Physical Model Library // This file: Plucked string // // This license also covers the SASL file in this directory. // The MIDI file is public domain, thanks to http://www.mutopiaproject.org // // // Copyright (c) 1999-2006, Regents of the University of California // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // Neither the name of the University of California, Berkeley nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Original Author: John Wawrzynek // Maintainer: John Lazzaro, lazzaro@cs.berkeley.edu // // // If you're interested in extending this model, don't modify this // file directly; see the README in sfront/lib/reson/ instead. // // // Global block for scaling string prototype // global { srate 44100; krate 1050; // hand-tuned version of qscale = 0.0393432*exp(0.0615256*x) for q scaling table string_qscale(expseg, -1, 0, 0.5*0.0393432, 128, 0.5*0.0393432*exp(0.0615256*128)); // implements gscale = 0.04*(1/70.0337)*exp(0.0735272*rms_x) for g scaling table string_gscale(expseg, -1, 0, 0.04*(1/70.0337), 128, 0.04*(1/70.0337)*exp(0.0735272*128)); // vel table holds status for each note // // -1 -- no instr active for this note // 0 -- instr active, no new strikes // > 0 -- new note strike, value of MIDIvel table string_vel(step, 128 , 0, -1, 128); ksig string_poly; // number of active notes at once sequence(string_kbd, string_audio); } // // The resinit iopcode initializes the resonance model for the thing being // struck or plucked. // iopcode string_resinit(ivar a[10], ivar b[10], ivar g[10], ivar notenum) { ivar r[10], freq[10], q[10]; ivar j, scale, norm; imports exports table string_qscale; imports exports table string_gscale; // set f/q/g for prototype bar norm = tableread(string_qscale, int(notenum)); scale = cpsmidi(notenum)/220; freq[0] = 440*scale; q[0] = 300*norm; freq[1] = 880*scale; q[1] = 300*norm; freq[2] = 1320*scale; q[2] = 300*norm; freq[3] = 1760*scale; q[3] = 300*norm; freq[4] = 2200*scale; q[4] = 300*norm; freq[5] = 2640*scale; q[5] = 300*norm; freq[6] = 3080*scale; q[6] = 320*norm; freq[7] = 3520*scale; q[7] = 300*norm; freq[8] = 3960*scale; q[8] = 190*norm; freq[9] = 4400*scale; q[9] = 300*norm; norm = tableread(string_gscale, int(notenum)); g[0] = (freq[0] < s_rate/2) ? norm*0.7 : 0.0; g[1] = (freq[1] < s_rate/2) ? norm*0.8 : 0.0; g[2] = (freq[2] < s_rate/2) ? norm*0.6 : 0.0; g[3] = (freq[3] < s_rate/2) ? norm*0.7 : 0.0; g[4] = (freq[4] < s_rate/2) ? norm*0.7 : 0.0; g[5] = (freq[5] < s_rate/2) ? norm*0.8 : 0.0; g[6] = (freq[6] < s_rate/2) ? norm*0.95 : 0.0; g[7] = (freq[7] < s_rate/2) ? norm*0.76 : 0.0; g[8] = (freq[8] < s_rate/2) ? norm*0.87 : 0.0; g[9] = (freq[9] < s_rate/2) ? norm*0.76 : 0.0; // compute actual resonator coefficients j = 0; while ( j < 10) { r[j] = exp(-freq[j]/(s_rate*q[j])); a[j] = 2*r[j]*cos(2* 3.14159265358979323846 *(freq[j]/s_rate)); b[j] = - r[j]*r[j]; j = j + 1; } } // // The strikeinit iopcode initializes the pluck model. // iopcode string_strikeinit(ivar aa, ivar ab, ivar sg, ivar vw, ivar vwn, ivar notenum) { ivar ar, afreq; afreq = 2000; // attack resonator frequency // Compute resonator bank coefficients ar = exp(-2* 3.14159265358979323846 *(afreq/s_rate)); aa = 2*ar; ab = -ar*ar; vw = (1/127) ; // keyboard normalization curve sg = 0.004; // "signal gain" empirical constant (should not scale). vwn = 0.02; // velocity scaling for nm } // // // k-pass semantics for the pluck model // kopcode string_strikeupdate(ksig ky[1], ksig nm, ksig silent, ksig notenum, ivar vw, ivar vwn) { imports exports table string_vel; imports exports ksig string_poly; ksig exit, count; count = silent ? (count + 1) : max(count - 1, 0); if (((count > 5) && (itime > 0.25)) || exit) { if (!exit) { turnoff; exit = 1; tablewrite(string_vel, int(notenum), -1); string_poly = string_poly - 1; } ky[0] = 0; } else { if (tableread(string_vel, int(notenum)) > 0) { ky[0] = vw*tableread(string_vel, int(notenum)); nm = ky[0]*vwn; tablewrite(string_vel, int(notenum), 0); } else { ky[0] = 0; } } } // // Instr for creating audio output. // instr string_audio(notenum) { ivar a[10], b[10], g[10]; ivar aa, ab, sg, vw, vwn; ksig nm, ky[1], silent; asig out; asig y[10], y1[10], y2[10]; asig sy[10]; asig ay, ay1, ay2, dummy, x; // happens at i-rate string_resinit(a, b, g, notenum); string_strikeinit(aa, ab, sg, vw, vwn, notenum); // happens at k-rate silent = (rms(out) < 8e-4); string_strikeupdate(ky, nm, silent, notenum, vw, vwn); // happens at a-rate dummy = 0; // until optimizer improves ay = aa*ay1 + ab*ay2 + ky[dummy]; // attack resonator x = (arand(nm) + sg)*ay; y = a*y1 + b*y2 + x; // resonator bank ay2 = ay1; // update filter state ay1 = (abs(ay)>1e-30) ? ay : 0.0; ky[dummy] = 0; y2 = y1; y1 = y; sy = g*y; // gain adjust out = (sy[0]+sy[1]+sy[2]+sy[3]+sy[4]+sy[5]+sy[6]+sy[7]+sy[8]+sy[9]) ; // sum over sy[] output(out); } // // Instr for handling MIDI control input. Updates string_vel table. // instr string_kbd(pitch, velocity) preset 1 { imports exports table string_vel; imports exports ksig string_poly; ksig vval, kpitch; // happens at k-rate vval = velocity; kpitch = pitch; if (tableread(string_vel, int(kpitch)) == -1) { if (string_poly < 24) { tablewrite(string_vel, int(pitch), vval); instr string_audio(0, -1, pitch); string_poly = string_poly + 1; } } else { tablewrite(string_vel, int(pitch), vval); } turnoff; }