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.
This commit is contained in:
Colin Snover 2016-11-24 09:24:00 -06:00
parent ffea222f5b
commit b8d70d26fa
56 changed files with 4425 additions and 991 deletions

View file

@ -91,3 +91,31 @@ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
Parts of the MT-32 emulator use the following license:
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.

1
NEWS
View file

@ -7,6 +7,7 @@ For a more comprehensive changelog of the latest experimental code, see:
the current or a specified directory.
- Many options in GUI could be applied without closing the dialog.
- On-the-fly language switching was implemented.
- Updated Munt MT-32 emulation code to version 2.0.0.
AGOS:
- Fixed subtitle speed setting in the Hebrew version of Simon the Sorcerer 1.

View file

@ -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"
#include "audio/softsynth/mt32/c_interface/cpp_interface.h"
namespace MT32Emu {
class ReportHandlerScummVM : public ReportHandler {
friend class Synth;
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,75 +179,90 @@ 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();
_outputRate = _service->getActualStereoOutputSamplerate();
MidiDriver_Emulated::open();
_initializing = false;
if (screenFormat.bytesPerPixel > 1)
g_system->fillScreen(screenFormat.RGBToColor(0, 0, 0));
else
g_system->fillScreen(0);
g_system->updateScreen();
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
return 0;
}
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) {

48
audio/softsynth/mt32/Analog.cpp Normal file → Executable file
View file

@ -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

15
audio/softsynth/mt32/Analog.h Normal file → Executable file
View file

@ -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

107
audio/softsynth/mt32/BReverbModel.cpp Normal file → Executable file
View file

@ -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 &currentSettings == &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

42
audio/softsynth/mt32/BReverbModel.h Normal file → Executable file
View file

@ -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 &currentSettings;
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

View file

@ -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) */

73
audio/softsynth/mt32/File.cpp Executable file
View file

@ -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

73
audio/softsynth/mt32/File.h Executable file
View file

@ -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

View file

@ -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

View file

@ -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

41
audio/softsynth/mt32/LA32FloatWaveGenerator.cpp Normal file → Executable file
View file

@ -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

22
audio/softsynth/mt32/LA32FloatWaveGenerator.h Normal file → Executable file
View file

@ -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, its 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

10
audio/softsynth/mt32/LA32Ramp.cpp Normal file → Executable file
View file

@ -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

9
audio/softsynth/mt32/LA32Ramp.h Normal file → Executable file
View file

@ -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

36
audio/softsynth/mt32/LA32WaveGenerator.cpp Normal file → Executable file
View file

@ -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/>.
*/
#if MT32EMU_USE_FLOAT_SAMPLES
#include "LA32FloatWaveGenerator.cpp"
#else
#include <cstddef>
//#include <cmath>
#include "mt32emu.h"
#include "mmath.h"
#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
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

24
audio/softsynth/mt32/LA32WaveGenerator.h Normal file → Executable file
View file

@ -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, its 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

18
audio/softsynth/mt32/MemoryRegion.h Normal file → Executable file
View file

@ -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

10
audio/softsynth/mt32/MidiEventQueue.h Normal file → Executable file
View file

@ -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

View file

@ -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;
}

View file

@ -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

62
audio/softsynth/mt32/Part.cpp Normal file → Executable file
View file

@ -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

16
audio/softsynth/mt32/Part.h Normal file → Executable file
View file

@ -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

27
audio/softsynth/mt32/Partial.cpp Normal file → Executable file
View file

@ -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

26
audio/softsynth/mt32/Partial.h Normal file → Executable file
View file

@ -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

15
audio/softsynth/mt32/PartialManager.cpp Normal file → Executable file
View file

@ -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

15
audio/softsynth/mt32/PartialManager.h Normal file → Executable file
View file

@ -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

12
audio/softsynth/mt32/Poly.cpp Normal file → Executable file
View file

@ -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

19
audio/softsynth/mt32/Poly.h Normal file → Executable file
View file

@ -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

90
audio/softsynth/mt32/ROMInfo.cpp Normal file → Executable file
View file

@ -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

52
audio/softsynth/mt32/ROMInfo.h Normal file → Executable file
View file

@ -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>
#include "globals.h"
#include "File.h"
namespace MT32Emu {
struct ControlROMFeatureSet;
// 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;
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;
MT32EMU_EXPORT File *getFile() const;
MT32EMU_EXPORT const ROMInfo *getROMInfo() const;
};
struct ControlROMFeatureSet {
private:
unsigned int defaultReverbMT32Compatible : 1;
unsigned int oldMT32AnalogLPF : 1;
} // namespace MT32Emu
public:
ControlROMFeatureSet(bool defaultReverbMT32Compatible, bool oldMT32AnalogLPF);
bool isDefaultReverbMT32Compatible() const;
bool isOldMT32AnalogLPF() const;
};
}
#endif
#endif // #ifndef MT32EMU_ROMINFO_H

41
audio/softsynth/mt32/Structures.h Normal file → Executable file
View file

@ -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

741
audio/softsynth/mt32/Synth.cpp Normal file → Executable file

File diff suppressed because it is too large Load diff

384
audio/softsynth/mt32/Synth.h Normal file → Executable file
View file

@ -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 Bit32u CONTROL_ROM_SIZE = 64 * 1024;
const unsigned int CONTROL_ROM_SIZE = 64 * 1024;
class ReportHandler {
friend class Synth;
// 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;
// Binary compatibility helper.
void *reserved;
// **************************** Implementation methods **************************
Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp);
bool isAbortingPoly() const { return abortingPoly != NULL; }
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);
void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len) const;
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 void muteSampleBuffer(Sample *buffer, Bit32u len) {
if (buffer == NULL) return;
static inline float clipSampleEx(float sampleEx) {
return sampleEx;
}
#if MT32EMU_USE_FLOAT_SAMPLES
template <class S>
static inline void muteSampleBuffer(S *buffer, Bit32u len) {
if (buffer == NULL) return;
memset(buffer, 0, len * sizeof(S));
}
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);
// 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 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);
// 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);
void setReverbEnabled(bool reverbEnabled);
bool isReverbEnabled() const;
// 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

44
audio/softsynth/mt32/TVA.cpp Normal file → Executable file
View file

@ -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

16
audio/softsynth/mt32/TVA.h Normal file → Executable file
View file

@ -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

24
audio/softsynth/mt32/TVF.cpp Normal file → Executable file
View file

@ -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

15
audio/softsynth/mt32/TVF.h Normal file → Executable file
View file

@ -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

44
audio/softsynth/mt32/TVP.cpp Normal file → Executable file
View file

@ -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

17
audio/softsynth/mt32/TVP.h Normal file → Executable file
View file

@ -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

30
audio/softsynth/mt32/Tables.cpp Normal file → Executable file
View file

@ -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

11
audio/softsynth/mt32/Tables.h Normal file → Executable file
View file

@ -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

10
audio/softsynth/mt32/Types.h Normal file → Executable file
View file

@ -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

View file

@ -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"

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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

119
audio/softsynth/mt32/globals.h Executable file
View file

@ -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 */

71
audio/softsynth/mt32/internals.h Normal file → Executable file
View file

@ -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
#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"
#endif
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

15
audio/softsynth/mt32/mmath.h Normal file → Executable file
View file

@ -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

View file

@ -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

86
audio/softsynth/mt32/mt32emu.h Normal file → Executable file
View file

@ -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
#include "config.h"
// 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
/* API Configuration */
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;
/* 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.
*/
// The default value for the maximum number of partials playing simultaneously.
const unsigned int DEFAULT_MAX_PARTIALS = 32;
#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 */
// 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;
/* MT32EMU_SHARED should be defined when building shared library, especially for Windows platforms. */
/*
#define MT32EMU_SHARED
*/
// 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 "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 */

View file

@ -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

View file

@ -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