bool MidiParser_SH::loadMusic(byte *musData, uint32 musDataSize) { Common::StackLock lock(_mutex); debugC(kDebugLevelMusic, "Music: loadMusic()"); unloadMusic(); _musData = musData; _musDataSize = musDataSize; byte *headerPtr = _musData + 12; // skip over the already checked SPACE header byte *pos = headerPtr; uint16 headerSize = READ_LE_UINT16(headerPtr); assert(headerSize == 0x7F); // Security check // Skip over header pos += headerSize; _lastEvent = 0; _trackEnd = _musData + _musDataSize; _numTracks = 1; _tracks[0] = pos; _ppqn = 1; setTempo(16667); setTrack(0); return true; }
bool MidiParser_S1D::loadMusic(byte *data, uint32 size) { unloadMusic(); if (!size) return false; // The original actually just ignores the first two bytes. byte *pos = data; if (*pos == 0xFC) { // SysEx found right at the start // this seems to happen since Elvira 2, we ignore it // 3rd byte after the SysEx seems to be saved into a global // We expect at least 4 bytes in total if (size < 4) return false; byte skipOffset = pos[2]; // get second byte after the SysEx // pos[1] seems to have been ignored // pos[3] is saved into a global inside the original interpreters // Waxworks + Simon 1 demo typical header is: // 0xFC 0x29 0x07 0x01 [0x00/0x01] // Elvira 2 typical header is: // 0xFC 0x04 0x06 0x06 if (skipOffset >= 6) { // should be at least 6, so that we skip over the 2 size bytes and the // smallest SysEx possible skipOffset -= 2; // 2 size bytes were already read by previous code outside of this method if (size <= skipOffset) // Skip to the end of file? -> something is not correct return false; // Do skip over the bytes pos += skipOffset; } else { warning("MidiParser_S1D: unexpected skip offset in music file"); } } // And now we're at the actual data. Only one track. _numTracks = 1; _data = pos; _tracks[0] = pos; // 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(666667); setTrack(0); return true; }
void AudioManager::stopMusic(const std::string &musicId, bool unload /* = false */) { _CHECK_AND_WARN_IF_NOT_EXISTS( "AudioManager::stopMusic", "MusicId is not loaded: (%s)", m_musicMap, musicId ); _AE::stop(m_musicMap[musicId].currInternalId); if(unload) unloadMusic(musicId); }
bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, int channelFilterMask, SciVersion soundVersion) { unloadMusic(); _track = track; _pSnd = psnd; _soundVersion = soundVersion; for (int i = 0; i < 16; i++) { _channelUsed[i] = false; _channelMuted[i] = false; _channelVolume[i] = 127; if (_soundVersion <= SCI_VERSION_0_LATE) _channelRemap[i] = i; else _channelRemap[i] = -1; } // FIXME: SSCI does not always start playing a track at the first byte. // By default it skips 10 (or 13?) bytes containing prio/voices, patch, // volume, pan commands in fixed locations, and possibly a signal // in channel 15. We should initialize state tracking to those values // so that they automatically get set up properly when the channels get // mapped. See also the related FIXME in MidiParser_SCI::processEvent. if (channelFilterMask) { // SCI0 only has 1 data stream, but we need to filter out channels depending on music hardware selection midiFilterChannels(channelFilterMask); } else { midiMixChannels(); } _numTracks = 1; _tracks[0] = _mixedData; if (_pSnd) setTrack(0); _loopTick = 0; return true; }
bool MidiParser_RO::loadMusic (byte *data, uint32 size) { unloadMusic(); byte *pos = data; if (memcmp (pos, "RO", 2)) { error("'RO' header expected but found '%c%c' instead", pos[0], pos[1]); return false; } _num_tracks = 1; _ppqn = 120; _tracks[0] = pos + 2; _markerCount = _lastMarkerCount = 0; // 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_S1D::loadMusic(byte *data, uint32 size) { unloadMusic(); byte *pos = data; if (*(pos++) != 0xFC) debug(1, "Expected 0xFC header but found 0x%02X instead", (int) *pos); // The next 3 bytes MIGHT be tempo, but we skip them and use the default. // setTempo (*(pos++) | (*(pos++) << 8) | (*(pos++) << 16)); pos += 3; // And now we're at the actual data. Only one track. _num_tracks = 1; _data = pos; _tracks[0] = pos; // 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(666667); setTrack(0); return true; }
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; }
void MidiParser_SH::parseNextEvent(EventInfo &info) { Common::StackLock lock(_mutex); // warning("parseNextEvent"); // there is no delta right at the start of the music data // this order is essential, otherwise notes will get delayed or even go missing if (_position._playPos != _tracks[0]) { info.delta = *(_position._playPos++); } else { info.delta = 0; } info.start = _position._playPos; info.event = *_position._playPos++; //warning("Event %x", info.event); _position._runningStatus = info.event; switch (info.command()) { case 0xC: { // program change int idx = *_position._playPos++; info.basic.param1 = idx & 0x7f; info.basic.param2 = 0; } break; case 0xD: info.basic.param1 = *_position._playPos++; info.basic.param2 = 0; break; case 0xB: info.basic.param1 = *_position._playPos++; info.basic.param2 = *_position._playPos++; info.length = 0; break; case 0x8: case 0x9: case 0xA: case 0xE: info.basic.param1 = *(_position._playPos++); info.basic.param2 = *(_position._playPos++); if (info.command() == 0x9 && info.basic.param2 == 0) { // NoteOn with param2==0 is a NoteOff info.event = info.channel() | 0x80; } info.length = 0; break; case 0xF: if (info.event == 0xFF) { error("SysEx META event 0xFF"); byte type = *(_position._playPos++); switch(type) { case 0x2F: // End of Track allNotesOff(); stopPlaying(); unloadMusic(); return; case 0x51: warning("TODO: 0xFF / 0x51"); return; default: warning("TODO: 0xFF / %x Unknown", type); break; } } else if (info.event == 0xFC) { // Official End-Of-Track signal debugC(kDebugLevelMusic, "Music: System META event 0xFC"); byte type = *(_position._playPos++); switch (type) { case 0x80: // end of track, triggers looping debugC(kDebugLevelMusic, "Music: META event triggered looping"); jumpToTick(0, true, true, false); break; case 0x81: // end of track, stop playing debugC(kDebugLevelMusic, "Music: META event triggered music stop"); stopPlaying(); unloadMusic(); break; default: error("MidiParser_SH::parseNextEvent: Unknown META event 0xFC type %x", type); break; } } else { warning("TODO: %x / Unknown", info.event); break; } break; default: warning("MidiParser_SH::parseNextEvent: Unsupported event code %x", info.event); break; }// switch (info.command()) }
MidiParser_SH::~MidiParser_SH() { Common::StackLock lock(_mutex); unloadMusic(); _driver = NULL; }
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; }