1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
;; PLUCK.ALG is based on the Pluck.t instrument from M4C
;;
; to assist with debugging, here are some calculated parameters
; dumped from a run of M4C:
;
; Setup Pluck at t= 6.000
; DUR= 1.000, pitch= 8.060, amp= 10000, DB_drop= 60.0, rfrac=0.00
; freq 369.995
; freq = 369.995361
; Final = 1000.000000, t= 0.052715, y = 0.000000, lF = 6.907755
; tdecay = 13.430565, sT = 369.995361, rho = 0.982869, stretch = 0.500000
; N = 59, x = 0.095342, cons = 0.825914
; x2 0.412957 x3 0.912957 stretch 0.5 cons 0.825914 SR 22050
; complete Pluck setup
;;
(PLUCK-ALG
(NAME "pluck")
(ARGUMENTS ("rate_type" "sr") ("double" "hz") ("time_type" "t0")
("time_type" "d") ("double" "final_amp"))
(SUPPORT-FUNCTIONS "
#define MAXLENGTH 20000
long pluck_parameters(double hz, double sr, double final, double dur,
double *stretch, double *cons, double *rho)
{
double t = PI * (hz / sr);
double y = fabs(cos(t));
/* original m4c code used ratio of initial amp to final amp in dB
and then converted to a ratio, e.g. you specify 60 and the
parameter Final is 1000.0. This is counterintuitive to me (RBD)
because I would expect the value to be -60dB or 0.001. That is
what I implemented, so to get this back into correspondence
with the m4c algorithm, I take the NEGATIVE log to get lf,
whereas m4c takes the positive log:
*/
double lf = -log(final);
double tdecay = -lf / (hz * log(y));
double st;
long len;
double x;
if (hz <= sr / MAXLENGTH) {
xlfail(\"pluck hz is too low\");
} else if (hz >= sr / 3) {
xlfail(\"pluck hz is too high\");
}
/*
* if desired decay time is shorter than the natural decay time,
* then introduce a loss factor. Otherwise, stretch note out.
*/
st = hz * dur;
if (dur < tdecay) {
*rho = exp(-lf / st) / y;
*stretch = 0.5;
} else {
*rho = 1;
*stretch = 0.5 + sqrt(0.25 -
(1 - exp(2 * lf * (hz - sr) / (st * sr))) /
(2 - 2 * cos(2 * t)));
}
/* delay line length is */
len = (int) ((sr / hz) - *stretch - 0.001);
/* tuning constant is */
x = (sr / hz) - len - *stretch;
*cons = (1.0 - x) / (1.0 + x);
if (len <= 1) {
xlfail(\"internal error: pluck delay line length too short\");
}
return len;
}
static unsigned int rnext = 1;
int krand()
{
rnext = rnext * 1103515245 + 12345;
return (rnext >> 16) & 0x7fff;
}
void pluck_initialize(sample_type *shiftreg, sample_type *array,
long len, double cons)
{
sample_type suma = 0.0F;
long k;
sample_type avea;
array[1] = 0;
for (k = len; k > 0; k--, array--) {
/* note: the m4c code has a bug. It claims to filter
the initial values, but it really just sets the
values to +1 or -1. The following does the same
thing with much less code:
*/
*array = (krand() & 2) - 1;
suma += *array; /* compute sum for the average */
}
avea = suma / len;
/* zero the average */
for (k = 0; k <= len + 1; k++) shiftreg[k] -= avea;
shiftreg[len] = 0;
shiftreg[len + 1] = 0;
}")
(STATE ("double" "stretch" "0")
("double" "cons" "0")
("double" "loss" "0")
("long" "len" "pluck_parameters(hz, sr, final_amp, d,
&susp->stretch, &susp->cons,
&susp->loss)")
("double" "x2" "-susp->cons * (susp->stretch - 1)")
("double" "x3" "susp->cons * susp->stretch - susp->stretch + 1")
("sample_type *" "shiftreg"
;; I think susp->len + 2 is the correct value, but I use +4 to be safe
"(sample_type *) calloc (susp->len + 4, sizeof(sample_type))")
("sample_type *" "i1" "susp->shiftreg + susp->len + 1")
("sample_type *" "i2" "susp->shiftreg + susp->len")
("sample_type *" "i3" "susp->shiftreg + susp->len - 1")
("sample_type *" "i4" "susp->shiftreg + susp->len - 2")
("sample_type *" "endptr" "susp->shiftreg + susp->len + 2;
pluck_initialize(susp->shiftreg, susp->i3,
susp->len, susp->cons)"))
(CONSTANT "stretch" "cons" "loss" "len" "x2" "x3" "endptr")
(SAMPLE-RATE "sr")
(NOT-REGISTER shiftreg)
(TERMINATE (AFTER "d"))
(INNER-LOOP " sample_type sum = (sample_type)
((*i1++ * x2) + (*i2++ * x3) +
(*i3++ * stretch) - (*i4++ * cons));
/* wrap pointers around shift register if necessary */
if (i1 == endptr) i1 = susp->shiftreg;
if (i2 == endptr) i2 = susp->shiftreg;
if (i3 == endptr) i3 = susp->shiftreg;
if (i4 == endptr) i4 = susp->shiftreg;
/* store new value in shift register */
*i4 = (sample_type) (sum * loss);
/* deliver sample */
output = sum;
")
(FINALIZATION " free(susp->shiftreg);\n")
)
|