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