MIDITempo.cxx

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001-2004 MUSIC TECHNOLOGY GROUP (MTG)
00003  * UNIVERSITAT POMPEU FABRA
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00018  *
00019  * MIDIFileReader C++ classes
00020  * This code is part of the CLAM library, but also usable stand-alone.
00021  * Maarten de Boer <mdeboer@iua.upf.es>
00022  *
00023  */
00024 #include "MIDITrack.hxx"
00025 #include "MIDISong.hxx"
00026 #include "MIDITempo.hxx"
00027 // #include "Defines.hxx"
00028 #include <CLAM/MIDIDataTypes.hxx>
00029 
00030 namespace MIDI
00031 {
00032 
00033         class TempoImpl
00034         /* hidden implementation of class Tempo */
00035         {
00036         friend class Tempo;
00037         private:
00038                 Song* mSong;
00039                 Track* mTrack;
00040                 Track::EventIterator mIterator;
00041                 int mUsPerQ; // microseconds per quarternote
00042                 bool mHasTempo;
00043                 
00044                 /* while iterating through the tempo events, we need to keep track of
00045                 ** the time, by applying all the tempo changes and calculation the 
00046                 ** time increment. these two vars are used in the process
00047                 */
00048                 int mLastTicks; 
00049                 int mLastTime;
00050                 
00051                 TempoImpl(Song* song = 0,Track* track = 0)
00052                 {
00053                         Init(song,track);
00054                 }
00055                 
00056                 void Init(Song* song = 0,Track* track = 0)
00057                 {
00058                         mHasTempo=false;
00059                         mSong = song;
00060                         mTrack = track;
00061                         if (mSong && mTrack==0)
00062                         {
00063                                 for (int i=0;i<mSong->Tracks();i++)
00064                                 {
00065                                         Track* t = mSong->GetTrack(i);
00066                                         if (t->HasTempoEvents())
00067                                         {
00068                                                 mTrack = t;
00069                                                 break;
00070                                         }
00071                                 }
00072                         }
00073                         if (mTrack)
00074                         {
00075                                 mHasTempo=true;
00076                                 mIterator = mTrack->Begin();
00077                         }
00078                         mUsPerQ = 500000;
00079                         mLastTime = 0;
00080                         mLastTicks = 0;
00081                 }
00082 
00083                 Milliseconds TicksToTime(Ticks t)
00084                 {
00085                         if(!mHasTempo)
00086                         {
00087                                 // return time based on tempo = 120 bpm because the track has not tempo events
00088                                 return (Milliseconds)((double)t*480.0/(double)mSong->GetTicksPerQ());
00089                         }
00090 
00091                         int i = 0;
00092 
00093                         /* move the iterator to the next tempo event */
00094                         while (mIterator!=mTrack->End())
00095                         {
00096                                 const Event &ev = **mIterator;
00097                                 if ( ev[0]==0xFF && ev[1]==0x51)
00098                                 {
00099                                         break;
00100                                 }
00101                                 mIterator++;
00102                         }
00103 
00104                         /* if we are at the end of the tempo track, or we are at a tempo 
00105                         ** event _after_ Ticks t, then we start from the beginning
00106                         */
00107                         if (mIterator==mTrack->End() || ((*mIterator)->GetTicks()>t))
00108                         {
00109                                 mIterator = mTrack->Begin();
00110                                 mUsPerQ = 500000;
00111                                 mLastTime = 0;
00112                                 mLastTicks = 0;
00113                         }
00114 
00115                         std::list<Event*>::const_iterator prevIterator = mIterator;
00116 
00117 
00118                         /* look for the first tempo event after Ticks t, and adjust
00119                         ** mLastTime and mLastTicks while doing this
00120                         */
00121                         while (mIterator!=mTrack->End())
00122                         {
00123                                 const Event &ev = **mIterator;
00124                                 if ( ev[0]==0xFF && ev[1]==0x51 )
00125                                 {
00126                                         if (ev.GetTicks()>t)
00127                                         {
00128                                                 break;
00129                                         }
00130 
00131                                         MetaEvent* ev = (MetaEvent*) *mIterator;
00132 
00133                                         // the following is to say: 
00134                                         // (ticks/ticksPerQ)*msPerQ
00135                                         // but we change the order to stay with integers
00136                                         mLastTime += 
00137                                                 ( (TInt64(ev->GetTicks() - mLastTicks) * TInt64(mUsPerQ)) / 
00138                                                         (TInt64(mSong->GetTicksPerQ())*TInt64(1000)) );
00139 
00140                                         mUsPerQ = 
00141                                                 (ev->mData[0]<<16) | 
00142                                                 (ev->mData[1]<<8) |
00143                                                 (ev->mData[0]);
00144 
00145                                         mLastTicks = ev->GetTicks();
00146 
00147                                         prevIterator = mIterator;
00148                                 }
00149                                 mIterator++;
00150                                 i++;
00151                         }
00152 
00153                         /* move one back, to the event before or at Ticks t */
00154                         mIterator = prevIterator;
00155 
00156                         // the following is to say: 
00157                         // (ticks/ticksPerQ)*msPerQ
00158                         // but we change the order to stay with integers
00159                         return mLastTime + Milliseconds(
00160                                         (TInt64(t - mLastTicks) * TInt64(mUsPerQ)) / 
00161                                         (TInt64(mSong->GetTicksPerQ())*TInt64(1000))); 
00162                 }
00163         };
00164 
00165         Tempo::Tempo(Song* song,Track* track)
00166         {
00167                 mImpl = new TempoImpl(song,track);
00168         }
00169 
00170         Tempo::~Tempo()
00171         {
00172                 delete mImpl;
00173         }
00174 
00175         void Tempo::Init(Song* song,Track* track)
00176         {
00177                 mImpl->Init(song,track);
00178         }
00179         
00180         Milliseconds Tempo::TicksToTime(Ticks t)
00181         {
00182                 return mImpl->TicksToTime(t);
00183         }
00184         
00185 };
00186 
Generated by  doxygen 1.6.3