diff options
author | Colin Snover | 2016-11-24 09:24:00 -0600 |
---|---|---|
committer | Colin Snover | 2016-12-16 15:44:29 -0600 |
commit | b8d70d26faea1ed71b0176326c97859b31afb8ef (patch) | |
tree | 89b7cff8de9359e5a7877ac0e4bc6b4212e861f3 /audio | |
parent | ffea222f5bfda8383cb52480b48998c9d0093911 (diff) | |
download | scummvm-rg350-b8d70d26faea1ed71b0176326c97859b31afb8ef.tar.gz scummvm-rg350-b8d70d26faea1ed71b0176326c97859b31afb8ef.tar.bz2 scummvm-rg350-b8d70d26faea1ed71b0176326c97859b31afb8ef.zip |
MT32: Update Munt to 2.0.1-pre
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.
Diffstat (limited to 'audio')
54 files changed, 4399 insertions, 994 deletions
diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp index aaf95b65da..6c4b845c87 100644 --- a/audio/softsynth/mt32.cpp +++ b/audio/softsynth/mt32.cpp @@ -25,9 +25,6 @@ #ifdef USE_MT32EMU -#include "audio/softsynth/mt32/mt32emu.h" -#include "audio/softsynth/mt32/ROMInfo.h" - #include "audio/softsynth/emumidi.h" #include "audio/musicplugin.h" #include "audio/mpu401.h" @@ -52,16 +49,12 @@ #include "gui/message.h" -namespace MT32Emu { +#include "audio/softsynth/mt32/c_interface/cpp_interface.h" -class ReportHandlerScummVM : public ReportHandler { -friend class Synth; +namespace MT32Emu { +class ScummVMReportHandler : public MT32Emu::IReportHandler { public: - virtual ~ReportHandlerScummVM() {} - -protected: - // Callback for debug messages, in vprintf() format void printDebug(const char *fmt, va_list list) { Common::String out = Common::String::vformat(fmt, list); @@ -70,18 +63,30 @@ protected: // Callbacks for reporting various errors and information void onErrorControlROM() { - GUI::MessageDialog dialog("MT32emu: Init Error - Missing or invalid Control ROM image", "OK"); + GUI::MessageDialog dialog("MT32Emu: Init Error - Missing or invalid Control ROM image", "OK"); dialog.runModal(); error("MT32emu: Init Error - Missing or invalid Control ROM image"); } void onErrorPCMROM() { - GUI::MessageDialog dialog("MT32emu: Init Error - Missing PCM ROM image", "OK"); + GUI::MessageDialog dialog("MT32Emu: Init Error - Missing PCM ROM image", "OK"); dialog.runModal(); error("MT32emu: Init Error - Missing PCM ROM image"); } void showLCDMessage(const char *message) { Common::OSDMessageQueue::instance().addMessage(message); } + + // Unused callbacks + virtual void onMIDIMessagePlayed() {} + virtual bool onMIDIQueueOverflow() { return false; } + virtual void onMIDISystemRealtime(Bit8u /* system_realtime */) {} + virtual void onDeviceReset() {} + virtual void onDeviceReconfig() {} + virtual void onNewReverbMode(Bit8u /* mode */) {} + virtual void onNewReverbTime(Bit8u /* time */) {} + virtual void onNewReverbLevel(Bit8u /* level */) {} + virtual void onPolyStateChanged(Bit8u /* part_num */) {} + virtual void onProgramChanged(Bit8u /* part_num */, const char * /* sound_group_name */, const char * /* patch_name */) {} }; } // end of namespace MT32Emu @@ -95,10 +100,9 @@ class MidiDriver_MT32 : public MidiDriver_Emulated { private: MidiChannel_MT32 _midiChannels[16]; uint16 _channelMask; - MT32Emu::Synth *_synth; - MT32Emu::ReportHandlerScummVM *_reportHandler; - const MT32Emu::ROMImage *_controlROM, *_pcmROM; - Common::File *_controlFile, *_pcmFile; + MT32Emu::Service *_service; + MT32Emu::ScummVMReportHandler *_reportHandler; + byte *_controlData, *_pcmData; void deleteMuntStructures(); int _outputRate; @@ -107,15 +111,13 @@ protected: void generateSamples(int16 *buf, int len); public: - bool _initializing; - MidiDriver_MT32(Audio::Mixer *mixer); virtual ~MidiDriver_MT32(); int open(); void close(); void send(uint32 b); - void setPitchBendRange (byte channel, uint range); + void setPitchBendRange(byte channel, uint range); void sysEx(const byte *msg, uint16 length); uint32 property(int prop, uint32 param); @@ -139,16 +141,11 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) { _midiChannels[i].init(this, i); } - _reportHandler = NULL; - _synth = NULL; + _service = nullptr; + _reportHandler = nullptr; _outputRate = 0; - _initializing = false; - - // Initialized in open() - _controlROM = NULL; - _pcmROM = NULL; - _controlFile = NULL; - _pcmFile = NULL; + _controlData = nullptr; + _pcmData = nullptr; } MidiDriver_MT32::~MidiDriver_MT32() { @@ -156,31 +153,20 @@ MidiDriver_MT32::~MidiDriver_MT32() { } void MidiDriver_MT32::deleteMuntStructures() { - delete _synth; - _synth = NULL; + delete _service; + _service = nullptr; delete _reportHandler; - _reportHandler = NULL; - - if (_controlROM) - MT32Emu::ROMImage::freeROMImage(_controlROM); - _controlROM = NULL; - if (_pcmROM) - MT32Emu::ROMImage::freeROMImage(_pcmROM); - _pcmROM = NULL; - - delete _controlFile; - _controlFile = NULL; - delete _pcmFile; - _pcmFile = NULL; + _reportHandler = nullptr; + delete _controlData; + _controlData = nullptr; + delete _pcmData; + _pcmData = nullptr; } int MidiDriver_MT32::open() { if (_isOpen) return MERR_ALREADY_OPEN; - _reportHandler = new MT32Emu::ReportHandlerScummVM(); - _synth = new MT32Emu::Synth(_reportHandler); - Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); if (screenFormat.bytesPerPixel == 1) { @@ -193,43 +179,56 @@ int MidiDriver_MT32::open() { g_system->getPaletteManager()->setPalette(dummy_palette, 0, 3); } - _initializing = true; debug(4, _s("Initializing MT-32 Emulator")); - _controlFile = new Common::File(); - if (!_controlFile->open("CM32L_CONTROL.ROM") && !_controlFile->open("MT32_CONTROL.ROM")) - error("Error opening MT32_CONTROL.ROM / CM32L_CONTROL.ROM"); - _pcmFile = new Common::File(); - if (!_pcmFile->open("CM32L_PCM.ROM") && !_pcmFile->open("MT32_PCM.ROM")) - error("Error opening MT32_PCM.ROM / CM32L_PCM.ROM"); - _controlROM = MT32Emu::ROMImage::makeROMImage(_controlFile); - _pcmROM = MT32Emu::ROMImage::makeROMImage(_pcmFile); - if (!_synth->open(*_controlROM, *_pcmROM)) + + Common::File controlFile; + if (!controlFile.open("CM32L_CONTROL.ROM") && !controlFile.open("MT32_CONTROL.ROM")) + error("Error opening MT32_CONTROL.ROM / CM32L_CONTROL.ROM. Check that your Extra Path in Paths settings is set to the correct directory"); + + Common::File pcmFile; + if (!pcmFile.open("CM32L_PCM.ROM") && !pcmFile.open("MT32_PCM.ROM")) + error("Error opening MT32_PCM.ROM / CM32L_PCM.ROM. Check that your Extra Path in Paths settings is set to the correct directory"); + + _controlData = new byte[controlFile.size()]; + controlFile.read(_controlData, controlFile.size()); + _pcmData = new byte[pcmFile.size()]; + pcmFile.read(_pcmData, pcmFile.size()); + + _reportHandler = new MT32Emu::ScummVMReportHandler(); + _service = new MT32Emu::Service(); + _service->createContext(*_reportHandler); + + if (_service->addROMData(_controlData, controlFile.size()) != MT32EMU_RC_ADDED_CONTROL_ROM) { + error("Adding control ROM failed. Check that your control ROM is valid"); + } + + controlFile.close(); + + if (_service->addROMData(_pcmData, pcmFile.size()) != MT32EMU_RC_ADDED_PCM_ROM) { + error("Adding PCM ROM failed. Check that your PCM ROM is valid"); + } + + pcmFile.close(); + + if (_service->openSynth() != MT32EMU_RC_OK) return MERR_DEVICE_NOT_AVAILABLE; double gain = (double)ConfMan.getInt("midi_gain") / 100.0; - _synth->setOutputGain(1.0f * gain); - _synth->setReverbOutputGain(0.68f * gain); + _service->setOutputGain(1.0f * gain); + _service->setReverbOutputGain(1.0f * gain); // We let the synthesizer play MIDI messages immediately. Our MIDI // handling is synchronous to sample generation. This makes delaying MIDI // events result in odd sound output in some cases. For example, the // shattering window in the Indiana Jones and the Fate of Atlantis intro // will sound like a bell if we use any delay here. // Bug #6242 "AUDIO: Built-In MT-32 MUNT Produces Wrong Sounds". - _synth->setMIDIDelayMode(MT32Emu::MIDIDelayMode_IMMEDIATE); + _service->setMIDIDelayMode(MT32Emu::MIDIDelayMode_IMMEDIATE); // We need to report the sample rate MUNT renders at as sample rate of our // AudioStream. - _outputRate = _synth->getStereoOutputSampleRate(); - MidiDriver_Emulated::open(); + _outputRate = _service->getActualStereoOutputSamplerate(); - _initializing = false; - - if (screenFormat.bytesPerPixel > 1) - g_system->fillScreen(screenFormat.RGBToColor(0, 0, 0)); - else - g_system->fillScreen(0); - - g_system->updateScreen(); + MidiDriver_Emulated::open(); _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); @@ -237,31 +236,33 @@ int MidiDriver_MT32::open() { } void MidiDriver_MT32::send(uint32 b) { - _synth->playMsg(b); + _service->playMsg(b); } +// Indiana Jones and the Fate of Atlantis (including the demo) uses +// setPitchBendRange, if you need a game for testing purposes void MidiDriver_MT32::setPitchBendRange(byte channel, uint range) { if (range > 24) { warning("setPitchBendRange() called with range > 24: %d", range); } - byte benderRangeSysex[9]; - benderRangeSysex[0] = 0x41; // Roland - benderRangeSysex[1] = channel; - benderRangeSysex[2] = 0x16; // MT-32 - benderRangeSysex[3] = 0x12; // Write - benderRangeSysex[4] = 0x00; - benderRangeSysex[5] = 0x00; - benderRangeSysex[6] = 0x04; - benderRangeSysex[7] = (byte)range; - benderRangeSysex[8] = MT32Emu::Synth::calcSysexChecksum(&benderRangeSysex[4], 4, 0); - sysEx(benderRangeSysex, 9); + byte benderRangeSysex[4] = { 0, 0, 4, (uint8)range }; + _service->writeSysex(channel, benderRangeSysex, 4); } void MidiDriver_MT32::sysEx(const byte *msg, uint16 length) { if (msg[0] == 0xf0) { - _synth->playSysex(msg, length); + _service->playSysex(msg, length); } else { - _synth->playSysexWithoutFraming(msg, length); + enum { + SYSEX_CMD_DT1 = 0x12, + SYSEX_CMD_DAT = 0x42 + }; + + if (msg[3] == SYSEX_CMD_DT1 || msg[3] == SYSEX_CMD_DAT) { + _service->writeSysex(msg[1], msg + 4, length - 5); + } else { + warning("Unused sysEx command %d", msg[3]); + } } } @@ -275,12 +276,12 @@ void MidiDriver_MT32::close() { // Detach the mixer callback handler _mixer->stopHandle(_mixerSoundHandle); - _synth->close(); + _service->closeSynth(); deleteMuntStructures(); } void MidiDriver_MT32::generateSamples(int16 *data, int len) { - _synth->render(data, len); + _service->renderBit16s(data, len); } uint32 MidiDriver_MT32::property(int prop, uint32 param) { diff --git a/audio/softsynth/mt32/Analog.cpp b/audio/softsynth/mt32/Analog.cpp index 8ac28e401a..31e88561c4 100644..100755 --- a/audio/softsynth/mt32/Analog.cpp +++ b/audio/softsynth/mt32/Analog.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,8 +15,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#include <cstring> +#include <cstring> + +#include "internals.h" + #include "Analog.h" +#include "Synth.h" namespace MT32Emu { @@ -106,7 +110,6 @@ static const Bit32u ACCURATE_LPF_DELTAS_OVERSAMPLED[][ACCURATE_LPF_NUMBER_OF_PHA class AbstractLowPassFilter { public: static AbstractLowPassFilter &createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF); - static void muteRingBuffer(SampleEx *ringBuffer, unsigned int length); virtual ~AbstractLowPassFilter() {} virtual SampleEx process(SampleEx sample) = 0; @@ -152,9 +155,9 @@ public: void addPositionIncrement(unsigned int positionIncrement); }; -Analog::Analog(const AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures) : - leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())), - rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())), +Analog::Analog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF) : + leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, oldMT32AnalogLPF)), + rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, oldMT32AnalogLPF)), synthGain(0), reverbGain(0) {} @@ -164,7 +167,7 @@ Analog::~Analog() { delete &rightChannelLPF; } -void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength) { +void Analog::process(Sample *outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength) { if (outStream == NULL) { leftChannelLPF.addPositionIncrement(outLength); rightChannelLPF.addPositionIncrement(outLength); @@ -179,8 +182,8 @@ void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Samp outSampleL = leftChannelLPF.process(0); outSampleR = rightChannelLPF.process(0); } else { - SampleEx inSampleL = ((SampleEx)*(nonReverbLeft++) + (SampleEx)*(reverbDryLeft++)) * synthGain + (SampleEx)*(reverbWetLeft++) * reverbGain; - SampleEx inSampleR = ((SampleEx)*(nonReverbRight++) + (SampleEx)*(reverbDryRight++)) * synthGain + (SampleEx)*(reverbWetRight++) * reverbGain; + SampleEx inSampleL = (SampleEx(*(nonReverbLeft++)) + SampleEx(*(reverbDryLeft++))) * synthGain + SampleEx(*(reverbWetLeft++)) * reverbGain; + SampleEx inSampleR = (SampleEx(*(nonReverbRight++)) + SampleEx(*(reverbDryRight++))) * synthGain + SampleEx(*(reverbWetRight++)) * reverbGain; #if !MT32EMU_USE_FLOAT_SAMPLES inSampleL >>= OUTPUT_GAIN_FRACTION_BITS; @@ -191,8 +194,8 @@ void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Samp outSampleR = rightChannelLPF.process(inSampleR); } - *((*outStream)++) = Synth::clipSampleEx(outSampleL); - *((*outStream)++) = Synth::clipSampleEx(outSampleR); + *(outStream++) = Synth::clipSampleEx(outSampleL); + *(outStream++) = Synth::clipSampleEx(outSampleR); } } @@ -236,23 +239,6 @@ AbstractLowPassFilter &AbstractLowPassFilter::createLowPassFilter(AnalogOutputMo } } -void AbstractLowPassFilter::muteRingBuffer(SampleEx *ringBuffer, unsigned int length) { - -#if MT32EMU_USE_FLOAT_SAMPLES - - SampleEx *p = ringBuffer; - while (length--) { - *(p++) = 0.0f; - } - -#else - - memset(ringBuffer, 0, length * sizeof(SampleEx)); - -#endif - -} - bool AbstractLowPassFilter::hasNextSample() const { return false; } @@ -273,7 +259,7 @@ CoarseLowPassFilter::CoarseLowPassFilter(bool oldMT32AnalogLPF) : LPF_TAPS(oldMT32AnalogLPF ? COARSE_LPF_TAPS_MT32 : COARSE_LPF_TAPS_CM32L), ringBufferPosition(0) { - muteRingBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH); + Synth::muteSampleBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH); } SampleEx CoarseLowPassFilter::process(const SampleEx inSample) { @@ -303,7 +289,7 @@ AccurateLowPassFilter::AccurateLowPassFilter(const bool oldMT32AnalogLPF, const ringBufferPosition(0), phase(0) { - muteRingBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH); + Synth::muteSampleBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH); } SampleEx AccurateLowPassFilter::process(const SampleEx inSample) { @@ -345,4 +331,4 @@ void AccurateLowPassFilter::addPositionIncrement(const unsigned int positionIncr phase = (phase + positionIncrement * phaseIncrement) % ACCURATE_LPF_NUMBER_OF_PHASES; } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Analog.h b/audio/softsynth/mt32/Analog.h index a48db72485..ee642f280d 100644..100755 --- a/audio/softsynth/mt32/Analog.h +++ b/audio/softsynth/mt32/Analog.h @@ -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 @@ -18,7 +18,10 @@ #ifndef MT32EMU_ANALOG_H #define MT32EMU_ANALOG_H -#include "mt32emu.h" +#include "globals.h" +#include "internals.h" +#include "Types.h" +#include "Enumerations.h" namespace MT32Emu { @@ -35,9 +38,9 @@ class AbstractLowPassFilter; */ class Analog { public: - Analog(AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures); + Analog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF); ~Analog(); - void process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, const Bit32u outLength); + void process(Sample *outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength); unsigned int getOutputSampleRate() const; Bit32u getDACStreamsLength(Bit32u outputLength) const; void setSynthOutputGain(float synthGain); @@ -52,6 +55,6 @@ private: Analog(Analog &); }; -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_ANALOG_H diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp index 5e02db8f99..2be1b418a2 100644..100755 --- a/audio/softsynth/mt32/BReverbModel.cpp +++ b/audio/softsynth/mt32/BReverbModel.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,9 +15,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#include <cstring> -#include "mt32emu.h" +#include <cstddef> + +#include "internals.h" + #include "BReverbModel.h" +#include "Synth.h" // Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that // the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF) @@ -43,14 +46,14 @@ const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; - static const Bit32u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60}; - static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60}; + static const Bit8u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; - static const Bit32u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0}; - static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; - static const Bit32u MODE_0_LPF_AMP = 0x60; + static const Bit8u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0}; + static const Bit8u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; + static const Bit8u MODE_0_LPF_AMP = 0x60; static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; @@ -58,14 +61,14 @@ const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; - static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; - static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; + static const Bit8u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; - static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; - static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; - static const Bit32u MODE_1_LPF_AMP = 0x60; + static const Bit8u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; + static const Bit8u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; + static const Bit8u MODE_1_LPF_AMP = 0x60; static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; @@ -73,25 +76,25 @@ const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; - static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; - static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; + static const Bit8u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; - static const Bit32u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0}; - static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; - static const Bit32u MODE_2_LPF_AMP = 0x80; + static const Bit8u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0}; + static const Bit8u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; + static const Bit8u MODE_2_LPF_AMP = 0x80; static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY}; static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000}; static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000}; - static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; - static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; - static const Bit32u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + static const Bit8u MODE_3_COMB_FACTOR[] = {0x68}; + static const Bit8u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; + static const Bit8u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50}; - static const Bit32u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; + static const Bit8u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP}; static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP}; @@ -112,14 +115,14 @@ const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) { static const Bit32u MODE_0_COMBS[] = {575 + PROCESS_DELAY, 2040, 2752, 3629}; static const Bit32u MODE_0_OUTL[] = {2040, 687, 1814}; static const Bit32u MODE_0_OUTR[] = {1019, 2072, 1}; - static const Bit32u MODE_0_COMB_FACTOR[] = {0xB0, 0x60, 0x60, 0x60}; - static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_0_COMB_FACTOR[] = {0xB0, 0x60, 0x60, 0x60}; + static const Bit8u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; - static const Bit32u MODE_0_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; - static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; - static const Bit32u MODE_0_LPF_AMP = 0x80; + static const Bit8u MODE_0_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; + static const Bit8u MODE_0_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; + static const Bit8u MODE_0_LPF_AMP = 0x80; static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; @@ -127,14 +130,14 @@ const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) { static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; - static const Bit32u MODE_1_COMB_FACTOR[] = {0x90, 0x60, 0x60, 0x60}; - static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_1_COMB_FACTOR[] = {0x90, 0x60, 0x60, 0x60}; + static const Bit8u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; - static const Bit32u MODE_1_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; - static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; - static const Bit32u MODE_1_LPF_AMP = 0x80; + static const Bit8u MODE_1_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; + static const Bit8u MODE_1_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; + static const Bit8u MODE_1_LPF_AMP = 0x80; static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; @@ -142,25 +145,25 @@ const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) { static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; - static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x60, 0x60, 0x60}; - static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_2_COMB_FACTOR[] = {0, 0x60, 0x60, 0x60}; + static const Bit8u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; - static const Bit32u MODE_2_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; - static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; - static const Bit32u MODE_2_LPF_AMP = 0x80; + static const Bit8u MODE_2_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; + static const Bit8u MODE_2_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; + static const Bit8u MODE_2_LPF_AMP = 0x80; static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY}; static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000}; static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000}; - static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; - static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; - static const Bit32u MODE_3_DRY_AMP[] = {0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + static const Bit8u MODE_3_COMB_FACTOR[] = {0x68}; + static const Bit8u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; + static const Bit8u MODE_3_DRY_AMP[] = {0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x20, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10}; - static const Bit32u MODE_3_WET_AMP[] = {0x08, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; + static const Bit8u MODE_3_WET_AMP[] = {0x08, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP}; static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP}; @@ -189,7 +192,7 @@ static Sample weirdMul(Sample a, Bit8u addMask, Bit8u carryMask) { } return res; #else - return Sample(((Bit32s)a * addMask) >> 8); + return Sample((Bit32s(a) * addMask) >> 8); #endif } @@ -252,7 +255,7 @@ Sample AllpassFilter::process(const Sample in) { #endif } -CombFilter::CombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {} +CombFilter::CombFilter(const Bit32u useSize, const Bit8u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {} void CombFilter::process(const Sample in) { // This model corresponds to the comb filter implementation of the real CM-32L device @@ -271,11 +274,11 @@ Sample CombFilter::getOutputAt(const Bit32u outIndex) const { return buffer[(size + index - outIndex) % size]; } -void CombFilter::setFeedbackFactor(const Bit32u useFeedbackFactor) { +void CombFilter::setFeedbackFactor(const Bit8u useFeedbackFactor) { feedbackFactor = useFeedbackFactor; } -DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp) +DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit8u useFilterFactor, const Bit8u useAmp) : CombFilter(useSize, useFilterFactor), amp(useAmp) {} void DelayWithLowPassFilter::process(const Sample in) { @@ -292,7 +295,7 @@ void DelayWithLowPassFilter::process(const Sample in) { buffer[index] = weirdMul(lpfOut, amp, 0xFF); } -TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : CombFilter(useSize, useFilterFactor) {} +TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit8u useFilterFactor) : CombFilter(useSize, useFilterFactor) {} void TapDelayCombFilter::process(const Sample in) { // the previously stored value @@ -430,7 +433,7 @@ bool BReverbModel::isMT32Compatible(const ReverbMode mode) const { return ¤tSettings == &getMT32Settings(mode); } -void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples) { +void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, Bit32u numSamples) { if (combs == NULL) { Synth::muteSampleBuffer(outLeft, numSamples); Synth::muteSampleBuffer(outRight, numSamples); @@ -501,9 +504,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample * * Analysing of the algorithm suggests that the overflow is most probable when the combs output is added below. * So, despite this isn't actually accurate, we only add the check here for performance reasons. */ - Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1)) + (SampleEx)outL2) + SampleEx(outL2 >> 1)) + (SampleEx)outL3); + Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(SampleEx(outL1) + (SampleEx(outL1) >> 1)) + SampleEx(outL2)) + (SampleEx(outL2) >> 1)) + SampleEx(outL3)); #else - Sample outSample = Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1) + (SampleEx)outL2 + SampleEx(outL2 >> 1) + (SampleEx)outL3); + Sample outSample = Synth::clipSampleEx(SampleEx(outL1) + (SampleEx(outL1) >> 1) + SampleEx(outL2) + (SampleEx(outL2) >> 1) + SampleEx(outL3)); #endif *(outLeft++) = weirdMul(outSample, wetLevel, 0xFF); } @@ -515,9 +518,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample * Sample outSample = 1.5f * (outR1 + outR2) + outR3; #elif MT32EMU_BOSS_REVERB_PRECISE_MODE // See the note above for the left channel output. - Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1)) + (SampleEx)outR2) + SampleEx(outR2 >> 1)) + (SampleEx)outR3); + Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(SampleEx(outR1) + (SampleEx(outR1) >> 1)) + SampleEx(outR2)) + (SampleEx(outR2) >> 1)) + SampleEx(outR3)); #else - Sample outSample = Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1) + (SampleEx)outR2 + SampleEx(outR2 >> 1) + (SampleEx)outR3); + Sample outSample = Synth::clipSampleEx(SampleEx(outR1) + (SampleEx(outR1) >> 1) + SampleEx(outR2) + (SampleEx(outR2) >> 1) + SampleEx(outR3)); #endif *(outRight++) = weirdMul(outSample, wetLevel, 0xFF); } @@ -525,4 +528,4 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample * } } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h index 764daf1a9e..8cfc5da8a3 100644..100755 --- a/audio/softsynth/mt32/BReverbModel.h +++ b/audio/softsynth/mt32/BReverbModel.h @@ -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 @@ -18,6 +18,10 @@ #ifndef MT32EMU_B_REVERB_MODEL_H #define MT32EMU_B_REVERB_MODEL_H +#include "globals.h" +#include "internals.h" +#include "Types.h" + namespace MT32Emu { struct BReverbSettings { @@ -27,11 +31,11 @@ struct BReverbSettings { const Bit32u * const combSizes; const Bit32u * const outLPositions; const Bit32u * const outRPositions; - const Bit32u * const filterFactors; - const Bit32u * const feedbackFactors; - const Bit32u * const dryAmps; - const Bit32u * const wetLevels; - const Bit32u lpfAmp; + const Bit8u * const filterFactors; + const Bit8u * const feedbackFactors; + const Bit8u * const dryAmps; + const Bit8u * const wetLevels; + const Bit8u lpfAmp; }; class RingBuffer { @@ -56,23 +60,23 @@ public: class CombFilter : public RingBuffer { protected: - const Bit32u filterFactor; - Bit32u feedbackFactor; + const Bit8u filterFactor; + Bit8u feedbackFactor; public: - CombFilter(const Bit32u size, const Bit32u useFilterFactor); + CombFilter(const Bit32u size, const Bit8u useFilterFactor); virtual void process(const Sample in); Sample getOutputAt(const Bit32u outIndex) const; - void setFeedbackFactor(const Bit32u useFeedbackFactor); + void setFeedbackFactor(const Bit8u useFeedbackFactor); }; class DelayWithLowPassFilter : public CombFilter { - Bit32u amp; + Bit8u amp; public: - DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp); + DelayWithLowPassFilter(const Bit32u useSize, const Bit8u useFilterFactor, const Bit8u useAmp); void process(const Sample in); - void setFeedbackFactor(const Bit32u) {} + void setFeedbackFactor(const Bit8u) {} }; class TapDelayCombFilter : public CombFilter { @@ -80,7 +84,7 @@ class TapDelayCombFilter : public CombFilter { Bit32u outR; public: - TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor); + TapDelayCombFilter(const Bit32u useSize, const Bit8u useFilterFactor); void process(const Sample in); Sample getLeftOutput() const; Sample getRightOutput() const; @@ -93,8 +97,8 @@ class BReverbModel { const BReverbSettings ¤tSettings; const bool tapDelayMode; - Bit32u dryAmp; - Bit32u wetLevel; + Bit8u dryAmp; + Bit8u wetLevel; static const BReverbSettings &getCM32L_LAPCSettings(const ReverbMode mode); static const BReverbSettings &getMT32Settings(const ReverbMode mode); @@ -108,11 +112,11 @@ public: void close(); void mute(); void setParameters(Bit8u time, Bit8u level); - void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples); + void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, Bit32u numSamples); bool isActive() const; bool isMT32Compatible(const ReverbMode mode) const; }; -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_B_REVERB_MODEL_H diff --git a/audio/softsynth/mt32/Enumerations.h b/audio/softsynth/mt32/Enumerations.h new file mode 100755 index 0000000000..9b0a35d0bf --- /dev/null +++ b/audio/softsynth/mt32/Enumerations.h @@ -0,0 +1,155 @@ +/* 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/>. + */ + +/* Using two guards since this file may be included twice with different MT32EMU_C_ENUMERATIONS define. */ + +#if (!defined MT32EMU_CPP_ENUMERATIONS_H && !defined MT32EMU_C_ENUMERATIONS) || (!defined MT32EMU_C_ENUMERATIONS_H && defined MT32EMU_C_ENUMERATIONS) + +#ifdef MT32EMU_C_ENUMERATIONS + +#define MT32EMU_C_ENUMERATIONS_H + +#define MT32EMU_DAC_INPUT_MODE_NAME mt32emu_dac_input_mode +#define MT32EMU_DAC_INPUT_MODE(ident) MT32EMU_DAC_##ident + +#define MT32EMU_MIDI_DELAY_MODE_NAME mt32emu_midi_delay_mode +#define MT32EMU_MIDI_DELAY_MODE(ident) MT32EMU_MDM_##ident + +#define MT32EMU_ANALOG_OUTPUT_MODE_NAME mt32emu_analog_output_mode +#define MT32EMU_ANALOG_OUTPUT_MODE(ident) MT32EMU_AOM_##ident + +#define MT32EMU_PARTIAL_STATE_NAME mt32emu_partial_state +#define MT32EMU_PARTIAL_STATE(ident) MT32EMU_PS_##ident + +#else /* #ifdef MT32EMU_C_ENUMERATIONS */ + +#define MT32EMU_CPP_ENUMERATIONS_H + +#define MT32EMU_DAC_INPUT_MODE_NAME DACInputMode +#define MT32EMU_DAC_INPUT_MODE(ident) DACInputMode_##ident + +#define MT32EMU_MIDI_DELAY_MODE_NAME MIDIDelayMode +#define MT32EMU_MIDI_DELAY_MODE(ident) MIDIDelayMode_##ident + +#define MT32EMU_ANALOG_OUTPUT_MODE_NAME AnalogOutputMode +#define MT32EMU_ANALOG_OUTPUT_MODE(ident) AnalogOutputMode_##ident + +#define MT32EMU_PARTIAL_STATE_NAME PartialState +#define MT32EMU_PARTIAL_STATE(ident) PartialState_##ident + +namespace MT32Emu { + +#endif /* #ifdef MT32EMU_C_ENUMERATIONS */ + +/** + * Methods for emulating the connection between the LA32 and the DAC, which involves + * some hacks in the real devices for doubling the volume. + * See also http://en.wikipedia.org/wiki/Roland_MT-32#Digital_overflow + */ +enum MT32EMU_DAC_INPUT_MODE_NAME { + /** + * Produces samples at double the volume, without tricks. + * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range) + * Higher quality than the real devices + */ + MT32EMU_DAC_INPUT_MODE(NICE), + + /** + * Produces samples that exactly match the bits output from the emulated LA32. + * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range) + * Much less likely to overdrive than any other mode. + * Half the volume of any of the other modes. + * Output gain is ignored for both LA32 and reverb output. + * Perfect for developers while debugging :) + */ + MT32EMU_DAC_INPUT_MODE(PURE), + + /** + * Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia). + * Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low): + * 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX + */ + MT32EMU_DAC_INPUT_MODE(GENERATION1), + + /** + * Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG). + * Bit order at DAC (where each number represents the original LA32 output bit number): + * 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14 + */ + MT32EMU_DAC_INPUT_MODE(GENERATION2) +}; + +/** Methods for emulating the effective delay of incoming MIDI messages introduced by a MIDI interface. */ +enum MT32EMU_MIDI_DELAY_MODE_NAME { + /** Process incoming MIDI events immediately. */ + MT32EMU_MIDI_DELAY_MODE(IMMEDIATE), + + /** + * Delay incoming short MIDI messages as if they where transferred via a MIDI cable to a real hardware unit and immediate sysex processing. + * This ensures more accurate timing of simultaneous NoteOn messages. + */ + MT32EMU_MIDI_DELAY_MODE(DELAY_SHORT_MESSAGES_ONLY), + + /** Delay all incoming MIDI events as if they where transferred via a MIDI cable to a real hardware unit.*/ + MT32EMU_MIDI_DELAY_MODE(DELAY_ALL) +}; + +/** Methods for emulating the effects of analogue circuits of real hardware units on the output signal. */ +enum MT32EMU_ANALOG_OUTPUT_MODE_NAME { + /** Only digital path is emulated. The output samples correspond to the digital signal at the DAC entrance. */ + MT32EMU_ANALOG_OUTPUT_MODE(DIGITAL_ONLY), + /** Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged. */ + MT32EMU_ANALOG_OUTPUT_MODE(COARSE), + /** + * Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz, + * which is passed through the LPF circuit without significant attenuation. + */ + MT32EMU_ANALOG_OUTPUT_MODE(ACCURATE), + /** + * Same as AnalogOutputMode_ACCURATE mode but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz. + * This makes subsequent resampling easier. Besides, due to nonlinear passband of the LPF emulated, it takes fewer number of MACs + * compared to a regular LPF FIR implementations. + */ + MT32EMU_ANALOG_OUTPUT_MODE(OVERSAMPLED) +}; + +enum MT32EMU_PARTIAL_STATE_NAME { + MT32EMU_PARTIAL_STATE(INACTIVE), + MT32EMU_PARTIAL_STATE(ATTACK), + MT32EMU_PARTIAL_STATE(SUSTAIN), + MT32EMU_PARTIAL_STATE(RELEASE) +}; + +#ifndef MT32EMU_C_ENUMERATIONS + +} // namespace MT32Emu + +#endif + +#undef MT32EMU_DAC_INPUT_MODE_NAME +#undef MT32EMU_DAC_INPUT_MODE + +#undef MT32EMU_MIDI_DELAY_MODE_NAME +#undef MT32EMU_MIDI_DELAY_MODE + +#undef MT32EMU_ANALOG_OUTPUT_MODE_NAME +#undef MT32EMU_ANALOG_OUTPUT_MODE + +#undef MT32EMU_PARTIAL_STATE_NAME +#undef MT32EMU_PARTIAL_STATE + +#endif /* #if (!defined MT32EMU_CPP_ENUMERATIONS_H && !defined MT32EMU_C_ENUMERATIONS) || (!defined MT32EMU_C_ENUMERATIONS_H && defined MT32EMU_C_ENUMERATIONS) */ diff --git a/audio/softsynth/mt32/File.cpp b/audio/softsynth/mt32/File.cpp new file mode 100755 index 0000000000..395a893358 --- /dev/null +++ b/audio/softsynth/mt32/File.cpp @@ -0,0 +1,73 @@ +/* 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 <cstring> + +#include "internals.h" + +#include "File.h" +#include "sha1/sha1.h" + +namespace MT32Emu { + +AbstractFile::AbstractFile() : sha1DigestCalculated(false), reserved(NULL) { + sha1Digest[0] = 0; +} + +AbstractFile::AbstractFile(const SHA1Digest &useSHA1Digest) : sha1DigestCalculated(true), reserved(NULL) { + memcpy(sha1Digest, useSHA1Digest, sizeof(SHA1Digest) - 1); + sha1Digest[sizeof(SHA1Digest) - 1] = 0; // Ensure terminator char. +} + +const File::SHA1Digest &AbstractFile::getSHA1() { + if (sha1DigestCalculated) { + return sha1Digest; + } + sha1DigestCalculated = true; + + size_t size = getSize(); + if (size == 0) { + return sha1Digest; + } + + const Bit8u *data = getData(); + if (data == NULL) { + return sha1Digest; + } + + unsigned char fileDigest[20]; + + sha1::calc(data, int(size), fileDigest); + sha1::toHexString(fileDigest, sha1Digest); + return sha1Digest; +} + +ArrayFile::ArrayFile(const Bit8u *useData, size_t useSize) : data(useData), size(useSize) +{} + +ArrayFile::ArrayFile(const Bit8u *useData, size_t useSize, const SHA1Digest &useSHA1Digest) : AbstractFile(useSHA1Digest), data(useData), size(useSize) +{} + +size_t ArrayFile::getSize() { + return size; +} + +const Bit8u *ArrayFile::getData() { + return data; +} + +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/File.h b/audio/softsynth/mt32/File.h new file mode 100755 index 0000000000..c9a7d582b4 --- /dev/null +++ b/audio/softsynth/mt32/File.h @@ -0,0 +1,73 @@ +/* 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/>. + */ + +#ifndef MT32EMU_FILE_H +#define MT32EMU_FILE_H + +#include <cstddef> + +#include "globals.h" +#include "Types.h" + +namespace MT32Emu { + +class MT32EMU_EXPORT File { +public: + // Includes terminator char. + typedef char SHA1Digest[41]; + + virtual ~File() {} + virtual size_t getSize() = 0; + virtual const Bit8u *getData() = 0; + virtual const SHA1Digest &getSHA1() = 0; + + virtual void close() = 0; +}; + +class MT32EMU_EXPORT AbstractFile : public File { +public: + const SHA1Digest &getSHA1(); + +protected: + AbstractFile(); + AbstractFile(const SHA1Digest &sha1Digest); + +private: + bool sha1DigestCalculated; + SHA1Digest sha1Digest; + + // Binary compatibility helper. + void *reserved; +}; + +class MT32EMU_EXPORT ArrayFile : public AbstractFile { +public: + ArrayFile(const Bit8u *data, size_t size); + ArrayFile(const Bit8u *data, size_t size, const SHA1Digest &sha1Digest); + + size_t getSize(); + const Bit8u *getData(); + void close() {} + +private: + const Bit8u *data; + size_t size; +}; + +} // namespace MT32Emu + +#endif // #ifndef MT32EMU_FILE_H diff --git a/audio/softsynth/mt32/FileStream.cpp b/audio/softsynth/mt32/FileStream.cpp new file mode 100755 index 0000000000..768c5fcdab --- /dev/null +++ b/audio/softsynth/mt32/FileStream.cpp @@ -0,0 +1,83 @@ +/* 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 "internals.h" + +#include "FileStream.h" + +namespace MT32Emu { + +using std::ios_base; + +FileStream::FileStream() : ifsp(*new std::ifstream), data(NULL), size(0) +{} + +FileStream::~FileStream() { + // destructor closes ifsp + delete &ifsp; + delete[] data; +} + +size_t FileStream::getSize() { + if (size != 0) { + return size; + } + if (!ifsp.is_open()) { + return 0; + } + ifsp.seekg(0, ios_base::end); + size = size_t(ifsp.tellg()); + return size; +} + +const Bit8u *FileStream::getData() { + if (data != NULL) { + return data; + } + if (!ifsp.is_open()) { + return NULL; + } + if (getSize() == 0) { + return NULL; + } + Bit8u *fileData = new Bit8u[size]; + if (fileData == NULL) { + return NULL; + } + ifsp.seekg(0); + ifsp.read(reinterpret_cast<char *>(fileData), std::streamsize(size)); + if (size_t(ifsp.tellg()) != size) { + delete[] fileData; + return NULL; + } + data = fileData; + close(); + return data; +} + +bool FileStream::open(const char *filename) { + ifsp.clear(); + ifsp.open(filename, ios_base::in | ios_base::binary); + return !ifsp.fail(); +} + +void FileStream::close() { + ifsp.close(); + ifsp.clear(); +} + +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/FileStream.h b/audio/softsynth/mt32/FileStream.h new file mode 100755 index 0000000000..2de6e801ff --- /dev/null +++ b/audio/softsynth/mt32/FileStream.h @@ -0,0 +1,46 @@ +/* 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/>. + */ + +#ifndef MT32EMU_FILE_STREAM_H +#define MT32EMU_FILE_STREAM_H + +#include <fstream> + +#include "globals.h" +#include "Types.h" +#include "File.h" + +namespace MT32Emu { + +class FileStream : public AbstractFile { +public: + MT32EMU_EXPORT FileStream(); + MT32EMU_EXPORT ~FileStream(); + MT32EMU_EXPORT size_t getSize(); + MT32EMU_EXPORT const Bit8u *getData(); + MT32EMU_EXPORT bool open(const char *filename); + MT32EMU_EXPORT void close(); + +private: + std::ifstream &ifsp; + const Bit8u *data; + size_t size; +}; + +} // namespace MT32Emu + +#endif // #ifndef MT32EMU_FILE_STREAM_H diff --git a/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp index 42d820ebad..824204e81b 100644..100755 --- a/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp +++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.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,10 +15,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#include <cmath> -#include "mt32emu.h" +#ifndef MT32EMU_LA32_WAVE_GENERATOR_CPP +#error This file should be included from LA32WaveGenerator.cpp only. +#endif + #include "mmath.h" -#include "internals.h" namespace MT32Emu { @@ -38,10 +39,10 @@ float LA32WaveGenerator::getPCMSample(unsigned int position) { return ((pcmSample & 32768) == 0) ? sampleValue : -sampleValue; } -void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) { - this->sawtoothWaveform = sawtoothWaveform; - this->pulseWidth = pulseWidth; - this->resonance = resonance; +void LA32WaveGenerator::initSynth(const bool useSawtoothWaveform, const Bit8u usePulseWidth, const Bit8u useResonance) { + sawtoothWaveform = useSawtoothWaveform; + pulseWidth = usePulseWidth; + resonance = useResonance; wavePos = 0.0f; lastFreq = 0.0f; @@ -50,24 +51,24 @@ void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulse active = true; } -void LA32WaveGenerator::initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated) { - this->pcmWaveAddress = pcmWaveAddress; - this->pcmWaveLength = pcmWaveLength; - this->pcmWaveLooped = pcmWaveLooped; - this->pcmWaveInterpolated = pcmWaveInterpolated; +void LA32WaveGenerator::initPCM(const Bit16s * const usePCMWaveAddress, const Bit32u usePCMWaveLength, const bool usePCMWaveLooped, const bool usePCMWaveInterpolated) { + pcmWaveAddress = usePCMWaveAddress; + pcmWaveLength = usePCMWaveLength; + pcmWaveLooped = usePCMWaveLooped; + pcmWaveInterpolated = usePCMWaveInterpolated; pcmPosition = 0.0f; active = true; } +// ampVal - Logarithmic amp of the wave generator +// pitch - Logarithmic frequency of the resulting wave +// cutoffRampVal - Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pitch, const Bit32u cutoffRampVal) { if (!active) { return 0.0f; } - this->amp = amp; - this->pitch = pitch; - float sample = 0.0f; // SEMI-CONFIRMED: From sample analysis: @@ -284,9 +285,9 @@ bool LA32WaveGenerator::isPCMWave() const { return pcmWaveAddress != NULL; } -void LA32PartialPair::init(const bool ringModulated, const bool mixed) { - this->ringModulated = ringModulated; - this->mixed = mixed; +void LA32PartialPair::init(const bool useRingModulated, const bool useMixed) { + ringModulated = useRingModulated; + mixed = useMixed; masterOutputSample = 0.0f; slaveOutputSample = 0.0f; } @@ -354,4 +355,4 @@ bool LA32PartialPair::isActive(const PairType useMaster) const { return useMaster == MASTER ? master.isActive() : slave.isActive(); } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/LA32FloatWaveGenerator.h b/audio/softsynth/mt32/LA32FloatWaveGenerator.h index 329e5de258..89b6fe479a 100644..100755 --- a/audio/softsynth/mt32/LA32FloatWaveGenerator.h +++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.h @@ -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 @@ -16,14 +16,15 @@ */ #ifndef MT32EMU_LA32_WAVE_GENERATOR_H -#define MT32EMU_LA32_WAVE_GENERATOR_H +#error This file should be included from LA32WaveGenerator.h only. +#endif namespace MT32Emu { /** * LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator. * The output square wave is created by adding high / low linear segments in-between - * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis. + * the rising and falling cosine segments. Basically, it's very similar to the phase distortion synthesis. * Behaviour of a true resonance filter is emulated by adding decaying sine wave. * The beginning and the ending of the resonant sine is multiplied by a cosine window. * To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave. @@ -38,12 +39,6 @@ class LA32WaveGenerator { // True means the resulting square wave is to be multiplied by the synchronous cosine bool sawtoothWaveform; - // Logarithmic amp of the wave generator - Bit32u amp; - - // Logarithmic frequency of the resulting wave - Bit16u pitch; - // Values in range [1..31] // Value 1 correspong to the minimum resonance Bit8u resonance; @@ -53,9 +48,6 @@ class LA32WaveGenerator { // Value 255 corresponds to the maximum possible asymmetric of the resulting wave Bit8u pulseWidth; - // Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier - Bit32u cutoffVal; - // Logarithmic PCM sample start address const Bit16s *pcmWaveAddress; @@ -96,7 +88,7 @@ public: // Return true if the WG engine generates PCM wave samples bool isPCMWave() const; -}; +}; // class LA32WaveGenerator // LA32PartialPair contains a structure of two partials being mixed / ring modulated class LA32PartialPair { @@ -135,8 +127,6 @@ public: // Return active state of the WG engine bool isActive(const PairType master) const; -}; +}; // class LA32PartialPair } // namespace MT32Emu - -#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp index 2b31a330d2..a4da4f57a8 100644..100755 --- a/audio/softsynth/mt32/LA32Ramp.cpp +++ b/audio/softsynth/mt32/LA32Ramp.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 @@ -47,12 +47,12 @@ We haven't fully explored: - Values when ramping between levels (though this is probably correct). - Transition timing (may not be 100% accurate, especially for very fast ramps). */ -//#include <cmath> -#include "mt32emu.h" -#include "mmath.h" #include "internals.h" +#include "LA32Ramp.h" +#include "Tables.h" + namespace MT32Emu { // SEMI-CONFIRMED from sample analysis. @@ -152,4 +152,4 @@ void LA32Ramp::reset() { interruptRaised = false; } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/LA32Ramp.h b/audio/softsynth/mt32/LA32Ramp.h index 796b4d1eec..5e4ddf720e 100644..100755 --- a/audio/softsynth/mt32/LA32Ramp.h +++ b/audio/softsynth/mt32/LA32Ramp.h @@ -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 @@ -18,6 +18,9 @@ #ifndef MT32EMU_LA32RAMP_H #define MT32EMU_LA32RAMP_H +#include "globals.h" +#include "Types.h" + namespace MT32Emu { class LA32Ramp { @@ -38,6 +41,6 @@ public: void reset(); }; -} +} // namespace MT32Emu -#endif /* TVA_H_ */ +#endif // #ifndef MT32EMU_LA32RAMP_H diff --git a/audio/softsynth/mt32/LA32WaveGenerator.cpp b/audio/softsynth/mt32/LA32WaveGenerator.cpp index 765f75fa61..a9c425beac 100644..100755 --- a/audio/softsynth/mt32/LA32WaveGenerator.cpp +++ b/audio/softsynth/mt32/LA32WaveGenerator.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,15 +15,19 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <cstddef> + +#include "internals.h" + +#include "LA32WaveGenerator.h" +#include "Tables.h" + #if MT32EMU_USE_FLOAT_SAMPLES +#define MT32EMU_LA32_WAVE_GENERATOR_CPP #include "LA32FloatWaveGenerator.cpp" +#undef MT32EMU_LA32_WAVE_GENERATOR_CPP #else -//#include <cmath> -#include "mt32emu.h" -#include "mmath.h" -#include "internals.h" - namespace MT32Emu { static const Bit32u SINE_SEGMENT_RELATIVE_LENGTH = 1 << 18; @@ -50,7 +54,7 @@ Bit16s LA32Utilites::unlog(const LogSample &logSample) { void LA32Utilites::addLogSamples(LogSample &logSample1, const LogSample &logSample2) { Bit32u logSampleValue = logSample1.logValue + logSample2.logValue; - logSample1.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + logSample1.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535; logSample1.sign = logSample1.sign == logSample2.sign ? LogSample::POSITIVE : LogSample::NEGATIVE; } @@ -130,9 +134,7 @@ void LA32WaveGenerator::advancePosition() { Bit32u lowLinearLength = (resonanceWaveLengthFactor << 8) - 4 * SINE_SEGMENT_RELATIVE_LENGTH - highLinearLength; computePositions(highLinearLength, lowLinearLength, resonanceWaveLengthFactor); - // resonancePhase computation hack - int *resonancePhaseAlias = (int *)&resonancePhase; - *resonancePhaseAlias = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3; + resonancePhase = ResonancePhase(((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3); } void LA32WaveGenerator::generateNextSquareWaveLogSample() { @@ -158,7 +160,7 @@ void LA32WaveGenerator::generateNextSquareWaveLogSample() { logSampleValue += (MIDDLE_CUTOFF_VALUE - cutoffVal) >> 9; } - squareLogSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + squareLogSample.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535; squareLogSample.sign = phase < NEGATIVE_FALLING_SINE_SEGMENT ? LogSample::POSITIVE : LogSample::NEGATIVE; } @@ -198,7 +200,7 @@ void LA32WaveGenerator::generateNextResonanceWaveLogSample() { // After all the amp decrements are added, it should be safe now to adjust the amp of the resonance wave to what we see on captures logSampleValue -= 1 << 12; - resonanceLogSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + resonanceLogSample.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535; resonanceLogSample.sign = resonancePhase < NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT ? LogSample::POSITIVE : LogSample::NEGATIVE; } @@ -216,7 +218,7 @@ void LA32WaveGenerator::generateNextSawtoothCosineLogSample(LogSample &logSample void LA32WaveGenerator::pcmSampleToLogSample(LogSample &logSample, const Bit16s pcmSample) const { Bit32u logSampleValue = (32787 - (pcmSample & 32767)) << 1; logSampleValue += amp >> 10; - logSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + logSample.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535; logSample.sign = pcmSample < 0 ? LogSample::NEGATIVE : LogSample::POSITIVE; } @@ -377,7 +379,7 @@ Bit16s LA32PartialPair::unlogAndMixWGOutput(const LA32WaveGenerator &wg) { Bit16s firstSample = LA32Utilites::unlog(wg.getOutputLogSample(true)); Bit16s secondSample = LA32Utilites::unlog(wg.getOutputLogSample(false)); if (wg.isPCMWave()) { - return Bit16s(firstSample + ((Bit32s(secondSample - firstSample) * wg.getPCMInterpolationFactor()) >> 7)); + return Bit16s(firstSample + (((Bit32s(secondSample) - Bit32s(firstSample)) * wg.getPCMInterpolationFactor()) >> 7)); } return firstSample + secondSample; } @@ -407,7 +409,7 @@ Bit16s LA32PartialPair::nextOutSample() { Bit16s slaveSample = slave.isPCMWave() ? LA32Utilites::unlog(slave.getOutputLogSample(true)) : unlogAndMixWGOutput(slave); slaveSample <<= 2; slaveSample >>= 2; - Bit16s ringModulatedSample = Bit16s(((Bit32s)masterSample * (Bit32s)slaveSample) >> 13); + Bit16s ringModulatedSample = Bit16s((Bit32s(masterSample) * Bit32s(slaveSample)) >> 13); return mixed ? nonOverdrivenMasterSample + ringModulatedSample : ringModulatedSample; } @@ -423,6 +425,6 @@ bool LA32PartialPair::isActive(const PairType useMaster) const { return useMaster == MASTER ? master.isActive() : slave.isActive(); } -} +} // namespace MT32Emu #endif // #if MT32EMU_USE_FLOAT_SAMPLES diff --git a/audio/softsynth/mt32/LA32WaveGenerator.h b/audio/softsynth/mt32/LA32WaveGenerator.h index 212abe2b19..6a40e44c98 100644..100755 --- a/audio/softsynth/mt32/LA32WaveGenerator.h +++ b/audio/softsynth/mt32/LA32WaveGenerator.h @@ -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,13 +15,17 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#ifndef MT32EMU_LA32_WAVE_GENERATOR_H +#define MT32EMU_LA32_WAVE_GENERATOR_H + +#include "globals.h" +#include "internals.h" +#include "Types.h" + #if MT32EMU_USE_FLOAT_SAMPLES #include "LA32FloatWaveGenerator.h" #else -#ifndef MT32EMU_LA32_WAVE_GENERATOR_H -#define MT32EMU_LA32_WAVE_GENERATOR_H - namespace MT32Emu { /** @@ -55,7 +59,7 @@ public: /** * LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator. * The output square wave is created by adding high / low linear segments in-between - * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis. + * the rising and falling cosine segments. Basically, it's very similar to the phase distortion synthesis. * Behaviour of a true resonance filter is emulated by adding decaying sine wave. * The beginning and the ending of the resonant sine is multiplied by a cosine window. * To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave. @@ -143,7 +147,7 @@ class LA32WaveGenerator { } phase; // Current phase of the resonance wave - enum { + enum ResonancePhase { POSITIVE_RISING_RESONANCE_SINE_SEGMENT, POSITIVE_FALLING_RESONANCE_SINE_SEGMENT, NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT, @@ -200,7 +204,7 @@ public: // Return current PCM interpolation factor Bit32u getPCMInterpolationFactor() const; -}; +}; // class LA32WaveGenerator // LA32PartialPair contains a structure of two partials being mixed / ring modulated class LA32PartialPair { @@ -239,10 +243,10 @@ public: // Return active state of the WG engine bool isActive(const PairType master) const; -}; +}; // class LA32PartialPair } // namespace MT32Emu -#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H - #endif // #if MT32EMU_USE_FLOAT_SAMPLES + +#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H diff --git a/audio/softsynth/mt32/MemoryRegion.h b/audio/softsynth/mt32/MemoryRegion.h index c0cb041e11..f8d7da18f8 100644..100755 --- a/audio/softsynth/mt32/MemoryRegion.h +++ b/audio/softsynth/mt32/MemoryRegion.h @@ -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 @@ -18,12 +18,20 @@ #ifndef MT32EMU_MEMORY_REGION_H #define MT32EMU_MEMORY_REGION_H +#include <cstddef> + +#include "globals.h" +#include "Types.h" +#include "Structures.h" + namespace MT32Emu { enum MemoryRegionType { MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset }; +class Synth; + class MemoryRegion { private: Synth *synth; @@ -84,7 +92,7 @@ public: } void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const; void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const; -}; +}; // class MemoryRegion class PatchTempMemoryRegion : public MemoryRegion { public: @@ -112,13 +120,13 @@ public: }; class DisplayMemoryRegion : public MemoryRegion { public: - DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1) {} + DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), SYSEX_BUFFER_SIZE - 1, 1) {} }; class ResetMemoryRegion : public MemoryRegion { public: ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {} }; -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_MEMORY_REGION_H diff --git a/audio/softsynth/mt32/MidiEventQueue.h b/audio/softsynth/mt32/MidiEventQueue.h index b1948c5f8e..1a5ff0ad33 100644..100755 --- a/audio/softsynth/mt32/MidiEventQueue.h +++ b/audio/softsynth/mt32/MidiEventQueue.h @@ -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 @@ -18,6 +18,9 @@ #ifndef MT32EMU_MIDI_EVENT_QUEUE_H #define MT32EMU_MIDI_EVENT_QUEUE_H +#include "globals.h" +#include "Types.h" + namespace MT32Emu { /** @@ -60,8 +63,9 @@ public: const MidiEvent *peekMidiEvent(); void dropMidiEvent(); bool isFull() const; + bool inline isEmpty() const; }; -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_MIDI_EVENT_QUEUE_H diff --git a/audio/softsynth/mt32/MidiStreamParser.cpp b/audio/softsynth/mt32/MidiStreamParser.cpp new file mode 100755 index 0000000000..f9ead3369c --- /dev/null +++ b/audio/softsynth/mt32/MidiStreamParser.cpp @@ -0,0 +1,289 @@ +/* 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; +} diff --git a/audio/softsynth/mt32/MidiStreamParser.h b/audio/softsynth/mt32/MidiStreamParser.h new file mode 100755 index 0000000000..bd31b77c89 --- /dev/null +++ b/audio/softsynth/mt32/MidiStreamParser.h @@ -0,0 +1,124 @@ +/* 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/>. + */ + +#ifndef MT32EMU_MIDI_STREAM_PARSER_H +#define MT32EMU_MIDI_STREAM_PARSER_H + +#include "globals.h" +#include "Types.h" + +namespace MT32Emu { + +class Synth; + +// Interface for a user-supplied class to receive parsed well-formed MIDI messages. +class MT32EMU_EXPORT MidiReceiver { +public: + // Invoked when a complete short MIDI message is parsed in the input MIDI stream. + virtual void handleShortMessage(const Bit32u message) = 0; + + // Invoked when a complete well-formed System Exclusive MIDI message is parsed in the input MIDI stream. + virtual void handleSysex(const Bit8u stream[], const Bit32u length) = 0; + + // Invoked when a System Realtime MIDI message is parsed in the input MIDI stream. + virtual void handleSystemRealtimeMessage(const Bit8u realtime) = 0; + +protected: + ~MidiReceiver() {} +}; + +// Interface for a user-supplied class to receive notifications of input MIDI stream parse errors. +class MT32EMU_EXPORT MidiReporter { +public: + // Invoked when an error occurs during processing the input MIDI stream. + virtual void printDebug(const char *debugMessage) = 0; + +protected: + ~MidiReporter() {} +}; + +// Provides a context for parsing a stream of MIDI events coming from a single source. +// There can be multiple MIDI sources feeding MIDI events to a single Synth object. +// NOTE: Calls from multiple threads which feed a single Synth object with data must be explicitly synchronised, +// although, no synchronisation is required with the rendering thread. +class MT32EMU_EXPORT MidiStreamParserImpl { +public: + // The first two arguments provide for implementations of essential interfaces needed. + // The third argument specifies streamBuffer initial capacity. The buffer capacity should be large enough to fit the longest SysEx expected. + // If a longer SysEx occurs, streamBuffer is reallocated to the maximum size of MAX_STREAM_BUFFER_SIZE (32768 bytes). + // Default capacity is SYSEX_BUFFER_SIZE (1000 bytes) which is enough to fit SysEx messages in common use. + MidiStreamParserImpl(MidiReceiver &, MidiReporter &, Bit32u initialStreamBufferCapacity = SYSEX_BUFFER_SIZE); + virtual ~MidiStreamParserImpl(); + + // Parses a block of raw MIDI bytes. All the parsed MIDI messages are sent in sequence to the user-supplied methods for further processing. + // SysEx messages are allowed to be fragmented across several calls to this method. Running status is also handled for short messages. + // NOTE: the total length of a SysEx message being fragmented shall not exceed MAX_STREAM_BUFFER_SIZE (32768 bytes). + void parseStream(const Bit8u *stream, Bit32u length); + + // Convenience method which accepts a Bit32u-encoded short MIDI message and sends it to the user-supplied method for further processing. + // The short MIDI message may contain no status byte, the running status is used in this case. + void processShortMessage(const Bit32u message); + +private: + Bit8u runningStatus; + Bit8u *streamBuffer; + Bit32u streamBufferCapacity; + Bit32u streamBufferSize; + MidiReceiver &midiReceiver; + MidiReporter &midiReporter; + + // Binary compatibility helper. + void *reserved; + + bool checkStreamBufferCapacity(const bool preserveContent); + bool processStatusByte(Bit8u &status); + Bit32u parseShortMessageStatus(const Bit8u stream[]); + Bit32u parseShortMessageDataBytes(const Bit8u stream[], Bit32u length); + Bit32u parseSysex(const Bit8u stream[], const Bit32u length); + Bit32u parseSysexFragment(const Bit8u stream[], const Bit32u length); +}; // class MidiStreamParserImpl + +// An abstract class that provides a context for parsing a stream of MIDI events coming from a single source. +class MT32EMU_EXPORT MidiStreamParser : public MidiStreamParserImpl, protected MidiReceiver, protected MidiReporter { +public: + // The argument specifies streamBuffer initial capacity. The buffer capacity should be large enough to fit the longest SysEx expected. + // If a longer SysEx occurs, streamBuffer is reallocated to the maximum size of MAX_STREAM_BUFFER_SIZE (32768 bytes). + // Default capacity is SYSEX_BUFFER_SIZE (1000 bytes) which is enough to fit SysEx messages in common use. + explicit MidiStreamParser(Bit32u initialStreamBufferCapacity = SYSEX_BUFFER_SIZE); +}; + +class MT32EMU_EXPORT DefaultMidiStreamParser : public MidiStreamParser { +public: + explicit DefaultMidiStreamParser(Synth &synth, Bit32u initialStreamBufferCapacity = SYSEX_BUFFER_SIZE); + void setTimestamp(const Bit32u useTimestamp); + void resetTimestamp(); + +protected: + void handleShortMessage(const Bit32u message); + void handleSysex(const Bit8u *stream, const Bit32u length); + void handleSystemRealtimeMessage(const Bit8u realtime); + void printDebug(const char *debugMessage); + +private: + Synth &synth; + bool timestampSet; + Bit32u timestamp; +}; + +} // namespace MT32Emu + +#endif // MT32EMU_MIDI_STREAM_PARSER_H diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp index cffc3ed744..c4d5b94551 100644..100755 --- a/audio/softsynth/mt32/Part.cpp +++ b/audio/softsynth/mt32/Part.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,12 +15,16 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#include <cstdio> -//#include <cstring> +#include <cstdio> +#include <cstring> -#include "mt32emu.h" #include "internals.h" + +#include "Part.h" +#include "Partial.h" #include "PartialManager.h" +#include "Poly.h" +#include "Synth.h" namespace MT32Emu { @@ -112,7 +116,7 @@ Bit32s Part::getPitchBend() const { void Part::setBend(unsigned int midiBend) { // CONFIRMED: - pitchBend = (((signed)midiBend - 8192) * pitchBenderRange) >> 14; // PORTABILITY NOTE: Assumes arithmetic shift + pitchBend = ((signed(midiBend) - 8192) * pitchBenderRange) >> 14; // PORTABILITY NOTE: Assumes arithmetic shift } Bit8u Part::getModulation() const { @@ -120,7 +124,7 @@ Bit8u Part::getModulation() const { } void Part::setModulation(unsigned int midiModulation) { - modulation = (Bit8u)midiModulation; + modulation = Bit8u(midiModulation); } void Part::resetAllControllers() { @@ -162,7 +166,7 @@ void Part::refresh() { patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0; } memcpy(currentInstr, timbreTemp->common.name, 10); - synth->newTimbreSet(partNum, patchTemp->patch.timbreGroup, currentInstr); + synth->newTimbreSet(partNum, patchTemp->patch.timbreGroup, patchTemp->patch.timbreNum, currentInstr); updatePitchBenderRange(); } @@ -255,26 +259,26 @@ void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) { switch (t) { case 0: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure12] & 0x2) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure12]; + cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure12)] & 0x2) ? true : false; + cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure12)]; cache[t].structurePosition = 0; cache[t].structurePair = 1; break; case 1: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure12] & 0x1) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure12]; + cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure12)] & 0x1) ? true : false; + cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure12)]; cache[t].structurePosition = 1; cache[t].structurePair = 0; break; case 2: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure34] & 0x2) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure34]; + cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure34)] & 0x2) ? true : false; + cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure34)]; cache[t].structurePosition = 0; cache[t].structurePair = 3; break; case 3: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure34] & 0x1) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure34]; + cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure34)] & 0x1) ? true : false; + cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure34)]; cache[t].structurePosition = 1; cache[t].structurePair = 2; break; @@ -308,7 +312,7 @@ const char *Part::getName() const { void Part::setVolume(unsigned int midiVolume) { // CONFIRMED: This calculation matches the table used in the control ROM - patchTemp->outputLevel = (Bit8u)(midiVolume * 100 / 127); + patchTemp->outputLevel = Bit8u(midiVolume * 100 / 127); //synth->printDebug("%s (%s): Set volume to %d", name, currentInstr, midiVolume); } @@ -322,7 +326,7 @@ Bit8u Part::getExpression() const { void Part::setExpression(unsigned int midiExpression) { // CONFIRMED: This calculation matches the table used in the control ROM - expression = (Bit8u)(midiExpression * 100 / 127); + expression = Bit8u(midiExpression * 100 / 127); } void RhythmPart::setPan(unsigned int midiPan) { @@ -337,9 +341,9 @@ void Part::setPan(unsigned int midiPan) { // NOTE: Panning is inverted compared to GM. // CM-32L: Divide by 8.5 - patchTemp->panpot = (Bit8u)((midiPan << 3) / 68); + patchTemp->panpot = Bit8u((midiPan << 3) / 68); // FIXME: MT-32: Divide by 9 - //patchTemp->panpot = (Bit8u)(midiPan / 9); + //patchTemp->panpot = Bit8u(midiPan / 9); //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot); } @@ -507,7 +511,7 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt #if MT32EMU_MONITOR_PARTIALS > 1 synth->printPartialUsage(); #endif - synth->polyStateChanged(partNum); + synth->reportHandler->onPolyStateChanged(Bit8u(partNum)); } void Part::allNotesOff() { @@ -593,16 +597,14 @@ void Part::partialDeactivated(Poly *poly) { if (!poly->isActive()) { activePolys.remove(poly); synth->partialManager->polyFreed(poly); - synth->polyStateChanged(partNum); + synth->reportHandler->onPolyStateChanged(Bit8u(partNum)); } } -//#define POLY_LIST_DEBUG - PolyList::PolyList() : firstPoly(NULL), lastPoly(NULL) {} bool PolyList::isEmpty() const { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if ((firstPoly == NULL || lastPoly == NULL) && firstPoly != lastPoly) { printf("PolyList: desynchronised firstPoly & lastPoly pointers\n"); } @@ -619,7 +621,7 @@ Poly *PolyList::getLast() const { } void PolyList::prepend(Poly *poly) { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if (poly->getNext() != NULL) { printf("PolyList: Non-NULL next field in a Poly being prepended is ignored\n"); } @@ -632,14 +634,14 @@ void PolyList::prepend(Poly *poly) { } void PolyList::append(Poly *poly) { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if (poly->getNext() != NULL) { printf("PolyList: Non-NULL next field in a Poly being appended is ignored\n"); } #endif poly->setNext(NULL); if (lastPoly != NULL) { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if (lastPoly->getNext() != NULL) { printf("PolyList: Non-NULL next field in the lastPoly\n"); } @@ -656,7 +658,7 @@ Poly *PolyList::takeFirst() { Poly *oldFirst = firstPoly; firstPoly = oldFirst->getNext(); if (firstPoly == NULL) { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if (lastPoly != oldFirst) { printf("PolyList: firstPoly != lastPoly in a list with a single Poly\n"); } @@ -675,7 +677,7 @@ void PolyList::remove(Poly * const polyToRemove) { for (Poly *poly = firstPoly; poly != NULL; poly = poly->getNext()) { if (poly->getNext() == polyToRemove) { if (polyToRemove == lastPoly) { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if (lastPoly->getNext() != NULL) { printf("PolyList: Non-NULL next field in the lastPoly\n"); } @@ -689,4 +691,4 @@ void PolyList::remove(Poly * const polyToRemove) { } } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Part.h b/audio/softsynth/mt32/Part.h index b458fb3988..f5171589d6 100644..100755 --- a/audio/softsynth/mt32/Part.h +++ b/audio/softsynth/mt32/Part.h @@ -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 @@ -18,9 +18,14 @@ #ifndef MT32EMU_PART_H #define MT32EMU_PART_H +#include "globals.h" +#include "internals.h" +#include "Types.h" +#include "Structures.h" + namespace MT32Emu { -class PartialManager; +class Poly; class Synth; class PolyList { @@ -123,7 +128,7 @@ public: // Abort the first poly in PolyState_HELD, or if none exists, the first active poly in any state. bool abortFirstPolyPreferHeld(); bool abortFirstPoly(); -}; +}; // class Part class RhythmPart: public Part { // Pointer to the area of the MT-32's memory dedicated to rhythm @@ -143,5 +148,6 @@ public: void setProgram(unsigned int patchNum); }; -} -#endif +} // namespace MT32Emu + +#endif // #ifndef MT32EMU_PART_H diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp index 7348087509..6afef364df 100644..100755 --- a/audio/softsynth/mt32/Partial.cpp +++ b/audio/softsynth/mt32/Partial.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,14 +15,19 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#include <cmath> -//#include <cstdlib> -//#include <cstring> +#include <cstddef> -#include "mt32emu.h" -#include "mmath.h" #include "internals.h" +#include "Partial.h" +#include "Part.h" +#include "Poly.h" +#include "Synth.h" +#include "Tables.h" +#include "TVA.h" +#include "TVF.h" +#include "TVP.h" + namespace MT32Emu { static const Bit8u PAN_NUMERATOR_MASTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7}; @@ -54,7 +59,7 @@ int Partial::debugGetPartialNum() const { } // Only used for debugging purposes -unsigned long Partial::debugGetSampleNum() const { +Bit32u Partial::debugGetSampleNum() const { return sampleNum; } @@ -266,7 +271,7 @@ void Partial::backupCache(const PatchCache &cache) { } } -bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length) { +bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length) { if (!isActive() || alreadyOutputed || isRingModulatingSlave()) { return false; } @@ -313,8 +318,8 @@ bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long len // Though, it is unknown whether this overflow is exploited somewhere. Sample leftOut = Sample((sample * leftPanValue) >> 8); Sample rightOut = Sample((sample * rightPanValue) >> 8); - *leftBuf = Synth::clipSampleEx((SampleEx)*leftBuf + (SampleEx)leftOut); - *rightBuf = Synth::clipSampleEx((SampleEx)*rightBuf + (SampleEx)rightOut); + *leftBuf = Synth::clipSampleEx(SampleEx(*leftBuf) + SampleEx(leftOut)); + *rightBuf = Synth::clipSampleEx(SampleEx(*rightBuf) + SampleEx(rightOut)); leftBuf++; rightBuf++; #endif @@ -341,4 +346,4 @@ void Partial::startDecayAll() { tvf->startDecay(); } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h index 749468135d..187665de51 100644..100755 --- a/audio/softsynth/mt32/Partial.h +++ b/audio/softsynth/mt32/Partial.h @@ -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 @@ -18,11 +18,21 @@ #ifndef MT32EMU_PARTIAL_H #define MT32EMU_PARTIAL_H +#include "globals.h" +#include "internals.h" +#include "Types.h" +#include "Structures.h" +#include "LA32Ramp.h" +#include "LA32WaveGenerator.h" + namespace MT32Emu { -class Synth; class Part; +class Poly; +class Synth; class TVA; +class TVF; +class TVP; struct ControlROMPCMStruct; // A partial represents one of up to four waveform generators currently playing within a poly. @@ -32,7 +42,7 @@ private: const int debugPartialNum; // Only used for debugging // Number of the sample currently being rendered by produceOutput(), or 0 if no run is in progress // This is only kept available for debugging purposes. - unsigned long sampleNum; + Bit32u sampleNum; // Actually, this is a 4-bit register but we abuse this to emulate inverted mixing. // Also we double the value to enable INACCURATE_SMOOTH_PAN, with respect to MoK. @@ -77,7 +87,7 @@ public: ~Partial(); int debugGetPartialNum() const; - unsigned long debugGetSampleNum() const; + Bit32u debugGetSampleNum() const; int getOwnerPart() const; const Poly *getPoly() const; @@ -100,9 +110,9 @@ public: // Returns true only if data written to buffer // This function (unlike the one below it) returns processed stereo samples // made from combining this single partial with its pair, if it has one. - bool produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length); -}; + bool produceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length); +}; // class Partial -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_PARTIAL_H diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp index 8ca6e4e3d7..7c702f7582 100644..100755 --- a/audio/softsynth/mt32/PartialManager.cpp +++ b/audio/softsynth/mt32/PartialManager.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,11 +15,16 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#include <cstring> +#include <cstddef> +#include <cstring> -#include "mt32emu.h" #include "internals.h" + #include "PartialManager.h" +#include "Part.h" +#include "Partial.h" +#include "Poly.h" +#include "Synth.h" namespace MT32Emu { @@ -275,7 +280,7 @@ void PartialManager::polyFreed(Poly *poly) { const Poly *activePoly = synth->getPart(partNum)->getFirstActivePoly(); Bit32u polyCount = 0; while (activePoly != NULL) { - activePoly->getNext(); + activePoly = activePoly->getNext(); polyCount++; } synth->printDebug("Part: %i, active poly count: %i\n", partNum, polyCount); @@ -286,4 +291,4 @@ void PartialManager::polyFreed(Poly *poly) { freePolys[firstFreePolyIndex] = poly; } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/PartialManager.h b/audio/softsynth/mt32/PartialManager.h index f2e8767bbb..b2908a5c21 100644..100755 --- a/audio/softsynth/mt32/PartialManager.h +++ b/audio/softsynth/mt32/PartialManager.h @@ -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 @@ -18,8 +18,15 @@ #ifndef MT32EMU_PARTIALMANAGER_H #define MT32EMU_PARTIALMANAGER_H +#include "globals.h" +#include "internals.h" +#include "Types.h" + namespace MT32Emu { +class Part; +class Partial; +class Poly; class Synth; class PartialManager { @@ -49,8 +56,8 @@ public: const Partial *getPartial(unsigned int partialNum) const; Poly *assignPolyToPart(Part *part); void polyFreed(Poly *poly); -}; +}; // class PartialManager -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_PARTIALMANAGER_H diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp index badcd8fb96..9a3948cf11 100644..100755 --- a/audio/softsynth/mt32/Poly.cpp +++ b/audio/softsynth/mt32/Poly.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,9 +15,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "mt32emu.h" +#include <cstddef> + #include "internals.h" +#include "Poly.h" +#include "Part.h" +#include "Partial.h" +#include "Synth.h" + namespace MT32Emu { Poly::Poly() { @@ -181,4 +187,4 @@ void Poly::setNext(Poly *poly) { next = poly; } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Poly.h b/audio/softsynth/mt32/Poly.h index e2614369bb..4b283231c8 100644..100755 --- a/audio/softsynth/mt32/Poly.h +++ b/audio/softsynth/mt32/Poly.h @@ -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 @@ -18,17 +18,14 @@ #ifndef MT32EMU_POLY_H #define MT32EMU_POLY_H +#include "globals.h" +#include "internals.h" + namespace MT32Emu { class Part; class Partial; - -enum PolyState { - POLY_Playing, - POLY_Held, // This marks keys that have been released on the keyboard, but are being held by the pedal - POLY_Releasing, - POLY_Inactive -}; +struct PatchCache; class Poly { private: @@ -66,8 +63,8 @@ public: Poly *getNext() const; void setNext(Poly *poly); -}; +}; // class Poly -} +} // namespace MT32Emu -#endif /* POLY_H_ */ +#endif // #ifndef MT32EMU_POLY_H diff --git a/audio/softsynth/mt32/ROMInfo.cpp b/audio/softsynth/mt32/ROMInfo.cpp index f6817c1a4d..ce78e693aa 100644..100755 --- a/audio/softsynth/mt32/ROMInfo.cpp +++ b/audio/softsynth/mt32/ROMInfo.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,27 +15,27 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#include <cstring> +#include <cstring> + +#include "internals.h" + #include "ROMInfo.h" namespace MT32Emu { -static const ROMInfo *getKnownROMInfoFromList(unsigned int index) { - static const ControlROMFeatureSet MT32_COMPATIBLE(true, true); - static const ControlROMFeatureSet CM32L_COMPATIBLE(false, false); - +static const ROMInfo *getKnownROMInfoFromList(Bit32u index) { // Known ROMs - static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; - static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; - static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; - static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; - static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; + static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL}; - static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL, &CM32L_COMPATIBLE}; - static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL, &CM32L_COMPATIBLE}; + static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL}; - static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL, NULL}; - static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL, NULL}; + static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL}; + static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL}; static const ROMInfo * const ROM_INFOS[] = { &CTRL_MT32_V1_04, @@ -52,23 +52,11 @@ static const ROMInfo *getKnownROMInfoFromList(unsigned int index) { return ROM_INFOS[index]; } -const ROMInfo* ROMInfo::getROMInfo(Common::File *file) { - size_t fileSize = file->size(); - Common::String fileName = file->getName(); - fileName.toUppercase(); - bool isCM32LROM = fileName.hasPrefix("CM32L_"); - // We haven't added the SHA1 checksum code in ScummVM, as the file size - // and ROM name suffices for our needs for now. - //const char *fileDigest = file->getSHA1(); - for (int i = 0; getKnownROMInfoFromList(i) != NULL; i++) { +const ROMInfo* ROMInfo::getROMInfo(File *file) { + size_t fileSize = file->getSize(); + for (Bit32u i = 0; getKnownROMInfoFromList(i) != NULL; i++) { const ROMInfo *romInfo = getKnownROMInfoFromList(i); - if (fileSize == romInfo->fileSize /*&& !strcmp(fileDigest, romInfo->sha1Digest)*/) { - if (fileSize == 65536) { - // If we are looking for a CM-32L ROM, make sure we return the first matching - // CM-32L ROM from the list, instead of the first matching MT-32 ROM - if (isCM32LROM && romInfo->controlROMFeatures->isDefaultReverbMT32Compatible()) - continue; - } + if (fileSize == romInfo->fileSize && !strcmp(file->getSHA1(), romInfo->sha1Digest)) { return romInfo; } } @@ -79,17 +67,17 @@ void ROMInfo::freeROMInfo(const ROMInfo *romInfo) { (void) romInfo; } -static int getROMCount() { - int count; +static Bit32u getROMCount() { + Bit32u count; for(count = 0; getKnownROMInfoFromList(count) != NULL; count++) { } return count; } -const ROMInfo** ROMInfo::getROMInfoList(unsigned int types, unsigned int pairTypes) { +const ROMInfo** ROMInfo::getROMInfoList(Bit32u types, Bit32u pairTypes) { const ROMInfo **romInfoList = new const ROMInfo*[getROMCount() + 1]; const ROMInfo **currentROMInList = romInfoList; - for(int i = 0; getKnownROMInfoFromList(i) != NULL; i++) { + for (Bit32u i = 0; getKnownROMInfoFromList(i) != NULL; i++) { const ROMInfo *romInfo = getKnownROMInfoFromList(i); if ((types & (1 << romInfo->type)) && (pairTypes & (1 << romInfo->pairType))) { *currentROMInList++ = romInfo; @@ -103,19 +91,22 @@ void ROMInfo::freeROMInfoList(const ROMInfo **romInfoList) { delete[] romInfoList; } -const ROMImage* ROMImage::makeROMImage(Common::File *file) { - ROMImage *romImage = new ROMImage; - romImage->file = file; - romImage->romInfo = ROMInfo::getROMInfo(romImage->file); - return romImage; +ROMImage::ROMImage(File *useFile) : file(useFile), romInfo(ROMInfo::getROMInfo(file)) +{} + +ROMImage::~ROMImage() { + ROMInfo::freeROMInfo(romInfo); +} + +const ROMImage* ROMImage::makeROMImage(File *file) { + return new ROMImage(file); } void ROMImage::freeROMImage(const ROMImage *romImage) { - ROMInfo::freeROMInfo(romImage->romInfo); delete romImage; } -Common::File* ROMImage::getFile() const { +File* ROMImage::getFile() const { return file; } @@ -123,17 +114,4 @@ const ROMInfo* ROMImage::getROMInfo() const { return romInfo; } -ControlROMFeatureSet::ControlROMFeatureSet(bool useDefaultReverbMT32Compatible, bool useOldMT32AnalogLPF) : - defaultReverbMT32Compatible(useDefaultReverbMT32Compatible), - oldMT32AnalogLPF(useOldMT32AnalogLPF) -{} - -bool ControlROMFeatureSet::isDefaultReverbMT32Compatible() const { - return defaultReverbMT32Compatible; -} - -bool ControlROMFeatureSet::isOldMT32AnalogLPF() const { - return oldMT32AnalogLPF; -} - -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/ROMInfo.h b/audio/softsynth/mt32/ROMInfo.h index 4682620a15..8a5ad141b6 100644..100755 --- a/audio/softsynth/mt32/ROMInfo.h +++ b/audio/softsynth/mt32/ROMInfo.h @@ -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 @@ -18,73 +18,63 @@ #ifndef MT32EMU_ROMINFO_H #define MT32EMU_ROMINFO_H -//#include <cstddef> -#include "common/file.h" +#include <cstddef> -namespace MT32Emu { +#include "globals.h" +#include "File.h" -struct ControlROMFeatureSet; +namespace MT32Emu { // Defines vital info about ROM file to be used by synth and applications struct ROMInfo { public: size_t fileSize; - const char *sha1Digest; + const File::SHA1Digest &sha1Digest; enum Type {PCM, Control, Reverb} type; const char *shortName; const char *description; enum PairType {Full, FirstHalf, SecondHalf, Mux0, Mux1} pairType; ROMInfo *pairROMInfo; - const ControlROMFeatureSet *controlROMFeatures; // Returns a ROMInfo struct by inspecting the size and the SHA1 hash - static const ROMInfo* getROMInfo(Common::File *file); + MT32EMU_EXPORT static const ROMInfo* getROMInfo(File *file); // Currently no-op - static void freeROMInfo(const ROMInfo *romInfo); + MT32EMU_EXPORT static void freeROMInfo(const ROMInfo *romInfo); // Allows retrieving a NULL-terminated list of ROMInfos for a range of types and pairTypes // (specified by bitmasks) // Useful for GUI/console app to output information on what ROMs it supports - static const ROMInfo** getROMInfoList(unsigned int types, unsigned int pairTypes); + MT32EMU_EXPORT static const ROMInfo** getROMInfoList(Bit32u types, Bit32u pairTypes); // Frees the list of ROMInfos given - static void freeROMInfoList(const ROMInfo **romInfos); + MT32EMU_EXPORT static void freeROMInfoList(const ROMInfo **romInfos); }; // Synth::open() is to require a full control ROMImage and a full PCM ROMImage to work class ROMImage { private: - Common::File *file; - const ROMInfo *romInfo; + File * const file; + const ROMInfo * const romInfo; -public: + ROMImage(File *file); + ~ROMImage(); +public: // Creates a ROMImage object given a ROMInfo and a File. Keeps a reference // to the File and ROMInfo given, which must be freed separately by the user // after the ROMImage is freed - static const ROMImage* makeROMImage(Common::File *file); + MT32EMU_EXPORT static const ROMImage* makeROMImage(File *file); // Must only be done after all Synths using the ROMImage are deleted - static void freeROMImage(const ROMImage *romImage); + MT32EMU_EXPORT static void freeROMImage(const ROMImage *romImage); - Common::File *getFile() const; - const ROMInfo *getROMInfo() const; -}; - -struct ControlROMFeatureSet { -private: - unsigned int defaultReverbMT32Compatible : 1; - unsigned int oldMT32AnalogLPF : 1; - -public: - ControlROMFeatureSet(bool defaultReverbMT32Compatible, bool oldMT32AnalogLPF); - bool isDefaultReverbMT32Compatible() const; - bool isOldMT32AnalogLPF() const; + MT32EMU_EXPORT File *getFile() const; + MT32EMU_EXPORT const ROMInfo *getROMInfo() const; }; -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_ROMINFO_H diff --git a/audio/softsynth/mt32/Structures.h b/audio/softsynth/mt32/Structures.h index 4dada3a847..6dcb9c86a9 100644..100755 --- a/audio/softsynth/mt32/Structures.h +++ b/audio/softsynth/mt32/Structures.h @@ -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 @@ -18,6 +18,9 @@ #ifndef MT32EMU_STRUCTURES_H #define MT32EMU_STRUCTURES_H +#include "globals.h" +#include "Types.h" + namespace MT32Emu { // MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it @@ -102,8 +105,8 @@ struct TimbreParam { Bit8u envTime[5]; // 0-100 Bit8u envLevel[4]; // 0-100 // [3]: SUSTAIN LEVEL } MT32EMU_ALIGN_PACKED tva; - } MT32EMU_ALIGN_PACKED partial[4]; -} MT32EMU_ALIGN_PACKED; + } MT32EMU_ALIGN_PACKED partial[4]; // struct PartialParam +} MT32EMU_ALIGN_PACKED; // struct TimbreParam struct PatchParam { Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm) @@ -163,7 +166,16 @@ struct MemParams { Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF) Bit8u masterVol; // MASTER VOLUME 0-100 } MT32EMU_ALIGN_PACKED system; -}; +}; // struct MemParams + +struct SoundGroup { + Bit8u timbreNumberTableAddrLow; + Bit8u timbreNumberTableAddrHigh; + Bit8u displayPosition; + Bit8u name[9]; + Bit8u timbreCount; + Bit8u pad; +} MT32EMU_ALIGN_PACKED; #if defined(_MSC_VER) || defined(__MINGW32__) #pragma pack(pop) @@ -171,10 +183,17 @@ struct MemParams { #pragma pack() #endif +struct ControlROMFeatureSet { + unsigned int quirkPitchEnvelopeOverflow : 1; + + // Features below don't actually depend on control ROM version, which is used to identify hardware model + unsigned int defaultReverbMT32Compatible : 1; + unsigned int oldMT32AnalogLPF : 1; +}; + struct ControlROMMap { - Bit16u idPos; - Bit16u idLen; - const char *idBytes; + const char *shortName; + const ControlROMFeatureSet &featureSet; Bit16u pcmTable; // 4 * pcmCount bytes Bit16u pcmCount; Bit16u timbreAMap; // 128 bytes @@ -194,6 +213,8 @@ struct ControlROMMap { Bit16u patchMaxTable; // 16 bytes Bit16u systemMaxTable; // 23 bytes Bit16u timbreMaxTable; // 72 bytes + Bit16u soundGroupsTable; // 14 bytes each entry + Bit16u soundGroupsCount; }; struct ControlROMPCMStruct { @@ -215,7 +236,7 @@ struct PatchCache { bool playPartial; bool PCMPartial; int pcm; - char waveform; + Bit8u waveform; Bit32u structureMix; int structurePosition; @@ -233,6 +254,6 @@ struct PatchCache { const TimbreParam::PartialParam *partialParam; }; -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_STRUCTURES_H diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp index 6df7eb9e31..162c5d8cee 100644..100755 --- 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,140 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#include <cerrno> -//#include <cmath> -//#include <cstdlib> -//#include <cstring> +#include <cstdio> -#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; +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; +} + +#if MT32EMU_USE_FLOAT_SAMPLES +static inline Bit16s convertSample(float sample) { + return Synth::clipSampleEx(Bit32s(sample * 16384.0f)); // This multiplier takes into account the DAC bit shift +} +#else +static inline float convertSample(Bit16s sample) { + return float(sample) / 16384.0f; // This multiplier takes into account the DAC bit shift +} +#endif + +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 <int BUFFER_SIZE_MULTIPLIER = 1> +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 +159,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 +190,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 +220,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 +293,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 +358,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 +377,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 +397,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 +407,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,18 +429,15 @@ 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; } bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) { - ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress]; + ControlROMPCMStruct *tps = reinterpret_cast<ControlROMPCMStruct *>(&controlROMData[mapAddress]); for (int i = 0; i < count; i++) { Bit32u rAddr = tps[i].pos * 0x800; Bit32u rLenExp = (tps[i].len & 0x70) >> 4; @@ -345,7 +457,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 +481,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 +503,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 = reinterpret_cast<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 +543,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 +561,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 +586,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 +594,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 +656,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 +691,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 +704,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 +721,14 @@ void Synth::close(bool forced) { parts[i] = NULL; } + delete[] soundGroupNames; + soundGroupNames = NULL; + delete[] pcmWaves; + pcmWaves = NULL; + delete[] pcmROMData; + pcmROMData = NULL; deleteMemoryRegions(); @@ -593,7 +738,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() { @@ -649,7 +804,7 @@ Bit32u Synth::getShortMessageLength(Bit32u msg) { } Bit32u Synth::addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp) { - Bit32u transferTime = Bit32u((double)len * MIDI_DATA_TRANSFER_RATE); + Bit32u transferTime = Bit32u(double(len) * MIDI_DATA_TRANSFER_RATE); // Dealing with wrapping if (Bit32s(timestamp - lastReceivedMIDIEventTimestamp) < 0) { timestamp = lastReceivedMIDIEventTimestamp; @@ -664,12 +819,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 +843,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 +873,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: @@ -836,23 +1005,23 @@ void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) { return; } if (sysex[0] != SYSEX_MANUFACTURER_ROLAND) { - printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); + printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3])); return; } if (sysex[2] == SYSEX_MDL_D50) { - printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); + printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3])); return; } else if (sysex[2] != SYSEX_MDL_MT32) { - printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); + printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3])); return; } 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); + 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) @@ -861,6 +1030,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 +1048,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 +1081,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 +1102,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 +1123,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 +1170,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); @@ -1004,12 +1189,12 @@ void Synth::initMemoryRegions() { pos += sizeof(TimbreParam::PartialParam); } memset(&paddedTimbreMaxTable[pos], 0, 10); // Padding - patchTempMemoryRegion = new PatchTempMemoryRegion(this, (Bit8u *)&mt32ram.patchTemp[0], &controlROMData[controlROMMap->patchMaxTable]); - rhythmTempMemoryRegion = new RhythmTempMemoryRegion(this, (Bit8u *)&mt32ram.rhythmTemp[0], &controlROMData[controlROMMap->rhythmMaxTable]); - timbreTempMemoryRegion = new TimbreTempMemoryRegion(this, (Bit8u *)&mt32ram.timbreTemp[0], paddedTimbreMaxTable); - patchesMemoryRegion = new PatchesMemoryRegion(this, (Bit8u *)&mt32ram.patches[0], &controlROMData[controlROMMap->patchMaxTable]); - timbresMemoryRegion = new TimbresMemoryRegion(this, (Bit8u *)&mt32ram.timbres[0], paddedTimbreMaxTable); - systemMemoryRegion = new SystemMemoryRegion(this, (Bit8u *)&mt32ram.system, &controlROMData[controlROMMap->systemMaxTable]); + patchTempMemoryRegion = new PatchTempMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.patchTemp[0]), &controlROMData[controlROMMap->patchMaxTable]); + rhythmTempMemoryRegion = new RhythmTempMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.rhythmTemp[0]), &controlROMData[controlROMMap->rhythmMaxTable]); + timbreTempMemoryRegion = new TimbreTempMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.timbreTemp[0]), paddedTimbreMaxTable); + patchesMemoryRegion = new PatchesMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.patches[0]), &controlROMData[controlROMMap->patchMaxTable]); + timbresMemoryRegion = new TimbresMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.timbres[0]), paddedTimbreMaxTable); + systemMemoryRegion = new SystemMemoryRegion(this, reinterpret_cast<Bit8u *>(&mt32ram.system), &controlROMData[controlROMMap->systemMaxTable]); displayMemoryRegion = new DisplayMemoryRegion(this); resetMemoryRegion = new ResetMemoryRegion(this); } @@ -1071,7 +1256,7 @@ void Synth::readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len for (m = 0; m < len; m += 2) { data[m] = 0xff; if (m + 1 < len) { - data[m+1] = (Bit8u)region->type; + data[m+1] = Bit8u(region->type); } } } @@ -1279,16 +1464,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 +1546,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 +1583,7 @@ void Synth::refreshSystem() { } void Synth::reset() { + if (!opened) return; #if MT32EMU_MONITOR_SYSEX > 0 printDebug("RESET"); #endif @@ -1413,7 +1599,7 @@ void Synth::reset() { } } refreshSystem(); - isEnabled = false; + isActive(); } MidiEvent::~MidiEvent() { @@ -1477,7 +1663,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 +1677,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,57 +1705,139 @@ 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) { + if (thisLen > Bit32u(samplesToNextEvent)) { thisLen = samplesToNextEvent; } } 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 +1846,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 +1856,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 +1870,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 +1880,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 +1919,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,51 +1953,81 @@ 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) { - keys[playingNotes] = (Bit8u)poly->getKey(); - velocities[playingNotes] = (Bit8u)poly->getVelocity(); + keys[playingNotes] = Bit8u(poly->getKey()); + velocities[playingNotes] = Bit8u(poly->getVelocity()); playingNotes++; poly = poly->getNext(); } @@ -1725,11 +2035,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; } @@ -1783,6 +2093,7 @@ void MemoryRegion::write(unsigned int entry, unsigned int off, const Bit8u *src, #if MT32EMU_MONITOR_SYSEX > 0 synth->printDebug("write[%d]: unwritable region: entry=%d, off=%d, len=%d", type, entry, off, len); #endif + return; } for (unsigned int i = 0; i < len; i++) { @@ -1807,4 +2118,4 @@ void MemoryRegion::write(unsigned int entry, unsigned int off, const Bit8u *src, } } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h index 97d4644ee2..5561d8d5db 100644..100755 --- a/audio/softsynth/mt32/Synth.h +++ b/audio/softsynth/mt32/Synth.h @@ -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 @@ -18,8 +18,13 @@ #ifndef MT32EMU_SYNTH_H #define MT32EMU_SYNTH_H -//#include <cstdarg> -//#include <cstring> +#include <cstdarg> +#include <cstddef> +#include <cstring> + +#include "globals.h" +#include "Types.h" +#include "Enumerations.h" namespace MT32Emu { @@ -31,6 +36,8 @@ class Part; class Poly; class Partial; class PartialManager; +class Renderer; +class ROMImage; class PatchTempMemoryRegion; class RhythmTempMemoryRegion; @@ -41,82 +48,11 @@ class SystemMemoryRegion; class DisplayMemoryRegion; class ResetMemoryRegion; +struct ControlROMFeatureSet; struct ControlROMMap; struct PCMWaveEntry; struct MemParams; -/** - * Methods for emulating the connection between the LA32 and the DAC, which involves - * some hacks in the real devices for doubling the volume. - * See also http://en.wikipedia.org/wiki/Roland_MT-32#Digital_overflow - */ -enum DACInputMode { - // Produces samples at double the volume, without tricks. - // * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range) - // * Higher quality than the real devices - DACInputMode_NICE, - - // Produces samples that exactly match the bits output from the emulated LA32. - // * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range) - // * Much less likely to overdrive than any other mode. - // * Half the volume of any of the other modes. - // * Output gain is ignored for both LA32 and reverb output. - // * Perfect for developers while debugging :) - DACInputMode_PURE, - - // Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia). - // Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low): - // 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX - DACInputMode_GENERATION1, - - // Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG). - // Bit order at DAC (where each number represents the original LA32 output bit number): - // 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14 - DACInputMode_GENERATION2 -}; - -// Methods for emulating the effective delay of incoming MIDI messages introduced by a MIDI interface. -enum MIDIDelayMode { - // Process incoming MIDI events immediately. - MIDIDelayMode_IMMEDIATE, - - // Delay incoming short MIDI messages as if they where transferred via a MIDI cable to a real hardware unit and immediate sysex processing. - // This ensures more accurate timing of simultaneous NoteOn messages. - MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY, - - // Delay all incoming MIDI events as if they where transferred via a MIDI cable to a real hardware unit. - MIDIDelayMode_DELAY_ALL -}; - -// Methods for emulating the effects of analogue circuits of real hardware units on the output signal. -enum AnalogOutputMode { - // Only digital path is emulated. The output samples correspond to the digital signal at the DAC entrance. - AnalogOutputMode_DIGITAL_ONLY, - // Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged. - AnalogOutputMode_COARSE, - // Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz, - // which is passed through the LPF circuit without significant attenuation. - AnalogOutputMode_ACCURATE, - // Same as AnalogOutputMode_ACCURATE mode but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz. - // This makes subsequent resampling easier. Besides, due to nonlinear passband of the LPF emulated, it takes fewer number of MACs - // compared to a regular LPF FIR implementations. - AnalogOutputMode_OVERSAMPLED -}; - -enum ReverbMode { - REVERB_MODE_ROOM, - REVERB_MODE_HALL, - REVERB_MODE_PLATE, - REVERB_MODE_TAP_DELAY -}; - -enum PartialState { - PartialState_INACTIVE, - PartialState_ATTACK, - PartialState_SUSTAIN, - PartialState_RELEASE -}; - const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41; const Bit8u SYSEX_MDL_MT32 = 0x16; @@ -132,47 +68,64 @@ const Bit8u SYSEX_CMD_EOD = 0x45; // End of data const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection -const int MAX_SYSEX_SIZE = 512; // FIXME: Does this correspond to a real MIDI buffer used in h/w devices? - -const unsigned int CONTROL_ROM_SIZE = 64 * 1024; - -class ReportHandler { -friend class Synth; +const Bit32u CONTROL_ROM_SIZE = 64 * 1024; + +// Set of multiplexed output streams appeared at the DAC entrance. +template <class T> +struct DACOutputStreams { + T *nonReverbLeft; + T *nonReverbRight; + T *reverbDryLeft; + T *reverbDryRight; + T *reverbWetLeft; + T *reverbWetRight; +}; +// Class for the client to supply callbacks for reporting various errors and information +class MT32EMU_EXPORT ReportHandler { public: virtual ~ReportHandler() {} -protected: - // Callback for debug messages, in vprintf() format virtual void printDebug(const char *fmt, va_list list); - - // Callbacks for reporting various errors and information + // Callbacks for reporting errors virtual void onErrorControlROM() {} virtual void onErrorPCMROM() {} + // Callback for reporting about displaying a new custom message on LCD virtual void showLCDMessage(const char *message); + // Callback for reporting actual processing of a MIDI message virtual void onMIDIMessagePlayed() {} + // Callback for reporting an overflow of the input MIDI queue. + // Returns true if a recovery action was taken and yet another attempt to enqueue the MIDI event is desired. + virtual bool onMIDIQueueOverflow() { return false; } + // Callback invoked when a System Realtime MIDI message is detected at the input. + virtual void onMIDISystemRealtime(Bit8u /* systemRealtime */) {} + // Callbacks for reporting system events virtual void onDeviceReset() {} virtual void onDeviceReconfig() {} + // Callbacks for reporting changes of reverb settings virtual void onNewReverbMode(Bit8u /* mode */) {} virtual void onNewReverbTime(Bit8u /* time */) {} virtual void onNewReverbLevel(Bit8u /* level */) {} - virtual void onPolyStateChanged(int /* partNum */) {} - virtual void onProgramChanged(int /* partNum */, int /* bankNum */, const char * /* patchName */) {} + // Callbacks for reporting various information + virtual void onPolyStateChanged(Bit8u /* partNum */) {} + virtual void onProgramChanged(Bit8u /* partNum */, const char * /* soundGroupName */, const char * /* patchName */) {} }; class Synth { +friend class DefaultMidiStreamParser; friend class Part; -friend class RhythmPart; -friend class Poly; friend class Partial; friend class PartialManager; -friend class Tables; -friend class MemoryRegion; +friend class Poly; +friend class Renderer; +friend class RhythmPart; friend class TVA; -friend class TVF; friend class TVP; + private: + // **************************** Implementation fields ************************** + PatchTempMemoryRegion *patchTempMemoryRegion; RhythmTempMemoryRegion *rhythmTempMemoryRegion; TimbreTempMemoryRegion *timbreTempMemoryRegion; @@ -184,8 +137,6 @@ private: Bit8u *paddedTimbreMaxTable; - bool isEnabled; - PCMWaveEntry *pcmWaves; // Array const ControlROMFeatureSet *controlROMFeatures; @@ -194,8 +145,11 @@ private: Bit16s *pcmROMData; size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM - unsigned int partialCount; - Bit8s chantable[32]; // FIXME: Need explanation why 32 is set, obviously it should be 16 + Bit8u soundGroupIx[128]; // For each standard timbre + const char (*soundGroupNames)[9]; // Array + + Bit32u partialCount; + Bit8u chantable[16]; // NOTE: value above 8 means that the channel is not assigned MidiEventQueue *midiQueue; volatile Bit32u lastReceivedMIDIEventTimestamp; @@ -215,7 +169,8 @@ private: bool reversedStereoEnabled; - bool isOpen; + bool opened; + bool activated; bool isDefaultReportHandler; ReportHandler *reportHandler; @@ -229,15 +184,17 @@ private: Poly *abortingPoly; Analog *analog; + Renderer &renderer; - Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp); + // Binary compatibility helper. + void *reserved; - void produceLA32Output(Sample *buffer, Bit32u len); - void convertSamplesToOutput(Sample *buffer, Bit32u len); - bool isAbortingPoly() const; - void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len); + // **************************** Implementation methods ************************** - void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len) const; + Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp); + bool isAbortingPoly() const { return abortingPoly != NULL; } + + void readSysex(Bit8u channel, const Bit8u *sysex, Bit32u len) const; void initMemoryRegions(); void deleteMemoryRegions(); MemoryRegion *findMemoryRegion(Bit32u addr); @@ -248,79 +205,97 @@ private: bool loadPCMROM(const ROMImage &pcmROMImage); bool initPCMList(Bit16u mapAddress, Bit16u count); - bool initTimbres(Bit16u mapAddress, Bit16u offset, int timbreCount, int startTimbre, bool compressed); - bool initCompressedTimbre(int drumNum, const Bit8u *mem, unsigned int memLen); + bool initTimbres(Bit16u mapAddress, Bit16u offset, Bit16u timbreCount, Bit16u startTimbre, bool compressed); + bool initCompressedTimbre(Bit16u drumNum, const Bit8u *mem, Bit32u memLen); + void initReverbModels(bool mt32CompatibleMode); + void initSoundGroups(char newSoundGroupNames[][9]); void refreshSystemMasterTune(); void refreshSystemReverbParameters(); void refreshSystemReserveSettings(); - void refreshSystemChanAssign(unsigned int firstPart, unsigned int lastPart); + void refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart); void refreshSystemMasterVol(); void refreshSystem(); void reset(); + void dispose(); - void printPartialUsage(unsigned long sampleOffset = 0); + void printPartialUsage(Bit32u sampleOffset = 0); - void polyStateChanged(int partNum); - void newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]); + void newTimbreSet(Bit8u partNum, Bit8u timbreGroup, Bit8u timbreNumber, const char patchName[]); void printDebug(const char *fmt, ...); // partNum should be 0..7 for Part 1..8, or 8 for Rhythm - const Part *getPart(unsigned int partNum) const; + const Part *getPart(Bit8u partNum) const; public: - static inline Sample clipSampleEx(SampleEx sampleEx) { -#if MT32EMU_USE_FLOAT_SAMPLES - return sampleEx; -#else + static inline Bit16s clipSampleEx(Bit32s sampleEx) { // Clamp values above 32767 to 32767, and values below -32768 to -32768 // FIXME: Do we really need this stuff? I think these branches are very well predicted. Instead, this introduces a chain. // The version below is actually a bit faster on my system... - //return ((sampleEx + 0x8000) & ~0xFFFF) ? (sampleEx >> 31) ^ 0x7FFF : (Sample)sampleEx; - return ((-0x8000 <= sampleEx) && (sampleEx <= 0x7FFF)) ? (Sample)sampleEx : (sampleEx >> 31) ^ 0x7FFF; -#endif + //return ((sampleEx + 0x8000) & ~0xFFFF) ? Bit16s((sampleEx >> 31) ^ 0x7FFF) : (Bit16s)sampleEx; + return ((-0x8000 <= sampleEx) && (sampleEx <= 0x7FFF)) ? Bit16s(sampleEx) : Bit16s((sampleEx >> 31) ^ 0x7FFF); + } + + static inline float clipSampleEx(float sampleEx) { + return sampleEx; } - static inline void muteSampleBuffer(Sample *buffer, Bit32u len) { + template <class S> + static inline void muteSampleBuffer(S *buffer, Bit32u len) { if (buffer == NULL) return; + memset(buffer, 0, len * sizeof(S)); + } -#if MT32EMU_USE_FLOAT_SAMPLES + static inline void muteSampleBuffer(float *buffer, Bit32u len) { + if (buffer == NULL) return; // FIXME: Use memset() where compatibility is guaranteed (if this turns out to be a win) while (len--) { *(buffer++) = 0.0f; } -#else - memset(buffer, 0, len * sizeof(Sample)); -#endif } - static Bit32u getShortMessageLength(Bit32u msg); - static Bit8u calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum = 0); + // Returns library version as an integer in format: 0x00MMmmpp, where: + // MM - major version number + // mm - minor version number + // pp - patch number + MT32EMU_EXPORT static Bit32u getLibraryVersionInt(); + // Returns library version as a C-string in format: "MAJOR.MINOR.PATCH" + MT32EMU_EXPORT static const char *getLibraryVersionString(); + + MT32EMU_EXPORT static Bit32u getShortMessageLength(Bit32u msg); + MT32EMU_EXPORT static Bit8u calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum = 0); + + // Returns output sample rate used in emulation of stereo analog circuitry of hardware units. + // See comment for AnalogOutputMode. + MT32EMU_EXPORT static Bit32u getStereoOutputSampleRate(AnalogOutputMode analogOutputMode); // Optionally sets callbacks for reporting various errors, information and debug messages - Synth(ReportHandler *useReportHandler = NULL); - ~Synth(); + MT32EMU_EXPORT explicit Synth(ReportHandler *useReportHandler = NULL); + MT32EMU_EXPORT ~Synth(); // Used to initialise the MT-32. Must be called before any other function. // Returns true if initialization was sucessful, otherwise returns false. // controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth. // usePartialCount sets the maximum number of partials playing simultaneously for this session (optional). // analogOutputMode sets the mode for emulation of analogue circuitry of the hardware units (optional). - bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS, AnalogOutputMode analogOutputMode = AnalogOutputMode_COARSE); + MT32EMU_EXPORT bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, Bit32u usePartialCount = DEFAULT_MAX_PARTIALS, AnalogOutputMode analogOutputMode = AnalogOutputMode_COARSE); // Overloaded method which opens the synth with default partial count. - bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode); + MT32EMU_EXPORT bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode); // Closes the MT-32 and deallocates any memory used by the synthesizer - void close(bool forced = false); + MT32EMU_EXPORT void close(); + + // Returns true if the synth is in completely initialized state, otherwise returns false. + MT32EMU_EXPORT bool isOpen() const; // All the enqueued events are processed by the synth immediately. - void flushMIDIQueue(); + MT32EMU_EXPORT void flushMIDIQueue(); // Sets size of the internal MIDI event queue. The queue size is set to the minimum power of 2 that is greater or equal to the size specified. // The queue is flushed before reallocation. // Returns the actual queue size being used. - Bit32u setMIDIEventQueueSize(Bit32u); + MT32EMU_EXPORT Bit32u setMIDIEventQueueSize(Bit32u); // Enqueues a MIDI event for subsequent playback. // The MIDI event will be processed not before the specified timestamp. @@ -330,14 +305,15 @@ public: // Calls from multiple threads must be synchronised, although, no synchronisation is required with the rendering thread. // The methods return false if the MIDI event queue is full and the message cannot be enqueued. - // Enqueues a single short MIDI message. The message must contain a status byte. - bool playMsg(Bit32u msg, Bit32u timestamp); - // Enqueues a single well formed System Exclusive MIDI message. - bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp); + // Enqueues a single short MIDI message to play at specified time. The message must contain a status byte. + MT32EMU_EXPORT bool playMsg(Bit32u msg, Bit32u timestamp); + // Enqueues a single well formed System Exclusive MIDI message to play at specified time. + MT32EMU_EXPORT bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp); - // Overloaded methods for the MIDI events to be processed ASAP. - bool playMsg(Bit32u msg); - bool playSysex(const Bit8u *sysex, Bit32u len); + // Enqueues a single short MIDI message to be processed ASAP. The message must contain a status byte. + MT32EMU_EXPORT bool playMsg(Bit32u msg); + // Enqueues a single well formed System Exclusive MIDI message to be processed ASAP. + MT32EMU_EXPORT bool playSysex(const Bit8u *sysex, Bit32u len); // WARNING: // The methods below don't ensure minimum 1-sample delay between sequential MIDI events, @@ -345,40 +321,60 @@ public: // A thread that invokes these methods must be explicitly synchronised with the thread performing sample rendering. // Sends a short MIDI message to the synth for immediate playback. The message must contain a status byte. - void playMsgNow(Bit32u msg); - void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity); - - // Sends a string of Sysex commands to the MT-32 for immediate interpretation - // The length is in bytes - void playSysexNow(const Bit8u *sysex, Bit32u len); - void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len); - void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len); - void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len); - - void setReverbEnabled(bool reverbEnabled); - bool isReverbEnabled() const; + // See the WARNING above. + MT32EMU_EXPORT void playMsgNow(Bit32u msg); + // Sends unpacked short MIDI message to the synth for immediate playback. The message must contain a status byte. + // See the WARNING above. + MT32EMU_EXPORT void playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity); + + // Sends a single well formed System Exclusive MIDI message for immediate processing. The length is in bytes. + // See the WARNING above. + MT32EMU_EXPORT void playSysexNow(const Bit8u *sysex, Bit32u len); + // Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes. + // See the WARNING above. + MT32EMU_EXPORT void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len); + // Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes. + // See the WARNING above. + MT32EMU_EXPORT void playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sysex, Bit32u len); + // Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes. + // See the WARNING above. + MT32EMU_EXPORT void writeSysex(Bit8u channel, const Bit8u *sysex, Bit32u len); + + // Allows to disable wet reverb output altogether. + MT32EMU_EXPORT void setReverbEnabled(bool reverbEnabled); + // Returns whether wet reverb output is enabled. + MT32EMU_EXPORT bool isReverbEnabled() const; // Sets override reverb mode. In this mode, emulation ignores sysexes (or the related part of them) which control the reverb parameters. // This mode is in effect until it is turned off. When the synth is re-opened, the override mode is unchanged but the state // of the reverb model is reset to default. - void setReverbOverridden(bool reverbOverridden); - bool isReverbOverridden() const; + MT32EMU_EXPORT void setReverbOverridden(bool reverbOverridden); + // Returns whether reverb settings are overridden. + MT32EMU_EXPORT bool isReverbOverridden() const; // Forces reverb model compatibility mode. By default, the compatibility mode corresponds to the used control ROM version. // Invoking this method with the argument set to true forces emulation of old MT-32 reverb circuit. // When the argument is false, emulation of the reverb circuit used in new generation of MT-32 compatible modules is enforced // (these include CM-32L and LAPC-I). - void setReverbCompatibilityMode(bool mt32CompatibleMode); - bool isMT32ReverbCompatibilityMode() const; - void setDACInputMode(DACInputMode mode); - DACInputMode getDACInputMode() const; - void setMIDIDelayMode(MIDIDelayMode mode); - MIDIDelayMode getMIDIDelayMode() const; + MT32EMU_EXPORT void setReverbCompatibilityMode(bool mt32CompatibleMode); + // Returns whether reverb is in old MT-32 compatibility mode. + MT32EMU_EXPORT bool isMT32ReverbCompatibilityMode() const; + // Returns whether default reverb compatibility mode is the old MT-32 compatibility mode. + MT32EMU_EXPORT bool isDefaultReverbMT32Compatible() const; + // Sets new DAC input mode. See DACInputMode for details. + MT32EMU_EXPORT void setDACInputMode(DACInputMode mode); + // Returns current DAC input mode. See DACInputMode for details. + MT32EMU_EXPORT DACInputMode getDACInputMode() const; + // Sets new MIDI delay mode. See MIDIDelayMode for details. + MT32EMU_EXPORT void setMIDIDelayMode(MIDIDelayMode mode); + // Returns current MIDI delay mode. See MIDIDelayMode for details. + MT32EMU_EXPORT MIDIDelayMode getMIDIDelayMode() const; // Sets output gain factor for synth output channels. Applied to all output samples and unrelated with the synth's Master volume, // it rather corresponds to the gain of the output analog circuitry of the hardware units. However, together with setReverbOutputGain() // it offers to the user a capability to control the gain of reverb and non-reverb output channels independently. // Ignored in DACInputMode_PURE - void setOutputGain(float); - float getOutputGain() const; + MT32EMU_EXPORT void setOutputGain(float gain); + // Returns current output gain factor for synth output channels. + MT32EMU_EXPORT float getOutputGain() const; // Sets output gain factor for the reverb wet output channels. It rather corresponds to the gain of the output // analog circuitry of the hardware units. However, together with setOutputGain() it offers to the user a capability @@ -389,59 +385,85 @@ public: // there is a difference in the reverb analogue circuit, and the resulting output gain is 0.68 // of that for LA32 analogue output. This factor is applied to the reverb output gain. // Ignored in DACInputMode_PURE - void setReverbOutputGain(float); - float getReverbOutputGain() const; + MT32EMU_EXPORT void setReverbOutputGain(float gain); + // Returns current output gain factor for reverb wet output channels. + MT32EMU_EXPORT float getReverbOutputGain() const; - void setReversedStereoEnabled(bool enabled); - bool isReversedStereoEnabled(); + // Swaps left and right output channels. + MT32EMU_EXPORT void setReversedStereoEnabled(bool enabled); + // Returns whether left and right output channels are swapped. + MT32EMU_EXPORT bool isReversedStereoEnabled() const; // Returns actual sample rate used in emulation of stereo analog circuitry of hardware units. // See comment for render() below. - unsigned int getStereoOutputSampleRate() const; + MT32EMU_EXPORT Bit32u getStereoOutputSampleRate() const; // Renders samples to the specified output stream as if they were sampled at the analog stereo output. - // When AnalogOutputMode is set to ACCURATE, the output signal is upsampled to 48 kHz in order + // When AnalogOutputMode is set to ACCURATE (OVERSAMPLED), the output signal is upsampled to 48 (96) kHz in order // to retain emulation accuracy in whole audible frequency spectra. Otherwise, native digital signal sample rate is retained. // getStereoOutputSampleRate() can be used to query actual sample rate of the output signal. - // The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes). - void render(Sample *stream, Bit32u len); + // The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes). Uses NATIVE byte ordering. + MT32EMU_EXPORT void render(Bit16s *stream, Bit32u len); + // Same as above but outputs to a float stereo stream. + MT32EMU_EXPORT void render(float *stream, Bit32u len); // Renders samples to the specified output streams as if they appeared at the DAC entrance. // No further processing performed in analog circuitry emulation is applied to the signal. - // NULL may be specified in place of any or all of the stream buffers. - // The length is in samples, not bytes. - void renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len); + // NULL may be specified in place of any or all of the stream buffers to skip it. + // The length is in samples, not bytes. Uses NATIVE byte ordering. + MT32EMU_EXPORT void renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len); + void renderStreams(const DACOutputStreams<Bit16s> &streams, Bit32u len) { + renderStreams(streams.nonReverbLeft, streams.nonReverbRight, streams.reverbDryLeft, streams.reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len); + } + // Same as above but outputs to float streams. + MT32EMU_EXPORT void renderStreams(float *nonReverbLeft, float *nonReverbRight, float *reverbDryLeft, float *reverbDryRight, float *reverbWetLeft, float *reverbWetRight, Bit32u len); + void renderStreams(const DACOutputStreams<float> &streams, Bit32u len) { + renderStreams(streams.nonReverbLeft, streams.nonReverbRight, streams.reverbDryLeft, streams.reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len); + } // Returns true when there is at least one active partial, otherwise false. - bool hasActivePartials() const; + MT32EMU_EXPORT bool hasActivePartials() const; - // Returns true if hasActivePartials() returns true, or reverb is (somewhat unreliably) detected as being active. - bool isActive() const; + // Returns true if the synth is active and subsequent calls to render() may result in non-trivial output (i.e. silence). + // The synth is considered active when either there are pending MIDI events in the queue, there is at least one active partial, + // or the reverb is (somewhat unreliably) detected as being active. + MT32EMU_EXPORT bool isActive(); // Returns the maximum number of partials playing simultaneously. - unsigned int getPartialCount() const; + MT32EMU_EXPORT Bit32u getPartialCount() const; // Fills in current states of all the parts into the array provided. The array must have at least 9 entries to fit values for all the parts. // If the value returned for a part is true, there is at least one active non-releasing partial playing on this part. // This info is useful in emulating behaviour of LCD display of the hardware units. - void getPartStates(bool *partStates) const; + MT32EMU_EXPORT void getPartStates(bool *partStates) const; + + // Returns current states of all the parts as a bit set. The least significant bit corresponds to the state of part 1, + // total of 9 bits hold the states of all the parts. If the returned bit for a part is set, there is at least one active + // non-releasing partial playing on this part. This info is useful in emulating behaviour of LCD display of the hardware units. + MT32EMU_EXPORT Bit32u getPartStates() const; // Fills in current states of all the partials into the array provided. The array must be large enough to accommodate states of all the partials. - void getPartialStates(PartialState *partialStates) const; + MT32EMU_EXPORT void getPartialStates(PartialState *partialStates) const; + + // Fills in current states of all the partials into the array provided. Each byte in the array holds states of 4 partials + // starting from the least significant bits. The state of each partial is packed in a pair of bits. + // The array must be large enough to accommodate states of all the partials (see getPartialCount()). + MT32EMU_EXPORT void getPartialStates(Bit8u *partialStates) const; // Fills in information about currently playing notes on the specified part into the arrays provided. The arrays must be large enough // to accommodate data for all the playing notes. The maximum number of simultaneously playing notes cannot exceed the number of partials. // Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. // Returns the number of currently playing notes on the specified part. - unsigned int getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const; + MT32EMU_EXPORT Bit32u getPlayingNotes(Bit8u partNumber, Bit8u *keys, Bit8u *velocities) const; // Returns name of the patch set on the specified part. // Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. - const char *getPatchName(unsigned int partNumber) const; + MT32EMU_EXPORT const char *getPatchName(Bit8u partNumber) const; - void readMemory(Bit32u addr, Bit32u len, Bit8u *data); -}; + // Stores internal state of emulated synth into an array provided (as it would be acquired from hardware). + MT32EMU_EXPORT void readMemory(Bit32u addr, Bit32u len, Bit8u *data); +}; // class Synth -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_SYNTH_H diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp index 894e53f14a..c20b8b6393 100644..100755 --- a/audio/softsynth/mt32/TVA.cpp +++ b/audio/softsynth/mt32/TVA.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 @@ -19,19 +19,23 @@ * This class emulates the calculations performed by the 8095 microcontroller in order to configure the LA-32's amplitude ramp for a single partial at each stage of its TVA envelope. * Unless we introduced bugs, it should be pretty much 100% accurate according to Mok's specifications. */ -//#include <cmath> -#include "mt32emu.h" -#include "mmath.h" #include "internals.h" +#include "TVA.h" +#include "Part.h" +#include "Partial.h" +#include "Poly.h" +#include "Synth.h" +#include "Tables.h" + namespace MT32Emu { // CONFIRMED: Matches a table in ROM - haven't got around to coming up with a formula for it yet. static Bit8u biasLevelToAmpSubtractionCoeff[13] = {255, 187, 137, 100, 74, 54, 40, 29, 21, 15, 10, 5, 0}; TVA::TVA(const Partial *usePartial, LA32Ramp *useAmpRamp) : - partial(usePartial), ampRamp(useAmpRamp), system_(&usePartial->getSynth()->mt32ram.system), phase(TVA_PHASE_DEAD) { + partial(usePartial), ampRamp(useAmpRamp), system(&usePartial->getSynth()->mt32ram.system), phase(TVA_PHASE_DEAD) { } void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) { @@ -91,15 +95,15 @@ static int calcVeloAmpSubtraction(Bit8u veloSensitivity, unsigned int velocity) // FIXME:KG: Better variable names int velocityMult = veloSensitivity - 50; int absVelocityMult = velocityMult < 0 ? -velocityMult : velocityMult; - velocityMult = (signed)((unsigned)(velocityMult * ((signed)velocity - 64)) << 2); + velocityMult = signed(unsigned(velocityMult * (signed(velocity) - 64)) << 2); return absVelocityMult - (velocityMult >> 8); // PORTABILITY NOTE: Assumes arithmetic shift } -static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system_, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression) { +static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression) { int amp = 155; if (!partial->isRingModulatingSlave()) { - amp -= tables->masterVolToAmpSubtraction[system_->masterVol]; + amp -= tables->masterVolToAmpSubtraction[system->masterVol]; if (amp < 0) { return 0; } @@ -140,7 +144,7 @@ static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemP return amp; } -int calcKeyTimeSubtraction(Bit8u envTimeKeyfollow, int key) { +static int calcKeyTimeSubtraction(Bit8u envTimeKeyfollow, int key) { if (envTimeKeyfollow == 0) { return 0; } @@ -165,7 +169,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial biasAmpSubtraction = calcBiasAmpSubtractions(partialParam, key); veloAmpSubtraction = calcVeloAmpSubtraction(partialParam->tva.veloSensitivity, velocity); - int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); + int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); int newPhase; if (partialParam->tva.envTime[0] == 0) { // Initially go to the TVA_PHASE_ATTACK target amp, and spend the next phase going from there to the TVA_PHASE_2 target amp @@ -182,7 +186,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial // "Go downward as quickly as possible". // Since the current value is 0, the LA32Ramp will notice that we're already at or below the target and trying to go downward, // and therefore jump to the target immediately and raise an interrupt. - startRamp((Bit8u)newTarget, 0x80 | 127, newPhase); + startRamp(Bit8u(newTarget), 0x80 | 127, newPhase); } void TVA::startAbort() { @@ -217,7 +221,7 @@ void TVA::recalcSustain() { } // We're sustaining. Recalculate all the values const Tables *tables = &Tables::getInstance(); - int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); + int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); newTarget += partialParam->tva.envLevel[3]; // Since we're in TVA_PHASE_SUSTAIN at this point, we know that target has been reached and an interrupt fired, so we can rely on it being the current amp. int targetDelta = newTarget - target; @@ -225,9 +229,9 @@ void TVA::recalcSustain() { // Calculate an increment to get to the new amp value in a short, more or less consistent amount of time Bit8u newIncrement; if (targetDelta >= 0) { - newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - 2; + newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - 2; } else { - newIncrement = (tables->envLogarithmicTime[(Bit8u)-targetDelta] - 2) | 0x80; + newIncrement = (tables->envLogarithmicTime[Bit8u(-targetDelta)] - 2) | 0x80; } // Configure so that once the transition's complete and nextPhase() is called, we'll just re-enter sustain phase (or decay phase, depending on parameters at the time). startRamp(newTarget, newIncrement, TVA_PHASE_SUSTAIN - 1); @@ -279,7 +283,7 @@ void TVA::nextPhase() { int envPointIndex = phase; if (!allLevelsZeroFromNowOn) { - newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); + newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); if (newPhase == TVA_PHASE_SUSTAIN || newPhase == TVA_PHASE_RELEASE) { if (partialParam->tva.envLevel[3] == 0) { @@ -311,7 +315,7 @@ void TVA::nextPhase() { int envTimeSetting = partialParam->tva.envTime[envPointIndex]; if (newPhase == TVA_PHASE_ATTACK) { - envTimeSetting -= ((signed)partial->getPoly()->getVelocity() - 64) >> (6 - partialParam->tva.envTimeVeloSensitivity); // PORTABILITY NOTE: Assumes arithmetic shift + envTimeSetting -= (signed(partial->getPoly()->getVelocity()) - 64) >> (6 - partialParam->tva.envTimeVeloSensitivity); // PORTABILITY NOTE: Assumes arithmetic shift if (envTimeSetting <= 0 && partialParam->tva.envTime[envPointIndex] != 0) { envTimeSetting = 1; @@ -338,14 +342,14 @@ void TVA::nextPhase() { } } targetDelta = -targetDelta; - newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - envTimeSetting; + newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - envTimeSetting; if (newIncrement <= 0) { newIncrement = 1; } newIncrement = newIncrement | 0x80; } else { // FIXME: The last 22 or so entries in this table are 128 - surely that fucks things up, since that ends up being -128 signed? - newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - envTimeSetting; + newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - envTimeSetting; if (newIncrement <= 0) { newIncrement = 1; } @@ -360,7 +364,7 @@ void TVA::nextPhase() { } } - startRamp((Bit8u)newTarget, (Bit8u)newIncrement, newPhase); + startRamp(Bit8u(newTarget), Bit8u(newIncrement), newPhase); } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/TVA.h b/audio/softsynth/mt32/TVA.h index a100107a69..f593b4e7d1 100644..100755 --- a/audio/softsynth/mt32/TVA.h +++ b/audio/softsynth/mt32/TVA.h @@ -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 @@ -18,9 +18,15 @@ #ifndef MT32EMU_TVA_H #define MT32EMU_TVA_H +#include "globals.h" +#include "Types.h" +#include "Structures.h" + namespace MT32Emu { +class LA32Ramp; class Part; +class Partial; // Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to // newPhase's value. @@ -57,7 +63,7 @@ class TVA { private: const Partial * const partial; LA32Ramp *ampRamp; - const MemParams::System * const system_; + const MemParams::System * const system; const Part *part; const TimbreParam::PartialParam *partialParam; @@ -87,8 +93,8 @@ public: bool isPlaying() const; int getPhase() const; -}; +}; // class TVA -} +} // namespace MT32Emu -#endif /* TVA_H_ */ +#endif // #ifndef MT32EMU_TVA_H diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp index 164cf2b4cb..b296c34132 100644..100755 --- a/audio/softsynth/mt32/TVF.cpp +++ b/audio/softsynth/mt32/TVF.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,12 +15,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#include <cmath> - -#include "mt32emu.h" -#include "mmath.h" #include "internals.h" +#include "TVF.h" +#include "LA32Ramp.h" +#include "Partial.h" +#include "Poly.h" +#include "Tables.h" + namespace MT32Emu { // Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to @@ -60,7 +62,7 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u static const Bit8s keyfollowMult21[] = {-21, -10, -5, 0, 2, 5, 8, 10, 13, 16, 18, 21, 26, 32, 42, 21, 21}; int baseCutoff = keyfollowMult21[partialParam->tvf.keyfollow] - keyfollowMult21[partialParam->wg.pitchKeyfollow]; // baseCutoff range now: -63 to 63 - baseCutoff *= (int)key - 60; + baseCutoff *= int(key) - 60; // baseCutoff range now: -3024 to 3024 int biasPoint = partialParam->tvf.biasPoint; if ((biasPoint & 0x40) == 0) { @@ -75,7 +77,7 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u // biasPoint range here: 64 to 127 int bias = biasPoint - 31 - key; // bias range here: -75 to 84 if (bias < 0) { - baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: −6375 to 6375 + baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: -6375 to 6375 // baseCutoff range now: -9399 to 9399 } } @@ -96,7 +98,7 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u if (baseCutoff > 255) { baseCutoff = 255; } - return (Bit8u)baseCutoff; + return Bit8u(baseCutoff); } TVF::TVF(const Partial *usePartial, LA32Ramp *useCutoffModifierRamp) : @@ -128,7 +130,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b int newLevelMult = velocity * newPartialParam->tvf.envVeloSensitivity; newLevelMult >>= 6; newLevelMult += 109 - newPartialParam->tvf.envVeloSensitivity; - newLevelMult += ((signed)key - 60) >> (4 - newPartialParam->tvf.envDepthKeyfollow); + newLevelMult += (signed(key) - 60) >> (4 - newPartialParam->tvf.envDepthKeyfollow); if (newLevelMult < 0) { newLevelMult = 0; } @@ -140,7 +142,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b levelMult = newLevelMult; if (newPartialParam->tvf.envTimeKeyfollow != 0) { - keyTimeSubtraction = ((signed)key - 60) >> (5 - newPartialParam->tvf.envTimeKeyfollow); + keyTimeSubtraction = (signed(key) - 60) >> (5 - newPartialParam->tvf.envTimeKeyfollow); } else { keyTimeSubtraction = 0; } @@ -228,4 +230,4 @@ void TVF::nextPhase() { startRamp(newTarget, newIncrement, newPhase); } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/TVF.h b/audio/softsynth/mt32/TVF.h index 1e2c6d1728..38dcef708c 100644..100755 --- a/audio/softsynth/mt32/TVF.h +++ b/audio/softsynth/mt32/TVF.h @@ -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 @@ -18,8 +18,15 @@ #ifndef MT32EMU_TVF_H #define MT32EMU_TVF_H +#include "globals.h" +#include "Types.h" +#include "Structures.h" + namespace MT32Emu { +class LA32Ramp; +class Partial; + class TVF { private: const Partial * const partial; @@ -47,8 +54,8 @@ public: Bit8u getBaseCutoff() const; void handleInterrupt(); void startDecay(); -}; +}; // class TVF -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_TVF_H diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp index a8003d96dc..dca0003843 100644..100755 --- a/audio/softsynth/mt32/TVP.cpp +++ b/audio/softsynth/mt32/TVP.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,12 +15,17 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#include <cmath> -//#include <cstdlib> +#include <cstdlib> -#include "mt32emu.h" #include "internals.h" +#include "TVP.h" +#include "Part.h" +#include "Partial.h" +#include "Poly.h" +#include "Synth.h" +#include "TVA.h" + namespace MT32Emu { // FIXME: Add Explanation @@ -47,7 +52,7 @@ static Bit16u keyToPitchTable[] = { }; TVP::TVP(const Partial *usePartial) : - partial(usePartial), system_(&usePartial->getSynth()->mt32ram.system) { + partial(usePartial), system(&usePartial->getSynth()->mt32ram.system) { // We want to do processing 4000 times per second. FIXME: This is pretty arbitrary. maxCounter = SAMPLE_RATE / 4000; // The timer runs at 500kHz. We only need to bother updating it every maxCounter samples, before we do processing. @@ -58,7 +63,7 @@ TVP::TVP(const Partial *usePartial) : static Bit16s keyToPitch(unsigned int key) { // We're using a table to do: return round_to_nearest_or_even((key - 60) * (4096.0 / 12.0)) // Banker's rounding is just slightly annoying to do in C++ - int k = (int)key; + int k = int(key); Bit16s pitch = keyToPitchTable[abs(k - 60)]; return key < 60 ? -pitch : pitch; } @@ -82,7 +87,7 @@ static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialPa const ControlROMPCMStruct *controlROMPCMStruct = partial->getControlROMPCMStruct(); if (controlROMPCMStruct != NULL) { - basePitch += (Bit32s)((((Bit32s)controlROMPCMStruct->pitchMSB) << 8) | (Bit32s)controlROMPCMStruct->pitchLSB); + basePitch += (Bit32s(controlROMPCMStruct->pitchMSB) << 8) | Bit32s(controlROMPCMStruct->pitchLSB); } else { if ((partialParam->wg.waveform & 1) == 0) { basePitch += 37133; // This puts Middle C at around 261.64Hz (assuming no other modifications, masterTune of 64, etc.) @@ -98,7 +103,7 @@ static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialPa if (basePitch > 59392) { basePitch = 59392; } - return (Bit32u)basePitch; + return Bit32u(basePitch); } static Bit32u calcVeloMult(Bit8u veloSensitivity, unsigned int velocity) { @@ -119,7 +124,7 @@ static Bit32u calcVeloMult(Bit8u veloSensitivity, unsigned int velocity) { static Bit32s calcTargetPitchOffsetWithoutLFO(const TimbreParam::PartialParam *partialParam, int levelIndex, unsigned int velocity) { int veloMult = calcVeloMult(partialParam->pitchEnv.veloSensitivity, velocity); int targetPitchOffsetWithoutLFO = partialParam->pitchEnv.level[levelIndex] - 50; - targetPitchOffsetWithoutLFO = (Bit32s)(targetPitchOffsetWithoutLFO * veloMult) >> (16 - partialParam->pitchEnv.depth); // PORTABILITY NOTE: Assumes arithmetic shift + targetPitchOffsetWithoutLFO = (targetPitchOffsetWithoutLFO * veloMult) >> (16 - partialParam->pitchEnv.depth); // PORTABILITY NOTE: Assumes arithmetic shift return targetPitchOffsetWithoutLFO; } @@ -140,7 +145,7 @@ void TVP::reset(const Part *usePart, const TimbreParam::PartialParam *usePartial phase = 0; if (partialParam->pitchEnv.timeKeyfollow) { - timeKeyfollowSubtraction = (key - 60) >> (5 - partialParam->pitchEnv.timeKeyfollow); // PORTABILITY NOTE: Assumes arithmetic shift + timeKeyfollowSubtraction = Bit32s(key - 60) >> (5 - partialParam->pitchEnv.timeKeyfollow); // PORTABILITY NOTE: Assumes arithmetic shift } else { timeKeyfollowSubtraction = 0; } @@ -163,7 +168,7 @@ void TVP::updatePitch() { if (!partial->isPCM() || (partial->getControlROMPCMStruct()->len & 0x01) == 0) { // FIXME: Use !partial->pcmWaveEntry->unaffectedByMasterTune instead // FIXME: masterTune recalculation doesn't really happen here, and there are various bugs not yet emulated // 171 is ~half a semitone. - newPitch += ((system_->masterTune - 64) * 171) >> 6; // PORTABILITY NOTE: Assumes arithmetic shift. + newPitch += ((system->masterTune - 64) * 171) >> 6; // PORTABILITY NOTE: Assumes arithmetic shift. } if ((partialParam->wg.pitchBenderEnabled & 1) != 0) { newPitch += part->getPitchBend(); @@ -172,14 +177,13 @@ void TVP::updatePitch() { newPitch = 0; } -// Note: Temporary #ifdef until we have proper "quirk" configuration -// This is about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning" -#ifndef MT32EMU_QUIRK_PITCH_ENVELOPE_OVERFLOW_MT32 - if (newPitch > 59392) { - newPitch = 59392; + // Skipping this check seems about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning" + if (partial->getSynth()->controlROMFeatures->quirkPitchEnvelopeOverflow == 0) { + if (newPitch > 59392) { + newPitch = 59392; + } } -#endif - pitch = (Bit16u)newPitch; + pitch = Bit16u(newPitch); // FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future. partial->getTVA()->recalcSustain(); @@ -317,10 +321,10 @@ void TVP::process() { negativeBigTicksRemaining = negativeBigTicksRemaining >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift rightShifts = 13; } - int newResult = ((Bit32s)(negativeBigTicksRemaining * pitchOffsetChangePerBigTick)) >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift + int newResult = (negativeBigTicksRemaining * pitchOffsetChangePerBigTick) >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift newResult += targetPitchOffsetWithoutLFO + lfoPitchOffset; currentPitchOffset = newResult; updatePitch(); } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/TVP.h b/audio/softsynth/mt32/TVP.h index e9d05ffa7a..be90f0ff08 100644..100755 --- a/audio/softsynth/mt32/TVP.h +++ b/audio/softsynth/mt32/TVP.h @@ -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 @@ -18,12 +18,19 @@ #ifndef MT32EMU_TVP_H #define MT32EMU_TVP_H +#include "globals.h" +#include "Types.h" +#include "Structures.h" + namespace MT32Emu { +class Part; +class Partial; + class TVP { private: const Partial * const partial; - const MemParams::System * const system_; // FIXME: Only necessary because masterTune calculation is done in the wrong place atm. + const MemParams::System * const system; // FIXME: Only necessary because masterTune calculation is done in the wrong place atm. const Part *part; const TimbreParam::PartialParam *partialParam; @@ -60,8 +67,8 @@ public: Bit32u getBasePitch() const; Bit16u nextPitch(); void startDecay(); -}; +}; // class TVP -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_TVP_H diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp index 7e165b5a7a..cb3493285a 100644..100755 --- a/audio/softsynth/mt32/Tables.cpp +++ b/audio/softsynth/mt32/Tables.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,11 +15,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#include <cmath> +#include "internals.h" -#include "mt32emu.h" -#include "mmath.h" #include "Tables.h" +#include "mmath.h" namespace MT32Emu { @@ -31,24 +30,25 @@ const Tables &Tables::getInstance() { } Tables::Tables() { - int lf; - for (lf = 0; lf <= 100; lf++) { + for (int lf = 0; lf <= 100; lf++) { // CONFIRMED:KG: This matches a ROM table found by Mok - float fVal = (2.0f - LOG10F((float)lf + 1.0f)) * 128.0f; - int val = (int)(fVal + 1.0); + float fVal = (2.0f - LOG10F(float(lf) + 1.0f)) * 128.0f; + int val = int(fVal + 1.0); if (val > 255) { val = 255; } - levelToAmpSubtraction[lf] = (Bit8u)val; + levelToAmpSubtraction[lf] = Bit8u(val); } envLogarithmicTime[0] = 64; - for (lf = 1; lf <= 255; lf++) { + for (int lf = 1; lf <= 255; lf++) { // CONFIRMED:KG: This matches a ROM table found by Mok - envLogarithmicTime[lf] = (Bit8u)ceil(64.0f + LOG2F((float)lf) * 8.0f); + envLogarithmicTime[lf] = Bit8u(ceil(64.0f + LOG2F(float(lf)) * 8.0f)); } -#ifdef EMULATE_LAPC_I // Dummy #ifdef - we'll have runtime emulation mode selection in future. +#if 0 + // The table below is to be used in conjunction with emulation of VCA of newer generation units which is currently missing. + // These relatively small values are rather intended to fine-tune the overall amplification of the VCA. // CONFIRMED: Based on a table found by Mok in the LAPC-I control ROM // Note that this matches the MT-32 table, but with the values clamped to a maximum of 8. memset(masterVolToAmpSubtraction, 8, 71); @@ -64,12 +64,12 @@ Tables::Tables() { // CONFIRMED: Based on a table found by Mok in the MT-32 control ROM masterVolToAmpSubtraction[0] = 255; for (int masterVol = 1; masterVol <= 100; masterVol++) { - masterVolToAmpSubtraction[masterVol] = (int)(106.31 - 16.0f * LOG2F((float)masterVol)); + masterVolToAmpSubtraction[masterVol] = Bit8u(106.31 - 16.0f * LOG2F(float(masterVol))); } #endif for (int i = 0; i <= 100; i++) { - pulseWidth100To255[i] = (int)(i * 255 / 100.0f + 0.5f); + pulseWidth100To255[i] = Bit8u(i * 255 / 100.0f + 0.5f); //synth->printDebug("%d: %d", i, pulseWidth100To255[i]); } @@ -94,4 +94,4 @@ Tables::Tables() { resAmpDecayFactor = resAmpDecayFactorTable; } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h index 8865c7fac8..249e32919a 100644..100755 --- a/audio/softsynth/mt32/Tables.h +++ b/audio/softsynth/mt32/Tables.h @@ -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 @@ -18,6 +18,9 @@ #ifndef MT32EMU_TABLES_H #define MT32EMU_TABLES_H +#include "globals.h" +#include "Types.h" + namespace MT32Emu { class Tables { @@ -52,8 +55,8 @@ public: Bit16u logsin9[512]; const Bit8u *resAmpDecayFactor; -}; +}; // class Tables -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_TABLES_H diff --git a/audio/softsynth/mt32/Types.h b/audio/softsynth/mt32/Types.h index 934b1a1173..f90dce19a4 100644..100755 --- a/audio/softsynth/mt32/Types.h +++ b/audio/softsynth/mt32/Types.h @@ -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 @@ -27,14 +27,6 @@ typedef signed short int Bit16s; typedef unsigned char Bit8u; typedef signed char Bit8s; -#if MT32EMU_USE_FLOAT_SAMPLES -typedef float Sample; -typedef float SampleEx; -#else -typedef Bit16s Sample; -typedef Bit32s SampleEx; -#endif - } #endif diff --git a/audio/softsynth/mt32/c_interface/c_interface.cpp b/audio/softsynth/mt32/c_interface/c_interface.cpp new file mode 100755 index 0000000000..6ae252bea5 --- /dev/null +++ b/audio/softsynth/mt32/c_interface/c_interface.cpp @@ -0,0 +1,624 @@ +/* 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 "../globals.h" +#include "../Types.h" +#include "../File.h" +#include "../FileStream.h" +#include "../ROMInfo.h" +#include "../Synth.h" +#include "../MidiStreamParser.h" + +#include "c_types.h" +#include "c_interface.h" + +using namespace MT32Emu; + +namespace MT32Emu { + +static mt32emu_service_version getSynthVersionID(mt32emu_service_i) { + return MT32EMU_SERVICE_VERSION_CURRENT; +} + +static const mt32emu_service_i_v0 SERVICE_VTABLE = { + getSynthVersionID, + mt32emu_get_supported_report_handler_version, + mt32emu_get_supported_midi_receiver_version, + mt32emu_get_library_version_int, + mt32emu_get_library_version_string, + mt32emu_get_stereo_output_samplerate, + mt32emu_create_context, + mt32emu_free_context, + mt32emu_add_rom_data, + mt32emu_add_rom_file, + mt32emu_get_rom_info, + mt32emu_set_partial_count, + mt32emu_set_analog_output_mode, + mt32emu_open_synth, + mt32emu_close_synth, + mt32emu_is_open, + mt32emu_get_actual_stereo_output_samplerate, + mt32emu_flush_midi_queue, + mt32emu_set_midi_event_queue_size, + mt32emu_set_midi_receiver, + mt32emu_parse_stream, + mt32emu_parse_stream_at, + mt32emu_play_short_message, + mt32emu_play_short_message_at, + mt32emu_play_msg, + mt32emu_play_sysex, + mt32emu_play_msg_at, + mt32emu_play_sysex_at, + mt32emu_play_msg_now, + mt32emu_play_msg_on_part, + mt32emu_play_sysex_now, + mt32emu_write_sysex, + mt32emu_set_reverb_enabled, + mt32emu_is_reverb_enabled, + mt32emu_set_reverb_overridden, + mt32emu_is_reverb_overridden, + mt32emu_set_reverb_compatibility_mode, + mt32emu_is_mt32_reverb_compatibility_mode, + mt32emu_is_default_reverb_mt32_compatible, + mt32emu_set_dac_input_mode, + mt32emu_get_dac_input_mode, + mt32emu_set_midi_delay_mode, + mt32emu_get_midi_delay_mode, + mt32emu_set_output_gain, + mt32emu_get_output_gain, + mt32emu_set_reverb_output_gain, + mt32emu_get_reverb_output_gain, + mt32emu_set_reversed_stereo_enabled, + mt32emu_is_reversed_stereo_enabled, + mt32emu_render_bit16s, + mt32emu_render_float, + mt32emu_render_bit16s_streams, + mt32emu_render_float_streams, + mt32emu_has_active_partials, + mt32emu_is_active, + mt32emu_get_partial_count, + mt32emu_get_part_states, + mt32emu_get_partial_states, + mt32emu_get_playing_notes, + mt32emu_get_patch_name, + mt32emu_read_memory +}; + +} // namespace MT32Emu + +struct mt32emu_data { + ReportHandler *reportHandler; + Synth *synth; + const ROMImage *controlROMImage; + const ROMImage *pcmROMImage; + DefaultMidiStreamParser *midiParser; + Bit32u partialCount; + AnalogOutputMode analogOutputMode; +}; + +// Internal C++ utility stuff + +namespace MT32Emu { + +class DelegatingReportHandlerAdapter : public ReportHandler { +public: + DelegatingReportHandlerAdapter(mt32emu_report_handler_i useReportHandler, void *useInstanceData) : + delegate(useReportHandler), instanceData(useInstanceData) {} + +protected: + const mt32emu_report_handler_i delegate; + void * const instanceData; + +private: + void printDebug(const char *fmt, va_list list) { + if (delegate.v0->printDebug == NULL) { + ReportHandler::printDebug(fmt, list); + } else { + delegate.v0->printDebug(instanceData, fmt, list); + } + } + + void onErrorControlROM() { + if (delegate.v0->onErrorControlROM == NULL) { + ReportHandler::onErrorControlROM(); + } else { + delegate.v0->onErrorControlROM(instanceData); + } + } + + void onErrorPCMROM() { + if (delegate.v0->onErrorPCMROM == NULL) { + ReportHandler::onErrorPCMROM(); + } else { + delegate.v0->onErrorPCMROM(instanceData); + } + } + + void showLCDMessage(const char *message) { + if (delegate.v0->showLCDMessage == NULL) { + ReportHandler::showLCDMessage(message); + } else { + delegate.v0->showLCDMessage(instanceData, message); + } + } + + void onMIDIMessagePlayed() { + if (delegate.v0->onMIDIMessagePlayed == NULL) { + ReportHandler::onMIDIMessagePlayed(); + } else { + delegate.v0->onMIDIMessagePlayed(instanceData); + } + } + + bool onMIDIQueueOverflow() { + if (delegate.v0->onMIDIQueueOverflow == NULL) { + return ReportHandler::onMIDIQueueOverflow(); + } + return delegate.v0->onMIDIQueueOverflow(instanceData) != MT32EMU_BOOL_FALSE; + } + + void onMIDISystemRealtime(Bit8u systemRealtime) { + if (delegate.v0->onMIDISystemRealtime == NULL) { + ReportHandler::onMIDISystemRealtime(systemRealtime); + } else { + delegate.v0->onMIDISystemRealtime(instanceData, systemRealtime); + } + } + + void onDeviceReset() { + if (delegate.v0->onDeviceReset == NULL) { + ReportHandler::onDeviceReset(); + } else { + delegate.v0->onDeviceReset(instanceData); + } + } + + void onDeviceReconfig() { + if (delegate.v0->onDeviceReconfig == NULL) { + ReportHandler::onDeviceReconfig(); + } else { + delegate.v0->onDeviceReconfig(instanceData); + } + } + + void onNewReverbMode(Bit8u mode) { + if (delegate.v0->onNewReverbMode == NULL) { + ReportHandler::onNewReverbMode(mode); + } else { + delegate.v0->onNewReverbMode(instanceData, mode); + } + } + + void onNewReverbTime(Bit8u time) { + if (delegate.v0->onNewReverbTime == NULL) { + ReportHandler::onNewReverbTime(time); + } else { + delegate.v0->onNewReverbTime(instanceData, time); + } + } + + void onNewReverbLevel(Bit8u level) { + if (delegate.v0->onNewReverbLevel == NULL) { + ReportHandler::onNewReverbLevel(level); + } else { + delegate.v0->onNewReverbLevel(instanceData, level); + } + } + + void onPolyStateChanged(Bit8u partNum) { + if (delegate.v0->onPolyStateChanged == NULL) { + ReportHandler::onPolyStateChanged(partNum); + } else { + delegate.v0->onPolyStateChanged(instanceData, partNum); + } + } + + void onProgramChanged(Bit8u partNum, const char *soundGroupName, const char *patchName) { + if (delegate.v0->onProgramChanged == NULL) { + ReportHandler::onProgramChanged(partNum, soundGroupName, patchName); + } else { + delegate.v0->onProgramChanged(instanceData, partNum, soundGroupName, patchName); + } + } +}; + +class DelegatingMidiStreamParser : public DefaultMidiStreamParser { +public: + DelegatingMidiStreamParser(const mt32emu_data *useData, mt32emu_midi_receiver_i useMIDIReceiver, void *useInstanceData) : + DefaultMidiStreamParser(*useData->synth), delegate(useMIDIReceiver), instanceData(useInstanceData) {} + +protected: + mt32emu_midi_receiver_i delegate; + void *instanceData; + +private: + void handleShortMessage(const Bit32u message) { + if (delegate.v0->handleShortMessage == NULL) { + DefaultMidiStreamParser::handleShortMessage(message); + } else { + delegate.v0->handleShortMessage(instanceData, message); + } + } + + void handleSysex(const Bit8u *stream, const Bit32u length) { + if (delegate.v0->handleSysex == NULL) { + DefaultMidiStreamParser::handleSysex(stream, length); + } else { + delegate.v0->handleSysex(instanceData, stream, length); + } + } + + void handleSystemRealtimeMessage(const Bit8u realtime) { + if (delegate.v0->handleSystemRealtimeMessage == NULL) { + DefaultMidiStreamParser::handleSystemRealtimeMessage(realtime); + } else { + delegate.v0->handleSystemRealtimeMessage(instanceData, realtime); + } + } +}; + +static mt32emu_return_code addROMFile(mt32emu_data *data, File *file) { + const ROMImage *image = ROMImage::makeROMImage(file); + const ROMInfo *info = image->getROMInfo(); + if (info == NULL) { + ROMImage::freeROMImage(image); + return MT32EMU_RC_ROM_NOT_IDENTIFIED; + } + if (info->type == ROMInfo::Control) { + if (data->controlROMImage != NULL) { + delete data->controlROMImage->getFile(); + ROMImage::freeROMImage(data->controlROMImage); + } + data->controlROMImage = image; + return MT32EMU_RC_ADDED_CONTROL_ROM; + } else if (info->type == ROMInfo::PCM) { + if (data->pcmROMImage != NULL) { + delete data->pcmROMImage->getFile(); + ROMImage::freeROMImage(data->pcmROMImage); + } + data->pcmROMImage = image; + return MT32EMU_RC_ADDED_PCM_ROM; + } + ROMImage::freeROMImage(image); + return MT32EMU_RC_OK; // No support for reverb ROM yet. +} + +} // namespace MT32Emu + +// C-visible implementation + +extern "C" { + +const mt32emu_service_i mt32emu_get_service_i() { + mt32emu_service_i i = { &SERVICE_VTABLE }; + return i; +} + +mt32emu_report_handler_version mt32emu_get_supported_report_handler_version() { + return MT32EMU_REPORT_HANDLER_VERSION_CURRENT; +} + +mt32emu_midi_receiver_version mt32emu_get_supported_midi_receiver_version() { + return MT32EMU_MIDI_RECEIVER_VERSION_CURRENT; +} + +mt32emu_bit32u mt32emu_get_library_version_int() { + return Synth::getLibraryVersionInt(); +} + +const char *mt32emu_get_library_version_string() { + return Synth::getLibraryVersionString(); +} + +mt32emu_bit32u mt32emu_get_stereo_output_samplerate(const mt32emu_analog_output_mode analog_output_mode) { + return Synth::getStereoOutputSampleRate(static_cast<AnalogOutputMode>(analog_output_mode)); +} + +mt32emu_context mt32emu_create_context(mt32emu_report_handler_i report_handler, void *instance_data) { + mt32emu_data *data = new mt32emu_data; + data->reportHandler = (report_handler.v0 != NULL) ? new DelegatingReportHandlerAdapter(report_handler, instance_data) : new ReportHandler; + data->synth = new Synth(data->reportHandler); + data->midiParser = new DefaultMidiStreamParser(*data->synth); + data->controlROMImage = NULL; + data->pcmROMImage = NULL; + data->partialCount = DEFAULT_MAX_PARTIALS; + data->analogOutputMode = AnalogOutputMode_COARSE; + return data; +} + +void mt32emu_free_context(mt32emu_context data) { + if (data == NULL) return; + if (data->controlROMImage != NULL) { + delete data->controlROMImage->getFile(); + ROMImage::freeROMImage(data->controlROMImage); + data->controlROMImage = NULL; + } + if (data->pcmROMImage != NULL) { + delete data->pcmROMImage->getFile(); + ROMImage::freeROMImage(data->pcmROMImage); + data->pcmROMImage = NULL; + } + delete data->midiParser; + data->midiParser = NULL; + delete data->synth; + data->synth = NULL; + delete data->reportHandler; + data->reportHandler = NULL; + delete data; +} + +mt32emu_return_code mt32emu_add_rom_data(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest) { + if (sha1_digest == NULL) return addROMFile(context, new ArrayFile(data, data_size)); + return addROMFile(context, new ArrayFile(data, data_size, *sha1_digest)); +} + +mt32emu_return_code mt32emu_add_rom_file(mt32emu_context context, const char *filename) { + mt32emu_return_code rc = MT32EMU_RC_OK; + FileStream *fs = new FileStream; + if (fs->open(filename)) { + if (fs->getData() != NULL) { + rc = addROMFile(context, fs); + if (rc > 0) return rc; + } else { + rc = MT32EMU_RC_FILE_NOT_LOADED; + } + } else { + rc = MT32EMU_RC_FILE_NOT_FOUND; + } + delete fs; + return rc; +} + +void mt32emu_get_rom_info(mt32emu_const_context context, mt32emu_rom_info *rom_info) { + const ROMInfo *romInfo = context->controlROMImage == NULL ? NULL : context->controlROMImage->getROMInfo(); + if (romInfo != NULL) { + rom_info->control_rom_id = romInfo->shortName; + rom_info->control_rom_description = romInfo->description; + rom_info->control_rom_sha1_digest = romInfo->sha1Digest; + } else { + rom_info->control_rom_id = NULL; + rom_info->control_rom_description = NULL; + rom_info->control_rom_sha1_digest = NULL; + } + romInfo = context->pcmROMImage == NULL ? NULL : context->pcmROMImage->getROMInfo(); + if (romInfo != NULL) { + rom_info->pcm_rom_id = romInfo->shortName; + rom_info->pcm_rom_description = romInfo->description; + rom_info->pcm_rom_sha1_digest = romInfo->sha1Digest; + } else { + rom_info->pcm_rom_id = NULL; + rom_info->pcm_rom_description = NULL; + rom_info->pcm_rom_sha1_digest = NULL; + } +} + +void mt32emu_set_partial_count(mt32emu_context context, const mt32emu_bit32u partial_count) { + context->partialCount = partial_count; +} + +void mt32emu_set_analog_output_mode(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode) { + context->analogOutputMode = static_cast<AnalogOutputMode>(analog_output_mode); +} + +mt32emu_return_code mt32emu_open_synth(mt32emu_const_context context) { + if ((context->controlROMImage == NULL) || (context->pcmROMImage == NULL)) { + return MT32EMU_RC_MISSING_ROMS; + } + if (context->synth->open(*context->controlROMImage, *context->pcmROMImage, context->partialCount, context->analogOutputMode)) { + return MT32EMU_RC_OK; + } + return MT32EMU_RC_FAILED; +} + +void mt32emu_close_synth(mt32emu_const_context context) { + context->synth->close(); +} + +mt32emu_boolean mt32emu_is_open(mt32emu_const_context context) { + return context->synth->isOpen() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +mt32emu_bit32u mt32emu_get_actual_stereo_output_samplerate(mt32emu_const_context context) { + return context->synth->getStereoOutputSampleRate(); +} + +void mt32emu_flush_midi_queue(mt32emu_const_context context) { + context->synth->flushMIDIQueue(); +} + +mt32emu_bit32u mt32emu_set_midi_event_queue_size(mt32emu_const_context context, const mt32emu_bit32u queue_size) { + return context->synth->setMIDIEventQueueSize(queue_size); +} + +void mt32emu_set_midi_receiver(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data) { + delete context->midiParser; + context->midiParser = (midi_receiver.v0 != NULL) ? new DelegatingMidiStreamParser(context, midi_receiver, instance_data) : new DefaultMidiStreamParser(*context->synth); +} + +void mt32emu_parse_stream(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length) { + context->midiParser->resetTimestamp(); + context->midiParser->parseStream(stream, length); +} + +void mt32emu_parse_stream_at(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp) { + context->midiParser->setTimestamp(timestamp); + context->midiParser->parseStream(stream, length); +} + +void mt32emu_play_short_message(mt32emu_const_context context, mt32emu_bit32u message) { + context->midiParser->resetTimestamp(); + context->midiParser->processShortMessage(message); +} + +void mt32emu_play_short_message_at(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp) { + context->midiParser->setTimestamp(timestamp); + context->midiParser->processShortMessage(message); +} + +mt32emu_return_code mt32emu_play_msg(mt32emu_const_context context, mt32emu_bit32u msg) { + if (!context->synth->isOpen()) return MT32EMU_RC_NOT_OPENED; + return (context->synth->playMsg(msg)) ? MT32EMU_RC_OK : MT32EMU_RC_QUEUE_FULL; +} + +mt32emu_return_code mt32emu_play_sysex(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len) { + if (!context->synth->isOpen()) return MT32EMU_RC_NOT_OPENED; + return (context->synth->playSysex(sysex, len)) ? MT32EMU_RC_OK : MT32EMU_RC_QUEUE_FULL; +} + +mt32emu_return_code mt32emu_play_msg_at(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp) { + if (!context->synth->isOpen()) return MT32EMU_RC_NOT_OPENED; + return (context->synth->playMsg(msg, timestamp)) ? MT32EMU_RC_OK : MT32EMU_RC_QUEUE_FULL; +} + +mt32emu_return_code mt32emu_play_sysex_at(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp) { + if (!context->synth->isOpen()) return MT32EMU_RC_NOT_OPENED; + return (context->synth->playSysex(sysex, len, timestamp)) ? MT32EMU_RC_OK : MT32EMU_RC_QUEUE_FULL; +} + +void mt32emu_play_msg_now(mt32emu_const_context context, mt32emu_bit32u msg) { + context->synth->playMsgNow(msg); +} + +void mt32emu_play_msg_on_part(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity) { + context->synth->playMsgOnPart(part, code, note, velocity); +} + +void mt32emu_play_sysex_now(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len) { + context->synth->playSysexNow(sysex, len); +} + +void mt32emu_write_sysex(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len) { + context->synth->writeSysex(channel, sysex, len); +} + +void mt32emu_set_reverb_enabled(mt32emu_const_context context, const mt32emu_boolean reverb_enabled) { + context->synth->setReverbEnabled(reverb_enabled != MT32EMU_BOOL_FALSE); +} + +mt32emu_boolean mt32emu_is_reverb_enabled(mt32emu_const_context context) { + return context->synth->isReverbEnabled() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +void mt32emu_set_reverb_overridden(mt32emu_const_context context, const mt32emu_boolean reverb_overridden) { + context->synth->setReverbOverridden(reverb_overridden != MT32EMU_BOOL_FALSE); +} + +mt32emu_boolean mt32emu_is_reverb_overridden(mt32emu_const_context context) { + return context->synth->isReverbOverridden() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +void mt32emu_set_reverb_compatibility_mode(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode) { + context->synth->setReverbCompatibilityMode(mt32_compatible_mode != MT32EMU_BOOL_FALSE); +} + +mt32emu_boolean mt32emu_is_mt32_reverb_compatibility_mode(mt32emu_const_context context) { + return context->synth->isMT32ReverbCompatibilityMode() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +mt32emu_boolean mt32emu_is_default_reverb_mt32_compatible(mt32emu_const_context context) { + return context->synth->isDefaultReverbMT32Compatible() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +void mt32emu_set_dac_input_mode(mt32emu_const_context context, const mt32emu_dac_input_mode mode) { + context->synth->setDACInputMode(static_cast<DACInputMode>(mode)); +} + +mt32emu_dac_input_mode mt32emu_get_dac_input_mode(mt32emu_const_context context) { + return static_cast<mt32emu_dac_input_mode>(context->synth->getDACInputMode()); +} + +void mt32emu_set_midi_delay_mode(mt32emu_const_context context, const mt32emu_midi_delay_mode mode) { + context->synth->setMIDIDelayMode(static_cast<MIDIDelayMode>(mode)); +} + +mt32emu_midi_delay_mode mt32emu_get_midi_delay_mode(mt32emu_const_context context) { + return static_cast<mt32emu_midi_delay_mode>(context->synth->getMIDIDelayMode()); +} + +void mt32emu_set_output_gain(mt32emu_const_context context, float gain) { + context->synth->setOutputGain(gain); +} + +float mt32emu_get_output_gain(mt32emu_const_context context) { + return context->synth->getOutputGain(); +} + +void mt32emu_set_reverb_output_gain(mt32emu_const_context context, float gain) { + context->synth->setReverbOutputGain(gain); +} + +float mt32emu_get_reverb_output_gain(mt32emu_const_context context) { + return context->synth->getReverbOutputGain(); +} + +void mt32emu_set_reversed_stereo_enabled(mt32emu_const_context context, const mt32emu_boolean enabled) { + context->synth->setReversedStereoEnabled(enabled != MT32EMU_BOOL_FALSE); +} + +mt32emu_boolean mt32emu_is_reversed_stereo_enabled(mt32emu_const_context context) { + return context->synth->isReversedStereoEnabled() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +void mt32emu_render_bit16s(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len) { + context->synth->render(stream, len); +} + +void mt32emu_render_float(mt32emu_const_context context, float *stream, mt32emu_bit32u len) { + context->synth->render(stream, len); +} + +void mt32emu_render_bit16s_streams(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len) { + context->synth->renderStreams(*reinterpret_cast<const DACOutputStreams<Bit16s> *>(streams), len); +} + +void mt32emu_render_float_streams(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len) { + context->synth->renderStreams(*reinterpret_cast<const DACOutputStreams<float> *>(streams), len); +} + +mt32emu_boolean mt32emu_has_active_partials(mt32emu_const_context context) { + return context->synth->hasActivePartials() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +mt32emu_boolean mt32emu_is_active(mt32emu_const_context context) { + return context->synth->isActive() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +mt32emu_bit32u mt32emu_get_partial_count(mt32emu_const_context context) { + return context->synth->getPartialCount(); +} + +mt32emu_bit32u mt32emu_get_part_states(mt32emu_const_context context) { + return context->synth->getPartStates(); +} + +void mt32emu_get_partial_states(mt32emu_const_context context, mt32emu_bit8u *partial_states) { + context->synth->getPartialStates(partial_states); +} + +mt32emu_bit32u mt32emu_get_playing_notes(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities) { + return context->synth->getPlayingNotes(part_number, keys, velocities); +} + +const char *mt32emu_get_patch_name(mt32emu_const_context context, mt32emu_bit8u part_number) { + return context->synth->getPatchName(part_number); +} + +void mt32emu_read_memory(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data) { + context->synth->readMemory(addr, len, data); +} + +} // extern "C" diff --git a/audio/softsynth/mt32/c_interface/c_interface.h b/audio/softsynth/mt32/c_interface/c_interface.h new file mode 100755 index 0000000000..a2bdcb1254 --- /dev/null +++ b/audio/softsynth/mt32/c_interface/c_interface.h @@ -0,0 +1,362 @@ +/* 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/>. + */ + +#ifndef MT32EMU_C_INTERFACE_H +#define MT32EMU_C_INTERFACE_H + +#include <stddef.h> + +#include "../globals.h" +#include "c_types.h" + +#undef MT32EMU_EXPORT +#define MT32EMU_EXPORT MT32EMU_EXPORT_ATTRIBUTE + +#ifdef __cplusplus +extern "C" { +#endif + +/* == Context-independent functions == */ + +/* === Interface handling === */ + +/** Returns mt32emu_service_i interface. */ +MT32EMU_EXPORT const mt32emu_service_i mt32emu_get_service_i(); + +#if MT32EMU_EXPORTS_TYPE == 2 +#undef MT32EMU_EXPORT +#define MT32EMU_EXPORT +#endif + +/** + * Returns the version ID of mt32emu_report_handler_i interface the library has been compiled with. + * This allows a client to fall-back gracefully instead of silently not receiving expected event reports. + */ +MT32EMU_EXPORT mt32emu_report_handler_version mt32emu_get_supported_report_handler_version(); + +/** + * Returns the version ID of mt32emu_midi_receiver_version_i interface the library has been compiled with. + * This allows a client to fall-back gracefully instead of silently not receiving expected MIDI messages. + */ +MT32EMU_EXPORT mt32emu_midi_receiver_version mt32emu_get_supported_midi_receiver_version(); + +/** + * Returns library version as an integer in format: 0x00MMmmpp, where: + * MM - major version number + * mm - minor version number + * pp - patch number + */ +MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_library_version_int(); + +/** + * Returns library version as a C-string in format: "MAJOR.MINOR.PATCH". + */ +MT32EMU_EXPORT const char *mt32emu_get_library_version_string(); + +/** + * Returns output sample rate used in emulation of stereo analog circuitry of hardware units for particular analog_output_mode. + * See comment for mt32emu_analog_output_mode. + */ +MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_stereo_output_samplerate(const mt32emu_analog_output_mode analog_output_mode); + +/* == Context-dependent functions == */ + +/** Initialises a new emulation context and installs custom report handler if non-NULL. */ +MT32EMU_EXPORT mt32emu_context mt32emu_create_context(mt32emu_report_handler_i report_handler, void *instance_data); + +/** Closes and destroys emulation context. */ +MT32EMU_EXPORT void mt32emu_free_context(mt32emu_context context); + +/** + * Adds new ROM identified by its SHA1 digest to the emulation context replacing previously added ROM of the same type if any. + * Argument sha1_digest can be NULL, in this case the digest will be computed using the actual ROM data. + * If sha1_digest is set to non-NULL, it is assumed being correct and will not be recomputed. + * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of mt32emu_open_synth(). + * Returns positive value upon success. + */ +MT32EMU_EXPORT mt32emu_return_code mt32emu_add_rom_data(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest); + +/** + * Loads a ROM file, identify it by SHA1 digest, and adds it to the emulation context replacing previously added ROM of the same type if any. + * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of mt32emu_open_synth(). + * Returns positive value upon success. + */ +MT32EMU_EXPORT mt32emu_return_code mt32emu_add_rom_file(mt32emu_context context, const char *filename); + +/** + * Fills in mt32emu_rom_info structure with identifiers and descriptions of control and PCM ROM files identified and added to the synth context. + * If one of the ROM files is not loaded and identified yet, NULL is returned in the corresponding fields of the mt32emu_rom_info structure. + */ +MT32EMU_EXPORT void mt32emu_get_rom_info(mt32emu_const_context context, mt32emu_rom_info *rom_info); + +/** + * Allows to override the default maximum number of partials playing simultaneously within the emulation session. + * This function doesn't immediately change the state of already opened synth. Newly set vale will take effect upon next call of mt32emu_open_synth(). + */ +MT32EMU_EXPORT void mt32emu_set_partial_count(mt32emu_context context, const mt32emu_bit32u partial_count); + +/** + * Allows to override the default mode for emulation of analogue circuitry of the hardware units within the emulation session. + * This function doesn't immediately change the state of already opened synth. Newly set vale will take effect upon next call of mt32emu_open_synth(). + */ +MT32EMU_EXPORT void mt32emu_set_analog_output_mode(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode); + +/** + * Prepares the emulation context to receive MIDI messages and produce output audio data using aforehand added set of ROMs, + * and optionally set the maximum partial count and the analog output mode. + * Returns MT32EMU_RC_OK upon success. + */ +MT32EMU_EXPORT mt32emu_return_code mt32emu_open_synth(mt32emu_const_context context); + +/** Closes the emulation context freeing allocated resources. Added ROMs remain unaffected and ready for reuse. */ +MT32EMU_EXPORT void mt32emu_close_synth(mt32emu_const_context context); + +/** Returns true if the synth is in completely initialized state, otherwise returns false. */ +MT32EMU_EXPORT mt32emu_boolean mt32emu_is_open(mt32emu_const_context context); + +/** + * Returns actual output sample rate used in emulation of stereo analog circuitry of hardware units. + * See comment for mt32emu_analog_output_mode. + */ +MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_actual_stereo_output_samplerate(mt32emu_const_context context); + +/** All the enqueued events are processed by the synth immediately. */ +MT32EMU_EXPORT void mt32emu_flush_midi_queue(mt32emu_const_context context); + +/** + * Sets size of the internal MIDI event queue. The queue size is set to the minimum power of 2 that is greater or equal to the size specified. + * The queue is flushed before reallocation. + * Returns the actual queue size being used. + */ +MT32EMU_EXPORT mt32emu_bit32u mt32emu_set_midi_event_queue_size(mt32emu_const_context context, const mt32emu_bit32u queue_size); + +/** + * Installs custom MIDI receiver object intended for receiving MIDI messages generated by MIDI stream parser. + * MIDI stream parser is involved when functions mt32emu_parse_stream() and mt32emu_play_short_message() or the likes are called. + * By default, parsed short MIDI messages and System Exclusive messages are sent to the synth input MIDI queue. + * This function allows to override default behaviour. If midi_receiver argument is set to NULL, the default behaviour is restored. + */ +MT32EMU_EXPORT void mt32emu_set_midi_receiver(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data); + +/* Enqueues a MIDI event for subsequent playback. + * The MIDI event will be processed not before the specified timestamp. + * The timestamp is measured as the global rendered sample count since the synth was created (at the native sample rate 32000 Hz). + * The minimum delay involves emulation of the delay introduced while the event is transferred via MIDI interface + * and emulation of the MCU busy-loop while it frees partials for use by a new Poly. + * Calls from multiple threads must be synchronised, although, no synchronisation is required with the rendering thread. + * onMIDIQueueOverflow callback is invoked when the MIDI event queue is full and the message cannot be enqueued. + */ + +/** + * Parses a block of raw MIDI bytes and enqueues parsed MIDI messages for further processing ASAP. + * SysEx messages are allowed to be fragmented across several calls to this method. Running status is also handled for short messages. + * When a System Realtime MIDI message is parsed, onMIDISystemRealtime callback is invoked. + * NOTE: the total length of a SysEx message being fragmented shall not exceed MT32EMU_MAX_STREAM_BUFFER_SIZE (32768 bytes). + */ +MT32EMU_EXPORT void mt32emu_parse_stream(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length); + +/** + * Parses a block of raw MIDI bytes and enqueues parsed MIDI messages to play at specified time. + * SysEx messages are allowed to be fragmented across several calls to this method. Running status is also handled for short messages. + * When a System Realtime MIDI message is parsed, onMIDISystemRealtime callback is invoked. + * NOTE: the total length of a SysEx message being fragmented shall not exceed MT32EMU_MAX_STREAM_BUFFER_SIZE (32768 bytes). + */ +MT32EMU_EXPORT void mt32emu_parse_stream_at(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp); + +/** + * Enqueues a single mt32emu_bit32u-encoded short MIDI message with full processing ASAP. + * The short MIDI message may contain no status byte, the running status is used in this case. + * When the argument is a System Realtime MIDI message, onMIDISystemRealtime callback is invoked. + */ +MT32EMU_EXPORT void mt32emu_play_short_message(mt32emu_const_context context, mt32emu_bit32u message); + +/** + * Enqueues a single mt32emu_bit32u-encoded short MIDI message to play at specified time with full processing. + * The short MIDI message may contain no status byte, the running status is used in this case. + * When the argument is a System Realtime MIDI message, onMIDISystemRealtime callback is invoked. + */ +MT32EMU_EXPORT void mt32emu_play_short_message_at(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp); + +/** Enqueues a single short MIDI message to be processed ASAP. The message must contain a status byte. */ +MT32EMU_EXPORT mt32emu_return_code mt32emu_play_msg(mt32emu_const_context context, mt32emu_bit32u msg); +/** Enqueues a single well formed System Exclusive MIDI message to be processed ASAP. */ +MT32EMU_EXPORT mt32emu_return_code mt32emu_play_sysex(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); + +/** Enqueues a single short MIDI message to play at specified time. The message must contain a status byte. */ +MT32EMU_EXPORT mt32emu_return_code mt32emu_play_msg_at(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp); +/** Enqueues a single well formed System Exclusive MIDI message to play at specified time. */ +MT32EMU_EXPORT mt32emu_return_code mt32emu_play_sysex_at(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp); + +/* WARNING: + * The methods below don't ensure minimum 1-sample delay between sequential MIDI events, + * and a sequence of NoteOn and immediately succeeding NoteOff messages is always silent. + * A thread that invokes these methods must be explicitly synchronised with the thread performing sample rendering. + */ + +/** + * Sends a short MIDI message to the synth for immediate playback. The message must contain a status byte. + * See the WARNING above. + */ +MT32EMU_EXPORT void mt32emu_play_msg_now(mt32emu_const_context context, mt32emu_bit32u msg); +/** + * Sends unpacked short MIDI message to the synth for immediate playback. The message must contain a status byte. + * See the WARNING above. + */ +MT32EMU_EXPORT void mt32emu_play_msg_on_part(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity); + +/** + * Sends a single well formed System Exclusive MIDI message for immediate processing. The length is in bytes. + * See the WARNING above. + */ +MT32EMU_EXPORT void mt32emu_play_sysex_now(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); +/** + * Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes. + * See the WARNING above. + */ +MT32EMU_EXPORT void mt32emu_write_sysex(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len); + +/** Allows to disable wet reverb output altogether. */ +MT32EMU_EXPORT void mt32emu_set_reverb_enabled(mt32emu_const_context context, const mt32emu_boolean reverb_enabled); +/** Returns whether wet reverb output is enabled. */ +MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reverb_enabled(mt32emu_const_context context); +/** + * Sets override reverb mode. In this mode, emulation ignores sysexes (or the related part of them) which control the reverb parameters. + * This mode is in effect until it is turned off. When the synth is re-opened, the override mode is unchanged but the state + * of the reverb model is reset to default. + */ +MT32EMU_EXPORT void mt32emu_set_reverb_overridden(mt32emu_const_context context, const mt32emu_boolean reverb_overridden); +/** Returns whether reverb settings are overridden. */ +MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reverb_overridden(mt32emu_const_context context); +/** + * Forces reverb model compatibility mode. By default, the compatibility mode corresponds to the used control ROM version. + * Invoking this method with the argument set to true forces emulation of old MT-32 reverb circuit. + * When the argument is false, emulation of the reverb circuit used in new generation of MT-32 compatible modules is enforced + * (these include CM-32L and LAPC-I). + */ +MT32EMU_EXPORT void mt32emu_set_reverb_compatibility_mode(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode); +/** Returns whether reverb is in old MT-32 compatibility mode. */ +MT32EMU_EXPORT mt32emu_boolean mt32emu_is_mt32_reverb_compatibility_mode(mt32emu_const_context context); +/** Returns whether default reverb compatibility mode is the old MT-32 compatibility mode. */ +MT32EMU_EXPORT mt32emu_boolean mt32emu_is_default_reverb_mt32_compatible(mt32emu_const_context context); + +/** Sets new DAC input mode. See mt32emu_dac_input_mode for details. */ +MT32EMU_EXPORT void mt32emu_set_dac_input_mode(mt32emu_const_context context, const mt32emu_dac_input_mode mode); +/** Returns current DAC input mode. See mt32emu_dac_input_mode for details. */ +MT32EMU_EXPORT mt32emu_dac_input_mode mt32emu_get_dac_input_mode(mt32emu_const_context context); + +/** Sets new MIDI delay mode. See mt32emu_midi_delay_mode for details. */ +MT32EMU_EXPORT void mt32emu_set_midi_delay_mode(mt32emu_const_context context, const mt32emu_midi_delay_mode mode); +/** Returns current MIDI delay mode. See mt32emu_midi_delay_mode for details. */ +MT32EMU_EXPORT mt32emu_midi_delay_mode mt32emu_get_midi_delay_mode(mt32emu_const_context context); + +/** + * Sets output gain factor for synth output channels. Applied to all output samples and unrelated with the synth's Master volume, + * it rather corresponds to the gain of the output analog circuitry of the hardware units. However, together with mt32emu_set_reverb_output_gain() + * it offers to the user a capability to control the gain of reverb and non-reverb output channels independently. + * Ignored in MT32EMU_DAC_PURE mode. + */ +MT32EMU_EXPORT void mt32emu_set_output_gain(mt32emu_const_context context, float gain); +/** Returns current output gain factor for synth output channels. */ +MT32EMU_EXPORT float mt32emu_get_output_gain(mt32emu_const_context context); + +/** + * Sets output gain factor for the reverb wet output channels. It rather corresponds to the gain of the output + * analog circuitry of the hardware units. However, together with mt32emu_set_output_gain() it offers to the user a capability + * to control the gain of reverb and non-reverb output channels independently. + * + * Note: We're currently emulate CM-32L/CM-64 reverb quite accurately and the reverb output level closely + * corresponds to the level of digital capture. Although, according to the CM-64 PCB schematic, + * there is a difference in the reverb analogue circuit, and the resulting output gain is 0.68 + * of that for LA32 analogue output. This factor is applied to the reverb output gain. + * Ignored in MT32EMU_DAC_PURE mode. + */ +MT32EMU_EXPORT void mt32emu_set_reverb_output_gain(mt32emu_const_context context, float gain); +/** Returns current output gain factor for reverb wet output channels. */ +MT32EMU_EXPORT float mt32emu_get_reverb_output_gain(mt32emu_const_context context); + +/** Swaps left and right output channels. */ +MT32EMU_EXPORT void mt32emu_set_reversed_stereo_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); +/** Returns whether left and right output channels are swapped. */ +MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reversed_stereo_enabled(mt32emu_const_context context); + +/** + * Renders samples to the specified output stream as if they were sampled at the analog stereo output. + * When mt32emu_analog_output_mode is set to ACCURATE (OVERSAMPLED), the output signal is upsampled to 48 (96) kHz in order + * to retain emulation accuracy in whole audible frequency spectra. Otherwise, native digital signal sample rate is retained. + * mt32emu_get_actual_stereo_output_samplerate() can be used to query actual sample rate of the output signal. + * The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes). Uses NATIVE byte ordering. + */ +MT32EMU_EXPORT void mt32emu_render_bit16s(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len); +/** Same as above but outputs to a float stereo stream. */ +MT32EMU_EXPORT void mt32emu_render_float(mt32emu_const_context context, float *stream, mt32emu_bit32u len); + +/** + * Renders samples to the specified output streams as if they appeared at the DAC entrance. + * No further processing performed in analog circuitry emulation is applied to the signal. + * NULL may be specified in place of any or all of the stream buffers to skip it. + * The length is in samples, not bytes. Uses NATIVE byte ordering. + */ +MT32EMU_EXPORT void mt32emu_render_bit16s_streams(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len); +/** Same as above but outputs to float streams. */ +MT32EMU_EXPORT void mt32emu_render_float_streams(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len); + +/** Returns true when there is at least one active partial, otherwise false. */ +MT32EMU_EXPORT mt32emu_boolean mt32emu_has_active_partials(mt32emu_const_context context); + +/** Returns true if mt32emu_has_active_partials() returns true, or reverb is (somewhat unreliably) detected as being active. */ +MT32EMU_EXPORT mt32emu_boolean mt32emu_is_active(mt32emu_const_context context); + +/** Returns the maximum number of partials playing simultaneously. */ +MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_partial_count(mt32emu_const_context context); + +/** + * Returns current states of all the parts as a bit set. The least significant bit corresponds to the state of part 1, + * total of 9 bits hold the states of all the parts. If the returned bit for a part is set, there is at least one active + * non-releasing partial playing on this part. This info is useful in emulating behaviour of LCD display of the hardware units. + */ +MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_part_states(mt32emu_const_context context); + +/** + * Fills in current states of all the partials into the array provided. Each byte in the array holds states of 4 partials + * starting from the least significant bits. The state of each partial is packed in a pair of bits. + * The array must be large enough to accommodate states of all the partials. + * @see getPartialCount() + */ +MT32EMU_EXPORT void mt32emu_get_partial_states(mt32emu_const_context context, mt32emu_bit8u *partial_states); + +/** + * Fills in information about currently playing notes on the specified part into the arrays provided. The arrays must be large enough + * to accommodate data for all the playing notes. The maximum number of simultaneously playing notes cannot exceed the number of partials. + * Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. + * Returns the number of currently playing notes on the specified part. + */ +MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_playing_notes(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities); + +/** + * Returns name of the patch set on the specified part. + * Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. + */ +MT32EMU_EXPORT const char *mt32emu_get_patch_name(mt32emu_const_context context, mt32emu_bit8u part_number); + +/** Stores internal state of emulated synth into an array provided (as it would be acquired from hardware). */ +MT32EMU_EXPORT void mt32emu_read_memory(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* #ifndef MT32EMU_C_INTERFACE_H */ diff --git a/audio/softsynth/mt32/c_interface/c_types.h b/audio/softsynth/mt32/c_interface/c_types.h new file mode 100755 index 0000000000..3cd8744235 --- /dev/null +++ b/audio/softsynth/mt32/c_interface/c_types.h @@ -0,0 +1,298 @@ +/* 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/>. + */ + +#ifndef MT32EMU_C_TYPES_H +#define MT32EMU_C_TYPES_H + +#include <stdarg.h> +#include <stddef.h> + +#include "../globals.h" + +#define MT32EMU_C_ENUMERATIONS +#include "../Enumerations.h" +#undef MT32EMU_C_ENUMERATIONS + +typedef unsigned int mt32emu_bit32u; +typedef signed int mt32emu_bit32s; +typedef unsigned short int mt32emu_bit16u; +typedef signed short int mt32emu_bit16s; +typedef unsigned char mt32emu_bit8u; +typedef signed char mt32emu_bit8s; + +typedef char mt32emu_sha1_digest[41]; + +typedef enum { + MT32EMU_BOOL_FALSE, MT32EMU_BOOL_TRUE +} mt32emu_boolean; + +typedef enum { + /* Operation completed normally. */ + MT32EMU_RC_OK = 0, + MT32EMU_RC_ADDED_CONTROL_ROM = 1, + MT32EMU_RC_ADDED_PCM_ROM = 2, + + /* Definite error occurred. */ + MT32EMU_RC_ROM_NOT_IDENTIFIED = -1, + MT32EMU_RC_FILE_NOT_FOUND = -2, + MT32EMU_RC_FILE_NOT_LOADED = -3, + MT32EMU_RC_MISSING_ROMS = -4, + MT32EMU_RC_NOT_OPENED = -5, + MT32EMU_RC_QUEUE_FULL = -6, + + /* Undefined error occurred. */ + MT32EMU_RC_FAILED = -100 +} mt32emu_return_code; + +/** Emulation context */ +typedef struct mt32emu_data *mt32emu_context; +typedef const struct mt32emu_data *mt32emu_const_context; + +/* Convenience aliases */ +#ifndef __cplusplus +typedef enum mt32emu_analog_output_mode mt32emu_analog_output_mode; +typedef enum mt32emu_dac_input_mode mt32emu_dac_input_mode; +typedef enum mt32emu_midi_delay_mode mt32emu_midi_delay_mode; +typedef enum mt32emu_partial_state mt32emu_partial_state; +#endif + +/** Contains identifiers and descriptions of ROM files being used. */ +typedef struct { + const char *control_rom_id; + const char *control_rom_description; + const char *control_rom_sha1_digest; + const char *pcm_rom_id; + const char *pcm_rom_description; + const char *pcm_rom_sha1_digest; +} mt32emu_rom_info; + +/** Set of multiplexed output bit16s streams appeared at the DAC entrance. */ +typedef struct { + mt32emu_bit16s *nonReverbLeft; + mt32emu_bit16s *nonReverbRight; + mt32emu_bit16s *reverbDryLeft; + mt32emu_bit16s *reverbDryRight; + mt32emu_bit16s *reverbWetLeft; + mt32emu_bit16s *reverbWetRight; +} mt32emu_dac_output_bit16s_streams; + +/** Set of multiplexed output float streams appeared at the DAC entrance. */ +typedef struct { + float *nonReverbLeft; + float *nonReverbRight; + float *reverbDryLeft; + float *reverbDryRight; + float *reverbWetLeft; + float *reverbWetRight; +} mt32emu_dac_output_float_streams; + +/* === Interface handling === */ + +/** Report handler interface versions */ +typedef enum { + MT32EMU_REPORT_HANDLER_VERSION_0 = 0, + MT32EMU_REPORT_HANDLER_VERSION_CURRENT = MT32EMU_REPORT_HANDLER_VERSION_0 +} mt32emu_report_handler_version; + +/** MIDI receiver interface versions */ +typedef enum { + MT32EMU_MIDI_RECEIVER_VERSION_0 = 0, + MT32EMU_MIDI_RECEIVER_VERSION_CURRENT = MT32EMU_MIDI_RECEIVER_VERSION_0 +} mt32emu_midi_receiver_version; + +/** Synth interface versions */ +typedef enum { + MT32EMU_SERVICE_VERSION_0 = 0, + MT32EMU_SERVICE_VERSION_CURRENT = MT32EMU_SERVICE_VERSION_0 +} mt32emu_service_version; + +/* === Report Handler Interface === */ + +typedef union mt32emu_report_handler_i mt32emu_report_handler_i; + +/** Interface for handling reported events (initial version) */ +typedef struct { + /** Returns the actual interface version ID */ + mt32emu_report_handler_version (*getVersionID)(mt32emu_report_handler_i i); + + /** Callback for debug messages, in vprintf() format */ + void (*printDebug)(void *instance_data, const char *fmt, va_list list); + /** Callbacks for reporting errors */ + void (*onErrorControlROM)(void *instance_data); + void (*onErrorPCMROM)(void *instance_data); + /** Callback for reporting about displaying a new custom message on LCD */ + void (*showLCDMessage)(void *instance_data, const char *message); + /** Callback for reporting actual processing of a MIDI message */ + void (*onMIDIMessagePlayed)(void *instance_data); + /** + * Callback for reporting an overflow of the input MIDI queue. + * Returns MT32EMU_BOOL_TRUE if a recovery action was taken + * and yet another attempt to enqueue the MIDI event is desired. + */ + mt32emu_boolean (*onMIDIQueueOverflow)(void *instance_data); + /** + * Callback invoked when a System Realtime MIDI message is detected in functions + * mt32emu_parse_stream and mt32emu_play_short_message and the likes. + */ + void (*onMIDISystemRealtime)(void *instance_data, mt32emu_bit8u system_realtime); + /** Callbacks for reporting system events */ + void (*onDeviceReset)(void *instance_data); + void (*onDeviceReconfig)(void *instance_data); + /** Callbacks for reporting changes of reverb settings */ + void (*onNewReverbMode)(void *instance_data, mt32emu_bit8u mode); + void (*onNewReverbTime)(void *instance_data, mt32emu_bit8u time); + void (*onNewReverbLevel)(void *instance_data, mt32emu_bit8u level); + /** Callbacks for reporting various information */ + void (*onPolyStateChanged)(void *instance_data, mt32emu_bit8u part_num); + void (*onProgramChanged)(void *instance_data, mt32emu_bit8u part_num, const char *sound_group_name, const char *patch_name); +} mt32emu_report_handler_i_v0; + +/** + * Extensible interface for handling reported events. + * Union intended to view an interface of any subsequent version as any parent interface not requiring a cast. + * Elements are to be addressed using the tag of the interface version when they were introduced. + */ +union mt32emu_report_handler_i { + const mt32emu_report_handler_i_v0 *v0; +}; + +/* === MIDI Receiver Interface === */ + +typedef union mt32emu_midi_receiver_i mt32emu_midi_receiver_i; + +/** Interface for receiving MIDI messages generated by MIDI stream parser (initial version) */ +typedef struct { + /** Returns the actual interface version ID */ + mt32emu_midi_receiver_version (*getVersionID)(mt32emu_midi_receiver_i i); + + /** Invoked when a complete short MIDI message is parsed in the input MIDI stream. */ + void (*handleShortMessage)(void *instance_data, const mt32emu_bit32u message); + + /** Invoked when a complete well-formed System Exclusive MIDI message is parsed in the input MIDI stream. */ + void (*handleSysex)(void *instance_data, const mt32emu_bit8u stream[], const mt32emu_bit32u length); + + /** Invoked when a System Realtime MIDI message is parsed in the input MIDI stream. */ + void (*handleSystemRealtimeMessage)(void *instance_data, const mt32emu_bit8u realtime); +} mt32emu_midi_receiver_i_v0; + +/** + * Extensible interface for receiving MIDI messages. + * Union intended to view an interface of any subsequent version as any parent interface not requiring a cast. + * Elements are to be addressed using the tag of the interface version when they were introduced. + */ +union mt32emu_midi_receiver_i { + const mt32emu_midi_receiver_i_v0 *v0; +}; + +/* === Service Interface === */ + +typedef union mt32emu_service_i mt32emu_service_i; + +/** + * Basic interface that defines all the library services (initial version). + * The members closely resemble C functions declared in c_interface.h, and the intention is to provide for easier + * access when the library is dynamically loaded in run-time, e.g. as a plugin. This way the client only needs + * to bind to mt32emu_get_service_i() function instead of binding to each function it needs to use. + * See c_interface.h for parameter description. + */ +typedef struct { + /** Returns the actual interface version ID */ + mt32emu_service_version (*getVersionID)(mt32emu_service_i i); + mt32emu_report_handler_version (*getSupportedReportHandlerVersionID)(); + mt32emu_midi_receiver_version (*getSupportedMIDIReceiverVersionID)(); + + mt32emu_bit32u (*getLibraryVersionInt)(); + const char *(*getLibraryVersionString)(); + + mt32emu_bit32u (*getStereoOutputSamplerate)(const mt32emu_analog_output_mode analog_output_mode); + + mt32emu_context (*createContext)(mt32emu_report_handler_i report_handler, void *instance_data); + void (*freeContext)(mt32emu_context context); + mt32emu_return_code (*addROMData)(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest); + mt32emu_return_code (*addROMFile)(mt32emu_context context, const char *filename); + void (*getROMInfo)(mt32emu_const_context context, mt32emu_rom_info *rom_info); + void (*setPartialCount)(mt32emu_context context, const mt32emu_bit32u partial_count); + void (*setAnalogOutputMode)(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode); + mt32emu_return_code (*openSynth)(mt32emu_const_context context); + void (*closeSynth)(mt32emu_const_context context); + mt32emu_boolean (*isOpen)(mt32emu_const_context context); + mt32emu_bit32u (*getActualStereoOutputSamplerate)(mt32emu_const_context context); + void (*flushMIDIQueue)(mt32emu_const_context context); + mt32emu_bit32u (*setMIDIEventQueueSize)(mt32emu_const_context context, const mt32emu_bit32u queue_size); + void (*setMIDIReceiver)(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data); + + void (*parseStream)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length); + void (*parseStream_At)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp); + void (*playShortMessage)(mt32emu_const_context context, mt32emu_bit32u message); + void (*playShortMessageAt)(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp); + mt32emu_return_code (*playMsg)(mt32emu_const_context context, mt32emu_bit32u msg); + mt32emu_return_code (*playSysex)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); + mt32emu_return_code (*playMsgAt)(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp); + mt32emu_return_code (*playSysexAt)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp); + + void (*playMsgNow)(mt32emu_const_context context, mt32emu_bit32u msg); + void (*playMsgOnPart)(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity); + void (*playSysexNow)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); + void (*writeSysex)(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len); + + void (*setReverbEnabled)(mt32emu_const_context context, const mt32emu_boolean reverb_enabled); + mt32emu_boolean (*isReverbEnabled)(mt32emu_const_context context); + void (*setReverbOverridden)(mt32emu_const_context context, const mt32emu_boolean reverb_overridden); + mt32emu_boolean (*isReverbOverridden)(mt32emu_const_context context); + void (*setReverbCompatibilityMode)(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode); + mt32emu_boolean (*isMT32ReverbCompatibilityMode)(mt32emu_const_context context); + mt32emu_boolean (*isDefaultReverbMT32Compatible)(mt32emu_const_context context); + + void (*setDACInputMode)(mt32emu_const_context context, const mt32emu_dac_input_mode mode); + mt32emu_dac_input_mode (*getDACInputMode)(mt32emu_const_context context); + + void (*setMIDIDelayMode)(mt32emu_const_context context, const mt32emu_midi_delay_mode mode); + mt32emu_midi_delay_mode (*getMIDIDelayMode)(mt32emu_const_context context); + + void (*setOutputGain)(mt32emu_const_context context, float gain); + float (*getOutputGain)(mt32emu_const_context context); + void (*setReverbOutputGain)(mt32emu_const_context context, float gain); + float (*getReverbOutputGain)(mt32emu_const_context context); + + void (*setReversedStereoEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); + mt32emu_boolean (*isReversedStereoEnabled)(mt32emu_const_context context); + + void (*renderBit16s)(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len); + void (*renderFloat)(mt32emu_const_context context, float *stream, mt32emu_bit32u len); + void (*renderBit16sStreams)(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len); + void (*renderFloatStreams)(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len); + + mt32emu_boolean (*hasActivePartials)(mt32emu_const_context context); + mt32emu_boolean (*isActive)(mt32emu_const_context context); + mt32emu_bit32u (*getPartialCount)(mt32emu_const_context context); + mt32emu_bit32u (*getPartStates)(mt32emu_const_context context); + void (*getPartialStates)(mt32emu_const_context context, mt32emu_bit8u *partial_states); + mt32emu_bit32u (*getPlayingNotes)(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities); + const char *(*getPatchName)(mt32emu_const_context context, mt32emu_bit8u part_number); + void (*readMemory)(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data); +} mt32emu_service_i_v0; + +/** + * Extensible interface for all the library services. + * Union intended to view an interface of any subsequent version as any parent interface not requiring a cast. + * Elements are to be addressed using the tag of the interface version when they were introduced. + */ +union mt32emu_service_i { + const mt32emu_service_i_v0 *v0; +}; + +#endif /* #ifndef MT32EMU_C_TYPES_H */ diff --git a/audio/softsynth/mt32/c_interface/cpp_interface.h b/audio/softsynth/mt32/c_interface/cpp_interface.h new file mode 100755 index 0000000000..3e86322faa --- /dev/null +++ b/audio/softsynth/mt32/c_interface/cpp_interface.h @@ -0,0 +1,436 @@ +/* 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/>. + */ + +#ifndef MT32EMU_CPP_INTERFACE_H +#define MT32EMU_CPP_INTERFACE_H + +#include <cstdarg> + +#include "../globals.h" +#include "c_types.h" + +#include "../Types.h" +#include "../Enumerations.h" + +#if MT32EMU_API_TYPE == 2 + +#define mt32emu_get_supported_report_handler_version i.v0->getSupportedReportHandlerVersionID +#define mt32emu_get_supported_midi_receiver_version i.v0->getSupportedMIDIReceiverVersionID +#define mt32emu_get_library_version_int i.v0->getLibraryVersionInt +#define mt32emu_get_library_version_string i.v0->getLibraryVersionString +#define mt32emu_get_stereo_output_samplerate i.v0->getStereoOutputSamplerate +#define mt32emu_create_context i.v0->createContext +#define mt32emu_free_context i.v0->freeContext +#define mt32emu_add_rom_data i.v0->addROMData +#define mt32emu_add_rom_file i.v0->addROMFile +#define mt32emu_get_rom_info i.v0->getROMInfo +#define mt32emu_set_partial_count i.v0->setPartialCount +#define mt32emu_set_analog_output_mode i.v0->setAnalogOutputMode +#define mt32emu_open_synth i.v0->openSynth +#define mt32emu_close_synth i.v0->closeSynth +#define mt32emu_is_open i.v0->isOpen +#define mt32emu_get_actual_stereo_output_samplerate i.v0->getActualStereoOutputSamplerate +#define mt32emu_flush_midi_queue i.v0->flushMIDIQueue +#define mt32emu_set_midi_event_queue_size i.v0->setMIDIEventQueueSize +#define mt32emu_set_midi_receiver i.v0->setMIDIReceiver +#define mt32emu_parse_stream i.v0->parseStream +#define mt32emu_parse_stream_at i.v0->parseStream_At +#define mt32emu_play_short_message i.v0->playShortMessage +#define mt32emu_play_short_message_at i.v0->playShortMessageAt +#define mt32emu_play_msg i.v0->playMsg +#define mt32emu_play_sysex i.v0->playSysex +#define mt32emu_play_msg_at i.v0->playMsgAt +#define mt32emu_play_sysex_at i.v0->playSysexAt +#define mt32emu_play_msg_now i.v0->playMsgNow +#define mt32emu_play_msg_on_part i.v0->playMsgOnPart +#define mt32emu_play_sysex_now i.v0->playSysexNow +#define mt32emu_write_sysex i.v0->writeSysex +#define mt32emu_set_reverb_enabled i.v0->setReverbEnabled +#define mt32emu_is_reverb_enabled i.v0->isReverbEnabled +#define mt32emu_set_reverb_overridden i.v0->setReverbOverridden +#define mt32emu_is_reverb_overridden i.v0->isReverbOverridden +#define mt32emu_set_reverb_compatibility_mode i.v0->setReverbCompatibilityMode +#define mt32emu_is_mt32_reverb_compatibility_mode i.v0->isMT32ReverbCompatibilityMode +#define mt32emu_is_default_reverb_mt32_compatible i.v0->isDefaultReverbMT32Compatible +#define mt32emu_set_dac_input_mode i.v0->setDACInputMode +#define mt32emu_get_dac_input_mode i.v0->getDACInputMode +#define mt32emu_set_midi_delay_mode i.v0->setMIDIDelayMode +#define mt32emu_get_midi_delay_mode i.v0->getMIDIDelayMode +#define mt32emu_set_output_gain i.v0->setOutputGain +#define mt32emu_get_output_gain i.v0->getOutputGain +#define mt32emu_set_reverb_output_gain i.v0->setReverbOutputGain +#define mt32emu_get_reverb_output_gain i.v0->getReverbOutputGain +#define mt32emu_set_reversed_stereo_enabled i.v0->setReversedStereoEnabled +#define mt32emu_is_reversed_stereo_enabled i.v0->isReversedStereoEnabled +#define mt32emu_render_bit16s i.v0->renderBit16s +#define mt32emu_render_float i.v0->renderFloat +#define mt32emu_render_bit16s_streams i.v0->renderBit16sStreams +#define mt32emu_render_float_streams i.v0->renderFloatStreams +#define mt32emu_has_active_partials i.v0->hasActivePartials +#define mt32emu_is_active i.v0->isActive +#define mt32emu_get_partial_count i.v0->getPartialCount +#define mt32emu_get_part_states i.v0->getPartStates +#define mt32emu_get_partial_states i.v0->getPartialStates +#define mt32emu_get_playing_notes i.v0->getPlayingNotes +#define mt32emu_get_patch_name i.v0->getPatchName +#define mt32emu_read_memory i.v0->readMemory + +#else // #if MT32EMU_API_TYPE == 2 + +#include "c_interface.h" + +#endif // #if MT32EMU_API_TYPE == 2 + +namespace MT32Emu { + +namespace CppInterfaceImpl { + +static const mt32emu_report_handler_i NULL_REPORT_HANDLER = { NULL }; +static mt32emu_report_handler_i getReportHandlerThunk(); +static mt32emu_midi_receiver_i getMidiReceiverThunk(); + +} + +/* + * The classes below correspond to the interfaces defined in c_types.h and provided for convenience when using C++. + * The approach used makes no assumption of any internal class data memory layout, since the C++ standard does not + * provide any detail in this area and leaves it up to the implementation. Therefore, this way portability is guaranteed, + * despite the implementation may be a little inefficient. + * See c_types.h and c_interface.h for description of the corresponding interface methods. + */ + +// Defines the interface for handling reported events. +// Corresponds to the current version of mt32emu_report_handler_i interface. +class IReportHandler { +public: + virtual void printDebug(const char *fmt, va_list list) = 0; + virtual void onErrorControlROM() = 0; + virtual void onErrorPCMROM() = 0; + virtual void showLCDMessage(const char *message) = 0; + virtual void onMIDIMessagePlayed() = 0; + virtual bool onMIDIQueueOverflow() = 0; + virtual void onMIDISystemRealtime(Bit8u system_realtime) = 0; + virtual void onDeviceReset() = 0; + virtual void onDeviceReconfig() = 0; + virtual void onNewReverbMode(Bit8u mode) = 0; + virtual void onNewReverbTime(Bit8u time) = 0; + virtual void onNewReverbLevel(Bit8u level) = 0; + virtual void onPolyStateChanged(Bit8u part_num) = 0; + virtual void onProgramChanged(Bit8u part_num, const char *sound_group_name, const char *patch_name) = 0; + +protected: + ~IReportHandler() {} +}; + +// Defines the interface for receiving MIDI messages generated by MIDI stream parser. +// Corresponds to the current version of mt32emu_midi_receiver_i interface. +class IMidiReceiver { +public: + virtual void handleShortMessage(const Bit32u message) = 0; + virtual void handleSysex(const Bit8u stream[], const Bit32u length) = 0; + virtual void handleSystemRealtimeMessage(const Bit8u realtime) = 0; + +protected: + ~IMidiReceiver() {} +}; + +// Defines all the library services. +// Corresponds to the current version of mt32emu_service_i interface. +class Service { +public: +#if MT32EMU_API_TYPE == 2 + explicit Service(mt32emu_service_i interface, mt32emu_context context = NULL) : i(interface), c(context) {} +#else + explicit Service(mt32emu_context context = NULL) : c(context) {} +#endif + ~Service() { if (c != NULL) mt32emu_free_context(c); } + + // Context-independent methods + +#if MT32EMU_API_TYPE == 2 + mt32emu_service_version getVersionID() { return i.v0->getVersionID(i); } +#endif + mt32emu_report_handler_version getSupportedReportHandlerVersionID() { return mt32emu_get_supported_report_handler_version(); } + mt32emu_midi_receiver_version getSupportedMIDIReceiverVersionID() { return mt32emu_get_supported_midi_receiver_version(); } + + Bit32u getLibraryVersionInt() { return mt32emu_get_library_version_int(); } + const char *getLibraryVersionString() { return mt32emu_get_library_version_string(); } + + Bit32u getStereoOutputSamplerate(const AnalogOutputMode analog_output_mode) { return mt32emu_get_stereo_output_samplerate(static_cast<mt32emu_analog_output_mode>(analog_output_mode)); } + + // Context-dependent methods + + mt32emu_context getContext() { return c; } + void createContext(mt32emu_report_handler_i report_handler = CppInterfaceImpl::NULL_REPORT_HANDLER, void *instance_data = NULL) { freeContext(); c = mt32emu_create_context(report_handler, instance_data); } + void createContext(IReportHandler &report_handler) { createContext(CppInterfaceImpl::getReportHandlerThunk(), &report_handler); } + void freeContext() { if (c != NULL) { mt32emu_free_context(c); c = NULL; } } + mt32emu_return_code addROMData(const Bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest = NULL) { return mt32emu_add_rom_data(c, data, data_size, sha1_digest); } + mt32emu_return_code addROMFile(const char *filename) { return mt32emu_add_rom_file(c, filename); } + void getROMInfo(mt32emu_rom_info *rom_info) { mt32emu_get_rom_info(c, rom_info); } + void setPartialCount(const Bit32u partial_count) { mt32emu_set_partial_count(c, partial_count); } + void setAnalogOutputMode(const AnalogOutputMode analog_output_mode) { mt32emu_set_analog_output_mode(c, static_cast<mt32emu_analog_output_mode>(analog_output_mode)); } + mt32emu_return_code openSynth() { return mt32emu_open_synth(c); } + void closeSynth() { mt32emu_close_synth(c); } + bool isOpen() { return mt32emu_is_open(c) != MT32EMU_BOOL_FALSE; } + Bit32u getActualStereoOutputSamplerate() { return mt32emu_get_actual_stereo_output_samplerate(c); } + void flushMIDIQueue() { mt32emu_flush_midi_queue(c); } + Bit32u setMIDIEventQueueSize(const Bit32u queue_size) { return mt32emu_set_midi_event_queue_size(c, queue_size); } + void setMIDIReceiver(mt32emu_midi_receiver_i midi_receiver, void *instance_data) { mt32emu_set_midi_receiver(c, midi_receiver, instance_data); } + void setMIDIReceiver(IMidiReceiver &midi_receiver) { setMIDIReceiver(CppInterfaceImpl::getMidiReceiverThunk(), &midi_receiver); } + + void parseStream(const Bit8u *stream, Bit32u length) { mt32emu_parse_stream(c, stream, length); } + void parseStream_At(const Bit8u *stream, Bit32u length, Bit32u timestamp) { mt32emu_parse_stream_at(c, stream, length, timestamp); } + void playShortMessage(Bit32u message) { mt32emu_play_short_message(c, message); } + void playShortMessageAt(Bit32u message, Bit32u timestamp) { mt32emu_play_short_message_at(c, message, timestamp); } + mt32emu_return_code playMsg(Bit32u msg) { return mt32emu_play_msg(c, msg); } + mt32emu_return_code playSysex(const Bit8u *sysex, Bit32u len) { return mt32emu_play_sysex(c, sysex, len); } + mt32emu_return_code playMsgAt(Bit32u msg, Bit32u timestamp) { return mt32emu_play_msg_at(c, msg, timestamp); } + mt32emu_return_code playSysexAt(const Bit8u *sysex, Bit32u len, Bit32u timestamp) { return mt32emu_play_sysex_at(c, sysex, len, timestamp); } + + void playMsgNow(Bit32u msg) { mt32emu_play_msg_now(c, msg); } + void playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity) { mt32emu_play_msg_on_part(c, part, code, note, velocity); } + void playSysexNow(const Bit8u *sysex, Bit32u len) { mt32emu_play_sysex_now(c, sysex, len); } + void writeSysex(Bit8u channel, const Bit8u *sysex, Bit32u len) { mt32emu_write_sysex(c, channel, sysex, len); } + + void setReverbEnabled(const bool reverb_enabled) { mt32emu_set_reverb_enabled(c, reverb_enabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } + bool isReverbEnabled() { return mt32emu_is_reverb_enabled(c) != MT32EMU_BOOL_FALSE; } + void setReverbOverridden(const bool reverb_overridden) { mt32emu_set_reverb_overridden(c, reverb_overridden ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } + bool isReverbOverridden() { return mt32emu_is_reverb_overridden(c) != MT32EMU_BOOL_FALSE; } + void setReverbCompatibilityMode(const bool mt32_compatible_mode) { mt32emu_set_reverb_compatibility_mode(c, mt32_compatible_mode ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } + bool isMT32ReverbCompatibilityMode() { return mt32emu_is_mt32_reverb_compatibility_mode(c) != MT32EMU_BOOL_FALSE; } + bool isDefaultReverbMT32Compatible() { return mt32emu_is_default_reverb_mt32_compatible(c) != MT32EMU_BOOL_FALSE; } + + void setDACInputMode(const DACInputMode mode) { mt32emu_set_dac_input_mode(c, static_cast<mt32emu_dac_input_mode>(mode)); } + DACInputMode getDACInputMode() { return static_cast<DACInputMode>(mt32emu_get_dac_input_mode(c)); } + + void setMIDIDelayMode(const MIDIDelayMode mode) { mt32emu_set_midi_delay_mode(c, static_cast<mt32emu_midi_delay_mode>(mode)); } + MIDIDelayMode getMIDIDelayMode() { return static_cast<MIDIDelayMode>(mt32emu_get_midi_delay_mode(c)); } + + void setOutputGain(float gain) { mt32emu_set_output_gain(c, gain); } + float getOutputGain() { return mt32emu_get_output_gain(c); } + void setReverbOutputGain(float gain) { mt32emu_set_reverb_output_gain(c, gain); } + float getReverbOutputGain() { return mt32emu_get_reverb_output_gain(c); } + + void setReversedStereoEnabled(const bool enabled) { mt32emu_set_reversed_stereo_enabled(c, enabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } + bool isReversedStereoEnabled() { return mt32emu_is_reversed_stereo_enabled(c) != MT32EMU_BOOL_FALSE; } + + void renderBit16s(Bit16s *stream, Bit32u len) { mt32emu_render_bit16s(c, stream, len); } + void renderFloat(float *stream, Bit32u len) { mt32emu_render_float(c, stream, len); } + void renderBit16sStreams(const mt32emu_dac_output_bit16s_streams *streams, Bit32u len) { mt32emu_render_bit16s_streams(c, streams, len); } + void renderFloatStreams(const mt32emu_dac_output_float_streams *streams, Bit32u len) { mt32emu_render_float_streams(c, streams, len); } + + bool hasActivePartials() { return mt32emu_has_active_partials(c) != MT32EMU_BOOL_FALSE; } + bool isActive() { return mt32emu_is_active(c) != MT32EMU_BOOL_FALSE; } + Bit32u getPartialCount() { return mt32emu_get_partial_count(c); } + Bit32u getPartStates() { return mt32emu_get_part_states(c); } + void getPartialStates(Bit8u *partial_states) { mt32emu_get_partial_states(c, partial_states); } + Bit32u getPlayingNotes(Bit8u part_number, Bit8u *keys, Bit8u *velocities) { return mt32emu_get_playing_notes(c, part_number, keys, velocities); } + const char *getPatchName(Bit8u part_number) { return mt32emu_get_patch_name(c, part_number); } + void readMemory(Bit32u addr, Bit32u len, Bit8u *data) { mt32emu_read_memory(c, addr, len, data); } + +private: +#if MT32EMU_API_TYPE == 2 + const mt32emu_service_i i; +#endif + mt32emu_context c; +}; + +namespace CppInterfaceImpl { + +static mt32emu_report_handler_version getReportHandlerVersionID(mt32emu_report_handler_i) { + return MT32EMU_REPORT_HANDLER_VERSION_CURRENT; +} + +static void printDebug(void *instance_data, const char *fmt, va_list list) { + ((IReportHandler *)instance_data)->printDebug(fmt, list); +} + +static void onErrorControlROM(void *instance_data) { + ((IReportHandler *)instance_data)->onErrorControlROM(); +} + +static void onErrorPCMROM(void *instance_data) { + ((IReportHandler *)instance_data)->onErrorPCMROM(); +} + +static void showLCDMessage(void *instance_data, const char *message) { + ((IReportHandler *)instance_data)->showLCDMessage(message); +} + +static void onMIDIMessagePlayed(void *instance_data) { + ((IReportHandler *)instance_data)->onMIDIMessagePlayed(); +} + +static mt32emu_boolean onMIDIQueueOverflow(void *instance_data) { + return ((IReportHandler *)instance_data)->onMIDIQueueOverflow() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +static void onMIDISystemRealtime(void *instance_data, mt32emu_bit8u system_realtime) { + ((IReportHandler *)instance_data)->onMIDISystemRealtime(system_realtime); +} + +static void onDeviceReset(void *instance_data) { + ((IReportHandler *)instance_data)->onDeviceReset(); +} + +static void onDeviceReconfig(void *instance_data) { + ((IReportHandler *)instance_data)->onDeviceReconfig(); +} + +static void onNewReverbMode(void *instance_data, mt32emu_bit8u mode) { + ((IReportHandler *)instance_data)->onNewReverbMode(mode); +} + +static void onNewReverbTime(void *instance_data, mt32emu_bit8u time) { + ((IReportHandler *)instance_data)->onNewReverbTime(time); +} + +static void onNewReverbLevel(void *instance_data, mt32emu_bit8u level) { + ((IReportHandler *)instance_data)->onNewReverbLevel(level); +} + +static void onPolyStateChanged(void *instance_data, mt32emu_bit8u part_num) { + ((IReportHandler *)instance_data)->onPolyStateChanged(part_num); +} + +static void onProgramChanged(void *instance_data, mt32emu_bit8u part_num, const char *sound_group_name, const char *patch_name) { + ((IReportHandler *)instance_data)->onProgramChanged(part_num, sound_group_name, patch_name); +} + +static mt32emu_report_handler_i getReportHandlerThunk() { + static const mt32emu_report_handler_i_v0 REPORT_HANDLER_V0_THUNK = { + getReportHandlerVersionID, + printDebug, + onErrorControlROM, + onErrorPCMROM, + showLCDMessage, + onMIDIMessagePlayed, + onMIDIQueueOverflow, + onMIDISystemRealtime, + onDeviceReset, + onDeviceReconfig, + onNewReverbMode, + onNewReverbTime, + onNewReverbLevel, + onPolyStateChanged, + onProgramChanged + }; + + static const mt32emu_report_handler_i REPORT_HANDLER_THUNK = { &REPORT_HANDLER_V0_THUNK }; + + return REPORT_HANDLER_THUNK; +} + +static mt32emu_midi_receiver_version getMidiReceiverVersionID(mt32emu_midi_receiver_i) { + return MT32EMU_MIDI_RECEIVER_VERSION_CURRENT; +} + +static void handleShortMessage(void *instance_data, const mt32emu_bit32u message) { + ((IMidiReceiver *)instance_data)->handleShortMessage(message); +} + +static void handleSysex(void *instance_data, const mt32emu_bit8u stream[], const mt32emu_bit32u length) { + ((IMidiReceiver *)instance_data)->handleSysex(stream, length); +} + +static void handleSystemRealtimeMessage(void *instance_data, const mt32emu_bit8u realtime) { + ((IMidiReceiver *)instance_data)->handleSystemRealtimeMessage(realtime); +} + +static mt32emu_midi_receiver_i getMidiReceiverThunk() { + static const mt32emu_midi_receiver_i_v0 MIDI_RECEIVER_V0_THUNK = { + getMidiReceiverVersionID, + handleShortMessage, + handleSysex, + handleSystemRealtimeMessage + }; + + static const mt32emu_midi_receiver_i MIDI_RECEIVER_THUNK = { &MIDI_RECEIVER_V0_THUNK }; + + return MIDI_RECEIVER_THUNK; +} + +} // namespace CppInterfaceImpl + +} // namespace MT32Emu + +#if MT32EMU_API_TYPE == 2 + +#undef mt32emu_get_supported_report_handler_version +#undef mt32emu_get_supported_midi_receiver_version +#undef mt32emu_get_library_version_int +#undef mt32emu_get_library_version_string +#undef mt32emu_get_stereo_output_samplerate +#undef mt32emu_create_context +#undef mt32emu_free_context +#undef mt32emu_add_rom_data +#undef mt32emu_add_rom_file +#undef mt32emu_get_rom_info +#undef mt32emu_set_partial_count +#undef mt32emu_set_analog_output_mode +#undef mt32emu_open_synth +#undef mt32emu_close_synth +#undef mt32emu_is_open +#undef mt32emu_get_actual_stereo_output_samplerate +#undef mt32emu_flush_midi_queue +#undef mt32emu_set_midi_event_queue_size +#undef mt32emu_set_midi_receiver +#undef mt32emu_parse_stream +#undef mt32emu_parse_stream_at +#undef mt32emu_play_short_message +#undef mt32emu_play_short_message_at +#undef mt32emu_play_msg +#undef mt32emu_play_sysex +#undef mt32emu_play_msg_at +#undef mt32emu_play_sysex_at +#undef mt32emu_play_msg_now +#undef mt32emu_play_msg_on_part +#undef mt32emu_play_sysex_now +#undef mt32emu_write_sysex +#undef mt32emu_set_reverb_enabled +#undef mt32emu_is_reverb_enabled +#undef mt32emu_set_reverb_overridden +#undef mt32emu_is_reverb_overridden +#undef mt32emu_set_reverb_compatibility_mode +#undef mt32emu_is_mt32_reverb_compatibility_mode +#undef mt32emu_is_default_reverb_mt32_compatible +#undef mt32emu_set_dac_input_mode +#undef mt32emu_get_dac_input_mode +#undef mt32emu_set_midi_delay_mode +#undef mt32emu_get_midi_delay_mode +#undef mt32emu_set_output_gain +#undef mt32emu_get_output_gain +#undef mt32emu_set_reverb_output_gain +#undef mt32emu_get_reverb_output_gain +#undef mt32emu_set_reversed_stereo_enabled +#undef mt32emu_is_reversed_stereo_enabled +#undef mt32emu_render_bit16s +#undef mt32emu_render_float +#undef mt32emu_render_bit16s_streams +#undef mt32emu_render_float_streams +#undef mt32emu_has_active_partials +#undef mt32emu_is_active +#undef mt32emu_get_partial_count +#undef mt32emu_get_part_states +#undef mt32emu_get_partial_states +#undef mt32emu_get_playing_notes +#undef mt32emu_get_patch_name +#undef mt32emu_read_memory + +#endif // #if MT32EMU_API_TYPE == 2 + +#endif /* #ifndef MT32EMU_CPP_INTERFACE_H */ diff --git a/audio/softsynth/mt32/config.h b/audio/softsynth/mt32/config.h new file mode 100644 index 0000000000..779cc4d4b3 --- /dev/null +++ b/audio/softsynth/mt32/config.h @@ -0,0 +1,28 @@ +/* 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/>. + */ + +#ifndef MT32EMU_CONFIG_H +#define MT32EMU_CONFIG_H + +#define MT32EMU_VERSION "2.0.0" +#define MT32EMU_VERSION_MAJOR 2 +#define MT32EMU_VERSION_MINOR 0 +#define MT32EMU_VERSION_PATCH 0 + +#define MT32EMU_EXPORTS_TYPE 3 + +#endif diff --git a/audio/softsynth/mt32/globals.h b/audio/softsynth/mt32/globals.h new file mode 100755 index 0000000000..49a5ecc250 --- /dev/null +++ b/audio/softsynth/mt32/globals.h @@ -0,0 +1,119 @@ +/* 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/>. + */ + +#ifndef MT32EMU_GLOBALS_H +#define MT32EMU_GLOBALS_H + +#include "config.h" + +/* Support for compiling shared library. */ +#ifdef MT32EMU_SHARED +#if defined _WIN32 || defined __CYGWIN__ +#ifdef _MSC_VER +#ifdef mt32emu_EXPORTS +#define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllexport) +#else /* #ifdef mt32emu_EXPORTS */ +#define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllimport) +#endif /* #ifdef mt32emu_EXPORTS */ +#else /* #ifdef _MSC_VER */ +#ifdef mt32emu_EXPORTS +#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllexport)) +#else /* #ifdef mt32emu_EXPORTS */ +#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllimport)) +#endif /* #ifdef mt32emu_EXPORTS */ +#endif /* #ifdef _MSC_VER */ +#else /* #if defined _WIN32 || defined __CYGWIN__ */ +#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((visibility("default"))) +#endif /* #if defined _WIN32 || defined __CYGWIN__ */ +#else /* #ifdef MT32EMU_SHARED */ +#define MT32EMU_EXPORT_ATTRIBUTE +#endif /* #ifdef MT32EMU_SHARED */ + +#if MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2 +#define MT32EMU_EXPORT +#else +#define MT32EMU_EXPORT MT32EMU_EXPORT_ATTRIBUTE +#endif + +/* Useful constants */ + +/* Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent. + * In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator, + * except the emulation of analogue path. + * The output from the synth is supposed to be resampled externally in order to convert to the desired sample rate. + */ +#define MT32EMU_SAMPLE_RATE 32000 + +/* The default value for the maximum number of partials playing simultaneously. */ +#define MT32EMU_DEFAULT_MAX_PARTIALS 32 + +/* The higher this number, the more memory will be used, but the more samples can be processed in one run - + * various parts of sample generation can be processed more efficiently in a single run. + * A run's maximum length is that given to Synth::render(), so giving a value here higher than render() is ever + * called with will give no gain (but simply waste the memory). + * Note that this value does *not* in any way impose limitations on the length given to render(), and has no effect + * on the generated audio. + * This value must be >= 1. + */ +#define MT32EMU_MAX_SAMPLES_PER_RUN 4096 + +/* The default size of the internal MIDI event queue. + * It holds the incoming MIDI events before the rendering engine actually processes them. + * The main goal is to fairly emulate the real hardware behaviour which obviously + * uses an internal MIDI event queue to gather incoming data as well as the delays + * introduced by transferring data via the MIDI interface. + * This also facilitates building of an external rendering loop + * as the queue stores timestamped MIDI events. + */ +#define MT32EMU_DEFAULT_MIDI_EVENT_QUEUE_SIZE 1024 + +/* Maximum allowed size of MIDI parser input stream buffer. + * Should suffice for any reasonable bulk dump SysEx, as the h/w units have only 32K of RAM onboard. + */ +#define MT32EMU_MAX_STREAM_BUFFER_SIZE 32768 + +/* This should correspond to the MIDI buffer size used in real h/w devices. + * CM-32L control ROM seems using 1000 bytes, old MT-32 isn't confirmed by now. + */ +#define MT32EMU_SYSEX_BUFFER_SIZE 1000 + +#if defined(__cplusplus) && MT32EMU_API_TYPE != 1 + +namespace MT32Emu +{ +const unsigned int SAMPLE_RATE = MT32EMU_SAMPLE_RATE; +#undef MT32EMU_SAMPLE_RATE + +const unsigned int DEFAULT_MAX_PARTIALS = MT32EMU_DEFAULT_MAX_PARTIALS; +#undef MT32EMU_DEFAULT_MAX_PARTIALS + +const unsigned int MAX_SAMPLES_PER_RUN = MT32EMU_MAX_SAMPLES_PER_RUN; +#undef MT32EMU_MAX_SAMPLES_PER_RUN + +const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = MT32EMU_DEFAULT_MIDI_EVENT_QUEUE_SIZE; +#undef MT32EMU_DEFAULT_MIDI_EVENT_QUEUE_SIZE + +const unsigned int MAX_STREAM_BUFFER_SIZE = MT32EMU_MAX_STREAM_BUFFER_SIZE; +#undef MT32EMU_MAX_STREAM_BUFFER_SIZE + +const unsigned int SYSEX_BUFFER_SIZE = MT32EMU_SYSEX_BUFFER_SIZE; +#undef MT32EMU_SYSEX_BUFFER_SIZE +} + +#endif /* #if defined(__cplusplus) && MT32EMU_API_TYPE != 1 */ + +#endif /* #ifndef MT32EMU_GLOBALS_H */ diff --git a/audio/softsynth/mt32/internals.h b/audio/softsynth/mt32/internals.h index ef56819a42..c64ba39212 100644..100755 --- a/audio/softsynth/mt32/internals.h +++ b/audio/softsynth/mt32/internals.h @@ -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 @@ -18,66 +18,111 @@ #ifndef MT32EMU_INTERNALS_H #define MT32EMU_INTERNALS_H +#include "Types.h" + // Debugging // 0: Standard debug output is not stamped with the rendered sample count // 1: Standard debug output is stamped with the rendered sample count // NOTE: The "samplestamp" corresponds to the end of the last completed rendering run. // This is important to bear in mind for debug output that occurs during a run. +#ifndef MT32EMU_DEBUG_SAMPLESTAMPS #define MT32EMU_DEBUG_SAMPLESTAMPS 0 +#endif // 0: No debug output for initialisation progress // 1: Debug output for initialisation progress +#ifndef MT32EMU_MONITOR_INIT #define MT32EMU_MONITOR_INIT 0 +#endif // 0: No debug output for MIDI events // 1: Debug output for weird MIDI events +#ifndef MT32EMU_MONITOR_MIDI #define MT32EMU_MONITOR_MIDI 0 +#endif // 0: No debug output for note on/off // 1: Basic debug output for note on/off // 2: Comprehensive debug output for note on/off +#ifndef MT32EMU_MONITOR_INSTRUMENTS #define MT32EMU_MONITOR_INSTRUMENTS 0 +#endif // 0: No debug output for partial allocations // 1: Show partial stats when an allocation fails // 2: Show partial stats with every new poly // 3: Show individual partial allocations/deactivations +#ifndef MT32EMU_MONITOR_PARTIALS #define MT32EMU_MONITOR_PARTIALS 0 +#endif // 0: No debug output for sysex // 1: Basic debug output for sysex +#ifndef MT32EMU_MONITOR_SYSEX #define MT32EMU_MONITOR_SYSEX 0 +#endif // 0: No debug output for sysex writes to the timbre areas // 1: Debug output with the name and location of newly-written timbres // 2: Complete dump of timbre parameters for newly-written timbres +#ifndef MT32EMU_MONITOR_TIMBRES #define MT32EMU_MONITOR_TIMBRES 0 +#endif // 0: No TVA/TVF-related debug output. // 1: Shows changes to TVA/TVF target, increment and phase. +#ifndef MT32EMU_MONITOR_TVA #define MT32EMU_MONITOR_TVA 0 +#endif +#ifndef MT32EMU_MONITOR_TVF #define MT32EMU_MONITOR_TVF 0 +#endif // Configuration +// 0: Use 16-bit signed samples and refined wave generator based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed. +// 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise. +#ifndef MT32EMU_USE_FLOAT_SAMPLES +#define MT32EMU_USE_FLOAT_SAMPLES 0 +#endif + // If non-zero, deletes reverb buffers that are not in use to save memory. // If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path. +#ifndef MT32EMU_REDUCE_REVERB_MEMORY #define MT32EMU_REDUCE_REVERB_MEMORY 1 +#endif // 0: Maximum speed at the cost of a bit lower emulation accuracy. // 1: Maximum achievable emulation accuracy. +#ifndef MT32EMU_BOSS_REVERB_PRECISE_MODE #define MT32EMU_BOSS_REVERB_PRECISE_MODE 0 +#endif -#include "Structures.h" -#include "Tables.h" -#include "Poly.h" -#include "LA32Ramp.h" -#include "LA32WaveGenerator.h" -#include "TVA.h" -#include "TVP.h" -#include "TVF.h" -#include "Partial.h" -#include "Part.h" +namespace MT32Emu { +enum PolyState { + POLY_Playing, + POLY_Held, // This marks keys that have been released on the keyboard, but are being held by the pedal + POLY_Releasing, + POLY_Inactive +}; + +enum ReverbMode { + REVERB_MODE_ROOM, + REVERB_MODE_HALL, + REVERB_MODE_PLATE, + REVERB_MODE_TAP_DELAY +}; + +#if MT32EMU_USE_FLOAT_SAMPLES +typedef float Sample; +typedef float SampleEx; +#else +typedef Bit16s Sample; +typedef Bit32s SampleEx; #endif + +} + +#endif // #ifndef MT32EMU_INTERNALS_H diff --git a/audio/softsynth/mt32/mmath.h b/audio/softsynth/mt32/mmath.h index 602242e74f..f233bedcbb 100644..100755 --- a/audio/softsynth/mt32/mmath.h +++ b/audio/softsynth/mt32/mmath.h @@ -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 @@ -18,12 +18,7 @@ #ifndef MT32EMU_MMATH_H #define MT32EMU_MMATH_H -#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y))) -#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y))) -#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point) -#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point)) - -#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x)) +#include <cmath> namespace MT32Emu { @@ -46,7 +41,7 @@ static inline float EXPF(float x) { static inline float EXP2F(float x) { #ifdef __APPLE__ // on OSX exp2f() is 1.59 times faster than "exp() and the multiplication with FLOAT_LN_2" - return exp2(x); + return exp2f(x); #else return exp(FLOAT_LN_2 * x); #endif @@ -68,6 +63,6 @@ static inline float LOG10F(float x) { return log10(x); } -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_MMATH_H diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk index f966da8d08..1a27492c2a 100644 --- a/audio/softsynth/mt32/module.mk +++ b/audio/softsynth/mt32/module.mk @@ -3,8 +3,11 @@ MODULE := audio/softsynth/mt32 MODULE_OBJS := \ Analog.o \ BReverbModel.o \ + File.o \ + FileStream.o \ LA32Ramp.o \ LA32WaveGenerator.o \ + MidiStreamParser.o \ Part.o \ Partial.o \ PartialManager.o \ @@ -14,7 +17,9 @@ MODULE_OBJS := \ Tables.o \ TVA.o \ TVF.o \ - TVP.o + TVP.o \ + sha1/sha1.o \ + c_interface/c_interface.o # Include common rules include $(srcdir)/rules.mk diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h index 1574c08f0d..9f2b058250 100644..100755 --- a/audio/softsynth/mt32/mt32emu.h +++ b/audio/softsynth/mt32/mt32emu.h @@ -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 @@ -18,44 +18,66 @@ #ifndef MT32EMU_MT32EMU_H #define MT32EMU_MT32EMU_H -// Configuration - -// 0: Use 16-bit signed samples and refined wave generator based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed. -// 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise. -#define MT32EMU_USE_FLOAT_SAMPLES 0 - -namespace MT32Emu -{ -// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent. -// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator, -// except the emulation of analogue path. -// The output from the synth is supposed to be resampled externally in order to convert to the desired sample rate. -const unsigned int SAMPLE_RATE = 32000; - -// The default value for the maximum number of partials playing simultaneously. -const unsigned int DEFAULT_MAX_PARTIALS = 32; - -// The higher this number, the more memory will be used, but the more samples can be processed in one run - -// various parts of sample generation can be processed more efficiently in a single run. -// A run's maximum length is that given to Synth::render(), so giving a value here higher than render() is ever -// called with will give no gain (but simply waste the memory). -// Note that this value does *not* in any way impose limitations on the length given to render(), and has no effect -// on the generated audio. -// This value must be >= 1. -const unsigned int MAX_SAMPLES_PER_RUN = 4096; - -// The default size of the internal MIDI event queue. -// It holds the incoming MIDI events before the rendering engine actually processes them. -// The main goal is to fairly emulate the real hardware behaviour which obviously -// uses an internal MIDI event queue to gather incoming data as well as the delays -// introduced by transferring data via the MIDI interface. -// This also facilitates building of an external rendering loop -// as the queue stores timestamped MIDI events. -const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = 1024; -} +#include "config.h" + +/* API Configuration */ + +/* 0: Use full-featured C++ API. Well suitable when the library is to be linked statically. + * When the library is shared, ABI compatibility may be an issue. Therefore, it should + * only be used within a project comprising of several modules to share the library code. + * 1: Use C-compatible API. Make the library looks as a regular C library with well-defined ABI. + * This is also crucial when the library is to be linked with modules in a different + * language, either statically or dynamically. + * 2: Use plugin-like API via C-interface wrapped in a C++ class. This is mainly intended + * for a shared library being dynamically loaded in run-time. To get access to all the library + * services, a client application only needs to bind with a single factory function. + * 3: Use optimised C++ API compatible with the plugin API (type 2). The facade class also wraps + * the C functions but they are invoked directly. This enables the compiler to generate better + * code for the library when linked statically yet being consistent with the plugin-like API. + */ + +#ifdef MT32EMU_API_TYPE +#if MT32EMU_API_TYPE == 0 && (MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2) +#error Incompatible setting MT32EMU_API_TYPE=0 +#elif MT32EMU_API_TYPE == 1 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2) +#error Incompatible setting MT32EMU_API_TYPE=1 +#elif MT32EMU_API_TYPE == 2 && (MT32EMU_EXPORTS_TYPE == 0) +#error Incompatible setting MT32EMU_API_TYPE=2 +#elif MT32EMU_API_TYPE == 3 && (MT32EMU_EXPORTS_TYPE == 0) +#error Incompatible setting MT32EMU_API_TYPE=3 +#endif +#else /* #ifdef MT32EMU_API_TYPE */ +#if 0 < MT32EMU_EXPORTS_TYPE && MT32EMU_EXPORTS_TYPE < 3 +#define MT32EMU_API_TYPE MT32EMU_EXPORTS_TYPE +#else +#define MT32EMU_API_TYPE 0 +#endif +#endif /* #ifdef MT32EMU_API_TYPE */ + +/* MT32EMU_SHARED should be defined when building shared library, especially for Windows platforms. */ +/* +#define MT32EMU_SHARED +*/ + +#include "globals.h" + +#if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 + +#include "c_interface/c_interface.h" + +#elif MT32EMU_API_TYPE == 2 || MT32EMU_API_TYPE == 3 + +#include "c_interface/cpp_interface.h" + +#else /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */ #include "Types.h" +#include "File.h" +#include "FileStream.h" #include "ROMInfo.h" #include "Synth.h" +#include "MidiStreamParser.h" -#endif +#endif /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */ + +#endif /* #ifndef MT32EMU_MT32EMU_H */ diff --git a/audio/softsynth/mt32/sha1/sha1.cpp b/audio/softsynth/mt32/sha1/sha1.cpp new file mode 100755 index 0000000000..9b91cd9f29 --- /dev/null +++ b/audio/softsynth/mt32/sha1/sha1.cpp @@ -0,0 +1,185 @@ +/* + Copyright (c) 2011, Micael Hildenborg + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Micael Hildenborg nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + Contributors: + Gustav + Several members in the gamedev.se forum. + Gregory Petrosyan + */ + +#include "sha1.h" + +namespace sha1 +{ + namespace // local + { + // Rotate an integer value to left. + inline unsigned int rol(const unsigned int value, + const unsigned int steps) + { + return ((value << steps) | (value >> (32 - steps))); + } + + // Sets the first 16 integers in the buffert to zero. + // Used for clearing the W buffert. + inline void clearWBuffert(unsigned int* buffert) + { + for (int pos = 16; --pos >= 0;) + { + buffert[pos] = 0; + } + } + + void innerHash(unsigned int* result, unsigned int* w) + { + unsigned int a = result[0]; + unsigned int b = result[1]; + unsigned int c = result[2]; + unsigned int d = result[3]; + unsigned int e = result[4]; + + int round = 0; + + #define sha1macro(func,val) \ + { \ + const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \ + e = d; \ + d = c; \ + c = rol(b, 30); \ + b = a; \ + a = t; \ + } + + while (round < 16) + { + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 20) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 40) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0x6ed9eba1) + ++round; + } + while (round < 60) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc) + ++round; + } + while (round < 80) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0xca62c1d6) + ++round; + } + + #undef sha1macro + + result[0] += a; + result[1] += b; + result[2] += c; + result[3] += d; + result[4] += e; + } + } // namespace + + void calc(const void* src, const int bytelength, unsigned char* hash) + { + // Init the result array. + unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 }; + + // Cast the void src pointer to be the byte array we can work with. + const unsigned char* sarray = static_cast<const unsigned char*>(src); + + // The reusable round buffer + unsigned int w[80]; + + // Loop through all complete 64byte blocks. + const int endOfFullBlocks = bytelength - 64; + int endCurrentBlock; + int currentBlock = 0; + + while (currentBlock <= endOfFullBlocks) + { + endCurrentBlock = currentBlock + 64; + + // Init the round buffer with the 64 byte block data. + for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) + { + // This line will swap endian on big endian and keep endian on little endian. + w[roundPos++] = static_cast<unsigned int>(sarray[currentBlock + 3]) + | (static_cast<unsigned int>(sarray[currentBlock + 2]) << 8) + | (static_cast<unsigned int>(sarray[currentBlock + 1]) << 16) + | (static_cast<unsigned int>(sarray[currentBlock]) << 24); + } + innerHash(result, w); + } + + // Handle the last and not full 64 byte block if existing. + endCurrentBlock = bytelength - currentBlock; + clearWBuffert(w); + int lastBlockBytes = 0; + for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes) + { + w[lastBlockBytes >> 2] |= static_cast<unsigned int>(sarray[lastBlockBytes + currentBlock]) << ((3 - (lastBlockBytes & 3)) << 3); + } + w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3); + if (endCurrentBlock >= 56) + { + innerHash(result, w); + clearWBuffert(w); + } + w[15] = bytelength << 3; + innerHash(result, w); + + // Store hash in result pointer, and make sure we get in in the correct order on both endian models. + for (int hashByte = 20; --hashByte >= 0;) + { + hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff; + } + } + + void toHexString(const unsigned char* hash, char* hexstring) + { + const char hexDigits[] = { "0123456789abcdef" }; + + for (int hashByte = 20; --hashByte >= 0;) + { + hexstring[hashByte << 1] = hexDigits[(hash[hashByte] >> 4) & 0xf]; + hexstring[(hashByte << 1) + 1] = hexDigits[hash[hashByte] & 0xf]; + } + hexstring[40] = 0; + } +} // namespace sha1 diff --git a/audio/softsynth/mt32/sha1/sha1.h b/audio/softsynth/mt32/sha1/sha1.h new file mode 100755 index 0000000000..96d8ce4da6 --- /dev/null +++ b/audio/softsynth/mt32/sha1/sha1.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2011, Micael Hildenborg + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Micael Hildenborg nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SHA1_DEFINED +#define SHA1_DEFINED + +namespace sha1 +{ + + /** + @param src points to any kind of data to be hashed. + @param bytelength the number of bytes to hash from the src pointer. + @param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in. + */ + void calc(const void* src, const int bytelength, unsigned char* hash); + + /** + @param hash is 20 bytes of sha1 hash. This is the same data that is the result from the calc function. + @param hexstring should point to a buffer of at least 41 bytes of size for storing the hexadecimal representation of the hash. A zero will be written at position 40, so the buffer will be a valid zero ended string. + */ + void toHexString(const unsigned char* hash, char* hexstring); + +} // namespace sha1 + +#endif // SHA1_DEFINED |