aboutsummaryrefslogtreecommitdiff
path: root/chuck/patches/0010-LiSa-fixes-new-features-and-clean-up.patch
diff options
context:
space:
mode:
Diffstat (limited to 'chuck/patches/0010-LiSa-fixes-new-features-and-clean-up.patch')
-rw-r--r--chuck/patches/0010-LiSa-fixes-new-features-and-clean-up.patch1073
1 files changed, 1073 insertions, 0 deletions
diff --git a/chuck/patches/0010-LiSa-fixes-new-features-and-clean-up.patch b/chuck/patches/0010-LiSa-fixes-new-features-and-clean-up.patch
new file mode 100644
index 0000000..3adc120
--- /dev/null
+++ b/chuck/patches/0010-LiSa-fixes-new-features-and-clean-up.patch
@@ -0,0 +1,1073 @@
+From d77b79446964fca0149bbaadc0e8354d92d6fafa Mon Sep 17 00:00:00 2001
+From: Robin Haberkorn <robin.haberkorn@googlemail.com>
+Date: Thu, 13 Sep 2012 01:47:37 +0200
+Subject: [PATCH 10/12] LiSa fixes, new features and clean up
+
+ * always check whether buffer is allocated (size set) if necessary fixing easy to provoke segfaults
+ * when configuring check values for validity (e.g. lisa.track())
+ * for every configuration parameter accepting a duration: support negative durations (translate to "from the end of the buffer") and durations longer than the buffer (translate to "from the beginning of the buffer")
+ * might be arbitrarily large
+ * tracking mode 1/2: accept input data < 0, or longer than buffer
+ * tracking mode 1: fixed playing whithout loop-playing (and arbitrarily set loop points)
+ * tracking mode 2: use all voices
+ * support loop start > loop end: loop over end of buffer/beginning of buffer
+etc.
+---
+ ugen_xxx.cpp | 668 ++++++++++++++++++++++++++++++++++------------------------
+ 1 files changed, 391 insertions(+), 277 deletions(-)
+
+diff --git a/src/ugen_xxx.cpp b/src/ugen_xxx.cpp
+index dfb1bca..17e39d6 100644
+--- a/src/ugen_xxx.cpp
++++ b/src/ugen_xxx.cpp
+@@ -51,6 +51,7 @@
+ #include "chuck_vm.h"
+ #include "chuck_globals.h"
+ #include "chuck_instr.h"
++#include "chuck_errmsg.h"
+
+ #include <fstream>
+ using namespace std;
+@@ -3556,6 +3557,7 @@ struct LiSaMulti_data
+ t_CKINT mdata_len;
+ t_CKINT maxvoices;
+ t_CKINT loop_start[LiSa_MAXVOICES], loop_end[LiSa_MAXVOICES], loop_end_rec;
++ t_CKINT loop_len[LiSa_MAXVOICES]; // caching of frequently used values
+ t_CKINT rindex; // record and play indices
+ t_CKBOOL record, looprec, loopplay[LiSa_MAXVOICES], reset, append, play[LiSa_MAXVOICES], bi[LiSa_MAXVOICES];
+ t_CKFLOAT coeff; // feedback coeff
+@@ -3577,51 +3579,118 @@ struct LiSaMulti_data
+ SAFE_DELETE( mdata );
+ }
+
++ inline t_CKDOUBLE normalize_index(t_CKDOUBLE index)
++ {
++ while( index < 0 ) index += mdata_len;
++ while( index >= mdata_len ) index -= mdata_len;
++
++ return index;
++ }
++
++ inline t_CKINT get_safe_voice(t_CKINT voice)
++ {
++ if (voice < 0 || voice >= maxvoices)
++ {
++ EM_error2( 0, "LiSa: Requesting invalid voice number %d (MAXVOICES=%d)",
++ voice, LiSa_MAXVOICES);
++ return 0;
++ }
++
++ return voice;
++ }
++
++ void update_cache(t_CKINT which = 0)
++ {
++ if( loop_start[which] <= loop_end[which] )
++ loop_len[which] = loop_end[which] - loop_start[which];
++ else /* loop_start[which] > loop_end[which] */
++ loop_len[which] = mdata_len - loop_start[which] + loop_end[which];
++
++ loop_len[which]++;
++ }
++
++ inline t_CKDUR set_loop_start(t_CKDUR v, t_CKINT which = 0)
++ {
++ if( !mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to set loop start on uninitialized buffer; ignoring" );
++ return 0.;
++ }
++
++ loop_start[which] = (t_CKINT)normalize_index( (t_CKDOUBLE)v );
++ update_cache( which );
++
++ return (t_CKDUR)loop_start[which];
++ }
++ inline t_CKDUR set_loop_end(t_CKDUR v, t_CKINT which = 0)
++ {
++ if( !mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to set loop end on uninitialized buffer; ignoring" );
++ return 0.;
++ }
++
++ loop_end[which] = (t_CKINT)normalize_index( (t_CKDOUBLE)v );
++ update_cache( which );
++
++ return (t_CKDUR)loop_end[which];
++ }
++
+ // allocate memory, length in samples
+- inline int buffer_alloc(t_CKINT length)
++ inline t_CKDUR buffer_alloc(t_CKDUR length)
+ {
++ if( (t_CKINT)length > LiSa_MAXBUFSIZE )
++ {
++ EM_error2( 0, "LiSa: Buffer size request too large, resizing to %d",
++ LiSa_MAXBUFSIZE );
++ length = LiSa_MAXBUFSIZE;
++ }
++
+ SAFE_DELETE( mdata );
++ mdata_len = 0;
+
+- mdata = new SAMPLE[length + 1]; //extra sample for safety....
+- if(!mdata) {
+- fprintf(stderr, "LiSaBasic: unable to allocate memory!\n");
+- return false;
++ mdata = new SAMPLE[(t_CKINT)length];
++ if( !mdata )
++ {
++ EM_error2( 0, "LiSa: Unable to allocate memory!");
++ return 0.;
+ }
++
++ mdata_len = (t_CKINT)length;
+ clear_buf();
+
+- mdata_len = length;
+ maxvoices = 10; // default; user can set
+ rec_ramplen = 0.;
+ rec_ramplen_inv = 1.;
+-
++
+ track = 0;
+-
+- for (t_CKINT i=0; i < LiSa_MAXVOICES; i++) {
++
++ for( t_CKINT i = 0; i < LiSa_MAXVOICES; i++ )
++ {
+ loop_start[i] = 0;
+- //loop_end[i] = length - 1; //no idea why i had this
+- loop_end[i] = length;
+- loop_end_rec = length;
+-
++ loop_end[i] = mdata_len - 1;
++ loop_end_rec = mdata_len - 1;
++
+ pindex[i] = rindex = 0;
+ play[i] = record = bi[i] = false;
+ looprec = loopplay[i] = true;
+ coeff = 0.;
+ p_inc[i] = 1.;
+- voiceGain[i] = 1.;
+- voicePan[i] = 0.5;
+-
++ voiceGain[i] = 1.;
++ voicePan[i] = 0.5;
++
+ // ramp stuff
+ rampup[i] = rampdown[i] = false;
+ rampup_len[i] = rampdown_len[i] = 0.;
+ rampup_len_inv[i] = rampdown_len_inv[i] = 1.;
+ rampctr[i] = 0.;
+-
+- for(t_CKINT j=0; j<LiSa_channels; j++) {
+- channelGain[i][j] = 1.;
+- }
++
++ for( t_CKINT j = 0; j < LiSa_channels; j++ ) channelGain[i][j] = 1.;
++
++ update_cache( i );
+ }
+-
+- return true;
++
++ return (t_CKDUR)mdata_len;
+ }
+
+ // dump a sample into the buffer; retain existing sample, scaled by "coeff"
+@@ -3658,98 +3727,126 @@ struct LiSaMulti_data
+ rindex++;
+ }
+ }
+-
++
++ inline bool isInLoop(t_CKDOUBLE where, t_CKINT which = 0)
++ {
++ if( loop_start[which] <= loop_end[which] )
++ return where >= loop_start[which] && where <= loop_end[which];
++ else
++ return where >= loop_start[which] || where <= loop_end[which];
++ }
++
+ // grab a sample from the buffer, with linear interpolation (add prc's SINC interp later)
+ // increment play index
+- // which specifies voice number
+- inline SAMPLE getNextSamp(t_CKINT which)
++ // which specifies voice number
++ inline SAMPLE getNextSamp(t_CKINT which = 0)
+ {
+ // constrain
+- if(loopplay[which]) {
+- if(bi[which]) { // change direction if bidirectional mode
+- if(pindex[which] >= loop_end[which] || pindex[which] < loop_start[which]) { //should be >= ?
+- pindex[which] -= p_inc[which];
+- p_inc[which] = -p_inc[which];
+- }
++ if( loopplay[which] )
++ {
++ bool wasOutsideLoop = false;
++
++ if ( loop_start[which] == loop_end[which] )
++ {
++ pindex[which] = loop_start[which];
++ }
++ else if ( loop_start[which] < loop_end[which] )
++ {
++ wasOutsideLoop = pindex[which] < loop_start[which] ||
++ pindex[which] > loop_end[which];
++
++ while( pindex[which] > loop_end[which] )
++ pindex[which] -= loop_len[which];
++ while( pindex[which] < loop_start[which] )
++ pindex[which] += loop_len[which];
+ }
+- if( loop_start[which] == loop_end[which] ) pindex[which] = loop_start[which]; //catch this condition to avoid infinite while loops
+- else {
+- while(pindex[which] >= loop_end[which]) pindex[which] = loop_start[which] + (pindex[which] - loop_end[which]); //again, >=?
+- while(pindex[which] < loop_start[which]) pindex[which] = loop_end[which] - (loop_start[which] - pindex[which]);
+- }
+-
+- } else if(pindex[which] >= mdata_len || pindex[which] < 0) { //should be >=, no?
+- play[which] = 0;
++ else /* loop_start[which] > loop_end[which] */
++ {
++ pindex[which] = normalize_index( pindex[which] );
++
++ wasOutsideLoop = pindex[which] < loop_start[which] &&
++ pindex[which] > loop_end[which];
++
++ if( wasOutsideLoop )
++ {
++ if( p_inc[which] > 0 )
++ {
++ /* after loop end */
++ while( pindex[which] > 0 )
++ pindex[which] -= loop_len[which];
++ pindex[which] += mdata_len;
++ }
++ else
++ {
++ /* before loop start */
++ while( pindex[which] < mdata_len )
++ pindex[which] += loop_len[which];
++ pindex[which] -= mdata_len;
++ }
++ }
++ }
++
++ // change direction if bidirectional mode
++ if( bi[which] && wasOutsideLoop ) p_inc[which] *= -1;
++ }
++ /* !loopplay[which] */
++ else if( pindex[which] >= mdata_len || pindex[which] < 0 )
++ {
++ play[which] = false;
+ //fprintf(stderr, "turning voice %d off!\n", which);
+- return (SAMPLE) 0.;
++ return SILENCE;
+ }
+-
++
+ // interp
+- t_CKINT whereTrunc = (t_CKINT) pindex[which];
++ t_CKINT whereTrunc = (t_CKINT)pindex[which];
+ t_CKDOUBLE whereFrac = pindex[which] - (t_CKDOUBLE)whereTrunc;
+ t_CKINT whereNext = whereTrunc + 1;
+-
+- if (loopplay[which]) {
+- if((whereNext) >= loop_end[which]) {
+- whereNext = loop_start[which];
+- }
+- if((whereTrunc) >= loop_end[which]) {
+- whereTrunc = loop_start[which];
+- }
+- } else {
+- if((whereTrunc) >= mdata_len) {
+- whereTrunc = mdata_len - 1; //should correct this, in case we've overshot by more than 1 sample
+- whereNext = 0;
+- }
+- if((whereNext) >= mdata_len) {
+- whereNext = 0;
+- }
+- }
+-
++
++ if( whereNext == mdata_len ) whereNext = 0;
++
++ if( loopplay[which] && !isInLoop( whereNext, which ) )
++ whereNext = loop_start[which];
++
+ pindex[which] += p_inc[which];
+-
++
+ t_CKDOUBLE outsample;
+- outsample = (t_CKDOUBLE)mdata[whereTrunc] + (t_CKDOUBLE)(mdata[whereNext] - mdata[whereTrunc]) * whereFrac;
+-
++ outsample = (t_CKDOUBLE)mdata[whereTrunc] +
++ (t_CKDOUBLE)(mdata[whereNext] - mdata[whereTrunc]) * whereFrac;
++
+ // ramp stuff
+- if(rampup[which]) {
++ if( rampup[which] )
++ {
+ outsample *= rampctr[which]++ * rampup_len_inv[which]; //remove divide
+- if(rampctr[which] >= rampup_len[which]) rampup[which] = false;
++ rampup[which] = rampctr[which] < rampup_len[which];
+ }
+- else if(rampdown[which]) {
++ else if( rampdown[which] )
++ {
+ outsample *= (rampdown_len[which] - rampctr[which]++) * rampdown_len_inv[which];
+- if(rampctr[which] >= rampdown_len[which]) {
+- rampdown[which] = false;
+- play[which] = false;
+- }
++ play[which] = rampdown[which] = rampctr[which] < rampdown_len[which];
+ }
+-
+- outsample *= voiceGain[which];
+-
+- return (SAMPLE)outsample;
++
++ return (SAMPLE)(outsample * voiceGain[which]);
+ }
+
+ // grab a sample from the buffer, with linear interpolation (add prc's SINC interp later)
+ // given a position within the buffer
+- inline SAMPLE getSamp(t_CKDOUBLE where, t_CKINT which)
++ inline SAMPLE getSamp(t_CKDOUBLE where, t_CKINT which = 0)
+ {
+- // constrain
+- if(where > loop_end[which]) where = loop_end[which];
+- if(where < loop_start[which]) where = loop_start[which];
+-
+ // interp
+- t_CKINT whereTrunc = (t_CKINT) where;
++ t_CKINT whereTrunc = (t_CKINT)where;
+ t_CKDOUBLE whereFrac = where - (t_CKDOUBLE)whereTrunc;
+ t_CKINT whereNext = whereTrunc + 1;
+-
+- if((whereNext) == loop_end[which]) whereNext = loop_start[which];
++
++ if( whereNext == mdata_len ) whereNext = 0;
++
++ if( loopplay[which] && !isInLoop( whereNext, which ) )
++ whereNext = loop_start[which];
+
+ t_CKDOUBLE outsample;
+- outsample = (t_CKDOUBLE)mdata[whereTrunc] + (t_CKDOUBLE)(mdata[whereNext] - mdata[whereTrunc]) * whereFrac;
+- outsample *= voiceGain[which];
+-
+- //add voiceGain ctl here; return (SAMPLE)vgain[which]*outsample;
+- return (SAMPLE)outsample;
++ outsample = (t_CKDOUBLE)mdata[whereTrunc] +
++ (t_CKDOUBLE)(mdata[whereNext] - mdata[whereTrunc]) * whereFrac;
++
++ return (SAMPLE)(outsample * voiceGain[which]);
+ }
+
+ // ramp stuff
+@@ -3792,6 +3889,11 @@ struct LiSaMulti_data
+ //fprintf ( stderr, "rec_ramplen = %f, inv = %f \n", rec_ramplen, rec_ramplen_inv );
+ }
+
++ inline void mix_voice(SAMPLE sample, t_CKINT which, SAMPLE *out)
++ {
++ for( t_CKINT i = 0; i < LiSa_channels; i++ )
++ out[i] += sample * channelGain[which][i];
++ }
+
+ //for simple stereo panning of a particular voice, and...
+ // l.channelGain(voice, channel, gain)
+@@ -3805,44 +3907,39 @@ struct LiSaMulti_data
+ case 0:
+ recordSamp( in );
+ for( t_CKINT i = 0; i < maxvoices; i++ )
+- {
+- if( play[i] )
+- {
+- SAMPLE tempsample = getNextSamp( i );
+- for( t_CKINT j = 0; j < LiSa_channels; j++ )
+- out[j] += tempsample * channelGain[i][j]; //channelGain should return gain for voice i in channel j
+- }
+- }
++ if( play[i] ) mix_voice( getNextSamp( i ), i, out );
+ break;
+
+ case 1:
+- if( in < 0. ) in = -in;
++ case 2:
+ for( t_CKINT i = 0; i < maxvoices; i++ )
+ {
+ if( play[i] )
+ {
+- SAMPLE tempsample = getSamp( (t_CKDOUBLE)in * (loop_end[i] - loop_start[i]) + loop_start[i], i );
+- for( t_CKINT j = 0; j < LiSa_channels; j++ )
+- out[j] += tempsample * channelGain[i][j];
++ t_CKDOUBLE where;
++
++ if( track == 2 )
++ where = (t_CKDOUBLE)in;
++ else if( loopplay[i] )
++ where = (t_CKDOUBLE)in*loop_len[i] + loop_start[i];
++ else
++ where = (t_CKDOUBLE)in*mdata_len;
++
++ mix_voice( getSamp( normalize_index( where ), i ), i, out );
+ }
+ }
+ break;
+
+- case 2:
+- if( in < 0. ) in = -in; //only use voice 0 when tracking with durs.
+- if( play[0] )
+- {
+- SAMPLE tempsample = getSamp( (t_CKDOUBLE)in, 0 );
+- for( t_CKINT j = 0; j < LiSa_channels; j++ )
+- out[j] = tempsample * channelGain[0][j];
+- }
++ default:
++ /* shouldn't happen */
+ break;
+ }
+ }
+
+ inline void clear_buf()
+ {
+- memset( mdata, 0, mdata_len*sizeof(SAMPLE) );
++ if( mdata )
++ memset( mdata, 0, mdata_len*sizeof(SAMPLE) );
+ }
+
+ inline t_CKINT get_free_voice()
+@@ -3856,40 +3953,59 @@ struct LiSaMulti_data
+ }
+
+ //stick sample in record buffer
+- inline void pokeSample( SAMPLE insample, t_CKINT index ) {
+-
+- if ( index >= mdata_len || index < 0 ) {
+- index = 0;
+- fprintf(stderr, "LiSa: trying to put sample out of buffer range; ignoring");
+- } else mdata[index] = insample;
+-
+- }
++ inline void pokeSample( SAMPLE insample, t_CKINT index )
++ {
++ mdata[index] = insample;
++ }
+
+ //grab sample directly from record buffer, with linear interpolation
+- inline SAMPLE grabSample ( t_CKDOUBLE where ) {
+-
+- if ( where > mdata_len || where < 0 ) {
+- where = 0;
+- fprintf(stderr, "LiSa: trying to grab sample out of buffer range; ignoring");
+- return 0.;
+- } else {
+-
+- // interp
+- t_CKINT whereTrunc = (t_CKINT) where;
+- t_CKDOUBLE whereFrac = where - (t_CKDOUBLE)whereTrunc;
+- t_CKINT whereNext = whereTrunc + 1;
+-
+- if((whereNext) == mdata_len) whereNext = 0;
++ inline SAMPLE grabSample( t_CKDOUBLE where )
++ {
++ // interp
++ t_CKINT whereTrunc = (t_CKINT) where;
++ t_CKDOUBLE whereFrac = where - (t_CKDOUBLE)whereTrunc;
++ t_CKINT whereNext = whereTrunc + 1;
+
+- t_CKDOUBLE outsample;
+- outsample = (t_CKDOUBLE)mdata[whereTrunc] + (t_CKDOUBLE)(mdata[whereNext] - mdata[whereTrunc]) * whereFrac;
+-
+- //add voiceGain ctl here; return (SAMPLE)vgain[which]*outsample;
+- return (SAMPLE)outsample;
+-
+- }
+- }
+-
++ if( whereNext == mdata_len ) whereNext = 0;
++
++ t_CKDOUBLE outsample;
++ outsample = (t_CKDOUBLE)mdata[whereTrunc] + (t_CKDOUBLE)(mdata[whereNext] - mdata[whereTrunc]) * whereFrac;
++
++ return (SAMPLE)outsample;
++ }
++
++ t_CKFLOAT set_voice_pan(t_CKFLOAT pan, t_CKINT which = 0)
++ {
++ voicePan[which] = pan;
++
++ for( t_CKINT i = 0; i < LiSa_channels; i++ ) channelGain[which][i] = 0.;
++
++ for( t_CKINT i = 0; i < LiSa_channels; i++ )
++ {
++ t_CKINT panTrunc = (t_CKINT)voicePan[which];
++ //fprintf(stderr, "panTrunc = %d, panFloat = %f, i = %d\n", panTrunc, voicePan[which], i);
++ if( i == panTrunc )
++ {
++ channelGain[which][i] = 1. - ( voicePan[which] - (t_CKFLOAT)i );
++ if( i == LiSa_channels - 1 )
++ {
++ channelGain[which][0] = 1. - channelGain[which][i];
++ channelGain[which][0] = sqrt( channelGain[which][0] );
++ }
++ else
++ {
++ channelGain[which][i+1] = 1. - channelGain[which][i];
++ channelGain[which][i+1] = sqrt( channelGain[which][i+1] );
++ }
++
++ channelGain[which][i] = sqrt( channelGain[which][i] );
++ }
++
++ //fprintf(stderr, "gain for channel %d and voice %d = %f\n", i, which, channelGain[which][i]);
++ }
++
++ return pan;
++ }
+ };
+
+
+@@ -3967,14 +4083,8 @@ CK_DLL_TICKF( LiSaMulti_tickf )
+ CK_DLL_CTRL( LiSaMulti_size )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKDUR buflen = GET_NEXT_DUR(ARGS);
+- if (buflen > LiSa_MAXBUFSIZE) {
+- fprintf(stderr, "LiSa: buffer size request too large, resizing\n");
+- buflen = LiSa_MAXBUFSIZE;
+- }
+- d->buffer_alloc((t_CKINT)buflen);
+-
+- RETURN->v_dur = (t_CKDUR)buflen;
++
++ RETURN->v_dur = d->buffer_alloc( GET_NEXT_DUR(ARGS) );
+ }
+
+ CK_DLL_CGET( LiSaMulti_cget_size )
+@@ -4005,7 +4115,7 @@ CK_DLL_CTRL( LiSaMulti_start_record )
+ CK_DLL_CTRL( LiSaMulti_start_play )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+ d->play[which] = GET_NEXT_INT(ARGS);
+ //fprintf(stderr, "voice %d playing = %d\n", which, d->play[which]);
+
+@@ -4044,7 +4154,7 @@ CK_DLL_CTRL( LiSaMulti_start_play0 )
+ CK_DLL_CTRL( LiSaMulti_ctrl_rate )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+ d->p_inc[which] = (t_CKDOUBLE)GET_NEXT_FLOAT(ARGS);
+
+ //fprintf(stderr, "setting voice %d rate to %f\n", which, d->p_inc[which]);
+@@ -4071,7 +4181,7 @@ CK_DLL_CTRL( LiSaMulti_ctrl_rate0 )
+ CK_DLL_CTRL( LiSaMulti_cget_rate )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+ //fprintf(stderr, "setting voice %d rate to %f\n", which, d->p_inc[which]);
+
+ RETURN->v_float = d->p_inc[which];
+@@ -4093,8 +4203,8 @@ CK_DLL_CTRL( LiSaMulti_cget_rate0 )
+ CK_DLL_CTRL( LiSaMulti_ctrl_pindex )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
+- d->pindex[which] = (t_CKDOUBLE)GET_NEXT_DUR(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
++ d->pindex[which] = d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
+
+ RETURN->v_dur = (t_CKDUR)d->pindex[which];
+ }
+@@ -4103,7 +4213,7 @@ CK_DLL_CTRL( LiSaMulti_ctrl_pindex )
+ CK_DLL_CTRL( LiSaMulti_ctrl_pindex0 )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- d->pindex[0] = (t_CKDOUBLE)GET_NEXT_DUR(ARGS);
++ d->pindex[0] = d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
+
+ RETURN->v_dur = (t_CKDUR)d->pindex[0];
+ }
+@@ -4116,7 +4226,7 @@ CK_DLL_CTRL( LiSaMulti_ctrl_pindex0 )
+ CK_DLL_CGET( LiSaMulti_cget_pindex )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+
+ // return
+ RETURN->v_dur = (t_CKDUR)d->pindex[which];
+@@ -4139,7 +4249,7 @@ CK_DLL_CGET( LiSaMulti_cget_pindex0 )
+ CK_DLL_CTRL( LiSaMulti_ctrl_rindex )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- d->rindex = /* gewang-> */(t_CKINT)GET_NEXT_DUR(ARGS);
++ d->rindex = (t_CKINT)d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
+
+ RETURN->v_dur = (t_CKDUR)d->rindex;
+ }
+@@ -4165,23 +4275,17 @@ CK_DLL_CGET( LiSaMulti_cget_rindex )
+ CK_DLL_CTRL( LiSaMulti_ctrl_lstart )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
+- d->loop_start[which] = /* gewang-> */(t_CKINT)GET_NEXT_DUR(ARGS);
+-
+- if (d->loop_start[which] < 0) d->loop_start[which] = 0;
+-
+- RETURN->v_dur = (t_CKDUR)d->loop_start[which];
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
++
++ RETURN->v_dur = d->set_loop_start( GET_NEXT_DUR(ARGS), which );
+ }
+
+
+ CK_DLL_CTRL( LiSaMulti_ctrl_lstart0 )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- d->loop_start[0] = /* gewang-> */(t_CKINT)GET_NEXT_DUR(ARGS);
+-
+- if (d->loop_start[0] < 0) d->loop_start[0] = 0;
+-
+- RETURN->v_dur = (t_CKDUR)d->loop_start[0];
++
++ RETURN->v_dur = d->set_loop_start( GET_NEXT_DUR(ARGS) );
+ }
+
+
+@@ -4193,7 +4297,7 @@ CK_DLL_CGET( LiSaMulti_cget_lstart )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+ // return
+ RETURN->v_dur = (t_CKDUR)d->loop_start[which];
+ }
+@@ -4215,25 +4319,17 @@ CK_DLL_CGET( LiSaMulti_cget_lstart0 )
+ CK_DLL_CTRL( LiSaMulti_ctrl_lend )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
+- d->loop_end[which] = /* gewang-> */(t_CKINT)GET_NEXT_DUR(ARGS);
+-
+- //check to make sure loop_end is not too large
+- if (d->loop_end[which] >= d->mdata_len) d->loop_end[which] = d->mdata_len - 1;
+-
+- RETURN->v_dur = (t_CKDUR)d->loop_end[which];
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
++
++ RETURN->v_dur = d->set_loop_end( GET_NEXT_DUR(ARGS), which );
+ }
+
+
+ CK_DLL_CTRL( LiSaMulti_ctrl_lend0 )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- d->loop_end[0] = /* gewang-> */(t_CKINT)GET_NEXT_DUR(ARGS);
+-
+- //check to make sure loop_end is not too large
+- if (d->loop_end[0] >= d->mdata_len) d->loop_end[0] = d->mdata_len - 1;
+-
+- RETURN->v_dur = (t_CKDUR)d->loop_end[0];
++
++ RETURN->v_dur = d->set_loop_end( GET_NEXT_DUR(ARGS) );
+ }
+
+
+@@ -4244,7 +4340,7 @@ CK_DLL_CTRL( LiSaMulti_ctrl_lend0 )
+ CK_DLL_CGET( LiSaMulti_cget_lend )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+
+ // return
+ RETURN->v_dur = (t_CKDUR)d->loop_end[which];
+@@ -4267,7 +4363,7 @@ CK_DLL_CGET( LiSaMulti_cget_lend0 )
+ CK_DLL_CTRL( LiSaMulti_ctrl_loop )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+ d->loopplay[which] = (t_CKBOOL)GET_NEXT_INT(ARGS);
+ }
+
+@@ -4288,7 +4384,7 @@ CK_DLL_CTRL( LiSaMulti_ctrl_loop0 )
+ CK_DLL_CGET( LiSaMulti_cget_loop )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+
+ // return
+ RETURN->v_int = (t_CKINT)d->loopplay[which];
+@@ -4311,7 +4407,7 @@ CK_DLL_CGET( LiSaMulti_cget_loop0 )
+ CK_DLL_CTRL( LiSaMulti_ctrl_bi )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+ d->bi[which] = (t_CKBOOL)GET_NEXT_INT(ARGS);
+
+ RETURN->v_int = (t_CKINT)d->bi[which];
+@@ -4334,7 +4430,7 @@ CK_DLL_CTRL( LiSaMulti_ctrl_bi0 )
+ CK_DLL_CGET( LiSaMulti_cget_bi )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+
+ // return
+ RETURN->v_int = (t_CKINT)d->bi[which];
+@@ -4357,8 +4453,15 @@ CK_DLL_CGET( LiSaMulti_cget_bi0 )
+ CK_DLL_CTRL( LiSaMulti_ctrl_loop_end_rec )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- d->loop_end_rec = /* gewang-> */(t_CKINT)GET_NEXT_DUR(ARGS);
+-
++ if( !d->mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to set loop recording end on uninitialized buffer; ignoring" );
++ RETURN->v_dur = 0.;
++ return;
++ }
++
++ d->loop_end_rec = (t_CKINT)d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
++
+ RETURN->v_dur = (t_CKDUR)d->loop_end_rec;
+ }
+
+@@ -4408,11 +4511,18 @@ CK_DLL_CGET( LiSaMulti_cget_loop_rec )
+ CK_DLL_CTRL( LiSaMulti_ctrl_sample )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- SAMPLE sample_in = (SAMPLE)GET_NEXT_FLOAT(ARGS);
+- int index_in = (t_CKINT)GET_NEXT_DUR(ARGS);
+-
+- d->pokeSample( sample_in, index_in );
+-
++ if( !d->mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to put sample into uninitialized buffer; ignoring" );
++ RETURN->v_float = 0.;
++ return;
++ }
++
++ SAMPLE sample_in = (SAMPLE)GET_NEXT_FLOAT(ARGS);
++ t_CKINT index_in = (t_CKINT)d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
++
++ d->pokeSample( sample_in, index_in );
++
+ RETURN->v_float = (t_CKFLOAT)sample_in; //pass input through
+ }
+
+@@ -4423,7 +4533,14 @@ CK_DLL_CTRL( LiSaMulti_ctrl_sample )
+ CK_DLL_CGET( LiSaMulti_cget_sample )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- double index_in = (t_CKDOUBLE)GET_NEXT_DUR(ARGS);
++ if( !d->mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to grab sample from uninitialized buffer; ignoring" );
++ RETURN->v_float = 0.;
++ return;
++ }
++
++ t_CKDOUBLE index_in = d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
+ // return
+ RETURN->v_float = (t_CKFLOAT)d->grabSample( index_in ); //change this to getSamp for interpolation
+ }
+@@ -4436,7 +4553,7 @@ CK_DLL_CGET( LiSaMulti_cget_sample )
+ CK_DLL_CTRL( LiSaMulti_ctrl_voicegain )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+ d->voiceGain[which] = (t_CKDOUBLE)GET_NEXT_FLOAT(ARGS);
+
+ RETURN->v_float = (t_CKFLOAT)d->voiceGain[which];
+@@ -4450,7 +4567,7 @@ CK_DLL_CTRL( LiSaMulti_ctrl_voicegain )
+ CK_DLL_CGET( LiSaMulti_cget_voicegain )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+
+ // return
+ RETURN->v_float = (t_CKFLOAT)d->voiceGain[which];
+@@ -4463,35 +4580,10 @@ CK_DLL_CGET( LiSaMulti_cget_voicegain )
+ CK_DLL_CTRL( LiSaMulti_ctrl_voicepan )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
+- d->voicePan[which] = (t_CKFLOAT)GET_NEXT_FLOAT(ARGS);
+-
+- t_CKINT i;
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
++ t_CKFLOAT pan = GET_NEXT_FLOAT(ARGS);
+
+- for(i=0; i<LiSa_channels; i++) d->channelGain[which][i] = 0.;
+-
+- for(i=0; i<LiSa_channels; i++) {
+- t_CKINT panTrunc = (t_CKINT)d->voicePan[which];
+- //fprintf(stderr, "panTrunc = %d, panFloat = %f, i = %d\n", panTrunc, d->voicePan[which], i);
+- if(i == panTrunc) {
+- d->channelGain[which][i] = 1. - ( d->voicePan[which] - (t_CKFLOAT)i );
+- if(i == LiSa_channels - 1) {
+- d->channelGain[which][0] = 1. - d->channelGain[which][i];
+- d->channelGain[which][0] = sqrt(d->channelGain[which][0]);
+- }
+- else {
+- d->channelGain[which][i+1] = 1. - d->channelGain[which][i];
+- d->channelGain[which][i+1] = sqrt(d->channelGain[which][i+1]);
+- }
+-
+- d->channelGain[which][i] = sqrt(d->channelGain[which][i]);
+-
+- }
+-
+- //fprintf(stderr, "gain for channel %d and voice %d = %f\n", i, which, d->channelGain[which][i]);
+- }
+-
+- RETURN->v_float = (t_CKFLOAT)d->voicePan[which];
++ RETURN->v_float = d->set_voice_pan( pan, which );
+ }
+
+
+@@ -4502,44 +4594,18 @@ CK_DLL_CTRL( LiSaMulti_ctrl_voicepan )
+ CK_DLL_CGET( LiSaMulti_cget_voicepan )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = GET_NEXT_INT(ARGS);
++ t_CKINT which = d->get_safe_voice( GET_NEXT_INT(ARGS) );
+
+ // return
+- RETURN->v_float = (t_CKFLOAT)d->voicePan[which];
++ RETURN->v_float = d->voicePan[which];
+ }
+
+ CK_DLL_CTRL( LiSaMulti_ctrl_voicepan0 )
+ {
+- LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT which = 0; //voice 0 for this one
+- d->voicePan[which] = (t_CKFLOAT)GET_NEXT_FLOAT(ARGS);
+-
+- t_CKINT i;
++ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
++ t_CKFLOAT pan = GET_NEXT_FLOAT(ARGS);
+
+- for(i=0; i<LiSa_channels; i++) d->channelGain[which][i] = 0.;
+-
+- for(i=0; i<LiSa_channels; i++) {
+- t_CKINT panTrunc = (t_CKINT)d->voicePan[which];
+- //fprintf(stderr, "panTrunc = %d, panFloat = %f, i = %d\n", panTrunc, d->voicePan[which], i);
+- if(i == panTrunc) {
+- d->channelGain[which][i] = 1. - ( d->voicePan[which] - (t_CKFLOAT)i );
+- if(i == LiSa_channels - 1) {
+- d->channelGain[which][0] = 1. - d->channelGain[which][i];
+- d->channelGain[which][0] = sqrt(d->channelGain[which][0]);
+- }
+- else {
+- d->channelGain[which][i+1] = 1. - d->channelGain[which][i];
+- d->channelGain[which][i+1] = sqrt(d->channelGain[which][i+1]);
+- }
+-
+- d->channelGain[which][i] = sqrt(d->channelGain[which][i]);
+-
+- }
+-
+- //fprintf(stderr, "gain for channel %d and voice %d = %f\n", i, which, d->channelGain[which][i]);
+- }
+-
+- RETURN->v_float = (t_CKFLOAT)d->voicePan[which];
++ RETURN->v_float = d->set_voice_pan( pan );
+ }
+
+ CK_DLL_CGET( LiSaMulti_cget_voicepan0 )
+@@ -4547,7 +4613,7 @@ CK_DLL_CGET( LiSaMulti_cget_voicepan0 )
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+
+ // return
+- RETURN->v_float = (t_CKFLOAT)d->voicePan[0];
++ RETURN->v_float = d->voicePan[0];
+ }
+
+ //-----------------------------------------------------------------------------
+@@ -4608,8 +4674,15 @@ CK_DLL_CGET( LiSaMulti_cget_voice )
+ CK_DLL_CTRL( LiSaMulti_ctrl_rampup )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT voice = GET_NEXT_INT(ARGS);
+- t_CKDUR len = GET_NEXT_DUR(ARGS);
++ if( !d->mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to set ramp up length on uninitialized buffer; ignoring" );
++ RETURN->v_dur = 0.;
++ return;
++ }
++
++ t_CKINT voice = d->get_safe_voice( GET_NEXT_INT(ARGS) );
++ t_CKDUR len = (t_CKDUR)d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
+
+ d->ramp_up(voice, len);
+
+@@ -4620,7 +4693,14 @@ CK_DLL_CTRL( LiSaMulti_ctrl_rampup )
+ CK_DLL_CTRL( LiSaMulti_ctrl_rampup0 )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKDUR len = GET_NEXT_DUR(ARGS);
++ if( !d->mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to set ramp up length on uninitialized buffer; ignoring" );
++ RETURN->v_dur = 0.;
++ return;
++ }
++
++ t_CKDUR len = (t_CKDUR)d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
+
+ d->ramp_up(0, len);
+
+@@ -4635,19 +4715,33 @@ CK_DLL_CTRL( LiSaMulti_ctrl_rampup0 )
+ CK_DLL_CTRL( LiSaMulti_ctrl_rampdown )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT voice = GET_NEXT_INT(ARGS);
+- t_CKDUR len = GET_NEXT_DUR(ARGS);
++ if( !d->mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to set ramp down length on uninitialized buffer; ignoring" );
++ RETURN->v_dur = 0.;
++ return;
++ }
++
++ t_CKINT voice = d->get_safe_voice( GET_NEXT_INT(ARGS) );
++ t_CKDUR len = (t_CKDUR)d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
+
+ d->ramp_down(voice, len);
+
+- RETURN->v_dur = (t_CKDUR)len;
++ RETURN->v_dur = (t_CKDUR)len;
+ }
+
+
+ CK_DLL_CTRL( LiSaMulti_ctrl_rampdown0 )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKDUR len = GET_NEXT_DUR(ARGS);
++ if( !d->mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to set ramp down length on uninitialized buffer; ignoring" );
++ RETURN->v_dur = 0.;
++ return;
++ }
++
++ t_CKDUR len = (t_CKDUR)d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
+
+ d->ramp_down(0, len);
+
+@@ -4662,7 +4756,14 @@ CK_DLL_CTRL( LiSaMulti_ctrl_rampdown0 )
+ CK_DLL_CTRL( LiSaMulti_ctrl_rec_ramplen )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKDUR newramplen = GET_NEXT_DUR(ARGS);
++ if( !d->mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to set recording ramp length on uninitialized buffer; ignoring" );
++ RETURN->v_dur = 0.;
++ return;
++ }
++
++ t_CKDUR newramplen = (t_CKDUR)d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
+ d->set_rec_ramplen(newramplen);
+
+ RETURN->v_dur = (t_CKDUR)newramplen;
+@@ -4679,7 +4780,7 @@ CK_DLL_CTRL( LiSaMulti_ctrl_maxvoices )
+ d->maxvoices = GET_NEXT_INT(ARGS);
+ if( d->maxvoices > LiSa_MAXVOICES) {
+ d->maxvoices = LiSa_MAXVOICES;
+- fprintf(stderr, "LiSa: MAXVOICES limited to %d.\n", LiSa_MAXVOICES);
++ EM_error2( 0, "LiSa: MAXVOICES limited to %d", LiSa_MAXVOICES );
+ }
+ RETURN->v_int = d->maxvoices;
+ }
+@@ -4705,21 +4806,33 @@ CK_DLL_CGET( LiSaMulti_cget_maxvoices )
+ CK_DLL_CGET( LiSaMulti_cget_value )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT voice = GET_NEXT_INT(ARGS);
+- t_CKDOUBLE where = (t_CKDOUBLE) GET_NEXT_DUR(ARGS);
++ if( !d->mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to grab value from uninitialized buffer; ignoring" );
++ RETURN->v_dur = 0.;
++ return;
++ }
+
+- // return
+- RETURN->v_dur = (t_CKDUR)d->getSamp(where, voice);
++ t_CKINT voice = d->get_safe_voice( GET_NEXT_INT(ARGS) );
++ t_CKDOUBLE where = d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
++
++ RETURN->v_dur = (t_CKDUR)d->getSamp( where, voice );
+ }
+
+
+ CK_DLL_CGET( LiSaMulti_cget_value0 )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
++ if( !d->mdata )
++ {
++ EM_error2( 0, "LiSa: Trying to grab value from uninitialized buffer; ignoring" );
++ RETURN->v_dur = 0.;
++ return;
++ }
+
+- t_CKDOUBLE where = (t_CKDOUBLE) GET_NEXT_DUR(ARGS);
+- // return
+- RETURN->v_dur = (t_CKDUR)d->getSamp(where, 0);
++ t_CKDOUBLE where = d->normalize_index( (t_CKDOUBLE)GET_NEXT_DUR(ARGS) );
++
++ RETURN->v_dur = (t_CKDUR)d->getSamp( where );
+ }
+
+
+@@ -4731,6 +4844,11 @@ CK_DLL_CTRL( LiSaMulti_ctrl_track )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+ d->track = (t_CKINT)GET_NEXT_INT(ARGS);
++ if( d->track < 0 || d->track > 2 )
++ {
++ EM_error2( 0, "LiSa: Invalid tracking mode %d; using 0", d->track );
++ d->track = 0;
++ }
+
+ RETURN->v_int = d->track;
+ }
+@@ -4756,12 +4874,8 @@ CK_DLL_CGET( LiSaMulti_cget_track )
+ CK_DLL_CGET( LiSaMulti_cget_playing )
+ {
+ LiSaMulti_data * d = (LiSaMulti_data *)OBJ_MEMBER_UINT(SELF, LiSaMulti_offset_data);
+- t_CKINT voice = GET_NEXT_INT(ARGS);
+- if(voice >= d->maxvoices) {
+- fprintf(stderr, "LiSa: requesting info greater than MAXVOICES %d.\n", LiSa_MAXVOICES);
+- voice = 0;
+- }
+-
++ t_CKINT voice = d->get_safe_voice( GET_NEXT_INT(ARGS) );
++
+ // return
+ RETURN->v_int = d->play[voice];
+ }
+--
+1.7.1
+