CLAM-Development  1.4.0
MIDITempo.cxx
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2001-2004 MUSIC TECHNOLOGY GROUP (MTG)
3  * UNIVERSITAT POMPEU FABRA
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  * MIDIFileReader C++ classes
20  * This code is part of the CLAM library, but also usable stand-alone.
21  * Maarten de Boer <mdeboer@iua.upf.es>
22  *
23  */
24 #include "MIDITrack.hxx"
25 #include "MIDISong.hxx"
26 #include "MIDITempo.hxx"
27 // #include "Defines.hxx"
28 #include <CLAM/MIDIDataTypes.hxx>
29 
30 namespace MIDI
31 {
32 
33  class TempoImpl
34  /* hidden implementation of class Tempo */
35  {
36  friend class Tempo;
37  private:
38  Song* mSong;
39  Track* mTrack;
40  Track::EventIterator mIterator;
41  int mUsPerQ; // microseconds per quarternote
42  bool mHasTempo;
43 
44  /* while iterating through the tempo events, we need to keep track of
45  ** the time, by applying all the tempo changes and calculation the
46  ** time increment. these two vars are used in the process
47  */
48  int mLastTicks;
49  int mLastTime;
50 
51  TempoImpl(Song* song = 0,Track* track = 0)
52  {
53  Init(song,track);
54  }
55 
56  void Init(Song* song = 0,Track* track = 0)
57  {
58  mHasTempo=false;
59  mSong = song;
60  mTrack = track;
61  if (mSong && mTrack==0)
62  {
63  for (int i=0;i<mSong->Tracks();i++)
64  {
65  Track* t = mSong->GetTrack(i);
66  if (t->HasTempoEvents())
67  {
68  mTrack = t;
69  break;
70  }
71  }
72  }
73  if (mTrack)
74  {
75  mHasTempo=true;
76  mIterator = mTrack->Begin();
77  }
78  mUsPerQ = 500000;
79  mLastTime = 0;
80  mLastTicks = 0;
81  }
82 
83  Milliseconds TicksToTime(Ticks t)
84  {
85  if(!mHasTempo)
86  {
87  // return time based on tempo = 120 bpm because the track has not tempo events
88  return (Milliseconds)((double)t*480.0/(double)mSong->GetTicksPerQ());
89  }
90 
91  int i = 0;
92 
93  /* move the iterator to the next tempo event */
94  while (mIterator!=mTrack->End())
95  {
96  const Event &ev = **mIterator;
97  if ( ev[0]==0xFF && ev[1]==0x51)
98  {
99  break;
100  }
101  mIterator++;
102  }
103 
104  /* if we are at the end of the tempo track, or we are at a tempo
105  ** event _after_ Ticks t, then we start from the beginning
106  */
107  if (mIterator==mTrack->End() || ((*mIterator)->GetTicks()>t))
108  {
109  mIterator = mTrack->Begin();
110  mUsPerQ = 500000;
111  mLastTime = 0;
112  mLastTicks = 0;
113  }
114 
115  std::list<Event*>::const_iterator prevIterator = mIterator;
116 
117 
118  /* look for the first tempo event after Ticks t, and adjust
119  ** mLastTime and mLastTicks while doing this
120  */
121  while (mIterator!=mTrack->End())
122  {
123  const Event &ev = **mIterator;
124  if ( ev[0]==0xFF && ev[1]==0x51 )
125  {
126  if (ev.GetTicks()>t)
127  {
128  break;
129  }
130 
131  MetaEvent* ev = (MetaEvent*) *mIterator;
132 
133  // the following is to say:
134  // (ticks/ticksPerQ)*msPerQ
135  // but we change the order to stay with integers
136  mLastTime +=
137  ( (TInt64(ev->GetTicks() - mLastTicks) * TInt64(mUsPerQ)) /
138  (TInt64(mSong->GetTicksPerQ())*TInt64(1000)) );
139 
140  mUsPerQ =
141  (ev->mData[0]<<16) |
142  (ev->mData[1]<<8) |
143  (ev->mData[0]);
144 
145  mLastTicks = ev->GetTicks();
146 
147  prevIterator = mIterator;
148  }
149  mIterator++;
150  i++;
151  }
152 
153  /* move one back, to the event before or at Ticks t */
154  mIterator = prevIterator;
155 
156  // the following is to say:
157  // (ticks/ticksPerQ)*msPerQ
158  // but we change the order to stay with integers
159  return mLastTime + Milliseconds(
160  (TInt64(t - mLastTicks) * TInt64(mUsPerQ)) /
161  (TInt64(mSong->GetTicksPerQ())*TInt64(1000)));
162  }
163  };
164 
165  Tempo::Tempo(Song* song,Track* track)
166  {
167  mImpl = new TempoImpl(song,track);
168  }
169 
171  {
172  delete mImpl;
173  }
174 
175  void Tempo::Init(Song* song,Track* track)
176  {
177  mImpl->Init(song,track);
178  }
179 
181  {
182  return mImpl->TicksToTime(t);
183  }
184 
185 };
186