1 //=========================================================
2 // MusE
3 // Linux Music Editor
4 // $Id: tempo.cpp,v 1.7.2.7 2008/05/21 00:28:52 terminator356 Exp $
5 //
6 // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de)
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; version 2 of
11 // the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 //
22 //=========================================================
23
24 #include <stdio.h>
25 #include <errno.h>
26 #include "muse_math.h"
27
28 #include "tempo.h"
29 #include "globals.h"
30 #include "gconfig.h"
31 #include "xml.h"
32
33 #include <stdint.h>
34
35 namespace MusEGlobal {
36 MusECore::TempoList tempomap;
37 MusECore::TempoRecList tempo_rec_list;
38 }
39
40 namespace MusECore {
41
42 //---------------------------------------------------------
43 // TempoList
44 //---------------------------------------------------------
45
TempoList()46 TempoList::TempoList()
47 {
48 _tempo = 500000;
49 insert(std::pair<const unsigned, TEvent*> (MAX_TICK+1, new TEvent(_tempo, 0)));
50 _tempoSN = 1;
51 _globalTempo = 100;
52 useList = true;
53 }
54
~TempoList()55 TempoList::~TempoList()
56 {
57 for (iTEvent i = begin(); i != end(); ++i)
58 delete i->second;
59 }
60
61 //---------------------------------------------------------
62 // copy
63 //---------------------------------------------------------
64
copy(const TempoList & src)65 void TempoList::copy(const TempoList& src)
66 {
67 // Clear and delete the existing destination list.
68 for (iTEvent i = begin(); i != end(); ++i)
69 delete i->second;
70 TEMPOLIST::clear();
71
72 for (ciTEvent i = src.cbegin(); i != src.cend(); ++i)
73 {
74 TEvent* new_e = new TEvent(*i->second);
75 std::pair<iTEvent, bool> res = insert(std::pair<const unsigned, TEvent*> (i->first, new_e));
76 if(!res.second)
77 {
78 fprintf(stderr, "TempoList::copy insert failed: tempolist:%p tempo:%p %d tick:%d\n",
79 this, new_e, new_e->tempo, new_e->tick);
80 }
81 }
82 }
83
84 //---------------------------------------------------------
85 // add
86 //---------------------------------------------------------
87
add(unsigned tick,int tempo,bool do_normalize)88 void TempoList::add(unsigned tick, int tempo, bool do_normalize)
89 {
90 if (tick > MAX_TICK)
91 tick = MAX_TICK;
92 iTEvent e = upper_bound(tick);
93
94 if (tick == e->second->tick)
95 e->second->tempo = tempo;
96 else {
97 TEvent* ne = e->second;
98 TEvent* ev = new TEvent(ne->tempo, ne->tick);
99 ne->tempo = tempo;
100 ne->tick = tick;
101 insert(std::pair<const unsigned, TEvent*> (tick, ev));
102 }
103 if(do_normalize)
104 normalize();
105 }
106
107
add(unsigned tick,TEvent * e,bool do_normalize)108 void TempoList::add(unsigned tick, TEvent* e, bool do_normalize)
109 {
110 int tempo = e->tempo;
111 std::pair<iTEvent, bool> res = insert(std::pair<const unsigned, TEvent*> (tick, e));
112 if(!res.second)
113 {
114 fprintf(stderr, "TempoList::add insert failed: tempolist:%p tempo:%p %d tick:%d\n",
115 this, e, tempo, e->tick);
116 }
117 else
118 {
119 iTEvent ine = res.first;
120 ++ine; // There is always a 'next' tempo event - there is always one at index MAX_TICK + 1.
121 TEvent* ne = ine->second;
122
123 // Swap the values. (This is how the tempo list works.)
124 e->tempo = ne->tempo;
125 e->tick = ne->tick;
126 ne->tempo = tempo;
127 ne->tick = tick;
128
129 if(do_normalize)
130 normalize();
131 }
132 }
133
134 //---------------------------------------------------------
135 // TempoList::normalize
136 //---------------------------------------------------------
137
normalize()138 void TempoList::normalize()
139 {
140 unsigned frame = 0;
141 const uint64_t numer = (uint64_t)MusEGlobal::sampleRate;
142 const uint64_t denom = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL;
143
144 for (iTEvent e = begin(); e != end(); ++e) {
145 e->second->frame = frame;
146 // Tick resolution is less than frame resolution.
147 // Round up so that the reciprocal function (frame to tick) matches value for value.
148 frame += muse_multiply_64_div_64_to_64(
149 numer * (uint64_t)e->second->tempo,
150 e->first - e->second->tick,
151 denom, LargeIntRoundUp);
152 }
153 // Invalidate all cached frame or tick values used in the program, such as in class Pos.
154 // On the very next call of tick2frame() or frame2tick(), serial numbers are compared
155 // and if they are the same a cached value is returned.
156 // Otherwise if the serial numbers are not the same the value is recalculated.
157 ++_tempoSN;
158 }
159
160 //---------------------------------------------------------
161 // TempoList::dump
162 //---------------------------------------------------------
163
dump() const164 void TempoList::dump() const
165 {
166 printf("\nTempoList:\n");
167 for (ciTEvent i = begin(); i != end(); ++i) {
168 printf("%6d %06d Tempo %6d Frame %d\n",
169 i->first, i->second->tick, i->second->tempo,
170 i->second->frame);
171 }
172 }
173
174 //---------------------------------------------------------
175 // clear
176 //---------------------------------------------------------
177
clear()178 void TempoList::clear()
179 {
180 for (iTEvent i = begin(); i != end(); ++i)
181 delete i->second;
182 TEMPOLIST::clear();
183 insert(std::pair<const unsigned, TEvent*> (MAX_TICK+1, new TEvent(500000, 0)));
184 ++_tempoSN;
185 }
186
187 //---------------------------------------------------------
188 // eraseRange
189 //---------------------------------------------------------
190
eraseRange(unsigned stick,unsigned etick)191 void TempoList::eraseRange(unsigned stick, unsigned etick)
192 {
193 if(stick >= etick || stick > MAX_TICK)
194 return;
195 if(etick > MAX_TICK)
196 etick = MAX_TICK;
197
198 iTEvent se = MusEGlobal::tempomap.upper_bound(stick);
199 if(se == end() || (se->first == MAX_TICK+1))
200 return;
201
202 iTEvent ee = MusEGlobal::tempomap.upper_bound(etick);
203
204 ee->second->tempo = se->second->tempo;
205 ee->second->tick = se->second->tick;
206
207 for(iTEvent ite = se; ite != ee; ++ite)
208 delete ite->second;
209 erase(se, ee); // Erase range does NOT include the last element.
210 normalize();
211 }
212
213 //---------------------------------------------------------
214 // tempo
215 //---------------------------------------------------------
216
tempo(unsigned tick) const217 int TempoList::tempo(unsigned tick) const
218 {
219 if (useList) {
220 ciTEvent i = upper_bound(tick);
221 if (i == end()) {
222 printf("no TEMPO at tick %d,0x%x\n", tick, tick);
223 return 1000;
224 }
225 return i->second->tempo;
226 }
227 else
228 return _tempo;
229 }
230
bpm(unsigned tick) const231 float TempoList::bpm(unsigned tick) const
232 {
233 return (float)globalTempo() * 600000.0f / (float)tempo(tick);
234 }
235
bpmAt(unsigned tick) const236 float TempoList::bpmAt(unsigned tick) const
237 {
238 return (float)globalTempo() * 600000.0f / (float)tempoAt(tick);
239 }
240
241 //---------------------------------------------------------
242 // tempoAt
243 // Bypass the useList flag and read from the list
244 //---------------------------------------------------------
245
tempoAt(unsigned tick) const246 int TempoList::tempoAt(unsigned tick) const
247 {
248 ciTEvent i = upper_bound(tick);
249 if (i == end()) {
250 printf("tempoAt: no TEMPO at tick %d,0x%x\n", tick, tick);
251 return 1000;
252 }
253 return i->second->tempo;
254 }
255
256 //---------------------------------------------------------
257 // del
258 //---------------------------------------------------------
259
del(unsigned tick,bool do_normalize)260 void TempoList::del(unsigned tick, bool do_normalize)
261 {
262 iTEvent e = find(tick);
263 if (e == end()) {
264 printf("TempoList::del(%d): not found\n", tick);
265 return;
266 }
267 del(e, do_normalize);
268 }
269
del(iTEvent e,bool do_normalize)270 void TempoList::del(iTEvent e, bool do_normalize)
271 {
272 iTEvent ne = e;
273 ++ne;
274 if (ne == end()) {
275 printf("TempoList::del() HALLO\n");
276 return;
277 }
278 ne->second->tempo = e->second->tempo;
279 ne->second->tick = e->second->tick;
280 erase(e);
281 if(do_normalize)
282 normalize();
283 }
284
285 //---------------------------------------------------------
286 // setTempo
287 // called from transport window
288 // & slave mode tempo changes
289 //---------------------------------------------------------
290
setTempo(unsigned tick,int newTempo)291 void TempoList::setTempo(unsigned tick, int newTempo)
292 {
293 if (useList)
294 add(tick, newTempo, true);
295 else
296 {
297 setStaticTempo(newTempo);
298 }
299 }
300
301 //---------------------------------------------------------
302 // setGlobalTempo
303 //---------------------------------------------------------
304
setGlobalTempo(int val)305 void TempoList::setGlobalTempo(int val)
306 {
307 _globalTempo = val;
308 normalize();
309 }
310
311 //---------------------------------------------------------
312 // addTempo
313 //---------------------------------------------------------
314
addTempo(unsigned t,int tempo,bool do_normalize)315 void TempoList::addTempo(unsigned t, int tempo, bool do_normalize)
316 {
317 add(t, tempo, do_normalize);
318 }
319
320 //---------------------------------------------------------
321 // delTempo
322 //---------------------------------------------------------
323
delTempo(unsigned tick,bool do_normalize)324 void TempoList::delTempo(unsigned tick, bool do_normalize)
325 {
326 del(tick, do_normalize);
327 }
328
329 //---------------------------------------------------------
330 // setStaticTempo
331 //---------------------------------------------------------
332
setStaticTempo(int newTempo)333 void TempoList::setStaticTempo(int newTempo)
334 {
335 _tempo = newTempo;
336 ++_tempoSN;
337 }
338
339 //---------------------------------------------------------
340 // setMasterFlag
341 //---------------------------------------------------------
342
setMasterFlag(unsigned,bool val)343 bool TempoList::setMasterFlag(unsigned /*tick*/, bool val)
344 {
345 if (useList != val) {
346 useList = val;
347 ++_tempoSN;
348 return true;
349 }
350 return false;
351 }
352
353 //---------------------------------------------------------
354 // ticks2frames
355 //---------------------------------------------------------
356
ticks2frames(unsigned ticks,unsigned tempoTick,LargeIntRoundMode round_mode) const357 unsigned TempoList::ticks2frames(unsigned ticks, unsigned tempoTick, LargeIntRoundMode round_mode) const
358 {
359 // Tick resolution is less than frame resolution.
360 // Round up so that the reciprocal function (frame to tick) matches value for value.
361 return muse_multiply_64_div_64_to_64(
362 (uint64_t)MusEGlobal::sampleRate * (uint64_t)tempo(tempoTick), ticks,
363 (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL, round_mode);
364 }
365
366 // TODO
367 // //---------------------------------------------------------
368 // // frames2ticks
369 // //---------------------------------------------------------
370 //
371 // unsigned TempoList::frames2ticks(unsigned frames, unsigned tempoTick) const
372 // {
373 // const uint64_t numer = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL;
374 // const uint64_t denom = (uint64_t)MusEGlobal::sampleRate;
375 // return muse_multiply_64_div_64_to_64(numer, frames, denom * (uint64_t)tempo(tempoTick));
376 // }
377
378 //---------------------------------------------------------
379 // tick2frame
380 //---------------------------------------------------------
381
tick2frame(unsigned tick,unsigned frame,int * sn,LargeIntRoundMode round_mode) const382 unsigned TempoList::tick2frame(unsigned tick, unsigned frame, int* sn, LargeIntRoundMode round_mode) const
383 {
384 return (*sn == _tempoSN) ? frame : tick2frame(tick, sn, round_mode);
385 }
386
387 //---------------------------------------------------------
388 // tick2frame
389 //---------------------------------------------------------
390
tick2frame(unsigned tick,int * sn,LargeIntRoundMode round_mode) const391 unsigned TempoList::tick2frame(unsigned tick, int* sn, LargeIntRoundMode round_mode) const
392 {
393 unsigned f;
394 const uint64_t numer = (uint64_t)MusEGlobal::sampleRate;
395 const uint64_t denom = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL;
396 if (useList) {
397 ciTEvent i = upper_bound(tick);
398 if (i == end()) {
399 printf("tick2frame(%d,0x%x): not found\n", tick, tick);
400 return 0;
401 }
402 // Tick resolution is less than frame resolution.
403 // Round up so that the reciprocal function (frame to tick) matches value for value.
404 f = i->second->frame + muse_multiply_64_div_64_to_64(
405 numer * (uint64_t)i->second->tempo, tick - i->second->tick, denom, round_mode);
406 }
407 else {
408 // Tick resolution is less than frame resolution.
409 // Round up so that the reciprocal function (frame to tick) matches value for value.
410 f = muse_multiply_64_div_64_to_64(numer * (uint64_t)_tempo, tick, denom, round_mode);
411 }
412 if (sn)
413 *sn = _tempoSN;
414 return f;
415 }
416
417 //---------------------------------------------------------
418 // frame2tick
419 // return cached value t if list did not change
420 //---------------------------------------------------------
421
frame2tick(unsigned frame,unsigned t,int * sn,LargeIntRoundMode round_mode) const422 unsigned TempoList::frame2tick(unsigned frame, unsigned t, int* sn, LargeIntRoundMode round_mode) const
423 {
424 return (*sn == _tempoSN) ? t : frame2tick(frame, sn, round_mode);
425 }
426
427 //---------------------------------------------------------
428 // frame2tick
429 //---------------------------------------------------------
430
frame2tick(unsigned frame,int * sn,LargeIntRoundMode round_mode) const431 unsigned TempoList::frame2tick(unsigned frame, int* sn, LargeIntRoundMode round_mode) const
432 {
433 unsigned tick;
434 const uint64_t numer = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL;
435 const uint64_t denom = (uint64_t)MusEGlobal::sampleRate;
436 if (useList) {
437 ciTEvent e;
438 for (e = begin(); e != end();) {
439 ciTEvent ee = e;
440 ++ee;
441 if (ee == end())
442 break;
443 if (frame < ee->second->frame)
444 break;
445 e = ee;
446 }
447 // Normally do not round up here since (audio) frame resolution is higher than tick resolution.
448 tick = e->second->tick + muse_multiply_64_div_64_to_64(
449 numer, frame - e->second->frame, denom * (uint64_t)e->second->tempo, round_mode);
450 }
451 else
452 // Normally do not round up here since (audio) frame resolution is higher than tick resolution.
453 tick = muse_multiply_64_div_64_to_64(numer, frame, denom * (uint64_t)_tempo, round_mode);
454 if (sn)
455 *sn = _tempoSN;
456 return tick;
457 }
458
459 //---------------------------------------------------------
460 // deltaTick2frame
461 //---------------------------------------------------------
462
deltaTick2frame(unsigned tick1,unsigned tick2,int * sn,LargeIntRoundMode round_mode) const463 unsigned TempoList::deltaTick2frame(unsigned tick1, unsigned tick2, int* sn, LargeIntRoundMode round_mode) const
464 {
465 unsigned int f1, f2;
466 const uint64_t numer = (uint64_t)MusEGlobal::sampleRate;
467 const uint64_t denom = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL;
468 if (useList) {
469 ciTEvent i = upper_bound(tick1);
470 if (i == end()) {
471 printf("TempoList::deltaTick2frame: tick1:%d not found\n", tick1);
472 // abort();
473 return 0;
474 }
475 // Tick resolution is less than frame resolution.
476 // Round up so that the reciprocal function (frame to tick) matches value for value.
477 f1 = i->second->frame + muse_multiply_64_div_64_to_64(
478 numer * (uint64_t)i->second->tempo, tick1 - i->second->tick, denom, round_mode);
479
480 i = upper_bound(tick2);
481 if (i == end()) {
482 return 0;
483 }
484 // Tick resolution is less than frame resolution.
485 // Round up so that the reciprocal function (frame to tick) matches value for value.
486 f2 = i->second->frame + muse_multiply_64_div_64_to_64(
487 numer * (uint64_t)i->second->tempo, tick2 - i->second->tick, denom, round_mode);
488 }
489 else {
490 // Tick resolution is less than frame resolution.
491 // Round up so that the reciprocal function (frame to tick) matches value for value.
492 f1 = muse_multiply_64_div_64_to_64(numer * (uint64_t)_tempo, tick1, denom, round_mode);
493 // Tick resolution is less than frame resolution.
494 // Round up so that the reciprocal function (frame to tick) matches value for value.
495 f2 = muse_multiply_64_div_64_to_64(numer * (uint64_t)_tempo, tick2, denom, round_mode);
496 }
497 if (sn)
498 *sn = _tempoSN;
499 // FIXME: Caution: This should be rounded off properly somehow, but how to do that?
500 // But it seems to work so far.
501 return f2 - f1;
502 }
503
504
505 //---------------------------------------------------------
506 // deltaFrame2tick
507 //---------------------------------------------------------
508
deltaFrame2tick(unsigned frame1,unsigned frame2,int * sn,LargeIntRoundMode round_mode) const509 unsigned TempoList::deltaFrame2tick(unsigned frame1, unsigned frame2, int* sn, LargeIntRoundMode round_mode) const
510 {
511 unsigned tick1, tick2;
512 const uint64_t numer = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL;
513 const uint64_t denom = (uint64_t)MusEGlobal::sampleRate;
514 if (useList) {
515 ciTEvent e;
516 for (e = begin(); e != end();) {
517 ciTEvent ee = e;
518 ++ee;
519 if (ee == end())
520 break;
521 if (frame1 < ee->second->frame)
522 break;
523 e = ee;
524 }
525 // Normally do not round up here since (audio) frame resolution is higher than tick resolution.
526 tick1 = e->second->tick + muse_multiply_64_div_64_to_64(
527 numer, frame1 - e->second->frame, denom * (uint64_t)e->second->tempo, round_mode);
528
529 for (e = begin(); e != end();) {
530 ciTEvent ee = e;
531 ++ee;
532 if (ee == end())
533 break;
534 if (frame2 < ee->second->frame)
535 break;
536 e = ee;
537 }
538 // Normally do not round up here since (audio) frame resolution is higher than tick resolution.
539 tick2 = e->second->tick + muse_multiply_64_div_64_to_64(
540 numer, frame2 - e->second->frame, denom * (uint64_t)e->second->tempo, round_mode);
541 }
542 else
543 {
544 // Normally do not round up here since (audio) frame resolution is higher than tick resolution.
545 tick1 = muse_multiply_64_div_64_to_64(numer, frame1, denom * (uint64_t)_tempo, round_mode);
546 tick2 = muse_multiply_64_div_64_to_64(numer, frame2, denom * (uint64_t)_tempo, round_mode);
547 }
548 if (sn)
549 *sn = _tempoSN;
550 // FIXME: Caution: This should be rounded off properly somehow, but how to do that?
551 // But it seems to work so far.
552 return tick2 - tick1;
553 }
554
555 //---------------------------------------------------------
556 // TempoList::write
557 //---------------------------------------------------------
558
write(int level,Xml & xml) const559 void TempoList::write(int level, Xml& xml) const
560 {
561 xml.put(level++, "<tempolist fix=\"%d\">", _tempo);
562 if (_globalTempo != 100)
563 xml.intTag(level, "globalTempo", _globalTempo);
564 for (ciTEvent i = begin(); i != end(); ++i)
565 i->second->write(level, xml, i->first);
566 xml.tag(level, "/tempolist");
567 }
568
569 //---------------------------------------------------------
570 // TempoList::read
571 //---------------------------------------------------------
572
read(Xml & xml)573 void TempoList::read(Xml& xml)
574 {
575 for (;;) {
576 Xml::Token token = xml.parse();
577 const QString& tag = xml.s1();
578 switch (token) {
579 case Xml::Error:
580 case Xml::End:
581 return;
582 case Xml::TagStart:
583 if (tag == "tempo") {
584 TEvent* t = new TEvent();
585 unsigned tick = t->read(xml);
586 iTEvent pos = find(tick);
587 if (pos != end())
588 erase(pos);
589 insert(std::pair<const int, TEvent*> (tick, t));
590 }
591 else if (tag == "globalTempo")
592 _globalTempo = xml.parseInt();
593 else
594 xml.unknown("TempoList");
595 break;
596 case Xml::Attribut:
597 if (tag == "fix")
598 _tempo = xml.s2().toInt();
599 break;
600 case Xml::TagEnd:
601 if (tag == "tempolist") {
602 normalize();
603 return;
604 }
605 default:
606 break;
607 }
608 }
609 }
610
611 //---------------------------------------------------------
612 // TEvent::write
613 //---------------------------------------------------------
614
write(int level,Xml & xml,int at) const615 void TEvent::write(int level, Xml& xml, int at) const
616 {
617 xml.tag(level++, "tempo at=\"%d\"", at);
618 xml.intTag(level, "tick", tick);
619 xml.intTag(level, "val", tempo);
620 xml.tag(level, "/tempo");
621 }
622
623 //---------------------------------------------------------
624 // TEvent::read
625 //---------------------------------------------------------
626
read(Xml & xml)627 int TEvent::read(Xml& xml)
628 {
629 int at = 0;
630 for (;;) {
631 Xml::Token token = xml.parse();
632 const QString& tag = xml.s1();
633 switch (token) {
634 case Xml::Error:
635 case Xml::End:
636 return 0;
637 case Xml::TagStart:
638 if (tag == "tick")
639 tick = xml.parseInt();
640 else if (tag == "val")
641 tempo = xml.parseInt();
642 else
643 xml.unknown("TEvent");
644 break;
645 case Xml::Attribut:
646 if (tag == "at")
647 at = xml.s2().toInt();
648 break;
649 case Xml::TagEnd:
650 if (tag == "tempo") {
651 return at;
652 }
653 default:
654 break;
655 }
656 }
657 return 0;
658 }
659
660 //---------------------------------------------------------
661 // put
662 // return true on fifo overflow
663 //---------------------------------------------------------
664
put(const TempoRecEvent & event)665 bool TempoFifo::put(const TempoRecEvent& event)
666 {
667 if (size < TEMPO_FIFO_SIZE) {
668 fifo[wIndex] = event;
669 wIndex = (wIndex + 1) % TEMPO_FIFO_SIZE;
670 // q_atomic_increment(&size);
671 ++size;
672 return false;
673 }
674 return true;
675 }
676
677 //---------------------------------------------------------
678 // get
679 //---------------------------------------------------------
680
get()681 TempoRecEvent TempoFifo::get()
682 {
683 TempoRecEvent event(fifo[rIndex]);
684 rIndex = (rIndex + 1) % TEMPO_FIFO_SIZE;
685 --size;
686 return event;
687 }
688
689 //---------------------------------------------------------
690 // peek
691 //---------------------------------------------------------
692
peek(int n)693 const TempoRecEvent& TempoFifo::peek(int n)
694 {
695 int idx = (rIndex + n) % TEMPO_FIFO_SIZE;
696 return fifo[idx];
697 }
698
699 //---------------------------------------------------------
700 // remove
701 //---------------------------------------------------------
702
remove()703 void TempoFifo::remove()
704 {
705 rIndex = (rIndex + 1) % TEMPO_FIFO_SIZE;
706 --size;
707 }
708
709 } // namespace MusECore
710
711