Update internal MUNT to version 2.7.0

This commit is contained in:
Alexander Babikov
2022-07-24 07:50:54 +05:00
parent 1bc4ae5337
commit be8784b17c
87 changed files with 3292 additions and 994 deletions

View File

@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
* Copyright (C) 2011-2022 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
@@ -22,6 +22,7 @@
#include "Synth.h"
#include "Analog.h"
#include "BReverbModel.h"
#include "Display.h"
#include "File.h"
#include "MemoryRegion.h"
#include "MidiEventQueue.h"
@@ -41,19 +42,35 @@ namespace MT32Emu {
// MIDI interface data transfer rate in samples. Used to simulate the transfer delay.
static const double MIDI_DATA_TRANSFER_RATE = double(SAMPLE_RATE) / 31250.0 * 8.0;
// FIXME: there should be more specific feature sets for various MT-32 control ROM versions
static const ControlROMFeatureSet OLD_MT32_COMPATIBLE = {
true, // quirkBasePitchOverflow
true, // quirkPitchEnvelopeOverflow
true, // quirkRingModulationNoMix
true, // quirkTVAZeroEnvLevels
true, // quirkPanMult
true, // quirkKeyShift
true, // quirkTVFBaseCutoffLimit
true, // defaultReverbMT32Compatible
true // oldMT32AnalogLPF
static const ControlROMFeatureSet OLD_MT32_ELDER = {
true, // quirkBasePitchOverflow
true, // quirkPitchEnvelopeOverflow
true, // quirkRingModulationNoMix
true, // quirkTVAZeroEnvLevels
true, // quirkPanMult
true, // quirkKeyShift
true, // quirkTVFBaseCutoffLimit
false, // quirkFastPitchChanges
true, // quirkDisplayCustomMessagePriority
true, // oldMT32DisplayFeatures
true, // defaultReverbMT32Compatible
true // oldMT32AnalogLPF
};
static const ControlROMFeatureSet CM32L_COMPATIBLE = {
static const ControlROMFeatureSet OLD_MT32_LATER = {
true, // quirkBasePitchOverflow
true, // quirkPitchEnvelopeOverflow
true, // quirkRingModulationNoMix
true, // quirkTVAZeroEnvLevels
true, // quirkPanMult
true, // quirkKeyShift
true, // quirkTVFBaseCutoffLimit
false, // quirkFastPitchChanges
false, // quirkDisplayCustomMessagePriority
true, // oldMT32DisplayFeatures
true, // defaultReverbMT32Compatible
true // oldMT32AnalogLPF
};
static const ControlROMFeatureSet NEW_MT32_COMPATIBLE = {
false, // quirkBasePitchOverflow
false, // quirkPitchEnvelopeOverflow
false, // quirkRingModulationNoMix
@@ -61,20 +78,41 @@ static const ControlROMFeatureSet CM32L_COMPATIBLE = {
false, // quirkPanMult
false, // quirkKeyShift
false, // quirkTVFBaseCutoffLimit
false, // quirkFastPitchChanges
false, // quirkDisplayCustomMessagePriority
false, // oldMT32DisplayFeatures
false, // defaultReverbMT32Compatible
false // oldMT32AnalogLPF
false // oldMT32AnalogLPF
};
static const ControlROMFeatureSet CM32LN_COMPATIBLE = {
false, // quirkBasePitchOverflow
false, // quirkPitchEnvelopeOverflow
false, // quirkRingModulationNoMix
false, // quirkTVAZeroEnvLevels
false, // quirkPanMult
false, // quirkKeyShift
false, // quirkTVFBaseCutoffLimit
true, // quirkFastPitchChanges
false, // quirkDisplayCustomMessagePriority
false, // oldMT32DisplayFeatures
false, // defaultReverbMT32Compatible
false // oldMT32AnalogLPF
};
static const ControlROMMap ControlROMMaps[8] = {
// ID Features PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax sndGrp sGC
{ "ctrl_mt32_1_04", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x7064, 19 },
{ "ctrl_mt32_1_05", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x70CA, 19 },
{ "ctrl_mt32_1_06", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C, 0x70CA, 19 },
{ "ctrl_mt32_1_07", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4, 0x70B0, 19 }, // MT-32 revision 1
{"ctrl_mt32_bluer", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228, 0x70CE, 19 }, // MT-32 Blue Ridge mod
{"ctrl_mt32_2_04", CM32L_COMPATIBLE, 0x8100, 128, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 30, 0x8580, 85, 0x4F5D, 0x4F78, 0x4F66, 0x4899, 0x489D, 0x48B6, 0x48CD, 0x5A58, 19 },
{"ctrl_cm32l_1_00", CM32L_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5, 0x5A6C, 19 },
{"ctrl_cm32l_1_02", CM32L_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF, 0x5A96, 19 } // CM-32L
static const ControlROMMap ControlROMMaps[] = {
// ID Features PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax sndGrp sGC stMsg sErMsg
{"ctrl_mt32_1_04", OLD_MT32_ELDER, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x7064, 19, 0x217A, 0x4BB6},
{"ctrl_mt32_1_05", OLD_MT32_ELDER, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x70CA, 19, 0x217A, 0x4BB6},
{"ctrl_mt32_1_06", OLD_MT32_LATER, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C, 0x70CA, 19, 0x217A, 0x4BBA},
{"ctrl_mt32_1_07", OLD_MT32_LATER, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4, 0x70B0, 19, 0x217A, 0x4B92},
{"ctrl_mt32_bluer", OLD_MT32_LATER, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228, 0x70CE, 19, 0x217A, 0x4BC6},
{"ctrl_mt32_2_03", NEW_MT32_COMPATIBLE, 0x8100, 128, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F49, 0x4F64, 0x4F52, 0x4885, 0x4889, 0x48A2, 0x48B9, 0x5A44, 19, 0x1EF0, 0x4066},
{"ctrl_mt32_2_04", NEW_MT32_COMPATIBLE, 0x8100, 128, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F5D, 0x4F78, 0x4F66, 0x4899, 0x489D, 0x48B6, 0x48CD, 0x5A58, 19, 0x1EF0, 0x406D},
{"ctrl_mt32_2_06", NEW_MT32_COMPATIBLE, 0x8100, 128, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F69, 0x4F84, 0x4F72, 0x48A5, 0x48A9, 0x48C2, 0x48D9, 0x5A64, 19, 0x1EF0, 0x4021},
{"ctrl_mt32_2_07", NEW_MT32_COMPATIBLE, 0x8100, 128, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F81, 0x4F9C, 0x4F8A, 0x48B9, 0x48BD, 0x48D6, 0x48ED, 0x5A78, 19, 0x1EE7, 0x4035},
{"ctrl_cm32l_1_00", NEW_MT32_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5, 0x5A6C, 19, 0x1EF0, 0x401D},
{"ctrl_cm32l_1_02", NEW_MT32_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF, 0x5A96, 19, 0x1EE7, 0x4047},
{"ctrl_cm32ln_1_00", CM32LN_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4EC7, 0x4EE2, 0x4ED0, 0x47FF, 0x4803, 0x481C, 0x4833, 0x55A2, 19, 0x1F59, 0x3F7C}
// (Note that old MT-32 ROMs actually have 86 entries for rhythmTemp)
};
@@ -138,6 +176,8 @@ protected:
synth.renderedSampleCount += count;
}
void updateDisplayState();
public:
Renderer(Synth &useSynth) : synth(useSynth) {}
@@ -209,10 +249,16 @@ public:
Bit32u midiEventQueueSize;
Bit32u midiEventQueueSysexStorageBufferSize;
Display *display;
bool oldMT32DisplayFeatures;
ReportHandler2 defaultReportHandler;
ReportHandler2 *reportHandler2;
};
Bit32u Synth::getLibraryVersionInt() {
return (MT32EMU_VERSION_MAJOR << 16) | (MT32EMU_VERSION_MINOR << 8) | (MT32EMU_VERSION_PATCH);
return MT32EMU_CURRENT_VERSION_INT;
}
const char *Synth::getLibraryVersionString() {
@@ -244,13 +290,8 @@ Synth::Synth(ReportHandler *useReportHandler) :
controlROMMap = NULL;
controlROMFeatures = NULL;
if (useReportHandler == NULL) {
reportHandler = new ReportHandler;
isDefaultReportHandler = true;
} else {
reportHandler = useReportHandler;
isDefaultReportHandler = false;
}
reportHandler = useReportHandler != NULL ? useReportHandler : &extensions.defaultReportHandler;
extensions.reportHandler2 = &extensions.defaultReportHandler;
extensions.preallocatedReverbMemory = false;
for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) {
@@ -289,18 +330,27 @@ Synth::Synth(ReportHandler *useReportHandler) :
lastReceivedMIDIEventTimestamp = 0;
memset(parts, 0, sizeof(parts));
renderedSampleCount = 0;
extensions.display = NULL;
extensions.oldMT32DisplayFeatures = false;
}
Synth::~Synth() {
close(); // Make sure we're closed and everything is freed
if (isDefaultReportHandler) {
delete reportHandler;
}
delete &mt32ram;
delete &mt32default;
delete &extensions;
}
void Synth::setReportHandler2(ReportHandler2 *reportHandler2) {
if (reportHandler2 != NULL) {
reportHandler = reportHandler2;
extensions.reportHandler2 = reportHandler2;
} else {
reportHandler = &extensions.defaultReportHandler;
extensions.reportHandler2 = &extensions.defaultReportHandler;
}
}
void ReportHandler::showLCDMessage(const char *data) {
printf("WRITE-LCD: %s\n", data);
}
@@ -310,26 +360,38 @@ void ReportHandler::printDebug(const char *fmt, va_list list) {
printf("\n");
}
void Synth::newTimbreSet(Bit8u partNum, Bit8u timbreGroup, Bit8u timbreNumber, const char patchName[]) {
const char *soundGroupName;
void Synth::rhythmNotePlayed() const {
extensions.display->rhythmNotePlayed();
}
void Synth::voicePartStateChanged(Bit8u partNum, bool partActivated) const {
extensions.display->voicePartStateChanged(partNum, partActivated);
}
void Synth::newTimbreSet(Bit8u partNum) const {
const Part *part = getPart(partNum);
reportHandler->onProgramChanged(partNum, getSoundGroupName(part), part->getCurrentInstr());
}
const char *Synth::getSoundGroupName(const Part *part) const {
const PatchParam &patch = part->getPatchTemp()->patch;
return getSoundGroupName(patch.timbreGroup, patch.timbreNum);
}
const char *Synth::getSoundGroupName(Bit8u timbreGroup, Bit8u timbreNumber) const {
switch (timbreGroup) {
case 1:
timbreNumber += 64;
// Fall-through
case 0:
soundGroupName = soundGroupNames[soundGroupIx[timbreNumber]];
break;
return soundGroupNames[soundGroupIx[timbreNumber]];
case 2:
soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 2];
break;
return soundGroupNames[controlROMMap->soundGroupsCount - 2];
case 3:
soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 1];
break;
return soundGroupNames[controlROMMap->soundGroupsCount - 1];
default:
soundGroupName = NULL;
break;
return NULL;
}
reportHandler->onProgramChanged(partNum, soundGroupName, patchName);
}
#define MT32EMU_PRINT_DEBUG \
@@ -450,6 +512,16 @@ float Synth::getReverbOutputGain() const {
return reverbOutputGain;
}
void Synth::setPartVolumeOverride(Bit8u partNumber, Bit8u volumeOverride) {
if (opened && partNumber < 9) {
parts[partNumber]->setVolumeOverride(volumeOverride);
}
}
Bit8u Synth::getPartVolumeOverride(Bit8u partNumber) const {
return (!opened || partNumber > 8) ? 255 : parts[partNumber]->getVolumeOverride();
}
void Synth::setReversedStereoEnabled(bool enabled) {
reversedStereoEnabled = enabled;
}
@@ -542,7 +614,7 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) {
int order[16] = {0, 9, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8};
Bit16s log = 0;
for (int u = 0; u < 15; u++) {
for (int u = 0; u < 16; u++) {
int bit;
if (order[u] < 8) {
bit = (s >> (7 - order[u])) & 0x1;
@@ -717,6 +789,16 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, B
return false;
}
if (controlROMMap->timbreRCount == 30) {
// We must initialise all 64 rhythm timbres to avoid undefined behaviour.
// SEMI-CONFIRMED: Old-gen MT-32 units likely map timbres 30..59 to 0..29.
// Attempts to play rhythm timbres 60..63 exhibit undefined behaviour.
// We want to emulate the wrap around, so merely copy the entire set of standard
// timbres once more. The last 4 dangerous timbres are zeroed out.
memcpy(&mt32ram.timbres[222], &mt32ram.timbres[192], sizeof(*mt32ram.timbres) * 30);
memset(&mt32ram.timbres[252], 0, sizeof(*mt32ram.timbres) * 4);
}
#if MT32EMU_MONITOR_INIT
printDebug("Initialising Timbre Bank M");
#endif
@@ -838,6 +920,9 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, B
return false;
}
extensions.display = new Display(*this);
extensions.oldMT32DisplayFeatures = controlROMFeatures->oldMT32DisplayFeatures;
opened = true;
activated = false;
@@ -850,6 +935,9 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, B
void Synth::dispose() {
opened = false;
delete extensions.display;
extensions.display = NULL;
delete midiQueue;
midiQueue = NULL;
@@ -1064,7 +1152,7 @@ void Synth::playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity) {
if (velocity == 0) {
// MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40
parts[part]->noteOff(note);
} else {
} else if (parts[part]->getVolumeOverride() > 0) {
parts[part]->noteOn(note, velocity);
}
break;
@@ -1130,16 +1218,21 @@ void Synth::playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity) {
#endif
return;
}
extensions.display->midiMessagePlayed();
break;
case 0xC: // Program change
//printDebug("Program change %01x", note);
parts[part]->setProgram(note);
if (part < 8) {
extensions.display->midiMessagePlayed();
extensions.display->programChanged(part);
}
break;
case 0xE: // Pitch bender
bend = (velocity << 7) | (note);
//printDebug("Pitch bender %02x", bend);
parts[part]->setBend(bend);
extensions.display->midiMessagePlayed();
break;
default:
#if MT32EMU_MONITOR_MIDI > 0
@@ -1197,12 +1290,19 @@ void Synth::playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sys
printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", int(device));
return;
}
// This is checked early in the real devices (before any sysex length checks or further processing)
// FIXME: Response to SYSEX_CMD_DAT reset with partials active (and in general) is untested.
if ((command == SYSEX_CMD_DT1 || command == SYSEX_CMD_DAT) && sysex[0] == 0x7F) {
reset();
// All models process the checksum before anything else and ignore messages lacking the checksum, or containing the checksum only.
if (len < 2) {
printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
return;
}
Bit8u checksum = calcSysexChecksum(sysex, len - 1);
if (checksum != sysex[len - 1]) {
printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
if (opened) extensions.display->checksumErrorOccurred();
return;
}
len -= 1; // Exclude checksum
if (command == SYSEX_CMD_EOD) {
#if MT32EMU_MONITOR_SYSEX > 0
@@ -1210,16 +1310,6 @@ void Synth::playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sys
#endif
return;
}
if (len < 4) {
printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
return;
}
Bit8u checksum = calcSysexChecksum(sysex, len - 1);
if (checksum != sysex[len - 1]) {
printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
return;
}
len -= 1; // Exclude checksum
switch (command) {
case SYSEX_CMD_WSD:
#if MT32EMU_MONITOR_SYSEX > 0
@@ -1259,12 +1349,34 @@ void Synth::readSysex(Bit8u /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/)
}
void Synth::writeSysex(Bit8u device, const Bit8u *sysex, Bit32u len) {
if (!opened) return;
if (!opened || len < 1) return;
// This is checked early in the real devices (before any sysex length checks or further processing)
if (sysex[0] == 0x7F) {
if (!isDisplayOldMT32Compatible()) extensions.display->midiMessagePlayed();
reset();
return;
}
extensions.display->midiMessagePlayed();
reportHandler->onMIDIMessagePlayed();
if (len < 3) {
// A short message of just 1 or 2 bytes may be written to the display area yet it may cause a user-visible effect,
// similarly to the reset area.
if (sysex[0] == 0x20) {
extensions.display->displayControlMessageReceived(sysex, len);
return;
}
printDebug("writeSysex: Message is too short (%d bytes)!", len);
return;
}
Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
addr = MT32EMU_MEMADDR(addr);
sysex += 3;
len -= 3;
//printDebug("Sysex addr: 0x%06x", MT32EMU_SYSEXMEMADDR(addr));
// NOTE: Please keep both lower and upper bounds in each check, for ease of reading
@@ -1345,6 +1457,7 @@ void Synth::writeSysexGlobal(Bit32u addr, const Bit8u *sysex, Bit32u len) {
if (region == NULL) {
printDebug("Sysex write to unrecognised address %06x, len %d", MT32EMU_SYSEXMEMADDR(addr), len);
// FIXME: Real devices may respond differently to a long SysEx that covers adjacent regions.
break;
}
writeMemoryRegion(region, addr, region->getClampedLen(addr, len), sysex);
@@ -1663,7 +1776,10 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le
}
break;
case MR_Display:
char buf[SYSEX_BUFFER_SIZE];
if (len > Display::LCD_TEXT_SIZE) len = Display::LCD_TEXT_SIZE;
if (!extensions.display->customDisplayMessageReceived(data, off, len)) break;
// Holds zero-terminated string of the maximum length.
char buf[Display::LCD_TEXT_SIZE + 1];
memcpy(&buf, &data[0], len);
buf[len] = 0;
#if MT32EMU_MONITOR_SYSEX > 0
@@ -1674,6 +1790,8 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le
case MR_Reset:
reset();
break;
default:
break;
}
}
@@ -1767,6 +1885,10 @@ void Synth::refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart) {
}
void Synth::refreshSystemMasterVol() {
// Note, this should only occur when the user turns the volume knob. When the master volume is set via a SysEx, display
// doesn't actually update on all real devices. However, we anyway update the display, as we don't foresee a dedicated
// API for setting the master volume yet it's rather dubious that one really needs this quirk to be fairly emulated.
if (opened) extensions.display->masterVolumeChanged();
#if MT32EMU_MONITOR_SYSEX > 0
printDebug(" Master volume: %d", mt32ram.system.masterVol);
#endif
@@ -1816,6 +1938,32 @@ Bit32s Synth::getMasterTunePitchDelta() const {
return extensions.masterTunePitchDelta;
}
bool Synth::getDisplayState(char *targetBuffer, bool narrowLCD) const {
if (!opened) {
memset(targetBuffer, ' ', Display::LCD_TEXT_SIZE);
targetBuffer[Display::LCD_TEXT_SIZE] = 0;
return false;
}
return extensions.display->getDisplayState(targetBuffer, narrowLCD);
}
void Synth::setMainDisplayMode() {
if (opened) extensions.display->setMainDisplayMode();
}
void Synth::setDisplayCompatibility(bool oldMT32CompatibilityEnabled) {
extensions.oldMT32DisplayFeatures = oldMT32CompatibilityEnabled;
}
bool Synth::isDisplayOldMT32Compatible() const {
return extensions.oldMT32DisplayFeatures;
}
bool Synth::isDefaultDisplayOldMT32Compatible() const {
return opened && controlROMFeatures->oldMT32DisplayFeatures;
}
/** Defines an interface of a class that maintains storage of variable-sized data of SysEx messages. */
class MidiEventQueue::SysexDataStorage {
public:
@@ -1994,6 +2142,15 @@ Bit32u Synth::getStereoOutputSampleRate() const {
return (analog == NULL) ? SAMPLE_RATE : analog->getOutputSampleRate();
}
void Renderer::updateDisplayState() {
bool midiMessageLEDState;
bool midiMessageLEDStateUpdated;
bool lcdUpdated;
synth.extensions.display->checkDisplayStateUpdated(midiMessageLEDState, midiMessageLEDStateUpdated, lcdUpdated);
if (midiMessageLEDStateUpdated) synth.extensions.reportHandler2->onMidiMessageLEDStateUpdated(midiMessageLEDState);
if (lcdUpdated) synth.extensions.reportHandler2->onLCDStateUpdated();
}
template <class Sample>
void RendererImpl<Sample>::doRender(Sample *stereoStream, Bit32u len) {
if (!isActivated()) {
@@ -2002,6 +2159,7 @@ void RendererImpl<Sample>::doRender(Sample *stereoStream, Bit32u len) {
printDebug("RendererImpl: Invalid call to Analog::process()!\n");
}
Synth::muteSampleBuffer(stereoStream, len << 1);
updateDisplayState();
return;
}
@@ -2358,6 +2516,7 @@ void RendererImpl<Sample>::produceStreams(const DACOutputStreams<Sample> &stream
getPartialManager().clearAlreadyOutputed();
incRenderedSampleCount(len);
updateDisplayState();
}
void Synth::printPartialUsage(Bit32u sampleOffset) {
@@ -2468,6 +2627,26 @@ const char *Synth::getPatchName(Bit8u partNumber) const {
return (!opened || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr();
}
bool Synth::getSoundGroupName(char *soundGroupName, Bit8u timbreGroup, Bit8u timbreNumber) const {
if (!opened || 63 < timbreNumber) return false;
const char *foundGroupName = getSoundGroupName(timbreGroup, timbreNumber);
if (foundGroupName == NULL) return false;
memcpy(soundGroupName, foundGroupName, 7);
soundGroupName[7] = 0;
return true;
}
bool Synth::getSoundName(char *soundName, Bit8u timbreGroup, Bit8u timbreNumber) const {
if (!opened || 3 < timbreGroup) return false;
Bit8u timbresInGroup = 3 == timbreGroup ? controlROMMap->timbreRCount : 64;
if (timbresInGroup <= timbreNumber) return false;
TimbreParam::CommonParam &timbreCommon = mt32ram.timbres[timbreGroup * 64 + timbreNumber].timbre.common;
if (timbreCommon.partialMute == 0) return false;
memcpy(soundName, timbreCommon.name, sizeof timbreCommon.name);
soundName[sizeof timbreCommon.name] = 0;
return true;
}
const Part *Synth::getPart(Bit8u partNum) const {
if (partNum > 8) {
return NULL;