CLAM-Development
1.1
|
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 00029 namespace MIDI 00030 { 00031 00032 class TempoImpl 00033 /* hidden implementation of class Tempo */ 00034 { 00035 friend class Tempo; 00036 private: 00037 Song* mSong; 00038 Track* mTrack; 00039 Track::EventIterator mIterator; 00040 int mUsPerQ; // microseconds per quarternote 00041 bool mHasTempo; 00042 00043 /* while iterating through the tempo events, we need to keep track of 00044 ** the time, by applying all the tempo changes and calculation the 00045 ** time increment. these two vars are used in the process 00046 */ 00047 int mLastTicks; 00048 int mLastTime; 00049 00050 TempoImpl(Song* song = 0,Track* track = 0) 00051 { 00052 Init(song,track); 00053 } 00054 00055 void Init(Song* song = 0,Track* track = 0) 00056 { 00057 mHasTempo=false; 00058 mSong = song; 00059 mTrack = track; 00060 if (mSong && mTrack==0) 00061 { 00062 for (int i=0;i<mSong->Tracks();i++) 00063 { 00064 Track* t = mSong->GetTrack(i); 00065 if (t->HasTempoEvents()) 00066 { 00067 mTrack = t; 00068 break; 00069 } 00070 } 00071 } 00072 if (mTrack) 00073 { 00074 mHasTempo=true; 00075 mIterator = mTrack->Begin(); 00076 } 00077 mUsPerQ = 500000; 00078 mLastTime = 0; 00079 mLastTicks = 0; 00080 } 00081 00082 Milliseconds TicksToTime(Ticks t) 00083 { 00084 if(!mHasTempo) 00085 { 00086 // return time based on tempo = 120 bpm because the track has not tempo events 00087 return (Milliseconds)((double)t*480.0/(double)mSong->GetTicksPerQ()); 00088 } 00089 00090 int i = 0; 00091 00092 /* move the iterator to the next tempo event */ 00093 while (mIterator!=mTrack->End()) 00094 { 00095 const Event &ev = **mIterator; 00096 if ( ev[0]==0xFF && ev[1]==0x51) 00097 { 00098 break; 00099 } 00100 mIterator++; 00101 } 00102 00103 /* if we are at the end of the tempo track, or we are at a tempo 00104 ** event _after_ Ticks t, then we start from the beginning 00105 */ 00106 if (mIterator==mTrack->End() || ((*mIterator)->GetTicks()>t)) 00107 { 00108 mIterator = mTrack->Begin(); 00109 mUsPerQ = 500000; 00110 mLastTime = 0; 00111 mLastTicks = 0; 00112 } 00113 00114 std::list<Event*>::const_iterator prevIterator = mIterator; 00115 00116 00117 /* look for the first tempo event after Ticks t, and adjust 00118 ** mLastTime and mLastTicks while doing this 00119 */ 00120 while (mIterator!=mTrack->End()) 00121 { 00122 const Event &ev = **mIterator; 00123 if ( ev[0]==0xFF && ev[1]==0x51 ) 00124 { 00125 if (ev.GetTicks()>t) 00126 { 00127 break; 00128 } 00129 00130 MetaEvent* ev = (MetaEvent*) *mIterator; 00131 00132 // the following is to say: 00133 // (ticks/ticksPerQ)*msPerQ 00134 // but we change the order to stay with integers 00135 mLastTime += 00136 ( (TInt64(ev->GetTicks() - mLastTicks) * TInt64(mUsPerQ)) / 00137 (TInt64(mSong->GetTicksPerQ())*TInt64(1000)) ); 00138 00139 mUsPerQ = 00140 (ev->mData[0]<<16) | 00141 (ev->mData[1]<<8) | 00142 (ev->mData[0]); 00143 00144 mLastTicks = ev->GetTicks(); 00145 00146 prevIterator = mIterator; 00147 } 00148 mIterator++; 00149 i++; 00150 } 00151 00152 /* move one back, to the event before or at Ticks t */ 00153 mIterator = prevIterator; 00154 00155 // the following is to say: 00156 // (ticks/ticksPerQ)*msPerQ 00157 // but we change the order to stay with integers 00158 return mLastTime + Milliseconds( 00159 (TInt64(t - mLastTicks) * TInt64(mUsPerQ)) / 00160 (TInt64(mSong->GetTicksPerQ())*TInt64(1000))); 00161 } 00162 }; 00163 00164 Tempo::Tempo(Song* song,Track* track) 00165 { 00166 mImpl = new TempoImpl(song,track); 00167 } 00168 00169 Tempo::~Tempo() 00170 { 00171 delete mImpl; 00172 } 00173 00174 void Tempo::Init(Song* song,Track* track) 00175 { 00176 mImpl->Init(song,track); 00177 } 00178 00179 Milliseconds Tempo::TicksToTime(Ticks t) 00180 { 00181 return mImpl->TicksToTime(t); 00182 } 00183 00184 }; 00185