FFT: Prevent user from attempting hops smaller than SC's block size
[supercollider.git] / server / plugins / BeatTrack2.cpp
blob6525000062c3d58bb2d06c1c8b1ffc95d8f6d4d4
1 /*
2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //BeatTrack2 UGen implemented by Nick Collins (http://www.informatics.sussex.ac.uk/users/nc81/)
22 //6 Nov 2007
24 #include "ML.h"
26 //need to add bestgroove option to store groove, else remove output which is currently always straight 16ths
29 static const int g_numtempi= 120;
30 static float g_periods[g_numtempi]= { 1, 0.98360655737705, 0.96774193548387, 0.95238095238095, 0.9375, 0.92307692307692, 0.90909090909091, 0.8955223880597, 0.88235294117647, 0.8695652173913, 0.85714285714286, 0.84507042253521, 0.83333333333333, 0.82191780821918, 0.81081081081081, 0.8, 0.78947368421053, 0.77922077922078, 0.76923076923077, 0.75949367088608, 0.75, 0.74074074074074, 0.73170731707317, 0.72289156626506, 0.71428571428571, 0.70588235294118, 0.69767441860465, 0.68965517241379, 0.68181818181818, 0.67415730337079, 0.66666666666667, 0.65934065934066, 0.65217391304348, 0.64516129032258, 0.63829787234043, 0.63157894736842, 0.625, 0.61855670103093, 0.61224489795918, 0.60606060606061, 0.6, 0.59405940594059, 0.58823529411765, 0.58252427184466, 0.57692307692308, 0.57142857142857, 0.56603773584906, 0.5607476635514, 0.55555555555556, 0.55045871559633, 0.54545454545455, 0.54054054054054, 0.53571428571429, 0.53097345132743, 0.52631578947368, 0.52173913043478, 0.51724137931034, 0.51282051282051, 0.50847457627119, 0.50420168067227, 0.5, 0.49586776859504, 0.49180327868852, 0.48780487804878, 0.48387096774194, 0.48, 0.47619047619048, 0.47244094488189, 0.46875, 0.46511627906977, 0.46153846153846, 0.45801526717557, 0.45454545454545, 0.45112781954887, 0.44776119402985, 0.44444444444444, 0.44117647058824, 0.43795620437956, 0.43478260869565, 0.43165467625899, 0.42857142857143, 0.42553191489362, 0.42253521126761, 0.41958041958042, 0.41666666666667, 0.41379310344828, 0.41095890410959, 0.40816326530612, 0.40540540540541, 0.40268456375839, 0.4, 0.39735099337748, 0.39473684210526, 0.3921568627451, 0.38961038961039, 0.38709677419355, 0.38461538461538, 0.38216560509554, 0.37974683544304, 0.37735849056604, 0.375, 0.37267080745342, 0.37037037037037, 0.3680981595092, 0.36585365853659, 0.36363636363636, 0.36144578313253, 0.35928143712575, 0.35714285714286, 0.35502958579882, 0.35294117647059, 0.35087719298246, 0.34883720930233, 0.34682080924855, 0.3448275862069, 0.34285714285714, 0.34090909090909, 0.33898305084746, 0.33707865168539, 0.33519553072626 };
31 //float g_tempoweight[g_numtempi]= { 0.8, 0.82581988897472, 0.83651483716701, 0.84472135955, 0.85163977794943, 0.85773502691896, 0.86324555320337, 0.8683130051064, 0.87302967433402, 0.87745966692415, 0.88164965809277, 0.88563488385777, 0.88944271909999, 0.89309493362513, 0.89660917830793, 0.9, 0.90327955589886, 0.90645812948448, 0.90954451150103, 0.91254628677423, 0.91547005383793, 0.91832159566199, 0.9211060141639, 0.92382783747338, 0.92649110640674, 0.92909944487358, 0.93165611772088, 0.93416407864999, 0.93662601021279, 0.93904435743076, 0.94142135623731, 0.94375905768565, 0.94605934866804, 0.94832396974191, 0.95055453054182, 0.95275252316519, 0.9549193338483, 0.95705625319186, 0.95916448515084, 0.96124515496597, 0.96329931618555, 0.96532795690183, 0.96733200530682, 0.969312334656, 0.97126976771554, 0.97320508075689, 0.97511900715418, 0.97701224063136, 0.97888543819998, 0.98073922282301, 0.98257418583506, 0.98439088914586, 0.98618986725025, 0.98797162906496, 0.9897366596101, 0.99148542155127, 0.99321835661586, 0.99493588689618, 0.99663841605004, 0.99832633040858, 1, 0.99832633040858, 0.99663841605004, 0.99493588689618, 0.99321835661586, 0.99148542155127, 0.9897366596101, 0.98797162906496, 0.98618986725025, 0.98439088914586, 0.98257418583506, 0.98073922282301, 0.97888543819998, 0.97701224063136, 0.97511900715418, 0.97320508075689, 0.97126976771554, 0.969312334656, 0.96733200530682, 0.96532795690183, 0.96329931618555, 0.96124515496597, 0.95916448515084, 0.95705625319186, 0.9549193338483, 0.95275252316519, 0.95055453054182, 0.94832396974191, 0.94605934866804, 0.94375905768565, 0.94142135623731, 0.93904435743076, 0.93662601021279, 0.93416407864999, 0.93165611772088, 0.92909944487358, 0.92649110640674, 0.92382783747338, 0.9211060141639, 0.91832159566199, 0.91547005383793, 0.91254628677423, 0.90954451150103, 0.90645812948448, 0.90327955589886, 0.9, 0.89660917830793, 0.89309493362513, 0.88944271909999, 0.88563488385777, 0.88164965809277, 0.87745966692415, 0.87302967433402, 0.8683130051064, 0.86324555320337, 0.85773502691896, 0.85163977794943, 0.84472135955, 0.83651483716701, 0.82581988897472 };
32 //const float g_groove = 0.32;
35 static float g_sep[8]= {0.0, 0.25, 0.5, 0.75, 0.0, 0.32, 0.5, 0.82};
36 //weight for particular step
37 static float g_weight[4]= {1.0,0.5,0.9,0.6};
38 //weight for blurring feature envelope locally
39 static float g_weight2[9]= {0.05, 0.1, 0.3,0.7,1.0,0.7,0.3, 0.1, 0.05};
41 //void BeatTrack2_dofft(BeatTrack2 *unit, uint32);
42 static void calculatetemplate(BeatTrack2 *unit, int which, int j);
43 static void finaldecision(BeatTrack2 *unit);
45 void BeatTrack2_Ctor(BeatTrack2* unit)
47 //unit->m_srate = unit->mWorld->mFullRate.mSampleRate;
48 float kblocklength= unit->mWorld->mFullRate.mBufDuration; //seconds per control block
49 unit->m_krlength= kblocklength;
50 //N features per block over numphases*2 variants for one of 120 tempi, so need at least 120 blocks to complete
52 unit->m_phaseaccuracy = ZIN0(3); //0.02; //20 msec resolution; could be argument of UGen
54 unit->m_numphases = (int*)RTAlloc(unit->mWorld, g_numtempi * sizeof(int));
55 //unit->m_phases = (float**)RTAlloc(unit->mWorld, g_numtempi * sizeof(float*));
57 for (int j=0; j<g_numtempi; ++j) {
59 float period= g_periods[j];
61 int num= (int)(period/unit->m_phaseaccuracy); //maximum will be 1.0/0.02 = 50
63 unit->m_numphases[j]=num;
66 // unit->m_phases[j]= (float*)RTAlloc(unit->mWorld, unit->m_numphases[j] * sizeof(float));
68 // float phase=0.0;
70 // for (i=0; i<num; ++i) {
71 // unit->m_phases[j][i] = phase;
72 // phase += unit->m_phaseaccuracy;
73 // }
77 unit->m_numfeatures = (int)(ZIN0(1)+0.001);
81 //for efficiency
82 unit->m_scores= (float*)RTAlloc(unit->mWorld, (2*unit->m_numfeatures) * sizeof(float));
84 unit->m_temporalwindowsize= ZIN0(2); //typically small, 2 seconds for fast reactions compared to 6 secs for BeatTrack
86 unit->m_fullwindowsize = unit->m_temporalwindowsize + 1.0 + 0.1; //plus one to cover all phases of the 60bpm based period, and a further 0.1 for indexing safety; ie looking at areas around the point you're interested in
88 unit->m_buffersize = (int)(unit->m_fullwindowsize/unit->m_krlength); //in control blocks
91 //printf("loading test blocklength %f numfeatures %d temporal %f full %f blocks %d \n",unit->m_krlength, unit->m_numfeatures, unit->m_temporalwindowsize, unit->m_fullwindowsize, unit->m_buffersize);
95 //float ** m_pastfeatures; //for each feature, a trail of last m_workingmemorysize values
96 unit->m_pastfeatures = (float**)RTAlloc(unit->mWorld, unit->m_numfeatures * sizeof(float*));
98 for (int j=0; j<unit->m_numfeatures; ++j) {
100 unit->m_pastfeatures[j]= (float*)RTAlloc(unit->mWorld, unit->m_buffersize * sizeof(float));
102 Clear(unit->m_buffersize, unit->m_pastfeatures[j]); //set all to zero at first
104 //for (i=0; i<unit->m_buffersize; ++i) {
105 // unit->m_pastfeatures[j][i] = 0.0;
106 // }
110 //main counter
111 unit->m_counter= 0;
113 //could avoid allocation by having a hard limit on
114 unit->bestscore= (float*)RTAlloc(unit->mWorld, 4 * unit->m_numfeatures * sizeof(float));
115 unit->bestphase= (int*)RTAlloc(unit->mWorld, 4 * unit->m_numfeatures * sizeof(int));
116 unit->besttempo= (int*)RTAlloc(unit->mWorld, 4 * unit->m_numfeatures * sizeof(int));
117 unit->bestgroove= (int*)RTAlloc(unit->mWorld, 4 * unit->m_numfeatures * sizeof(int));
119 for (int i=0; i<4; ++i) {
121 int basepos= i*unit->m_numfeatures;
123 for (int j=0; j<unit->m_numfeatures; ++j) {
124 unit->bestscore[basepos+j]= -9999.0;
125 unit->bestphase[basepos+j]= 0;
126 unit->besttempo[basepos+j]= 60;
127 unit->bestgroove[basepos+j]= 0;
131 unit->m_phase= 0.0;
132 unit->m_period= 0.5;
133 unit->m_groove= 0;
134 unit->m_currtempo=2;
135 unit->m_phaseperblock= unit->m_krlength/unit->m_period;
137 unit->m_predictphase= 0.4f;
138 unit->m_predictperiod = 0.3f;
141 unit->m_outputphase= unit->m_phase;
142 unit->m_outputtempo= unit->m_currtempo;
143 unit->m_outputgroove= unit->m_groove;
144 unit->m_outputphaseperblock= unit->m_phaseperblock;
148 unit->m_calculationperiod= 0.5; //every half second; could also be additional argument to UGen
149 unit->m_calculationschedule= 0.0;
151 //printf("srate %f conversion factor %f frame period %f \n", unit->m_srate, unit->m_srateconversion, unit->m_frameperiod);
154 int bufnum = (int)(ZIN0(5)+0.001f);
155 if (bufnum >= unit->mWorld->mNumSndBufs)
156 bufnum = 0;
158 if (bufnum<0)
159 unit->m_weightingscheme = bufnum<2 ? 0 : 1;
160 else {
161 SndBuf *buf = unit->mWorld->mSndBufs + bufnum;
162 unit->m_tempoweights= buf;
163 unit->m_weightingscheme=2;
166 //printf("bufnum %d weightingscheme %d check %f %f\n", bufnum, unit->m_weightingscheme, unit->m_tempoweights[0], unit->m_tempoweights[119]);
169 unit->halftrig=0;
170 unit->q1trig=0;
171 unit->q2trig=0;
174 unit->mCalcFunc = (UnitCalcFunc)&BeatTrack2_next;
179 void BeatTrack2_Dtor(BeatTrack2 *unit)
181 RTFree(unit->mWorld, unit->m_numphases);
183 RTFree(unit->mWorld, unit->m_scores);
185 RTFree(unit->mWorld, unit->bestscore);
186 RTFree(unit->mWorld, unit->bestphase);
187 RTFree(unit->mWorld, unit->besttempo);
189 for (int j=0; j<unit->m_numfeatures; ++j)
190 RTFree(unit->mWorld, unit->m_pastfeatures[j]);
192 RTFree(unit->mWorld, unit->m_pastfeatures);
197 //over phases and for each groove
198 void calculatetemplate(BeatTrack2 *unit, int which, int j)
200 int tmpindex;
202 int startcounter= unit->m_startcounter;
204 int numphases= unit->m_numphases[which];
206 float period= g_periods[which];
208 float blockconvert= unit->m_krlength;
210 float windowsize = unit->m_temporalwindowsize;
212 int buffersize= unit->m_buffersize; //unit->m_fullwindowsize/unit->m_krlength; //in control blocks
214 float ** pastfeatures= unit->m_pastfeatures;
215 //unit->m_pastfeatures = (float**)RTAlloc(unit->mWorld, unit->m_numfeatures * sizeof(float*));
217 int beatsfit= (int)(windowsize/period); //complete beats only, or also fit as many as possible?
219 float weight; //compensation for number of events matched; may alter equation later
221 switch (unit->m_weightingscheme)
223 case 0:
224 weight = 1.0f; //flat
225 break;
226 case 1:
227 weight= 1.0f/(beatsfit*4); //compensate for number of time points tested
228 break;
229 case 2:
230 SndBuf * buf = unit->m_tempoweights;
231 if (buf->data)
232 weight = buf->data[which]; //user defined temmpo biases (usually a mask on allowed tempi)
233 else
234 weight = 1.f;
235 break;
238 int numfeatures= unit->m_numfeatures;
240 float * scores = unit->m_scores; //[2*numfeatures];
242 float * bestscore = unit->bestscore;
243 int * bestphase = unit->bestphase;
244 int * besttempo = unit->besttempo;
245 int * bestgroove = unit->bestgroove;
247 for (int i=0; i<numphases; ++i) {
249 //initialise scores
250 //for (j=0; j<2; ++j)
251 for (int k=0; k<numfeatures; ++k)
252 scores[2*k+j]=0.0;
254 float phaseadd = i*unit->m_phaseaccuracy;
256 //calculation for a particular phase of template
257 //for (j=0; j<2; ++j) {
259 for(int h=0; h<beatsfit; ++h) {
261 for(int l=0; l<4; ++l) {
263 float sep= phaseadd+ (h*period)+ ((g_sep[j*4+l]) * period);
264 float weight= g_weight[l];
266 int blocks= (int)((sep/blockconvert)+0.5); //round to nearest
268 //convert sep to control periods and find appropriate point in source data
269 int index= (startcounter+ buffersize - blocks)%(buffersize);
272 //widen over four either side
273 for (int m= (-4); m<5;++m) {
275 int actualindex= (index+buffersize+m)%(buffersize);
277 for (int k=0; k<numfeatures; ++k) {
279 int scoreindexnow = 2*k+j;
281 //could widen this value here, even based on cubic interpolation etc
282 scores[scoreindexnow] += weight * (g_weight2[m+4])* (pastfeatures[k][actualindex]);
286 //scores[2*k+j] += weight * (pastfeatures[k][index]);
296 //update any winners from scores
297 //for (j=0; j<2; ++j) {
299 for (int k=0; k<numfeatures; ++k) {
301 float scorenow= (scores[2*k+j]) * weight;
303 //NEED TO STORE J IF PRESERVING SENSE OF GROOVE
305 if(scorenow>bestscore[k]) {
307 tmpindex= numfeatures+k;
308 //shift up to make room
309 bestscore[tmpindex]= bestscore[k]; bestphase[tmpindex]= bestphase[k];
310 besttempo[tmpindex]= besttempo[k]; bestgroove[tmpindex]=bestgroove[k];
312 bestscore[k]= scorenow; bestphase[k]= i; besttempo[k]= which; bestgroove[k]=j;
314 //printf("bestscore %f bestphase %d besttempo %d bestgroove %d \n", bestscore[k],bestphase[k],besttempo[k], bestgroove[k]);
316 else if (scorenow>bestscore[numfeatures+k]) {
318 tmpindex= numfeatures+k;
319 bestscore[tmpindex]= scorenow; bestphase[tmpindex]= i;
320 besttempo[tmpindex]= which; bestgroove[tmpindex]=j;
333 //a winner must appear at least twice, across features, and be superior to the secondbest in those features too by some margins
334 //a consistency check could also run to look at change from last time to this
335 void finaldecision(BeatTrack2 *unit)
337 int foundgood= 0;
338 int bestcandidate =0;
339 int bestpreviousmatchsum=0; //(-1); //should be 0, but allowing different for now
340 float excess; //, consistency;
341 //int exactmatches, closematches; //can be out by a few indices on period; could match on tempo but not phase etc
342 //combine these four factors in one master score?
344 for (int i=0; i<unit->m_numfeatures; ++i) {
346 int matchsum=0;
348 float secondbest= unit->bestscore[unit->m_numfeatures+i];
349 excess= (secondbest!=0) ? (unit->bestscore[i]/ secondbest): unit->bestscore[i];
350 int tempo = unit->besttempo[i];
352 //could check consistency too by looking at phase update from last prediction in same feature
354 for (int j=0; j<unit->m_numfeatures; ++j) {
356 if(j!=i) {
358 if (abs(unit->besttempo[j]-tempo)<5) matchsum++;
362 //check over all previous features
363 if (abs(unit->besttempo[2*unit->m_numfeatures+j]- tempo)<5) matchsum++;
367 //printf("i %d matchsum %d excess %f \n",i, matchsum, excess);
369 if(secondbest!= 0) matchsum += (int)excess;
371 //so must have at least one match //&& (excess>1.03)
372 if ((matchsum>bestpreviousmatchsum)) {bestcandidate = i; bestpreviousmatchsum= matchsum; foundgood=1;}
377 //consistency: could require it to win twice; have a candidatepending which makes a phase prediction; only let through if prediction fulfilled
379 //unit->m_amortlength will be numtempi *2 = 240
381 float bestphase = fmod( ((unit->bestphase[bestcandidate] * unit->m_phaseaccuracy) + (unit->m_krlength * (unit->m_amortlength)))/(unit->m_period), (float)1.0);
383 //if(unit->m_prediction) {
385 if ((fabs(bestphase - unit->m_predictphase)< ((2*(unit->m_phaseaccuracy))/unit->m_predictperiod)) && (fabs( (g_periods[unit->besttempo[bestcandidate]]) - unit->m_predictperiod ) <0.04) ) {
387 unit->m_period = unit->m_predictperiod;
388 //time elapsed since a known beat is phase of winner in seconds, to calculation start point, plus time for calculation (120 control blocks) divided by period, modulo 1.0
389 unit->m_phase= bestphase;
390 unit->m_currtempo = 1.f/unit->m_period;
391 unit->m_phaseperblock = unit->m_krlength/unit->m_period;
397 //unit->m_prediction=false;
400 //if(foundgood) {
401 //if clear winner
403 unit->m_predictperiod = g_periods[unit->besttempo[bestcandidate]];
405 //time elapsed since a known beat is phase of winner in seconds, to calculation start point, plus time for calculation (120 control blocks) divided by period, modulo 1.0
406 unit->m_predictphase= fmod( ( (unit->bestphase[bestcandidate] * unit->m_phaseaccuracy) + (unit->m_krlength * (unit->m_amortlength)) + unit->m_calculationperiod)/(unit->m_period),(float)1.0);
410 //if(foundgood) {
411 ////if clear winner
413 //unit->m_period = g_periods[unit->besttempo[bestcandidate]];
414 ////time elapsed since a known beat is phase of winner in seconds, to calculation start point, plus time for calculation (120 control blocks) divided by period, modulo 1.0
415 //unit->m_phase= fmod( ((unit->bestphase[bestcandidate] * unit->m_phaseaccuracy) + (unit->m_krlength * 120))/(unit->m_period), 1.0);
417 //unit->m_currtempo = 1.0/unit->m_period;
418 //unit->m_phaseperblock = unit->m_krlength/unit->m_period;
426 void BeatTrack2_next(BeatTrack2 *unit, int wrongNumSamples)
428 //keep updating feature memories
429 unit->m_counter= (unit->m_counter+1)%(unit->m_buffersize);
431 int busnum = (int)(ZIN0(0)+0.001f);
433 //unit->m_features = unit->mWorld->mControlBus + busnum;
435 float * features= unit->mWorld->mControlBus + busnum;
437 //hmm, is this pointer guaranteed to stay the same? may have to update each time...
438 for (int j=0; j<unit->m_numfeatures; ++j) {
439 unit->m_pastfeatures[j][unit->m_counter]= features[j]; //unit->m_features[j];
442 unit->m_calculationschedule += unit->m_krlength;
444 //check for new calculation round
445 if(unit->m_calculationschedule> unit->m_calculationperiod) {
447 unit->m_calculationschedule -= unit->m_calculationperiod;
449 //reset best scores and move old to previous slots
450 for (int i=0; i<2; ++i) {
452 int pos1= (2+i)*unit->m_numfeatures;
453 int pos2= i*unit->m_numfeatures;
455 for (int j=0; j<unit->m_numfeatures; ++j) {
456 unit->bestscore[pos1+j]= unit->bestscore[pos2+j];
457 unit->bestscore[pos2+j]= -9999.0;
458 unit->bestphase[pos1+j]= unit->bestphase[pos2+j];
459 unit->bestphase[pos2+j]= 0;
460 unit->besttempo[pos1+j]= unit->besttempo[pos2+j];
461 unit->besttempo[pos2+j]= 60;
466 //state 0 is do nothing
467 unit->m_amortisationstate=1;
468 unit->m_amortcount=0;
469 unit->m_amortlength=g_numtempi*2; //
470 //unit->m_amortisationsteps=0;
472 //store essential data
473 unit->m_startcounter = unit->m_counter;
475 unit->m_currphase=unit->m_phase;
479 //keeps incrementing but will be reset with each calculation run
480 //unit->m_amortisationsteps=unit->m_amortisationsteps+1;
482 //if state nonzero do something
483 switch(unit->m_amortisationstate) {
484 case 0:
485 break; //do nothing case
486 case 1: //calculate acf
487 calculatetemplate(unit,unit->m_amortcount >> 1, unit->m_amortcount %2);
489 unit->m_amortcount=unit->m_amortcount+1;
491 if(unit->m_amortcount==unit->m_amortlength) {
492 unit->m_amortisationstate=2;
493 //unit->m_amortlength=1;
494 //unit->m_amortcount=0;
496 break;
497 case 2: //done calculating template matches, now decide whether to follow through
498 finaldecision(unit);
499 unit->m_amortisationstate=0;
500 break;
502 default:
503 break;
509 //test if impulse to output
510 unit->m_phase+=unit->m_phaseperblock;
512 //if(unit->m_counter%400==0) printf("phase %f period %f\n", unit->m_phase, unit->m_period);
514 //if not locked, update output phase from model phase, else keep a separate output phase
516 float lock= ZIN0(4);
517 //printf("lock %f \n",lock);
519 if(lock<0.5f) {
521 unit->m_outputphase= unit->m_phase;
522 unit->m_outputtempo= unit->m_currtempo;
523 unit->m_outputgroove= unit->m_groove;
524 unit->m_outputphaseperblock= unit->m_phaseperblock;
525 } else {
527 unit->m_outputphase+=unit->m_outputphaseperblock;
531 if (unit->m_phase >= 1.f) {unit->m_phase-= 1.f;}
533 //0 is beat, 1 is quaver, 2 is semiquaver, 3 is actual current tempo in bps
534 //so no audio accuracy with beats, just asap, may as well be control rate
535 ZOUT0(0)=0.0;
536 ZOUT0(1)=0.0;
537 ZOUT0(2)=0.0;
538 ZOUT0(3)=unit->m_outputtempo; //*0.016666667;
539 ZOUT0(4)=unit->m_outputphase;
540 ZOUT0(5)=unit->m_outputgroove;
542 //output beat
543 if (unit->m_outputphase >= 1.f) {
545 //printf("beat \n");
547 unit->m_outputphase -= 1.f;
548 ZOUT0(0)=1.0;
549 ZOUT0(1)=1.0;
550 ZOUT0(2)=1.0;
551 unit->halftrig=0;
552 unit->q1trig=0;
553 unit->q2trig=0;
556 if (unit->m_outputphase>=0.5 && unit->halftrig==0) {
557 ZOUT0(1)=1.0;
558 ZOUT0(2)=1.0;
559 unit->halftrig=1;
562 float groove= unit->m_outputgroove *0.07;
564 if (unit->m_outputphase>=(0.25+groove) && unit->q1trig==0) {
565 ZOUT0(2)=1.0;
566 unit->q1trig=1;
569 if (unit->m_outputphase>=(0.75+groove) && unit->q2trig==0) {
570 ZOUT0(2)=1.0;
571 unit->q2trig=1;