bool MidiParser_SMF::loadMusic(byte *data, uint32 size) { uint32 len; byte midi_type; uint32 total_size; bool isGMF; unloadMusic(); byte *pos = data; isGMF = false; if (!memcmp(pos, "RIFF", 4)) { // Skip the outer RIFF header. pos += 8; } if (!memcmp(pos, "MThd", 4)) { // SMF with MTHd information. pos += 4; len = read4high(pos); if (len != 6) { warning("MThd length 6 expected but found %d", (int)len); return false; } // Verify that this MIDI either is a Type 2 // or has only 1 track. We do not support // multitrack Type 1 files. _num_tracks = pos[2] << 8 | pos[3]; midi_type = pos[1]; if (midi_type > 2 /*|| (midi_type < 2 && _num_tracks > 1)*/) { warning("No support for a Type %d MIDI with %d tracks", (int)midi_type, (int)_num_tracks); return false; } _ppqn = pos[4] << 8 | pos[5]; pos += len; } else if (!memcmp(pos, "GMF\x1", 4)) { // Older GMD/MUS file with no header info. // Assume 1 track, 192 PPQN, and no MTrk headers. isGMF = true; midi_type = 0; _num_tracks = 1; _ppqn = 192; pos += 7; // 'GMD\x1' + 3 bytes of useless (translate: unknown) information } else { warning("Expected MThd or GMD header but found '%c%c%c%c' instead", pos[0], pos[1], pos[2], pos[3]); return false; } // Now we identify and store the location for each track. if (_num_tracks > ARRAYSIZE(_tracks)) { warning("Can only handle %d tracks but was handed %d", (int)ARRAYSIZE(_tracks), (int)_num_tracks); return false; } total_size = 0; int tracks_read = 0; while (tracks_read < _num_tracks) { if (memcmp(pos, "MTrk", 4) && !isGMF) { warning("Position: %p ('%c')", pos, *pos); warning("Hit invalid block '%c%c%c%c' while scanning for track locations", pos[0], pos[1], pos[2], pos[3]); return false; } // If needed, skip the MTrk and length bytes _tracks[tracks_read] = pos + (isGMF ? 0 : 8); if (!isGMF) { pos += 4; len = read4high(pos); total_size += len; pos += len; } else { // An SMF End of Track meta event must be placed // at the end of the stream. data[size++] = 0xFF; data[size++] = 0x2F; data[size++] = 0x00; data[size++] = 0x00; } ++tracks_read; } // If this is a Type 1 MIDI, we need to now compress // our tracks down into a single Type 0 track. free(_buffer); _buffer = 0; if (midi_type == 1) { // FIXME: Doubled the buffer size to prevent crashes with the // Inherit the Earth MIDIs. Jamieson630 said something about a // better fix, but this will have to do in the meantime. _buffer = (byte *)malloc(size * 2); compressToType0(); _num_tracks = 1; _tracks[0] = _buffer; } // Note that we assume the original data passed in // will persist beyond this call, i.e. we do NOT // copy the data to our own buffer. Take warning.... resetTracking(); setTempo(500000); setTrack(0); return true; }
bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { uint32 i = 0; byte *start; uint32 len; uint32 chunk_len; char buf[32]; _loopCount = -1; unloadMusic(); byte *pos = data; if (!memcmp(pos, "FORM", 4)) { pos += 4; // Read length of len = read4high(pos); start = pos; // XDIRless XMIDI, we can handle them here. if (!memcmp(pos, "XMID", 4)) { warning("XMIDI doesn't have XDIR"); pos += 4; _num_tracks = 1; } else if (memcmp(pos, "XDIR", 4)) { // Not an XMIDI that we recognise warning("Expected 'XDIR' but found '%c%c%c%c'", pos[0], pos[1], pos[2], pos[3]); return false; } else { // Seems Valid pos += 4; _num_tracks = 0; for (i = 4; i < len; i++) { // Read 4 bytes of type memcpy(buf, pos, 4); pos += 4; // Read length of chunk chunk_len = read4high(pos); // Add eight bytes i += 8; if (memcmp(buf, "INFO", 4)) { // Must align pos += (chunk_len + 1) & ~1; i += (chunk_len + 1) & ~1; continue; } // Must be at least 2 bytes long if (chunk_len < 2) { warning("Invalid chunk length %d for 'INFO' block", (int)chunk_len); return false; } _num_tracks = (byte)read2low(pos); if (chunk_len > 2) { warning("Chunk length %d is greater than 2", (int)chunk_len); pos += chunk_len - 2; } break; } // Didn't get to fill the header if (_num_tracks == 0) { warning("Didn't find a valid track count"); return false; } // Ok now to start part 2 // Goto the right place pos = start + ((len + 1) & ~1); if (memcmp(pos, "CAT ", 4)) { // Not an XMID warning("Expected 'CAT ' but found '%c%c%c%c'", pos[0], pos[1], pos[2], pos[3]); return false; } pos += 4; // Now read length of this track len = read4high(pos); if (memcmp(pos, "XMID", 4)) { // Not an XMID warning("Expected 'XMID' but found '%c%c%c%c'", pos[0], pos[1], pos[2], pos[3]); return false; } pos += 4; } // Ok it's an XMIDI. // We're going to identify and store the location for each track. if (_num_tracks > ARRAYSIZE(_tracks)) { warning("Can only handle %d tracks but was handed %d", (int)ARRAYSIZE(_tracks), (int)_num_tracks); return false; } int tracks_read = 0; while (tracks_read < _num_tracks) { if (!memcmp(pos, "FORM", 4)) { // Skip this plus the 4 bytes after it. pos += 8; } else if (!memcmp(pos, "XMID", 4)) { // Skip this. pos += 4; } else if (!memcmp(pos, "TIMB", 4)) { // Custom timbres? // We don't support them. // Read the length, skip it, and hope there was nothing there. pos += 4; len = read4high(pos); pos += (len + 1) & ~1; } else if (!memcmp(pos, "EVNT", 4)) { // Ahh! What we're looking for at last. _tracks[tracks_read] = pos + 8; // Skip the EVNT and length bytes pos += 4; len = read4high(pos); pos += (len + 1) & ~1; ++tracks_read; } else { warning("Hit invalid block '%c%c%c%c' while scanning for track locations", pos[0], pos[1], pos[2], pos[3]); return false; } } // If we got this far, we successfully established // the locations for each of our tracks. // Note that we assume the original data passed in // will persist beyond this call, i.e. we do NOT // copy the data to our own buffer. Take warning.... _ppqn = 60; resetTracking(); setTempo(500000); _inserted_delta = 0; setTrack(0); return true; } return false; }