This update uses upstream commit f88ef828a600ce66d1f730c8fb2a7f580f6f6165. This update switches to use the new Munt C++ interface, which will allow ScummVM to link to an external Munt library instead of requiring it to be built-in in the future. For the moment, the emulator is still built-in, since it is not available from most package repositories. The Munt driver in ScummVM now uses writeSysex instead of the (now-private) playSysexWithoutFraming, per recommendation from the Munt team <https://github.com/munt/munt/pull/30>. This changeset also removes direct modifications that used to be made to Munt code, to ease future updates. To update Munt code in the future: 1. Replace all source files in the `softsynth/mt32` directory with new files from the upstream `mt32emu/src` directory; 2. Update `config.h` with the correct version number for the new version of Munt; 3. Update `module.mk` to add any new source files that need to be built.
289 lines
10 KiB
C++
Executable file
289 lines
10 KiB
C++
Executable file
/* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
#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 < 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;
|
|
}
|