XMidiSequence.cpp

Go to the documentation of this file.
00001 /*
00002 Copyright (C) 2003  The Pentagram Team
00003 
00004 This program is free software; you can redistribute it and/or
00005 modify it under the terms of the GNU General Public License
00006 as published by the Free Software Foundation; either version 2
00007 of the License, or (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 */
00018 
00019 #include "pent_include.h"
00020 
00021 #include "XMidiSequence.h"
00022 #include "XMidiSequenceHandler.h"
00023 #include "XMidiFile.h"
00024 
00025 // Define this to stop the Midisequencer from attempting to
00026 // catch up time if it has missed over 1200 ticks or 1/5th of a second
00027 // This is useful for when debugging, since the Sequencer will not attempt
00028 // to play hundreds of events at the same time if execution is broken, and
00029 // later resumed.
00030 #define XMIDISEQUENCER_NO_CATCHUP_WAIT_OVER 1200
00031 
00032 const uint16    XMidiSequence::ChannelShadow::centre_value = 0x2000;
00033 const uint8             XMidiSequence::ChannelShadow::fine_value = centre_value & 127;
00034 const uint8             XMidiSequence::ChannelShadow::coarse_value = centre_value >> 7;
00035 const uint16    XMidiSequence::ChannelShadow::combined_value = (coarse_value << 8) | fine_value;
00036 
00037 XMidiSequence::XMidiSequence(XMidiSequenceHandler *Handler, uint16 seq_id, XMidiEventList *events, 
00038                                                 bool Repeat, int volume, int branch) :
00039         handler(Handler), sequence_id(seq_id), evntlist(events), event(0), 
00040         repeat(Repeat),  notes_on(), last_tick(0), loop_num(-1), vol_multi(volume),
00041         paused(false), speed(100)
00042 {
00043         std::memset(loop_event,0,XMIDI_MAX_FOR_LOOP_COUNT*sizeof(int));
00044         std::memset(loop_count,0,XMIDI_MAX_FOR_LOOP_COUNT*sizeof(int));
00045         event = evntlist->events;
00046 
00047         for (int i = 0; i < 16; i++)
00048         {
00049                 shadows[i].reset();
00050                 handler->sequenceSendEvent(sequence_id, i | (MIDI_STATUS_CONTROLLER << 4) | (XMIDI_CONTROLLER_BANK_CHANGE << 8));
00051         }
00052 
00053         // Jump to branch
00054         XMidiEvent *brnch = events->findBranchEvent(branch);
00055         if (brnch)
00056         {
00057                 last_tick = brnch->time;
00058                 event = brnch;
00059 
00060                 XMidiEvent  *event = evntlist->events;
00061                 while (event != brnch)
00062                 {
00063                         updateShadowForEvent(event);
00064                         event=event->next;
00065                 }
00066                 for (int i = 0; i < 16; i++) gainChannel(i);
00067         }
00068 
00069         initClock();
00070 }
00071 
00072 XMidiSequence::~XMidiSequence()
00073 {
00074         // Handle note off's here
00075         while (XMidiEvent *note = notes_on.Pop())
00076                 handler->sequenceSendEvent(sequence_id, note->status + (note->data[0] << 8));
00077 
00078         for (int i = 0; i < 16; i++) 
00079         {
00080                 shadows[i].reset();
00081                 applyShadow(i);
00082         }
00083 
00084         // 'Release' it
00085         evntlist->decerementCounter();
00086 }
00087 
00088 void XMidiSequence::ChannelShadow::reset()
00089 {
00090         pitchWheel = combined_value;
00091 
00092         program = -1;
00093 
00094         // Bank Select
00095         bank[0] = 0;
00096         bank[1] = 0;
00097 
00098         // Modulation Wheel
00099         modWheel[0] = coarse_value;
00100         modWheel[1] = fine_value;
00101         
00102         // Foot pedal
00103         footpedal[0] = 0;
00104         footpedal[1] = 0;
00105         
00106         // Volume
00107         volumes[0] = coarse_value;
00108         volumes[1] = fine_value;
00109 
00110         // Pan
00111         pan[0] = coarse_value;
00112         pan[1] = fine_value;
00113 
00114         // Balance
00115         balance[0] = coarse_value;
00116         balance[1] = fine_value;
00117 
00118         // Expression
00119         expression[0] = 127;
00120         expression[1] = 0;
00121 
00122         // sustain
00123         sustain = 0;
00124 
00125         // Effects (Reverb)
00126         effects = 0;
00127 
00128         // Chorus
00129         chorus = 0;
00130 
00131         // Xmidi Bank 
00132         xbank = 0;
00133 }
00134 
00135 int XMidiSequence::playEvent()
00136 {
00137         XMidiEvent *note;
00138 
00139         // Handle note off's here
00140         while ((note = notes_on.PopTime(getRealTime())) != 0)
00141                 handler->sequenceSendEvent(sequence_id, note->status + (note->data[0] << 8));
00142 
00143         // No events left, but we still have notes on, so say we are still playing, if not report we've finished
00144         if (!event)
00145         {
00146                 if (notes_on.GetNotes()) return 1;
00147                 else return -1;
00148         }
00149 
00150         // Effectively paused, so indicate it 
00151         if (speed <= 0 || paused) return 1;
00152 
00153         // Play all waiting notes;
00154         sint32 aim = ((event->time-last_tick)*5000)/speed;
00155         sint32 diff = aim - getTime ();
00156 
00157         if (diff > 5) return 1;
00158 
00159         addOffset(aim);
00160 
00161         last_tick = event->time;
00162 
00163 #ifdef XMIDISEQUENCER_NO_CATCHUP_WAIT_OVER
00164         if (diff < -XMIDISEQUENCER_NO_CATCHUP_WAIT_OVER) addOffset(-diff);
00165 #endif
00166 
00167         // Handle note off's here too
00168         while ((note = notes_on.PopTime(getRealTime())) != 0)
00169                 handler->sequenceSendEvent(sequence_id, note->status + (note->data[0] << 8));
00170 
00171                 // XMidi For Loop
00172         if ((event->status >> 4) == MIDI_STATUS_CONTROLLER && event->data[0] == XMIDI_CONTROLLER_FOR_LOOP)
00173         {
00174                 if (loop_num < XMIDI_MAX_FOR_LOOP_COUNT) loop_num++;
00175 
00176                 loop_count[loop_num] = event->data[1];
00177                 loop_event[loop_num] = event;
00178 
00179         }       // XMidi Next/Break
00180         else if ((event->status >> 4) == MIDI_STATUS_CONTROLLER && event->data[0] == XMIDI_CONTROLLER_NEXT_BREAK)
00181         {
00182                 if (loop_num != -1)
00183                 {
00184                         if (event->data[1] < 64)
00185                         {
00186                                 loop_num--;
00187                         }
00188                 }
00189                 else
00190                 {
00191                         // See if we can find the branch index
00192                         // If we can, jump to that
00193                         XMidiEvent *branch = evntlist->findBranchEvent(126);
00194 
00195                         if (branch)
00196                         {
00197                                 loop_num = 0;
00198                                 loop_count[loop_num] = 1;
00199                                 loop_event[loop_num] = branch;
00200                         }
00201                 }
00202                 event = NULL;
00203         }       // XMidi Callback Trigger
00204         else if ((event->status >> 4) == MIDI_STATUS_CONTROLLER && event->data[0] == XMIDI_CONTROLLER_CALLBACK_TRIG)
00205         {
00206                 handler->handleCallbackTrigger(sequence_id, event->data[1]);
00207         }       // Not SysEx
00208         else if (event->status < 0xF0)
00209         {
00210                 sendEvent();
00211         }
00212                 // SysEx gets sent immediately
00213         else if (event->status != 0xFF)
00214         {
00215                 handler->sequenceSendSysEx(sequence_id, event->status, 
00216                                 event->ex.sysex_data.buffer, event->ex.sysex_data.len);
00217         }
00218 
00219         // If we've got another note, play that next
00220         if (event) event = event->next;
00221 
00222         // Now, handle what to do when we are at the end
00223         if (!event)
00224         {
00225                 // If we have for loop events, follow them
00226                 if (loop_num != -1)
00227                 {
00228                         event = loop_event[loop_num]->next;
00229                         last_tick = loop_event[loop_num]->time;
00230 
00231                         if (loop_count[loop_num])
00232                                 if (!--loop_count[loop_num])
00233                                         loop_num--;
00234                 }
00235                 // Or, if we are repeating, but there hasn't been any for loop events,
00236                 // repeat from the start
00237                 else if (repeat)
00238                 {
00239                         event = evntlist->events;
00240                         if (last_tick == 0) return 1;
00241                         last_tick = 0;
00242                 }
00243                 // If we are not repeating, then return saying we are end
00244                 else
00245                 {
00246                         if (notes_on.GetNotes()) return 1;
00247                         return -1;
00248                 }
00249         }
00250 
00251         if (!event)
00252         {
00253                 if (notes_on.GetNotes()) return 1;
00254                 else return -1;
00255         }
00256 
00257         aim = ((event->time-last_tick)*5000)/speed;
00258         diff = aim - getTime ();
00259 
00260         if (diff < 0) return 0; // Next event is ready now!
00261         return 1; 
00262 }
00263 
00264 sint32 XMidiSequence::timeTillNext()
00265 {
00266         sint32 sixthoToNext = 0x7FFFFFFF; // Int max
00267 
00268         // Time remaining on notes currently being played
00269         XMidiEvent *note;
00270         if (note = notes_on.GetNotes()) {
00271                 sint32 diff = note->ex.note_on.note_time - getRealTime();
00272                 if (diff < sixthoToNext) sixthoToNext = diff; 
00273         }
00274 
00275         // Time till the next event, if we are playing
00276         if (speed > 0 && event && !paused)
00277         {
00278                 sint32 aim = ((event->time-last_tick)*5000)/speed;
00279                 sint32 diff = aim - getTime ();
00280 
00281                 if (diff < sixthoToNext) sixthoToNext = diff;
00282         }
00283         return sixthoToNext/6;
00284 }
00285 
00286 void XMidiSequence::updateShadowForEvent(XMidiEvent *event)
00287 {
00288         unsigned int chan = event->status & 0xF;
00289         unsigned int type = event->status >> 4;
00290         uint32 data = event->data[0] | (event->data[1] << 8);
00291 
00292         // Shouldn't be required. XMidi should automatically detect all anyway
00293         //evntlist->chan_mask |= 1 << chan; 
00294 
00295         // Update the shadows here
00296 
00297         if (type == MIDI_STATUS_CONTROLLER) {
00298                 // Channel volume
00299                 if (event->data[0] == 7) {
00300                         shadows[chan].volumes[0] = event->data[1];
00301                 }
00302                 else if (event->data[0] == 39) {
00303                         shadows[chan].volumes[1] = event->data[1];
00304                 }
00305                 // Bank
00306                 else if (event->data[0] == 0 || event->data[0] == 32) {
00307                         shadows[chan].bank[event->data[0]/32] = event->data[1];
00308                 }
00309                 // modWheel
00310                 else if (event->data[0] == 1 || event->data[0] == 33) {
00311                         shadows[chan].modWheel[event->data[0]/32] = event->data[1];
00312                 }
00313                 // footpedal
00314                 else if (event->data[0] == 4 || event->data[0] == 36) {
00315                         shadows[chan].footpedal[event->data[0]/32] = event->data[1];
00316                 }
00317                 // pan
00318                 else if (event->data[0] == 9 || event->data[0] == 41) {
00319                         shadows[chan].pan[event->data[0]/32] = event->data[1];
00320                 }
00321                 // balance
00322                 else if (event->data[0] == 10 || event->data[0] == 42) {
00323                         shadows[chan].balance[event->data[0]/32] = event->data[1];
00324                 }
00325                 // expression
00326                 else if (event->data[0] == 11 || event->data[0] == 43) {
00327                         shadows[chan].expression[event->data[0]/32] = event->data[1];
00328                 }
00329                 // sustain
00330                 else if (event->data[0] == 64) {
00331                         shadows[chan].effects = event->data[1];
00332                 }
00333                 // effect
00334                 else if (event->data[0] == 91) {
00335                         shadows[chan].effects = event->data[1];
00336                 }
00337                 // chorus
00338                 else if (event->data[0] == 93) {
00339                         shadows[chan].chorus = event->data[1];
00340                 }
00341                 // XMidi bank
00342                 else if (event->data[0] == XMIDI_CONTROLLER_BANK_CHANGE) {
00343                         shadows[chan].xbank = event->data[1];
00344                 }
00345         }
00346         else if (type == MIDI_STATUS_PROG_CHANGE)
00347         {
00348                 shadows[chan].program = data;
00349         }
00350         else if (type == MIDI_STATUS_PITCH_WHEEL)
00351         {
00352                 shadows[chan].pitchWheel = data;
00353         }
00354 }
00355 
00356 void XMidiSequence::sendEvent()
00357 {
00358         unsigned int chan = event->status & 0xF;
00359         unsigned int type = event->status >> 4;
00360         uint32 data = event->data[0] | (event->data[1] << 8);
00361 
00362         // Shouldn't be required. XMidi should automatically detect all anyway
00363         //evntlist->chan_mask |= 1 << chan; 
00364 
00365         // Update the shadows here
00366         updateShadowForEvent(event);
00367 
00368         if (type == MIDI_STATUS_CONTROLLER) {
00369                 // Channel volume
00370                 if (event->data[0] == 7) {
00371                         data = event->data[0] | (((event->data[1] * vol_multi)/0xFF)<<8);
00372                 }
00373         }
00374         else if (type == MIDI_STATUS_AFTERTOUCH) {
00375                 notes_on.SetAftertouch(event);
00376         }
00377 
00378         if ((type != MIDI_STATUS_NOTE_ON || event->data[1]) && type != MIDI_STATUS_NOTE_OFF) {
00379 
00380                 if (type == MIDI_STATUS_NOTE_ON) {
00381 
00382                         if (!event->data[1]) return;
00383 
00384                         notes_on.Remove(event);
00385 
00386                         handler->sequenceSendEvent(sequence_id, event->status | (data<<8));
00387                         event->ex.note_on.actualvel = event->data[1];
00388 
00389                         notes_on.Push (event, ((event->ex.note_on.duration-1)*5000/speed) + getStart());
00390                 }
00391                 // Only send IF the channel has been marked enabled
00392                 else 
00393                         handler->sequenceSendEvent(sequence_id, event->status | (data<<8));
00394         }
00395 }
00396 
00397 #define SendController(ctrl,name) \
00398         handler->sequenceSendEvent(sequence_id, i | (MIDI_STATUS_CONTROLLER << 4) | (ctrl << 8) | (shadows[i].name[0] << 16)); \
00399         handler->sequenceSendEvent(sequence_id, i | (MIDI_STATUS_CONTROLLER << 4) | ((ctrl+32) << 8) | (shadows[i].name[1] << 16));
00400 
00401 void XMidiSequence::applyShadow(int i)
00402 {
00403         // Pitch Wheel
00404         handler->sequenceSendEvent(sequence_id, i | (MIDI_STATUS_PITCH_WHEEL << 4) | (shadows[i].pitchWheel << 8));
00405         
00406         // Modulation Wheel
00407         SendController(1,modWheel);
00408         
00409         // Footpedal
00410         SendController(4,footpedal);
00411         
00412         // Volume
00413         handler->sequenceSendEvent(sequence_id, i | (MIDI_STATUS_CONTROLLER << 4) | (7 << 8) | (((shadows[i].volumes[0]*vol_multi)/0xFF) << 16));
00414         handler->sequenceSendEvent(sequence_id, i | (MIDI_STATUS_CONTROLLER << 4) | (39 << 8) | (shadows[i].volumes[1] << 16));
00415 
00416         // Pan
00417         SendController(9,pan);
00418 
00419         // Balance
00420         SendController(10,balance);
00421 
00422         // expression
00423         SendController(11,expression);
00424 
00425         // Sustain
00426         handler->sequenceSendEvent(sequence_id, i | (MIDI_STATUS_CONTROLLER << 4) | (64 << 8) | (shadows[i].sustain << 16));
00427 
00428         // Effects (Reverb)
00429         handler->sequenceSendEvent(sequence_id, i | (MIDI_STATUS_CONTROLLER << 4) | (91 << 8) | (shadows[i].effects << 16));
00430 
00431         // Chorus
00432         handler->sequenceSendEvent(sequence_id, i | (MIDI_STATUS_CONTROLLER << 4) | (93 << 8) | (shadows[i].chorus << 16));
00433 
00434         // XMidi Bank
00435         handler->sequenceSendEvent(sequence_id, i | (MIDI_STATUS_CONTROLLER << 4) | (XMIDI_CONTROLLER_BANK_CHANGE << 8) | (shadows[i].xbank << 16));
00436 
00437         // Bank Select
00438         if (shadows[i].program != -1) handler->sequenceSendEvent(sequence_id, i | (MIDI_STATUS_PROG_CHANGE << 4) | (shadows[i].program << 8));
00439         SendController(0,bank);
00440 }
00441 
00442 void XMidiSequence::setVolume(int new_volume)
00443 {
00444         vol_multi = new_volume;
00445         new_volume = -1;
00446 
00447         // Only update used channels
00448         for (int i = 0; i < 16; i++) if (evntlist->chan_mask & (1 << i)) {
00449                 uint32 message = i;
00450                 message |= MIDI_STATUS_CONTROLLER << 4;
00451                 message |= 7 << 8;
00452                 message |= ((shadows[i].volumes[0] * vol_multi)/0xFF)<<16;
00453                 handler->sequenceSendEvent(sequence_id, message);
00454         }
00455 }
00456 
00457 void XMidiSequence::loseChannel(int i)
00458 {
00459         // If the channel matches, send a note off for any note
00460         XMidiEvent* note = notes_on.GetNotes();
00461         while (note)
00462         {
00463                 if ((note->status&0xF) == i) 
00464                         handler->sequenceSendEvent(sequence_id, note->status + (note->data[0] << 8));
00465                 note = note->ex.note_on.next_note;
00466         }
00467         
00468 }
00469 
00470 void XMidiSequence::gainChannel(int i)
00471 {
00472         applyShadow(i);
00473 
00474         // If the channel matches, send a note on for any note
00475         XMidiEvent* note = notes_on.GetNotes();
00476         while (note)
00477         {
00478                 if ((note->status&0xF) == i) 
00479                         handler->sequenceSendEvent(sequence_id, note->status | (note->data[0] << 8) | (note->ex.note_on.actualvel << 16));
00480                 note = note->ex.note_on.next_note;
00481         }
00482 }
00483 
00484 void XMidiSequence::pause()
00485 {
00486         paused = true; 
00487         for (int i = 0; i < 16; i++)
00488                 if (evntlist->chan_mask & (1 << i))
00489                         loseChannel(i);
00490 }
00491 
00492 void XMidiSequence::unpause()
00493 { 
00494         paused = false; 
00495         for (int i = 0; i < 16; i++)
00496                 if (evntlist->chan_mask & (1 << i))
00497                         applyShadow(i);
00498 }
00499 
00500 int XMidiSequence::countNotesOn(int chan)
00501 {
00502         if (paused) return 0;
00503 
00504         int ret = 0;
00505         XMidiEvent* note = notes_on.GetNotes();
00506         while (note)
00507         {
00508                 if ((note->status&0xF) == chan) 
00509                         ret++;
00510                 note = note->ex.note_on.next_note;
00511         }
00512         return ret;
00513 }
00514 
00515 // Protection

Generated on Fri Jul 27 22:27:55 2007 for pentagram by  doxygen 1.4.7