All simulations for this paper were carried out using Perry Cook's Synthesis ToolKit (STK) in C++ [2]. The method stringVelocityAtPosition(int position) below can be added to bowed.cpp in the STK to facilitate extracting the string state for display as shown in Fig. 6. (Animations of bowed-string motion using this added method were found to be especially valuable for obtaining insight into bowed string dynamics.)
MY_FLOAT BowedStr :: stringVelocityAtPosition(int p) /* p from 0 to nsamples-1 */ { int bdelrt = (int)bridgeDelay->delay()+1; /* bow-to-bridge-to-bow + p.l. delay */ int ndelrt = (int)neckDelay->delay()+1; /* bow-to-nut-to-bow + p.l. delay */ int bdel = bdelrt >> 1; /* number of spatial samples from bridge to bow */ int ndx = bdel - p; /* convert (0:N-1) position to delay (1:N) on left */ MY_FLOAT leftGoingAtP; MY_FLOAT rightGoingAtP; if (p < bdel) { /* "Now" is always where the InPoint points which is not yet written */ /* OutPoint points to "now - delay" */ /* Bridge is on the left, nut on the right */ /* "Now" is at the bow */ /* Position zero is at far left = half way along delay line */ leftGoingAtP = bridgeDelay->contentsAtNowMinus(ndx); int rndx = bdelrt-ndx+1; if (rndx < bdelrt) { /* last sample delay resides in lastOutput variable */ rightGoingAtP = -bridgeDelay->contentsAtNowMinus(rndx); } else { rightGoingAtP = -bridgeDelay->lastOut(); } } else { /* nut side */ ndx = p - bdel + 1; /* convert (0:N-1) position to delay (1:N) */ rightGoingAtP = neckDelay->contentsAtNowMinus(ndx); int lndx = ndelrt-ndx+1; if (lndx < ndelrt) { leftGoingAtP = -neckDelay->contentsAtNowMinus(lndx); } else { leftGoingAtP = -neckDelay->lastOut(); } } return rightGoingAtP + leftGoingAtP; }
Usage of the above method is illustrated in the following code fragment:
MY_FLOAT stringState[MAXPERIOD]; for (i=0;i<period/2;i++) stringState[i] = 0; for (i=0;i<samples;i++) { /* main sample loop */ ... if (i<20*period) { MY_FLOAT v = 0.0; long len = period/2; for (int j=0; j<len; j++) { v = vscale*vln->stringVelocityAtPosition(j); /* m/s */ stringState[j] += v*ONE_OVER_SRATE; /* m */ stringOut->tick(STRINGSCALING*stringState[j]); /* to soundfile */ } }
The following matlab function was used to generate highly helpful animations of the string state. Each string snapshot was written successively into one long sound file, and this routine was called with the sound data along with M set to the snapshot length in samples:
function out=datamovie(in,M,sleep,ax); %DATAMOVIE datamovie(in,M,sleep,ax); % Display sequence of data frames of length M. % If sleep>0, that many cycles are waited between plots. % If sleep == -1, RETURN is needed to advance to the next plot. % If ax is a quoted string, "axis(ax)" is called. clf; if (nargin<2), M=length(in); end if (nargin<3), sleep=0; end if (nargin<4), ax = [1 M min(in) 1.1*max(in)]; end skip = M; h=plot(in(1:M),'erasemode','background'); axis(ax); drawnow; if sleep == -1, disp '*** PAUSING *** RETURN to continue'; pause; end for i=1:(length(in)-M)/skip set(h,'ydata',in(skip*i+1:skip*i+M)); if sleep == -1, disp '*** PAUSING *** RETURN to continue'; pause; elseif sleep>0, for j=1:sleep, y = tan(j); end; end end
Usage of the datamovie function is illustrated by the matlab script below:
% seestr.m - matlab script for viewing bowed string waveshape evolution ilen = 50; % Number of spatial samples along string sleep = 0; % pause/speed control to datamovie name1 = 'string'; [strdata fs len header] = loadsig(name1); % for NeXT .snd files strdata = strdata/32768.0; datamovie(strdata,ilen,sleep);