From b4dbd6d3c275097b4be964b7da4478ff930cbaa7 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 24 Nov 2016 09:24:00 -0600 Subject: MT32: Update Munt to 2.0.0 This changeset also removes unnecessary direct modifications to Munt code to ease future updates. To update Munt 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 match the list of sources given in `mt32emu/CMakeLists.txt libmt32emu_SOURCES`. --- audio/softsynth/mt32/Synth.cpp | 702 +++++++++++++++++++++++++++++------------ 1 file changed, 505 insertions(+), 197 deletions(-) (limited to 'audio/softsynth/mt32/Synth.cpp') diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp index 6df7eb9e31..ca28898205 100644 --- a/audio/softsynth/mt32/Synth.cpp +++ b/audio/softsynth/mt32/Synth.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * 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 @@ -15,42 +15,138 @@ * along with this program. If not, see . */ -//#include -//#include -//#include -//#include +#include -#include "mt32emu.h" -#include "mmath.h" #include "internals.h" +#include "Synth.h" #include "Analog.h" #include "BReverbModel.h" +#include "File.h" #include "MemoryRegion.h" #include "MidiEventQueue.h" +#include "Part.h" +#include "Partial.h" #include "PartialManager.h" +#include "Poly.h" +#include "ROMInfo.h" +#include "TVA.h" 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, true, true }; +static const ControlROMFeatureSet CM32L_COMPATIBLE = { false, false, false }; + static const ControlROMMap ControlROMMaps[7] = { - // ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO, tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax - {0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A}, - {0x4014, 22, "\000 ver1.05 06 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A}, - {0x4014, 22, "\000 ver1.06 31 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C}, - {0x4010, 22, "\000 ver1.07 10 Oct, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4}, // MT-32 revision 1 - {0x4010, 22, "\000verX.XX 30 Sep, 88 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228}, // MT-32 Blue Ridge mod - {0x2205, 22, "\000CM32/LAPC1.00 890404", 0x8100, 256, 0x8000, 0x8000, false, 0x8080, 0x8000, false, 0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5}, - {0x2205, 22, "\000CM32/LAPC1.02 891205", 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF} // CM-32L + // 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_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 // (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp) }; -static inline void advanceStreamPosition(Sample *&stream, Bit32u posDelta) { - if (stream != NULL) { - stream += posDelta; +static const PartialState PARTIAL_PHASE_TO_STATE[8] = { + PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, + PartialState_SUSTAIN, PartialState_SUSTAIN, PartialState_RELEASE, PartialState_INACTIVE +}; + +static inline PartialState getPartialState(PartialManager *partialManager, unsigned int partialNum) { + const Partial *partial = partialManager->getPartial(partialNum); + return partial->isActive() ? PARTIAL_PHASE_TO_STATE[partial->getTVA()->getPhase()] : PartialState_INACTIVE; +} + +static inline Bit16s convertSample(float sample) { + return Synth::clipSampleEx(Bit32s(sample * 16384.0f)); // This multiplier takes into account the DAC bit shift +} + +static inline float convertSample(Bit16s sample) { + return float(sample) / 16384.0f; // This multiplier takes into account the DAC bit shift +} + +class SampleFormatConverter { +protected: +#if MT32EMU_USE_FLOAT_SAMPLES + Bit16s *outBuffer; +#else + float *outBuffer; +#endif + +public: + Sample *sampleBuffer; + + SampleFormatConverter(Sample *buffer) : outBuffer(NULL), sampleBuffer(buffer) {} + + inline bool isConversionNeeded() { + return outBuffer != NULL; + } + + inline void convert(Bit32u len) { + if (sampleBuffer == NULL) return; + if (outBuffer == NULL) { + sampleBuffer += len; + return; + } + Sample *inBuffer = sampleBuffer; + while (len--) { + *(outBuffer++) = convertSample(*(inBuffer++)); + } + } + + inline void addSilence(Bit32u len) { + if (outBuffer != NULL) { + Synth::muteSampleBuffer(outBuffer, len); + outBuffer += len; + } else if (sampleBuffer != NULL) { + Synth::muteSampleBuffer(sampleBuffer, len); + sampleBuffer += len; + } + } +}; + +template +class BufferedSampleFormatConverter : public SampleFormatConverter { + Sample renderingBuffer[BUFFER_SIZE_MULTIPLIER * MAX_SAMPLES_PER_RUN]; + +public: +#if MT32EMU_USE_FLOAT_SAMPLES + BufferedSampleFormatConverter(Bit16s *buffer) +#else + BufferedSampleFormatConverter(float *buffer) +#endif + : SampleFormatConverter(renderingBuffer) + { + outBuffer = buffer; + if (buffer == NULL) sampleBuffer = NULL; } +}; + +class Renderer { + Synth &synth; + +public: + Renderer(Synth &useSynth) : synth(useSynth) {} + + void render(SampleFormatConverter &converter, Bit32u len); + void renderStreams(SampleFormatConverter &nonReverbLeft, SampleFormatConverter &nonReverbRight, SampleFormatConverter &reverbDryLeft, SampleFormatConverter &reverbDryRight, SampleFormatConverter &reverbWetLeft, SampleFormatConverter &reverbWetRight, Bit32u len); + void produceLA32Output(Sample *buffer, Bit32u len); + void convertSamplesToOutput(Sample *buffer, Bit32u len); + void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len); +}; + +Bit32u Synth::getLibraryVersionInt() { + return (MT32EMU_VERSION_MAJOR << 16) | (MT32EMU_VERSION_MINOR << 8) | (MT32EMU_VERSION_PATCH); +} + +const char *Synth::getLibraryVersionString() { + return MT32EMU_VERSION; } Bit8u Synth::calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum) { @@ -61,10 +157,17 @@ Bit8u Synth::calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u return Bit8u(checksum & 0x7f); } -Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams()), mt32default(*new MemParams()) { - isOpen = false; +Bit32u Synth::getStereoOutputSampleRate(AnalogOutputMode analogOutputMode) { + static const unsigned int SAMPLE_RATES[] = {SAMPLE_RATE, SAMPLE_RATE, SAMPLE_RATE * 3 / 2, SAMPLE_RATE * 3}; + + return SAMPLE_RATES[analogOutputMode]; +} + +Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams), mt32default(*new MemParams), renderer(*new Renderer(*this)) { + opened = false; reverbOverridden = false; partialCount = DEFAULT_MAX_PARTIALS; + controlROMMap = NULL; controlROMFeatures = NULL; if (useReportHandler == NULL) { @@ -85,11 +188,27 @@ Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams()), mt32d setOutputGain(1.0f); setReverbOutputGain(1.0f); setReversedStereoEnabled(false); + + patchTempMemoryRegion = NULL; + rhythmTempMemoryRegion = NULL; + timbreTempMemoryRegion = NULL; + patchesMemoryRegion = NULL; + timbresMemoryRegion = NULL; + systemMemoryRegion = NULL; + displayMemoryRegion = NULL; + resetMemoryRegion = NULL; + paddedTimbreMaxTable = NULL; + partialManager = NULL; + pcmWaves = NULL; + pcmROMData = NULL; + soundGroupNames = NULL; midiQueue = NULL; lastReceivedMIDIEventTimestamp = 0; memset(parts, 0, sizeof(parts)); renderedSampleCount = 0; + + reserved = NULL; } Synth::~Synth() { @@ -99,47 +218,52 @@ Synth::~Synth() { } delete &mt32ram; delete &mt32default; + delete &renderer; } void ReportHandler::showLCDMessage(const char *data) { - // We cannot use printf here. Since we already implement our own - // ReportHandler we simply disable the default implementation since it is - // never called anyway. -#if 0 - printf("WRITE-LCD: %s", data); - printf("\n"); -#endif + printf("WRITE-LCD: %s\n", data); } void ReportHandler::printDebug(const char *fmt, va_list list) { - // We cannot use (v)printf here. Since we already implement our own - // ReportHandler we simply disable the default implementation since it is - // never called anyway. -#if 0 vprintf(fmt, list); printf("\n"); -#endif } -void Synth::polyStateChanged(int partNum) { - reportHandler->onPolyStateChanged(partNum); -} - -void Synth::newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]) { - reportHandler->onProgramChanged(partNum, timbreGroup, patchName); +void Synth::newTimbreSet(Bit8u partNum, Bit8u timbreGroup, Bit8u timbreNumber, const char patchName[]) { + const char *soundGroupName; + switch (timbreGroup) { + case 1: + timbreNumber += 64; + // Fall-through + case 0: + soundGroupName = soundGroupNames[soundGroupIx[timbreNumber]]; + break; + case 2: + soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 2]; + break; + case 3: + soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 1]; + break; + default: + soundGroupName = NULL; + break; + } + reportHandler->onProgramChanged(partNum, soundGroupName, patchName); } void Synth::printDebug(const char *fmt, ...) { va_list ap; va_start(ap, fmt); #if MT32EMU_DEBUG_SAMPLESTAMPS > 0 - reportHandler->printDebug("[%u] ", (char *)&renderedSampleCount); + reportHandler->printDebug("[%u]", (va_list)&renderedSampleCount); #endif reportHandler->printDebug(fmt, ap); va_end(ap); } void Synth::setReverbEnabled(bool newReverbEnabled) { + if (!opened) return; if (isReverbEnabled() == newReverbEnabled) return; if (newReverbEnabled) { bool oldReverbOverridden = reverbOverridden; @@ -167,30 +291,23 @@ bool Synth::isReverbOverridden() const { } void Synth::setReverbCompatibilityMode(bool mt32CompatibleMode) { - if (reverbModels[REVERB_MODE_ROOM] != NULL) { - if (isMT32ReverbCompatibilityMode() == mt32CompatibleMode) return; - setReverbEnabled(false); - for (int i = 0; i < 4; i++) { - delete reverbModels[i]; - } - } - reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM, mt32CompatibleMode); - reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL, mt32CompatibleMode); - reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE, mt32CompatibleMode); - reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY, mt32CompatibleMode); -#if !MT32EMU_REDUCE_REVERB_MEMORY - for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) { - reverbModels[i]->open(); - } -#endif - if (isOpen) { - setReverbOutputGain(reverbOutputGain); - setReverbEnabled(true); + if (!opened || (isMT32ReverbCompatibilityMode() == mt32CompatibleMode)) return; + bool oldReverbEnabled = isReverbEnabled(); + setReverbEnabled(false); + for (int i = 0; i < 4; i++) { + delete reverbModels[i]; } + initReverbModels(mt32CompatibleMode); + setReverbEnabled(oldReverbEnabled); + setReverbOutputGain(reverbOutputGain); } bool Synth::isMT32ReverbCompatibilityMode() const { - return isOpen && (reverbModels[REVERB_MODE_ROOM]->isMT32Compatible(REVERB_MODE_ROOM)); + return opened && (reverbModels[REVERB_MODE_ROOM]->isMT32Compatible(REVERB_MODE_ROOM)); +} + +bool Synth::isDefaultReverbMT32Compatible() const { + return opened && controlROMFeatures->defaultReverbMT32Compatible; } void Synth::setDACInputMode(DACInputMode mode) { @@ -239,22 +356,18 @@ void Synth::setReversedStereoEnabled(bool enabled) { reversedStereoEnabled = enabled; } -bool Synth::isReversedStereoEnabled() { +bool Synth::isReversedStereoEnabled() const { return reversedStereoEnabled; } bool Synth::loadControlROM(const ROMImage &controlROMImage) { - Common::File *file = controlROMImage.getFile(); + File *file = controlROMImage.getFile(); const ROMInfo *controlROMInfo = controlROMImage.getROMInfo(); if ((controlROMInfo == NULL) || (controlROMInfo->type != ROMInfo::Control) || (controlROMInfo->pairType != ROMInfo::Full)) { - return false; - } - controlROMFeatures = controlROMImage.getROMInfo()->controlROMFeatures; - if (controlROMFeatures == NULL) { #if MT32EMU_MONITOR_INIT - printDebug("Invalid Control ROM Info provided without feature set"); + printDebug("Invalid Control ROM Info provided"); #endif return false; } @@ -262,13 +375,16 @@ bool Synth::loadControlROM(const ROMImage &controlROMImage) { #if MT32EMU_MONITOR_INIT printDebug("Found Control ROM: %s, %s", controlROMInfo->shortName, controlROMInfo->description); #endif - file->read(controlROMData, CONTROL_ROM_SIZE); + const Bit8u *fileData = file->getData(); + memcpy(controlROMData, fileData, CONTROL_ROM_SIZE); // Control ROM successfully loaded, now check whether it's a known type controlROMMap = NULL; + controlROMFeatures = NULL; for (unsigned int i = 0; i < sizeof(ControlROMMaps) / sizeof(ControlROMMaps[0]); i++) { - if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) { + if (strcmp(controlROMInfo->shortName, ControlROMMaps[i].shortName) == 0) { controlROMMap = &ControlROMMaps[i]; + controlROMFeatures = &controlROMMap->featureSet; return true; } } @@ -279,7 +395,7 @@ bool Synth::loadControlROM(const ROMImage &controlROMImage) { } bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { - Common::File *file = pcmROMImage.getFile(); + File *file = pcmROMImage.getFile(); const ROMInfo *pcmROMInfo = pcmROMImage.getROMInfo(); if ((pcmROMInfo == NULL) || (pcmROMInfo->type != ROMInfo::PCM) @@ -289,24 +405,21 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { #if MT32EMU_MONITOR_INIT printDebug("Found PCM ROM: %s, %s", pcmROMInfo->shortName, pcmROMInfo->description); #endif - size_t fileSize = file->size(); + size_t fileSize = file->getSize(); if (fileSize != (2 * pcmROMSize)) { #if MT32EMU_MONITOR_INIT printDebug("PCM ROM file has wrong size (expected %d, got %d)", 2 * pcmROMSize, fileSize); #endif return false; } - - byte *buffer = new byte[file->size()]; - file->read(buffer, file->size()); - const byte *fileData = buffer; + const Bit8u *fileData = file->getData(); for (size_t i = 0; i < pcmROMSize; i++) { Bit8u s = *(fileData++); Bit8u c = *(fileData++); int order[16] = {0, 9, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8}; - signed short log = 0; + Bit16s log = 0; for (int u = 0; u < 15; u++) { int bit; if (order[u] < 8) { @@ -314,13 +427,10 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { } else { bit = (c >> (7 - (order[u] - 8))) & 0x1; } - log = log | (short)(bit << (15 - u)); + log = log | (Bit16s)(bit << (15 - u)); } pcmROMData[i] = log; } - - delete[] buffer; - return true; } @@ -345,7 +455,7 @@ bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) { return false; } -bool Synth::initCompressedTimbre(int timbreNum, const Bit8u *src, unsigned int srcLen) { +bool Synth::initCompressedTimbre(Bit16u timbreNum, const Bit8u *src, Bit32u srcLen) { // "Compressed" here means that muted partials aren't present in ROM (except in the case of partial 0 being muted). // Instead the data from the previous unmuted partial is used. if (srcLen < sizeof(TimbreParam::CommonParam)) { @@ -369,7 +479,7 @@ bool Synth::initCompressedTimbre(int timbreNum, const Bit8u *src, unsigned int s return true; } -bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTimbre, bool compressed) { +bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, Bit16u count, Bit16u startTimbre, bool compressed) { const Bit8u *timbreMap = &controlROMData[mapAddress]; for (Bit16u i = 0; i < count * 2; i += 2) { Bit16u address = (timbreMap[i + 1] << 8) | timbreMap[i]; @@ -391,12 +501,32 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi return true; } +void Synth::initReverbModels(bool mt32CompatibleMode) { + reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM, mt32CompatibleMode); + reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL, mt32CompatibleMode); + reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE, mt32CompatibleMode); + reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY, mt32CompatibleMode); +#if !MT32EMU_REDUCE_REVERB_MEMORY + for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) { + reverbModels[i]->open(); + } +#endif +} + +void Synth::initSoundGroups(char newSoundGroupNames[][9]) { + memcpy(soundGroupIx, &controlROMData[controlROMMap->soundGroupsTable - sizeof(soundGroupIx)], sizeof(soundGroupIx)); + const SoundGroup *table = (SoundGroup *)&controlROMData[controlROMMap->soundGroupsTable]; + for (unsigned int i = 0; i < controlROMMap->soundGroupsCount; i++) { + memcpy(&newSoundGroupNames[i][0], table[i].name, sizeof(table[i].name)); + } +} + bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode) { return open(controlROMImage, pcmROMImage, DEFAULT_MAX_PARTIALS, analogOutputMode); } bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount, AnalogOutputMode analogOutputMode) { - if (isOpen) { + if (opened) { return false; } partialCount = usePartialCount; @@ -411,6 +541,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u if (!loadControlROM(controlROMImage)) { printDebug("Init Error - Missing or invalid Control ROM image"); reportHandler->onErrorControlROM(); + dispose(); return false; } @@ -428,22 +559,24 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u if (!loadPCMROM(pcmROMImage)) { printDebug("Init Error - Missing PCM ROM image"); reportHandler->onErrorPCMROM(); + dispose(); return false; } #if MT32EMU_MONITOR_INIT printDebug("Initialising Reverb Models"); #endif - bool mt32CompatibleReverb = controlROMFeatures->isDefaultReverbMT32Compatible(); + bool mt32CompatibleReverb = controlROMFeatures->defaultReverbMT32Compatible; #if MT32EMU_MONITOR_INIT printDebug("Using %s Compatible Reverb Models", mt32CompatibleReverb ? "MT-32" : "CM-32L"); #endif - setReverbCompatibilityMode(mt32CompatibleReverb); + initReverbModels(mt32CompatibleReverb); #if MT32EMU_MONITOR_INIT printDebug("Initialising Timbre Bank A"); #endif if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0x40, 0, controlROMMap->timbreACompressed)) { + dispose(); return false; } @@ -451,6 +584,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u printDebug("Initialising Timbre Bank B"); #endif if (!initTimbres(controlROMMap->timbreBMap, controlROMMap->timbreBOffset, 0x40, 64, controlROMMap->timbreBCompressed)) { + dispose(); return false; } @@ -458,6 +592,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u printDebug("Initialising Timbre Bank R"); #endif if (!initTimbres(controlROMMap->timbreRMap, 0, controlROMMap->timbreRCount, 192, true)) { + dispose(); return false; } @@ -519,6 +654,10 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u refreshSystem(); reverbOverridden = oldReverbOverridden; + char(*writableSoundGroupNames)[9] = new char[controlROMMap->soundGroupsCount][9]; + soundGroupNames = writableSoundGroupNames; + initSoundGroups(writableSoundGroupNames); + for (int i = 0; i < 9; i++) { MemParams::PatchTemp *patchTemp = &mt32ram.patchTemp[i]; @@ -550,12 +689,12 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u midiQueue = new MidiEventQueue(); - analog = new Analog(analogOutputMode, controlROMFeatures); + analog = new Analog(analogOutputMode, controlROMFeatures->oldMT32AnalogLPF); setOutputGain(outputGain); setReverbOutputGain(reverbOutputGain); - isOpen = true; - isEnabled = false; + opened = true; + activated = false; #if MT32EMU_MONITOR_INIT printDebug("*** Initialisation complete ***"); @@ -563,10 +702,8 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u return true; } -void Synth::close(bool forced) { - if (!forced && !isOpen) { - return; - } +void Synth::dispose() { + opened = false; delete midiQueue; midiQueue = NULL; @@ -582,8 +719,14 @@ void Synth::close(bool forced) { parts[i] = NULL; } + delete[] soundGroupNames; + soundGroupNames = NULL; + delete[] pcmWaves; + pcmWaves = NULL; + delete[] pcmROMData; + pcmROMData = NULL; deleteMemoryRegions(); @@ -593,7 +736,17 @@ void Synth::close(bool forced) { } reverbModel = NULL; controlROMFeatures = NULL; - isOpen = false; + controlROMMap = NULL; +} + +void Synth::close() { + if (opened) { + dispose(); + } +} + +bool Synth::isOpen() const { + return opened; } void Synth::flushMIDIQueue() { @@ -664,12 +817,19 @@ bool Synth::playMsg(Bit32u msg) { } bool Synth::playMsg(Bit32u msg, Bit32u timestamp) { + if ((msg & 0xF8) == 0xF8) { + reportHandler->onMIDISystemRealtime((Bit8u)msg); + return true; + } if (midiQueue == NULL) return false; if (midiDelayMode != MIDIDelayMode_IMMEDIATE) { timestamp = addMIDIInterfaceDelay(getShortMessageLength(msg), timestamp); } - if (!isEnabled) isEnabled = true; - return midiQueue->pushShortMessage(msg, timestamp); + if (!activated) activated = true; + do { + if (midiQueue->pushShortMessage(msg, timestamp)) return true; + } while (reportHandler->onMIDIQueueOverflow()); + return false; } bool Synth::playSysex(const Bit8u *sysex, Bit32u len) { @@ -681,24 +841,28 @@ bool Synth::playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp) { if (midiDelayMode == MIDIDelayMode_DELAY_ALL) { timestamp = addMIDIInterfaceDelay(len, timestamp); } - if (!isEnabled) isEnabled = true; - return midiQueue->pushSysex(sysex, len, timestamp); + if (!activated) activated = true; + do { + if (midiQueue->pushSysex(sysex, len, timestamp)) return true; + } while (reportHandler->onMIDIQueueOverflow()); + return false; } void Synth::playMsgNow(Bit32u msg) { + if (!opened) return; + // NOTE: Active sense IS implemented in real hardware. However, realtime processing is clearly out of the library scope. // It is assumed that realtime consumers of the library respond to these MIDI events as appropriate. - unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4); - unsigned char chan = (unsigned char)(msg & 0x00000F); - unsigned char note = (unsigned char)((msg & 0x007F00) >> 8); - unsigned char velocity = (unsigned char)((msg & 0x7F0000) >> 16); - if (!isEnabled) isEnabled = true; + Bit8u code = (Bit8u)((msg & 0x0000F0) >> 4); + Bit8u chan = (Bit8u)(msg & 0x00000F); + Bit8u note = (Bit8u)((msg & 0x007F00) >> 8); + Bit8u velocity = (Bit8u)((msg & 0x7F0000) >> 16); //printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note); - char part = chantable[chan]; - if (part < 0 || part > 8) { + Bit8u part = chantable[chan]; + if (part > 8) { #if MT32EMU_MONITOR_MIDI > 0 printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity); #endif @@ -707,9 +871,12 @@ void Synth::playMsgNow(Bit32u msg) { playMsgOnPart(part, code, note, velocity); } -void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) { +void Synth::playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity) { + if (!opened) return; + Bit32u bend; + if (!activated) activated = true; //printDebug("Synth::playMsgOnPart(%02x, %02x, %02x, %02x)", part, code, note, velocity); switch (code) { case 0x8: @@ -849,7 +1016,7 @@ void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) { playSysexWithoutHeader(sysex[1], sysex[3], sysex + 4, len - 4); } -void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len) { +void Synth::playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sysex, Bit32u len) { if (device > 0x10) { // We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", (int)device); @@ -861,6 +1028,13 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, reset(); return; } + + if (command == SYSEX_CMD_EOD) { +#if MT32EMU_MONITOR_SYSEX > 0 + printDebug("playSysexWithoutHeader: Ignored unsupported command %02x", command); +#endif + return; + } if (len < 4) { printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len); return; @@ -872,12 +1046,19 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, } len -= 1; // Exclude checksum switch (command) { + case SYSEX_CMD_WSD: +#if MT32EMU_MONITOR_SYSEX > 0 + printDebug("playSysexWithoutHeader: Ignored unsupported command %02x", command); +#endif + break; case SYSEX_CMD_DAT: + /* Outcommented until we (ever) actually implement handshake communication if (hasActivePartials()) { printDebug("playSysexWithoutHeader: Got SYSEX_CMD_DAT but partials are active - ignoring"); // FIXME: We should send SYSEX_CMD_RJC in this case break; } + */ // Deliberate fall-through case SYSEX_CMD_DT1: writeSysex(device, sysex, len); @@ -898,11 +1079,12 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, } } -void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) const { +void Synth::readSysex(Bit8u /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) const { // NYI } -void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) { +void Synth::writeSysex(Bit8u device, const Bit8u *sysex, Bit32u len) { + if (!opened) return; reportHandler->onMIDIMessagePlayed(); Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]); addr = MT32EMU_MEMADDR(addr); @@ -918,7 +1100,7 @@ void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) { #endif if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) { int offset; - if (chantable[device] == -1) { + if (chantable[device] > 8) { #if MT32EMU_MONITOR_SYSEX > 0 printDebug(" (Channel not mapped to a part... 0 offset)"); #endif @@ -939,7 +1121,7 @@ void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) { addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000); } else if (/*addr >= MT32EMU_MEMADDR(0x020000) && */ addr < MT32EMU_MEMADDR(0x030000)) { int offset; - if (chantable[device] == -1) { + if (chantable[device] > 8) { #if MT32EMU_MONITOR_SYSEX > 0 printDebug(" (Channel not mapped to a part... 0 offset)"); #endif @@ -986,6 +1168,7 @@ void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) { } void Synth::readMemory(Bit32u addr, Bit32u len, Bit8u *data) { + if (!opened) return; const MemoryRegion *region = findMemoryRegion(addr); if (region != NULL) { readMemoryRegion(region, addr, len, data); @@ -1279,16 +1462,16 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le if(firstPart < 0) firstPart = 0; int lastPart = off + len - SYSTEM_CHAN_ASSIGN_START_OFF; - if(lastPart > 9) - lastPart = 9; - refreshSystemChanAssign(firstPart, lastPart); + if(lastPart > 8) + lastPart = 8; + refreshSystemChanAssign((Bit8u)firstPart, (Bit8u)lastPart); } if (off <= SYSTEM_MASTER_VOL_OFF && off + len > SYSTEM_MASTER_VOL_OFF) { refreshSystemMasterVol(); } break; case MR_Display: - char buf[MAX_SYSEX_SIZE]; + char buf[SYSEX_BUFFER_SIZE]; memcpy(&buf, &data[0], len); buf[len] = 0; #if MT32EMU_MONITOR_SYSEX > 0 @@ -1361,19 +1544,19 @@ void Synth::refreshSystemReserveSettings() { partialManager->setReserve(rset); } -void Synth::refreshSystemChanAssign(unsigned int firstPart, unsigned int lastPart) { - memset(chantable, -1, sizeof(chantable)); +void Synth::refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart) { + memset(chantable, 0xFF, sizeof(chantable)); // CONFIRMED: In the case of assigning a channel to multiple parts, the lower part wins. - for (unsigned int i = 0; i <= 8; i++) { + for (Bit32u i = 0; i <= 8; i++) { if (parts[i] != NULL && i >= firstPart && i <= lastPart) { // CONFIRMED: Decay is started for all polys, and all controllers are reset, for every part whose assignment was touched by the sysex write. parts[i]->allSoundOff(); parts[i]->resetAllControllers(); } - int chan = mt32ram.system.chanAssign[i]; - if (chan != 16 && chantable[chan] == -1) { - chantable[chan] = i; + Bit8u chan = mt32ram.system.chanAssign[i]; + if (chan < 16 && chantable[chan] > 8) { + chantable[chan] = (Bit8u)i; } } @@ -1398,6 +1581,7 @@ void Synth::refreshSystem() { } void Synth::reset() { + if (!opened) return; #if MT32EMU_MONITOR_SYSEX > 0 printDebug("RESET"); #endif @@ -1413,7 +1597,7 @@ void Synth::reset() { } } refreshSystem(); - isEnabled = false; + isActive(); } MidiEvent::~MidiEvent() { @@ -1477,7 +1661,7 @@ bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32 } const MidiEvent *MidiEventQueue::peekMidiEvent() { - return (startPosition == endPosition) ? NULL : &ringBuffer[startPosition]; + return isEmpty() ? NULL : &ringBuffer[startPosition]; } void MidiEventQueue::dropMidiEvent() { @@ -1491,15 +1675,24 @@ bool MidiEventQueue::isFull() const { return startPosition == ((endPosition + 1) & ringBufferMask); } -unsigned int Synth::getStereoOutputSampleRate() const { +bool MidiEventQueue::isEmpty() const { + return startPosition == endPosition; +} + +Bit32u Synth::getStereoOutputSampleRate() const { return (analog == NULL) ? SAMPLE_RATE : analog->getOutputSampleRate(); } -void Synth::render(Sample *stream, Bit32u len) { - if (!isEnabled) { - renderedSampleCount += analog->getDACStreamsLength(len); - analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len); - muteSampleBuffer(stream, len << 1); +void Renderer::render(SampleFormatConverter &converter, Bit32u len) { + if (!synth.opened) { + converter.addSilence(len << 1); + return; + } + + if (!synth.activated) { + synth.renderedSampleCount += synth.analog->getDACStreamsLength(len); + synth.analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len); + converter.addSilence(len << 1); return; } @@ -1510,19 +1703,53 @@ void Synth::render(Sample *stream, Bit32u len) { while (len > 0) { Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len; - renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, analog->getDACStreamsLength(thisPassLen)); - analog->process(&stream, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen); + synth.renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, synth.analog->getDACStreamsLength(thisPassLen)); + synth.analog->process(converter.sampleBuffer, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen); + converter.convert(thisPassLen << 1); len -= thisPassLen; } } -void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) { +void Synth::render(Bit16s *stream, Bit32u len) { +#if MT32EMU_USE_FLOAT_SAMPLES + BufferedSampleFormatConverter<2> converter(stream); +#else + SampleFormatConverter converter(stream); +#endif + renderer.render(converter, len); +} + +void Synth::render(float *stream, Bit32u len) { +#if MT32EMU_USE_FLOAT_SAMPLES + SampleFormatConverter converter(stream); +#else + BufferedSampleFormatConverter<2> converter(stream); +#endif + renderer.render(converter, len); +} + +void Renderer::renderStreams( + SampleFormatConverter &nonReverbLeft, SampleFormatConverter &nonReverbRight, + SampleFormatConverter &reverbDryLeft, SampleFormatConverter &reverbDryRight, + SampleFormatConverter &reverbWetLeft, SampleFormatConverter &reverbWetRight, + Bit32u len) +{ + if (!synth.opened) { + nonReverbLeft.addSilence(len); + nonReverbRight.addSilence(len); + reverbDryLeft.addSilence(len); + reverbDryRight.addSilence(len); + reverbWetLeft.addSilence(len); + reverbWetRight.addSilence(len); + return; + } + while (len > 0) { // We need to ensure zero-duration notes will play so add minimum 1-sample delay. Bit32u thisLen = 1; - if (!isAbortingPoly()) { - const MidiEvent *nextEvent = midiQueue->peekMidiEvent(); - Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - renderedSampleCount) : MAX_SAMPLES_PER_RUN; + if (!synth.isAbortingPoly()) { + const MidiEvent *nextEvent = synth.midiQueue->peekMidiEvent(); + Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - synth.renderedSampleCount) : MAX_SAMPLES_PER_RUN; if (samplesToNextEvent > 0) { thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len; if (thisLen > (Bit32u)samplesToNextEvent) { @@ -1530,37 +1757,85 @@ void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample } } else { if (nextEvent->sysexData == NULL) { - playMsgNow(nextEvent->shortMessageData); + synth.playMsgNow(nextEvent->shortMessageData); // If a poly is aborting we don't drop the event from the queue. // Instead, we'll return to it again when the abortion is done. - if (!isAbortingPoly()) { - midiQueue->dropMidiEvent(); + if (!synth.isAbortingPoly()) { + synth.midiQueue->dropMidiEvent(); } } else { - playSysexNow(nextEvent->sysexData, nextEvent->sysexLength); - midiQueue->dropMidiEvent(); + synth.playSysexNow(nextEvent->sysexData, nextEvent->sysexLength); + synth.midiQueue->dropMidiEvent(); } } } - doRenderStreams(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, thisLen); - advanceStreamPosition(nonReverbLeft, thisLen); - advanceStreamPosition(nonReverbRight, thisLen); - advanceStreamPosition(reverbDryLeft, thisLen); - advanceStreamPosition(reverbDryRight, thisLen); - advanceStreamPosition(reverbWetLeft, thisLen); - advanceStreamPosition(reverbWetRight, thisLen); + doRenderStreams( + nonReverbLeft.sampleBuffer, nonReverbRight.sampleBuffer, + reverbDryLeft.sampleBuffer, reverbDryRight.sampleBuffer, + reverbWetLeft.sampleBuffer, reverbWetRight.sampleBuffer, + thisLen); + nonReverbLeft.convert(thisLen); + nonReverbRight.convert(thisLen); + reverbDryLeft.convert(thisLen); + reverbDryRight.convert(thisLen); + reverbWetLeft.convert(thisLen); + reverbWetRight.convert(thisLen); len -= thisLen; } } +void Synth::renderStreams( + Bit16s *nonReverbLeft, Bit16s *nonReverbRight, + Bit16s *reverbDryLeft, Bit16s *reverbDryRight, + Bit16s *reverbWetLeft, Bit16s *reverbWetRight, + Bit32u len) +{ +#if MT32EMU_USE_FLOAT_SAMPLES + BufferedSampleFormatConverter<> convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight); + BufferedSampleFormatConverter<> convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight); + BufferedSampleFormatConverter<> convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight); +#else + SampleFormatConverter convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight); + SampleFormatConverter convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight); + SampleFormatConverter convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight); +#endif + renderer.renderStreams( + convNonReverbLeft, convNonReverbRight, + convReverbDryLeft, convReverbDryRight, + convReverbWetLeft, convReverbWetRight, + len); +} + +void Synth::renderStreams( + float *nonReverbLeft, float *nonReverbRight, + float *reverbDryLeft, float *reverbDryRight, + float *reverbWetLeft, float *reverbWetRight, + Bit32u len) +{ +#if MT32EMU_USE_FLOAT_SAMPLES + SampleFormatConverter convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight); + SampleFormatConverter convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight); + SampleFormatConverter convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight); +#else + BufferedSampleFormatConverter<> convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight); + BufferedSampleFormatConverter<> convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight); + BufferedSampleFormatConverter<> convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight); +#endif + renderer.renderStreams( + convNonReverbLeft, convNonReverbRight, + convReverbDryLeft, convReverbDryRight, + convReverbWetLeft, convReverbWetRight, + len); +} + // In GENERATION2 units, the output from LA32 goes to the Boss chip already bit-shifted. // In NICE mode, it's also better to increase volume before the reverb processing to preserve accuracy. -void Synth::produceLA32Output(Sample *buffer, Bit32u len) { +void Renderer::produceLA32Output(Sample *buffer, Bit32u len) { #if MT32EMU_USE_FLOAT_SAMPLES (void)buffer; (void)len; #else - switch (dacInputMode) { + switch (synth.dacInputMode) { case DACInputMode_GENERATION2: while (len--) { *buffer = (*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE) | ((*buffer >> 14) & 0x0001); @@ -1569,7 +1844,7 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) { break; case DACInputMode_NICE: while (len--) { - *buffer = clipSampleEx(SampleEx(*buffer) << 1); + *buffer = Synth::clipSampleEx(SampleEx(*buffer) << 1); ++buffer; } break; @@ -1579,12 +1854,12 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) { #endif } -void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len) { +void Renderer::convertSamplesToOutput(Sample *buffer, Bit32u len) { #if MT32EMU_USE_FLOAT_SAMPLES (void)buffer; (void)len; #else - if (dacInputMode == DACInputMode_GENERATION1) { + if (synth.dacInputMode == DACInputMode_GENERATION1) { while (len--) { *buffer = Sample((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE)); ++buffer; @@ -1593,7 +1868,7 @@ void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len) { #endif } -void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) { +void Renderer::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) { // Even if LA32 output isn't desired, we proceed anyway with temp buffers Sample tmpBufNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpBufNonReverbRight[MAX_SAMPLES_PER_RUN]; if (nonReverbLeft == NULL) nonReverbLeft = tmpBufNonReverbLeft; @@ -1603,30 +1878,30 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl if (reverbDryLeft == NULL) reverbDryLeft = tmpBufReverbDryLeft; if (reverbDryRight == NULL) reverbDryRight = tmpBufReverbDryRight; - if (isEnabled) { - muteSampleBuffer(nonReverbLeft, len); - muteSampleBuffer(nonReverbRight, len); - muteSampleBuffer(reverbDryLeft, len); - muteSampleBuffer(reverbDryRight, len); + if (synth.activated) { + Synth::muteSampleBuffer(nonReverbLeft, len); + Synth::muteSampleBuffer(nonReverbRight, len); + Synth::muteSampleBuffer(reverbDryLeft, len); + Synth::muteSampleBuffer(reverbDryRight, len); - for (unsigned int i = 0; i < getPartialCount(); i++) { - if (partialManager->shouldReverb(i)) { - partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len); + for (unsigned int i = 0; i < synth.getPartialCount(); i++) { + if (synth.partialManager->shouldReverb(i)) { + synth.partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len); } else { - partialManager->produceOutput(i, nonReverbLeft, nonReverbRight, len); + synth.partialManager->produceOutput(i, nonReverbLeft, nonReverbRight, len); } } produceLA32Output(reverbDryLeft, len); produceLA32Output(reverbDryRight, len); - if (isReverbEnabled()) { - reverbModel->process(reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, len); + if (synth.isReverbEnabled()) { + synth.reverbModel->process(reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, len); if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len); if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len); } else { - muteSampleBuffer(reverbWetLeft, len); - muteSampleBuffer(reverbWetRight, len); + Synth::muteSampleBuffer(reverbWetLeft, len); + Synth::muteSampleBuffer(reverbWetRight, len); } // Don't bother with conversion if the output is going to be unused @@ -1642,29 +1917,32 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len); } else { // Avoid muting buffers that wasn't requested - if (nonReverbLeft != tmpBufNonReverbLeft) muteSampleBuffer(nonReverbLeft, len); - if (nonReverbRight != tmpBufNonReverbRight) muteSampleBuffer(nonReverbRight, len); - if (reverbDryLeft != tmpBufReverbDryLeft) muteSampleBuffer(reverbDryLeft, len); - if (reverbDryRight != tmpBufReverbDryRight) muteSampleBuffer(reverbDryRight, len); - muteSampleBuffer(reverbWetLeft, len); - muteSampleBuffer(reverbWetRight, len); + if (nonReverbLeft != tmpBufNonReverbLeft) Synth::muteSampleBuffer(nonReverbLeft, len); + if (nonReverbRight != tmpBufNonReverbRight) Synth::muteSampleBuffer(nonReverbRight, len); + if (reverbDryLeft != tmpBufReverbDryLeft) Synth::muteSampleBuffer(reverbDryLeft, len); + if (reverbDryRight != tmpBufReverbDryRight) Synth::muteSampleBuffer(reverbDryRight, len); + Synth::muteSampleBuffer(reverbWetLeft, len); + Synth::muteSampleBuffer(reverbWetRight, len); } - partialManager->clearAlreadyOutputed(); - renderedSampleCount += len; + synth.partialManager->clearAlreadyOutputed(); + synth.renderedSampleCount += len; } -void Synth::printPartialUsage(unsigned long sampleOffset) { +void Synth::printPartialUsage(Bit32u sampleOffset) { unsigned int partialUsage[9]; partialManager->getPerPartPartialUsage(partialUsage); if (sampleOffset > 0) { - printDebug("[+%lu] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount()); + printDebug("[+%u] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount()); } else { printDebug("Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount()); } } bool Synth::hasActivePartials() const { + if (!opened) { + return false; + } for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) { if (partialManager->getPartial(partialNum)->isActive()) { return true; @@ -1673,46 +1951,76 @@ bool Synth::hasActivePartials() const { return false; } -bool Synth::isAbortingPoly() const { - return abortingPoly != NULL; -} - -bool Synth::isActive() const { - if (hasActivePartials()) { +bool Synth::isActive() { + if (!opened) { + return false; + } + if (!midiQueue->isEmpty() || hasActivePartials()) { return true; } - if (isReverbEnabled()) { - return reverbModel->isActive(); + if (isReverbEnabled() && reverbModel->isActive()) { + return true; } + activated = false; return false; } -unsigned int Synth::getPartialCount() const { +Bit32u Synth::getPartialCount() const { return partialCount; } void Synth::getPartStates(bool *partStates) const { + if (!opened) { + memset(partStates, 0, 9 * sizeof(bool)); + return; + } for (int partNumber = 0; partNumber < 9; partNumber++) { const Part *part = parts[partNumber]; partStates[partNumber] = part->getActiveNonReleasingPartialCount() > 0; } } +Bit32u Synth::getPartStates() const { + if (!opened) return 0; + bool partStates[9]; + getPartStates(partStates); + Bit32u bitSet = 0; + for (int partNumber = 8; partNumber >= 0; partNumber--) { + bitSet = (bitSet << 1) | (partStates[partNumber] ? 1 : 0); + } + return bitSet; +} + void Synth::getPartialStates(PartialState *partialStates) const { - static const PartialState partialPhaseToState[8] = { - PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, - PartialState_SUSTAIN, PartialState_SUSTAIN, PartialState_RELEASE, PartialState_INACTIVE - }; + if (!opened) { + memset(partialStates, 0, partialCount * sizeof(PartialState)); + return; + } + for (unsigned int partialNum = 0; partialNum < partialCount; partialNum++) { + partialStates[partialNum] = getPartialState(partialManager, partialNum); + } +} - for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) { - const Partial *partial = partialManager->getPartial(partialNum); - partialStates[partialNum] = partial->isActive() ? partialPhaseToState[partial->getTVA()->getPhase()] : PartialState_INACTIVE; +void Synth::getPartialStates(Bit8u *partialStates) const { + if (!opened) { + memset(partialStates, 0, ((partialCount + 3) >> 2)); + return; + } + for (unsigned int quartNum = 0; (4 * quartNum) < partialCount; quartNum++) { + Bit8u packedStates = 0; + for (unsigned int i = 0; i < 4; i++) { + unsigned int partialNum = (4 * quartNum) + i; + if (partialCount <= partialNum) break; + PartialState partialState = getPartialState(partialManager, partialNum); + packedStates |= (partialState & 3) << (2 * i); + } + partialStates[quartNum] = packedStates; } } -unsigned int Synth::getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const { - unsigned int playingNotes = 0; - if (isOpen && (partNumber < 9)) { +Bit32u Synth::getPlayingNotes(Bit8u partNumber, Bit8u *keys, Bit8u *velocities) const { + Bit32u playingNotes = 0; + if (opened && (partNumber < 9)) { const Part *part = parts[partNumber]; const Poly *poly = part->getFirstActivePoly(); while (poly != NULL) { @@ -1725,11 +2033,11 @@ unsigned int Synth::getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u return playingNotes; } -const char *Synth::getPatchName(unsigned int partNumber) const { - return (!isOpen || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr(); +const char *Synth::getPatchName(Bit8u partNumber) const { + return (!opened || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr(); } -const Part *Synth::getPart(unsigned int partNum) const { +const Part *Synth::getPart(Bit8u partNum) const { if (partNum > 8) { return NULL; } @@ -1807,4 +2115,4 @@ void MemoryRegion::write(unsigned int entry, unsigned int off, const Bit8u *src, } } -} +} // namespace MT32Emu -- cgit v1.2.3