/* 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 * * 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 . */ #ifndef MT32EMU_SYNTH_H #define MT32EMU_SYNTH_H //#include //#include namespace MT32Emu { class Analog; class BReverbModel; class MemoryRegion; class MidiEventQueue; class Part; class Poly; class Partial; class PartialManager; class PatchTempMemoryRegion; class RhythmTempMemoryRegion; class TimbreTempMemoryRegion; class PatchesMemoryRegion; class TimbresMemoryRegion; class SystemMemoryRegion; class DisplayMemoryRegion; class ResetMemoryRegion; 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; const Bit8u SYSEX_MDL_D50 = 0x14; const Bit8u SYSEX_CMD_RQ1 = 0x11; // Request data #1 const Bit8u SYSEX_CMD_DT1 = 0x12; // Data set 1 const Bit8u SYSEX_CMD_WSD = 0x40; // Want to send data const Bit8u SYSEX_CMD_RQD = 0x41; // Request data const Bit8u SYSEX_CMD_DAT = 0x42; // Data set const Bit8u SYSEX_CMD_ACK = 0x43; // Acknowledge const Bit8u SYSEX_CMD_EOD = 0x45; // End of data const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection const int MAX_SYSEX_SIZE = 512; // FIXME: Does this correspond to a real MIDI buffer used in h/w devices? const unsigned int CONTROL_ROM_SIZE = 64 * 1024; class ReportHandler { friend class Synth; 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 virtual void onErrorControlROM() {} virtual void onErrorPCMROM() {} virtual void showLCDMessage(const char *message); virtual void onMIDIMessagePlayed() {} virtual void onDeviceReset() {} virtual void onDeviceReconfig() {} 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 */) {} }; class Synth { friend class Part; friend class RhythmPart; friend class Poly; friend class Partial; friend class PartialManager; friend class Tables; friend class MemoryRegion; friend class TVA; friend class TVF; friend class TVP; private: PatchTempMemoryRegion *patchTempMemoryRegion; RhythmTempMemoryRegion *rhythmTempMemoryRegion; TimbreTempMemoryRegion *timbreTempMemoryRegion; PatchesMemoryRegion *patchesMemoryRegion; TimbresMemoryRegion *timbresMemoryRegion; SystemMemoryRegion *systemMemoryRegion; DisplayMemoryRegion *displayMemoryRegion; ResetMemoryRegion *resetMemoryRegion; Bit8u *paddedTimbreMaxTable; bool isEnabled; PCMWaveEntry *pcmWaves; // Array const ControlROMFeatureSet *controlROMFeatures; const ControlROMMap *controlROMMap; Bit8u controlROMData[CONTROL_ROM_SIZE]; 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 MidiEventQueue *midiQueue; volatile Bit32u lastReceivedMIDIEventTimestamp; volatile Bit32u renderedSampleCount; MemParams &mt32ram, &mt32default; BReverbModel *reverbModels[4]; BReverbModel *reverbModel; bool reverbOverridden; MIDIDelayMode midiDelayMode; DACInputMode dacInputMode; float outputGain; float reverbOutputGain; bool reversedStereoEnabled; bool isOpen; bool isDefaultReportHandler; ReportHandler *reportHandler; PartialManager *partialManager; Part *parts[9]; // When a partial needs to be aborted to free it up for use by a new Poly, // the controller will busy-loop waiting for the sound to finish. // We emulate this by delaying new MIDI events processing until abortion finishes. Poly *abortingPoly; Analog *analog; Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp); 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 initMemoryRegions(); void deleteMemoryRegions(); MemoryRegion *findMemoryRegion(Bit32u addr); void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data); void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data); bool loadControlROM(const ROMImage &controlROMImage); 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); void refreshSystemMasterTune(); void refreshSystemReverbParameters(); void refreshSystemReserveSettings(); void refreshSystemChanAssign(unsigned int firstPart, unsigned int lastPart); void refreshSystemMasterVol(); void refreshSystem(); void reset(); void printPartialUsage(unsigned long sampleOffset = 0); void polyStateChanged(int partNum); void newTimbreSet(int partNum, Bit8u timbreGroup, 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; public: static inline Sample clipSampleEx(SampleEx sampleEx) { #if MT32EMU_USE_FLOAT_SAMPLES return sampleEx; #else // 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 } static inline void muteSampleBuffer(Sample *buffer, Bit32u len) { if (buffer == NULL) return; #if MT32EMU_USE_FLOAT_SAMPLES // 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); // Optionally sets callbacks for reporting various errors, information and debug messages Synth(ReportHandler *useReportHandler = NULL); ~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); // Overloaded method which opens the synth with default partial count. 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); // All the enqueued events are processed by the synth immediately. 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); // 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. // 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); // Overloaded methods for the MIDI events to be processed ASAP. bool playMsg(Bit32u msg); bool playSysex(const Bit8u *sysex, Bit32u len); // 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. void playMsgNow(Bit32u msg); void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity); // Sends a string of Sysex commands to the MT-32 for immediate interpretation // The length is in bytes void playSysexNow(const Bit8u *sysex, Bit32u len); void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len); void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len); void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len); void setReverbEnabled(bool reverbEnabled); bool isReverbEnabled() const; // 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; // 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; // 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; // 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 // 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 DACInputMode_PURE void setReverbOutputGain(float); float getReverbOutputGain() const; void setReversedStereoEnabled(bool enabled); bool isReversedStereoEnabled(); // Returns actual sample rate used in emulation of stereo analog circuitry of hardware units. // See comment for render() below. unsigned int 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 // 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); // 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); // Returns true when there is at least one active partial, otherwise false. bool hasActivePartials() const; // Returns true if hasActivePartials() returns true, or reverb is (somewhat unreliably) detected as being active. bool isActive() const; // Returns the maximum number of partials playing simultaneously. unsigned int 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; // 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; // 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; // 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; void readMemory(Bit32u addr, Bit32u len, Bit8u *data); }; } #endif