// // GetTrackVDWord // // Attempts to parse a variable length DWORD from the given track. A VDWord // in a MIDI file // (a) is in lo-hi format // (b) has the high bit set on every byte except the last // // Returns the DWORD in *lpdw and TRUE on success; else // FALSE if we hit end of track first. Sets ITS_F_ENDOFTRK // if we hit end of track. // static BOOL GetTrackVDWord( PINTRACKSTATE ptsTrack, LPDWORD lpdw ) { BYTE byByte; DWORD dw = 0; if( ptsTrack->fdwTrack & ITS_F_ENDOFTRK ) return( TRUE ); do { if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk ) { ptsTrack->fdwTrack |= ITS_F_ENDOFTRK; return( TRUE ); } if( GetTrackByte( ptsTrack, &byByte )) return( TRUE ); dw = ( dw << 7 ) | ( byByte & 0x7F ); } while( byByte & 0x80 ); *lpdw = dw; return( FALSE ); }
/* GetTrackVDWord * * Parse a variable length DWORD from the given track. A VDWord in * a MIDI file is in lo-hi format and has the high bit set on every * byte except the last. * * Returns the DWORD in *v and zero on success; else non-zero if we * hit end of track. Sets ITS_F_ENDOFTRK if we hit end of track. */ static int GetTrackVDWord (track_state_t *ts, DWORD *v) { BYTE b; DWORD val = 0; if (ts->status & ITS_F_ENDOFTRK) return 1; do { if (!ts->left_in_buffer && !ts->left_on_disk) { ts->status |= ITS_F_ENDOFTRK; return 1; } if (GetTrackByte(ts, &b)) return 1; val = (val << 7) | (b & 0x7F); } while (b & 0x80); *v = val; return 0; }
// // GetTrackEvent // // Fills in the event struct with the next event from the track // // pteTemp->tkEvent will contain the absolute tick time of the event // pteTemp->byShortData[0] will contain // MIDI_META if the event is a meta event; // in this case pteTemp->byShortData[1] will contain the meta class // MIDI_SYSEX or MIDI_SYSEXEND if the event is a SysEx event // Otherwise, the event is a channel message and pteTemp->byShortData[1] // and pteTemp->byShortData[2] will contain the rest of the event. // // pteTemp->dwEventLength will contain // The total length of the channel message in pteTemp->byShortData if // the event is a channel message // The total length of the paramter data pointed to by // pteTemp->pLongData otherwise // // pteTemp->pLongData will point at any additional paramters if the // event is a SysEx or meta event with non-zero length; else // it will contain NULL // // Returns FALSE on success or TRUE on any kind of parse error // Prints its own error message ONLY in the debug version // // Maintains the state of the input track (i.e. ptsTrack->dwLeftInBuffer, // ptsTrack->pTrackPointers, and ptsTrack->byRunningStatus). // static BOOL GetTrackEvent( INTRACKSTATE *ptsTrack, PTEMPEVENT pteTemp ) { DWORD idx; BYTE byByte; UINT dwEventLength; // Clear out the temporary event structure to get rid of old data... memset( pteTemp, 0, sizeof(TEMPEVENT)); // Already at end of track? There's nothing to read. // if(( ptsTrack->fdwTrack & ITS_F_ENDOFTRK ) || ( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk )) return( TRUE ); // Get the first byte, which determines the type of event. // if( GetTrackByte( ptsTrack, &byByte )) return( TRUE ); // If the high bit is not set, then this is a channel message // which uses the status byte from the last channel message // we saw. NOTE: We do not clear running status across SysEx or // meta events even though the spec says to because there are // actually files out there which contain that sequence of data. // if( !( byByte & 0x80 )) { // No previous status byte? We're hosed. if( !ptsTrack->byRunningStatus ) { TRACKERR(ptsTrack, gteBadRunStat); return( TRUE ); } pteTemp->byShortData[0] = ptsTrack->byRunningStatus; pteTemp->byShortData[1] = byByte; byByte = pteTemp->byShortData[0] & 0xF0; pteTemp->dwEventLength = 2; // Only program change and channel pressure events are 2 bytes long; // the rest are 3 and need another byte // if(( byByte != MIDI_PRGMCHANGE ) && ( byByte != MIDI_CHANPRESS )) { if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk ) { TRACKERR( ptsTrack, gteRunStatMsgTrunc ); ptsTrack->fdwTrack |= ITS_F_ENDOFTRK; return( TRUE ); } if( GetTrackByte( ptsTrack, &pteTemp->byShortData[2] )) return( TRUE ); ++pteTemp->dwEventLength; } } else if(( byByte & 0xF0 ) != MIDI_SYSEX ) { // Not running status, not in SysEx range - must be // normal channel message (0x80-0xEF) // pteTemp->byShortData[0] = byByte; ptsTrack->byRunningStatus = byByte; // Strip off channel and just keep message type // byByte &= 0xF0; dwEventLength = ( byByte == MIDI_PRGMCHANGE || byByte == MIDI_CHANPRESS ) ? 1 : 2; pteTemp->dwEventLength = dwEventLength + 1; if(( ptsTrack->dwLeftInBuffer + ptsTrack->dwLeftOnDisk ) < dwEventLength ) { TRACKERR( ptsTrack, gteChanMsgTrunc ); ptsTrack->fdwTrack |= ITS_F_ENDOFTRK; return( TRUE ); } if( GetTrackByte( ptsTrack, &pteTemp->byShortData[1] )) return( TRUE ); if( dwEventLength == 2 ) if( GetTrackByte( ptsTrack, &pteTemp->byShortData[2] )) return( TRUE ); } else if(( byByte == MIDI_SYSEX ) || ( byByte == MIDI_SYSEXEND )) { // One of the SysEx types. (They are the same as far as we're concerned; // there is only a semantic difference in how the data would actually // get sent when the file is played. We must take care to put the proper // event type back on the output track, however.) // // Parse the general format of: // BYTE bEvent (MIDI_SYSEX or MIDI_SYSEXEND) // VDWORD cbParms // BYTE abParms[cbParms] // pteTemp->byShortData[0] = byByte; if( GetTrackVDWord( ptsTrack, &pteTemp->dwEventLength )) { TRACKERR( ptsTrack, gteSysExLenTrunc ); return( TRUE ); } if(( ptsTrack->dwLeftInBuffer + ptsTrack->dwLeftOnDisk ) < pteTemp->dwEventLength ) { TRACKERR( ptsTrack, gteSysExTrunc ); ptsTrack->fdwTrack |= ITS_F_ENDOFTRK; return( TRUE ); } // Malloc a temporary memory block to hold the parameter data if(( pteTemp->pLongData = malloc( pteTemp->dwEventLength )) == NULL ) { TRACKERR( ptsTrack, gteNoMem ); return( TRUE ); } // Copy from the input buffer to the parameter data buffer for( idx = 0; idx < pteTemp->dwEventLength; idx++ ) if( GetTrackByte( ptsTrack, pteTemp->pLongData + idx )) { TRACKERR( ptsTrack, gteSysExTrunc ); return( TRUE ); } // Increment our counter, which tells the program to look around for // a malloc block to free, should it need to exit or reset before the // block would normally be freed dwMallocBlocks++; } else if( byByte == MIDI_META ) { // It's a meta event. Parse the general form: // BYTE bEvent (MIDI_META) // BYTE bClass // VDWORD cbParms // BYTE abParms[cbParms] // pteTemp->byShortData[0] = byByte; if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk ) { TRACKERR(ptsTrack, gteMetaNoClass ); ptsTrack->fdwTrack |= ITS_F_ENDOFTRK; return( TRUE ); } if( GetTrackByte( ptsTrack, &pteTemp->byShortData[1] )) return( TRUE ); if( GetTrackVDWord( ptsTrack, &pteTemp->dwEventLength )) { TRACKERR( ptsTrack, gteMetaLenTrunc ); return( TRUE ); } // NOTE: It's perfectly valid to have a meta with no data // In this case, dwEventLength == 0 and pLongData == NULL // if( pteTemp->dwEventLength ) { if(( ptsTrack->dwLeftInBuffer + ptsTrack->dwLeftOnDisk ) < pteTemp->dwEventLength ) { TRACKERR( ptsTrack, gteMetaTrunc ); ptsTrack->fdwTrack |= ITS_F_ENDOFTRK; return( TRUE ); } // Malloc a temporary memory block to hold the parameter data if(( pteTemp->pLongData = malloc( pteTemp->dwEventLength )) == NULL ) { TRACKERR( ptsTrack, gteNoMem ); return( TRUE ); } // Copy from the input buffer to the parameter data buffer for( idx = 0; idx < pteTemp->dwEventLength; idx++ ) if( GetTrackByte( ptsTrack, pteTemp->pLongData + idx )) { TRACKERR( ptsTrack, gteMetaTrunc ); return( TRUE ); } // Increment our counter, which tells the program to look around for // a malloc block to free, should it need to exit or reset before the // block would normally be freed dwMallocBlocks++; } if( pteTemp->byShortData[1] == MIDI_META_EOT ) ptsTrack->fdwTrack |= ITS_F_ENDOFTRK; } else { // Messages in this range are system messages and aren't supposed to // be in a normal MIDI file. If they are, we've either misparsed or the // authoring software is stupid. // return( TRUE ); } // Event time was already stored as the current track time // pteTemp->tkEvent = ptsTrack->tkNextEventDue; // Now update to the next event time. The code above MUST properly // maintain the end of track flag in case the end of track meta is // missing. NOTE: This code is a continuation of the track event // time pre-read which is done at the end of track initialization. // if( !( ptsTrack->fdwTrack & ITS_F_ENDOFTRK )) { DWORD tkDelta; if( GetTrackVDWord( ptsTrack, &tkDelta )) return( TRUE ); ptsTrack->tkNextEventDue += tkDelta; } return( FALSE ); }
/* GetTrackEvent * * Fills in the event struct with the next event from the track * * te->event_time will contain the absolute tick time of the event. * * te->shortdata[0] will contain: * - MIDI_META_EVENT if the event is a meta event; in this case * te->shortdata[1] will contain the meta class. * - MIDICMD_SYSEX or MIDICMD_SYSEX_END if the event is a SysEx event * - Otherwise, event is a channel message and te->shortdata[1] and * te->shortdata[2] will contain the rest of the event. * * te->event_len will contain: * - The total length of the channel message in te->shortdata if the * event is a channel message, * - The total length of the paramter data pointed to by te->longdata, * otherwise. * * te->longdata will point at any additional paramters if the event is * a SysEx or meta event with non-zero length; else it will be NULL. * * Returns zero on success or non-zero on any kind of parse error * Maintains the state of the input track (i.e. ts->left_in_buffer, * ts->pTrackPointers, and ts->running_status). */ static int GetTrackEvent (track_state_t *ts, temp_event_t *te) { DWORD idx; BYTE b; UINT event_len; /* Clear out the temporary event structure to get rid of old data */ memset(te, 0, sizeof(struct _temp_event_s)); /* Already at end of track? There's nothing to read. */ if (ts->status & ITS_F_ENDOFTRK) return 1; if (!ts->left_in_buffer && !ts->left_on_disk) return 1; /* Get the first byte, which determines the type of event. */ if (GetTrackByte(ts, &b)) return 1; /* If the high bit is not set, then this is a channel message * which uses the status byte from the last channel message * we saw. NOTE: We do not clear running status across SysEx or * meta events even though the spec says to because there are * actually files out there which contain that sequence of data. */ if ( !(b & 0x80)) { /* No previous status byte? We're hosed. */ if (!ts->running_status) { TRACKERR(ts, err_bad_runstat); return 1; } te->shortdata[0] = ts->running_status; te->shortdata[1] = b; b = te->shortdata[0] & 0xF0; te->event_len = 2; /* Only program change and channel pressure events are * 2 bytes long. the rest are 3 and need another byte. */ if (b != MIDICMD_PGM_CHANGE && b != MIDICMD_CHANNEL_PRESSURE) { if (!ts->left_in_buffer && !ts->left_on_disk) { TRACKERR(ts, err_trunc_runstat); ts->status |= ITS_F_ENDOFTRK; return 1; } if (GetTrackByte(ts, &te->shortdata[2])) return 1; ++te->event_len; } } else if ((b & 0xF0) != MIDICMD_SYSEX) { /* Not running status, not in SysEx range - must be * normal channel message (0x80-0xEF) */ te->shortdata[0] = b; ts->running_status = b; /* Strip off channel and just keep message type */ b &= 0xF0; event_len = (b == MIDICMD_PGM_CHANGE || b == MIDICMD_CHANNEL_PRESSURE) ? 1 : 2; te->event_len = event_len + 1; if ((ts->left_in_buffer + ts->left_on_disk) < event_len) { TRACKERR(ts, err_trunc_chan_msg); ts->status |= ITS_F_ENDOFTRK; return 1; } if (GetTrackByte(ts, &te->shortdata[1])) return 1; if (event_len == 2) { if (GetTrackByte(ts, &te->shortdata[2])) return 1; } } else if (b == MIDICMD_SYSEX || b == MIDICMD_SYSEX_END) { /* One of the SysEx types. (They are the same as far as we're * concerned; there is only a semantic difference in how the * data would actually get sent when the file is played. * We must take care to put the proper event type back on the * output track, however.) * * Parse the general format of: * BYTE event (MIDICMD_SYSEX or MIDICMD_SYSEX_END) * VDWORD num_parms * BYTE ab_parms[num_parms] */ te->shortdata[0] = b; if (GetTrackVDWord(ts, &te->event_len)) { TRACKERR(ts, err_trunc_sysex_len); return 1; } if ((ts->left_in_buffer + ts->left_on_disk) < te->event_len) { TRACKERR(ts, err_trunc_sysex); ts->status |= ITS_F_ENDOFTRK; return 1; } te->longdata = (BYTE *) Z_Malloc(te->event_len, Z_MAINZONE); for (idx = 0; idx < te->event_len; idx++) { if (GetTrackByte(ts, te->longdata + idx)) { Z_Free(te->longdata); te->longdata = NULL; TRACKERR(ts, err_trunc_sysex); return 1; } } } else if (b == MIDI_META_EVENT) { /* It's a meta event. Parse the general form: * BYTE event (MIDI_META_EVENT) * BYTE class * VDWORD num_parms * BYTE ab_parms[num_parms] */ te->shortdata[0] = b; if (!ts->left_in_buffer && !ts->left_on_disk) { TRACKERR(ts, err_meta_noclass); ts->status |= ITS_F_ENDOFTRK; return 1; } if (GetTrackByte(ts, &te->shortdata[1])) return 1; if (GetTrackVDWord(ts, &te->event_len)) { TRACKERR(ts, err_trunc_meta_len); return 1; } /* NOTE: It's perfectly valid to have a meta with no data * In this case, event_len == 0 and longdata == NULL */ if (te->event_len) { if ((ts->left_in_buffer + ts->left_on_disk) < te->event_len) { TRACKERR(ts, err_trunc_meta); ts->status |= ITS_F_ENDOFTRK; return 1; } te->longdata = (BYTE *) Z_Malloc(te->event_len, Z_MAINZONE); for (idx = 0; idx < te->event_len; idx++) { if (GetTrackByte(ts, te->longdata + idx)) { Z_Free(te->longdata); te->longdata = NULL; TRACKERR(ts, err_trunc_meta); return 1; } } } if (te->shortdata[1] == MIDI_META_EOT) ts->status |= ITS_F_ENDOFTRK; } else { /* Messages in this range are system messages and aren't * supposed to be in a normal MIDI file. If they are, we * have either misparsed or the authoring software is stupid. */ return 1; } /* Event time was already stored as the current track time */ te->event_time = ts->next_event_time; /* Now update to the next event time. The code above MUST properly * maintain the end of track flag in case the end of track meta is * missing. NOTE: This code is a continuation of the track event * time pre-read which is done at the end of track initialization. */ if ( !(ts->status & ITS_F_ENDOFTRK)) { DWORD delta_time; if (GetTrackVDWord(ts, &delta_time)) return 1; ts->next_event_time += delta_time; } return 0; }