/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include #include #include "internals.h" #include "MidiStreamParser.h" #include "Synth.h" using namespace MT32Emu; DefaultMidiStreamParser::DefaultMidiStreamParser(Synth &useSynth, Bit32u initialStreamBufferCapacity) : MidiStreamParser(initialStreamBufferCapacity), synth(useSynth), timestampSet(false) {} void DefaultMidiStreamParser::setTimestamp(const Bit32u useTimestamp) { timestampSet = true; timestamp = useTimestamp; } void DefaultMidiStreamParser::resetTimestamp() { timestampSet = false; } void DefaultMidiStreamParser::handleShortMessage(const Bit32u message) { do { if (timestampSet) { if (synth.playMsg(message, timestamp)) return; } else { if (synth.playMsg(message)) return; } } while (synth.reportHandler->onMIDIQueueOverflow()); } void DefaultMidiStreamParser::handleSysex(const Bit8u *stream, const Bit32u length) { do { if (timestampSet) { if (synth.playSysex(stream, length, timestamp)) return; } else { if (synth.playSysex(stream, length)) return; } } while (synth.reportHandler->onMIDIQueueOverflow()); } void DefaultMidiStreamParser::handleSystemRealtimeMessage(const Bit8u realtime) { synth.reportHandler->onMIDISystemRealtime(realtime); } void DefaultMidiStreamParser::printDebug(const char *debugMessage) { synth.printDebug("%s", debugMessage); } MidiStreamParser::MidiStreamParser(Bit32u initialStreamBufferCapacity) : MidiStreamParserImpl(*this, *this, initialStreamBufferCapacity) {} MidiStreamParserImpl::MidiStreamParserImpl(MidiReceiver &useReceiver, MidiReporter &useReporter, Bit32u initialStreamBufferCapacity) : midiReceiver(useReceiver), midiReporter(useReporter) { if (initialStreamBufferCapacity < (Bit32u)SYSEX_BUFFER_SIZE) initialStreamBufferCapacity = SYSEX_BUFFER_SIZE; if (MAX_STREAM_BUFFER_SIZE < initialStreamBufferCapacity) initialStreamBufferCapacity = MAX_STREAM_BUFFER_SIZE; streamBufferCapacity = initialStreamBufferCapacity; streamBuffer = new Bit8u[streamBufferCapacity]; streamBufferSize = 0; runningStatus = 0; reserved = NULL; } MidiStreamParserImpl::~MidiStreamParserImpl() { delete[] streamBuffer; } void MidiStreamParserImpl::parseStream(const Bit8u *stream, Bit32u length) { while (length > 0) { Bit32u parsedMessageLength = 0; if (0xF8 <= *stream) { // Process System Realtime immediately and go on midiReceiver.handleSystemRealtimeMessage(*stream); parsedMessageLength = 1; // No effect on the running status } else if (streamBufferSize > 0) { // Check if there is something in streamBuffer waiting for being processed if (*streamBuffer == 0xF0) { parsedMessageLength = parseSysexFragment(stream, length); } else { parsedMessageLength = parseShortMessageDataBytes(stream, length); } } else { if (*stream == 0xF0) { runningStatus = 0; // SysEx clears the running status parsedMessageLength = parseSysex(stream, length); } else { parsedMessageLength = parseShortMessageStatus(stream); } } // Parsed successfully stream += parsedMessageLength; length -= parsedMessageLength; } } void MidiStreamParserImpl::processShortMessage(const Bit32u message) { // Adds running status to the MIDI message if it doesn't contain one Bit8u status = (Bit8u)message; if (0xF8 <= status) { midiReceiver.handleSystemRealtimeMessage(status); } else if (processStatusByte(status)) { midiReceiver.handleShortMessage((message << 8) | status); } else if (0x80 <= status) { // If no running status available yet, skip this message midiReceiver.handleShortMessage(message); } } // We deal with SysEx messages below 512 bytes long in most cases. Nevertheless, it seems reasonable to support a possibility // to load bulk dumps using a single message. However, this is known to fail with a real device due to limited input buffer size. bool MidiStreamParserImpl::checkStreamBufferCapacity(const bool preserveContent) { if (streamBufferSize < streamBufferCapacity) return true; if (streamBufferCapacity < MAX_STREAM_BUFFER_SIZE) { Bit8u *oldStreamBuffer = streamBuffer; streamBufferCapacity = MAX_STREAM_BUFFER_SIZE; streamBuffer = new Bit8u[streamBufferCapacity]; if (preserveContent) memcpy(streamBuffer, oldStreamBuffer, streamBufferSize); delete[] oldStreamBuffer; return true; } return false; } // Checks input byte whether it is a status byte. If not, replaces it with running status when available. // Returns true if the input byte was changed to running status. bool MidiStreamParserImpl::processStatusByte(Bit8u &status) { if (status < 0x80) { // First byte isn't status, try running status if (runningStatus < 0x80) { // No running status available yet midiReporter.printDebug("processStatusByte: No valid running status yet, MIDI message ignored"); return false; } status = runningStatus; return true; } else if (status < 0xF0) { // Store current status as running for a Voice message runningStatus = status; } else if (status < 0xF8) { // System Common clears running status runningStatus = 0; } // System Realtime doesn't affect running status return false; } // Returns # of bytes parsed Bit32u MidiStreamParserImpl::parseShortMessageStatus(const Bit8u stream[]) { Bit8u status = *stream; Bit32u parsedLength = processStatusByte(status) ? 0 : 1; if (0x80 <= status) { // If no running status available yet, skip one byte *streamBuffer = status; ++streamBufferSize; } return parsedLength; } // Returns # of bytes parsed Bit32u MidiStreamParserImpl::parseShortMessageDataBytes(const Bit8u stream[], Bit32u length) { const Bit32u shortMessageLength = Synth::getShortMessageLength(*streamBuffer); Bit32u parsedLength = 0; // Append incoming bytes to streamBuffer while ((streamBufferSize < shortMessageLength) && (length-- > 0)) { Bit8u dataByte = *(stream++); if (dataByte < 0x80) { // Add data byte to streamBuffer streamBuffer[streamBufferSize++] = dataByte; } else if (dataByte < 0xF8) { // Discard invalid bytes and start over char s[128]; sprintf(s, "parseShortMessageDataBytes: Invalid short message: status %02x, expected length %i, actual %i -> ignored", *streamBuffer, shortMessageLength, streamBufferSize); midiReporter.printDebug(s); streamBufferSize = 0; // Clear streamBuffer return parsedLength; } else { // Bypass System Realtime message midiReceiver.handleSystemRealtimeMessage(dataByte); } ++parsedLength; } if (streamBufferSize < shortMessageLength) return parsedLength; // Still lacks data bytes // Assemble short message Bit32u shortMessage = streamBuffer[0]; for (Bit32u i = 1; i < shortMessageLength; ++i) { shortMessage |= streamBuffer[i] << (i << 3); } midiReceiver.handleShortMessage(shortMessage); streamBufferSize = 0; // Clear streamBuffer return parsedLength; } // Returns # of bytes parsed Bit32u MidiStreamParserImpl::parseSysex(const Bit8u stream[], const Bit32u length) { // Find SysEx length Bit32u sysexLength = 1; while (sysexLength < length) { Bit8u nextByte = stream[sysexLength++]; if (0x80 <= nextByte) { if (nextByte == 0xF7) { // End of SysEx midiReceiver.handleSysex(stream, sysexLength); return sysexLength; } if (0xF8 <= nextByte) { // The System Realtime message must be processed right after return // but the SysEx is actually fragmented and to be reconstructed in streamBuffer --sysexLength; break; } // Illegal status byte in SysEx message, aborting midiReporter.printDebug("parseSysex: SysEx message lacks end-of-sysex (0xf7), ignored"); // Continue parsing from that point return sysexLength - 1; } } // Store incomplete SysEx message for further processing streamBufferSize = sysexLength; if (checkStreamBufferCapacity(false)) { memcpy(streamBuffer, stream, sysexLength); } else { // Not enough buffer capacity, don't care about the real buffer content, just mark the first byte *streamBuffer = *stream; streamBufferSize = streamBufferCapacity; } return sysexLength; } // Returns # of bytes parsed Bit32u MidiStreamParserImpl::parseSysexFragment(const Bit8u stream[], const Bit32u length) { Bit32u parsedLength = 0; while (parsedLength < length) { Bit8u nextByte = stream[parsedLength++]; if (nextByte < 0x80) { // Add SysEx data byte to streamBuffer if (checkStreamBufferCapacity(true)) streamBuffer[streamBufferSize++] = nextByte; continue; } if (0xF8 <= nextByte) { // Bypass System Realtime message midiReceiver.handleSystemRealtimeMessage(nextByte); continue; } if (nextByte != 0xF7) { // Illegal status byte in SysEx message, aborting midiReporter.printDebug("parseSysexFragment: SysEx message lacks end-of-sysex (0xf7), ignored"); // Clear streamBuffer and continue parsing from that point streamBufferSize = 0; --parsedLength; break; } // End of SysEx if (checkStreamBufferCapacity(true)) { streamBuffer[streamBufferSize++] = nextByte; midiReceiver.handleSysex(streamBuffer, streamBufferSize); streamBufferSize = 0; // Clear streamBuffer break; } // Encountered streamBuffer overrun midiReporter.printDebug("parseSysexFragment: streamBuffer overrun while receiving SysEx message, ignored. Max allowed size of fragmented SysEx is 32768 bytes."); streamBufferSize = 0; // Clear streamBuffer break; } return parsedLength; }