diff options
Diffstat (limited to 'backends/midi')
24 files changed, 0 insertions, 9135 deletions
diff --git a/backends/midi/adlib.cpp b/backends/midi/adlib.cpp deleted file mode 100644 index 1c4e4cf35f..0000000000 --- a/backends/midi/adlib.cpp +++ /dev/null @@ -1,1509 +0,0 @@ -/* ScummVM - Scumm Interpreter - * Copyright (C) 2001-2004 The ScummVM project - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * $Header$ - */ - -#include "emumidi.h" -#include "common/util.h" -#include "sound/fmopl.h" - -#ifdef DEBUG_ADLIB -static int tick; -#endif - -class MidiDriver_ADLIB; -struct AdlibVoice; - -struct InstrumentExtra { - byte a, b, c, d, e, f, g, h; -}; - -struct AdlibInstrument { - byte flags_1; - byte oplvl_1; - byte atdec_1; - byte sustrel_1; - byte waveform_1; - byte flags_2; - byte oplvl_2; - byte atdec_2; - byte sustrel_2; - byte waveform_2; - byte feedback; - byte flags_a; - InstrumentExtra extra_a; - byte flags_b; - InstrumentExtra extra_b; - byte duration; - - AdlibInstrument() { memset(this, 0, sizeof(AdlibInstrument)); } -}; - -class AdlibPart : public MidiChannel { - friend class MidiDriver_ADLIB; - -protected: -// AdlibPart *_prev, *_next; - AdlibVoice *_voice; - int16 _pitchbend; - byte _pitchbend_factor; - int8 _transpose_eff; - byte _vol_eff; - int8 _detune_eff; - byte _modwheel; - bool _pedal; - byte _program; - byte _pri_eff; - AdlibInstrument _part_instr; - -protected: - MidiDriver_ADLIB *_owner; - bool _allocated; - byte _channel; - - void init(MidiDriver_ADLIB *owner, byte channel); - void allocate() { _allocated = true; } - -public: - AdlibPart() { - _voice = 0; - _pitchbend = 0; - _pitchbend_factor = 2; - _transpose_eff = 0; - _vol_eff = 0; - _detune_eff = 0; - _modwheel = 0; - _pedal = 0; - _program = 0; - _pri_eff = 0; - - _owner = 0; - _allocated = false; - _channel = 0; - } - - MidiDriver *device(); - byte getNumber() { return _channel; } - void release() { _allocated = false; } - - void send (uint32 b); - - // Regular messages - void noteOff(byte note); - void noteOn(byte note, byte velocity); - void programChange(byte program); - void pitchBend(int16 bend); - - // Control Change messages - void controlChange(byte control, byte value); - void modulationWheel(byte value); - void volume(byte value); - void panPosition(byte value) { return; } // Not supported - void pitchBendFactor(byte value); - void detune(byte value); - void priority(byte value); - void sustain(bool value); - void effectLevel(byte value) { return; } // Not supported - void chorusLevel(byte value) { return; } // Not supported - void allNotesOff(); - - // SysEx messages - void sysEx_customInstrument(uint32 type, byte *instr); -}; - -// FYI (Jamieson630) -// It is assumed that any invocation to AdlibPercussionChannel -// will be done through the MidiChannel base class as opposed to the -// AdlibPart base class. If this were NOT the case, all the functions -// listed below would need to be virtual in AdlibPart as well as MidiChannel. -class AdlibPercussionChannel : public AdlibPart { - friend class MidiDriver_ADLIB; - -protected: - void init(MidiDriver_ADLIB *owner, byte channel); - -public: - void noteOff(byte note); - void noteOn(byte note, byte velocity); - void programChange(byte program) { } - void pitchBend(int16 bend) { } - - // Control Change messages - void controlChange(byte control, byte value) { } - void modulationWheel(byte value) { } - void pitchBendFactor(byte value) { } - void detune(byte value) { } - void priority(byte value) { } - void sustain(bool value) { } - - // SysEx messages - void sysEx_customInstrument(uint32 type, byte *instr) { } -}; - -struct Struct10 { - byte active; - int16 cur_val; - int16 count; - uint16 max_value; - int16 start_value; - byte loop; - byte table_a[4]; - byte table_b[4]; - int8 unk3; - int8 modwheel; - int8 modwheel_last; - uint16 speed_lo_max; - uint16 num_steps; - int16 speed_hi; - int8 direction; - uint16 speed_lo; - uint16 speed_lo_counter; -}; - -struct Struct11 { - int16 modify_val; - byte param, flag0x40, flag0x10; - Struct10 *s10; -}; - -struct AdlibVoice { - AdlibPart *_part; - AdlibVoice *_next, *_prev; - byte _waitforpedal; - byte _note; - byte _channel; - byte _twochan; - byte _vol_1, _vol_2; - int16 _duration; - - Struct10 _s10a; - Struct11 _s11a; - Struct10 _s10b; - Struct11 _s11b; - - AdlibVoice() { memset(this, 0, sizeof(AdlibVoice)); } -}; - -struct AdlibSetParams { - byte a, b, c, d; -}; - -static const byte channel_mappings[9] = { - 0, 1, 2, 8, - 9, 10, 16, 17, - 18 -}; - -static const byte channel_mappings_2[9] = { - 3, 4, 5, 11, - 12, 13, 19, 20, - 21 -}; - -static const AdlibSetParams adlib_setparam_table[] = { - {0x40, 0, 63, 63}, // level - {0xE0, 2, 0, 0}, // unused - {0x40, 6, 192, 0}, // level key scaling - {0x20, 0, 15, 0}, // modulator frequency multiple - {0x60, 4, 240, 15}, // attack rate - {0x60, 0, 15, 15}, // decay rate - {0x80, 4, 240, 15}, // sustain level - {0x80, 0, 15, 15}, // release rate - {0xE0, 0, 3, 0}, // waveform select - {0x20, 7, 128, 0}, // amp mod - {0x20, 6, 64, 0}, // vib - {0x20, 5, 32, 0}, // eg typ - {0x20, 4, 16, 0}, // ksr - {0xC0, 0, 1, 0}, // decay alg - {0xC0, 1, 14, 0} // feedback -}; - -const byte param_table_1[16] = { - 29, 28, 27, 0, - 3, 4, 7, 8, - 13, 16, 17, 20, - 21, 30, 31, 0 -}; - -const uint16 maxval_table[16] = { - 0x2FF, 0x1F, 0x7, 0x3F, - 0x0F, 0x0F, 0x0F, 0x3, - 0x3F, 0x0F, 0x0F, 0x0F, - 0x3, 0x3E, 0x1F, 0 -}; - -static const uint16 num_steps_table[] = { - 1, 2, 4, 5, - 6, 7, 8, 9, - 10, 12, 14, 16, - 18, 21, 24, 30, - 36, 50, 64, 82, - 100, 136, 160, 192, - 240, 276, 340, 460, - 600, 860, 1200, 1600 -}; - -static const byte note_to_f_num[] = { - 90, 91, 92, 92, 93, 94, 94, 95, - 96, 96, 97, 98, 98, 99, 100, 101, - 101, 102, 103, 104, 104, 105, 106, 107, - 107, 108, 109, 110, 111, 111, 112, 113, - 114, 115, 115, 116, 117, 118, 119, 120, - 121, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 132, 133, 134, - 135, 136, 137, 138, 139, 140, 141, 142, - 143, 145, 146, 147, 148, 149, 150, 151, - 152, 153, 154, 155, 157, 158, 159, 160, - 161, 162, 163, 165, 166, 167, 168, 169, - 171, 172, 173, 174, 176, 177, 178, 180, - 181, 182, 184, 185, 186, 188, 189, 190, - 192, 193, 194, 196, 197, 199, 200, 202, - 203, 205, 206, 208, 209, 211, 212, 214, - 215, 217, 218, 220, 222, 223, 225, 226, - 228, 230, 231, 233, 235, 236, 238, 240, - 242, 243, 245, 247, 249, 251, 252, 254, -}; - -static const byte map_gm_to_fm[128][30] = { - // 0x00 -{ 0xC2, 0xC5, 0x2B, 0x99, 0x58, 0xC2, 0x1F, 0x1E, 0xC8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x23 }, -{ 0x22, 0x53, 0x0E, 0x8A, 0x30, 0x14, 0x06, 0x1D, 0x7A, 0x5C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x06, 0x00, 0x1C, 0x79, 0x40, 0x02, 0x00, 0x4B, 0x79, 0x58, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xC2, 0x89, 0x2A, 0x89, 0x49, 0xC2, 0x16, 0x1C, 0xB8, 0x7C, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x23 }, -{ 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x20, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x84, 0x40, 0x3B, 0x5A, 0x6F, 0x81, 0x0E, 0x3B, 0x5A, 0x7F, 0x0B, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x8C, 0x80, 0x05, 0xEA, 0x59, 0x82, 0x0A, 0x3C, 0xAA, 0x64, 0x07, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x85, 0x40, 0x0D, 0xEC, 0x71, 0x84, 0x58, 0x3E, 0xCB, 0x7C, 0x01, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x8A, 0xC0, 0x0C, 0xDC, 0x50, 0x88, 0x58, 0x3D, 0xDA, 0x7C, 0x01, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xC9, 0x40, 0x2B, 0x78, 0x42, 0xC2, 0x04, 0x4C, 0x8A, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x1A }, -{ 0x2A, 0x0E, 0x17, 0x89, 0x28, 0x22, 0x0C, 0x1B, 0x09, 0x70, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE7, 0x9B, 0x08, 0x08, 0x26, 0xE2, 0x06, 0x0A, 0x08, 0x70, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xC5, 0x05, 0x00, 0xFC, 0x40, 0x84, 0x00, 0x00, 0xDC, 0x50, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x86, 0x40, 0x5D, 0x5A, 0x41, 0x81, 0x00, 0x0B, 0x5A, 0x7F, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, - // 0x10 -{ 0xED, 0x00, 0x7B, 0xC8, 0x40, 0xE1, 0x99, 0x4A, 0xE9, 0x7E, 0x07, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE8, 0x4F, 0x3A, 0xD7, 0x7C, 0xE2, 0x97, 0x49, 0xF9, 0x7D, 0x05, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE1, 0x10, 0x2F, 0xF7, 0x7D, 0xF3, 0x45, 0x8F, 0xC7, 0x62, 0x07, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x01, 0x8C, 0x9F, 0xDA, 0x70, 0xE4, 0x50, 0x9F, 0xDA, 0x6A, 0x09, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x08, 0xD5, 0x9D, 0xA5, 0x45, 0xE2, 0x3F, 0x9F, 0xD6, 0x49, 0x07, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE5, 0x0F, 0x7D, 0xB8, 0x2E, 0xA2, 0x0F, 0x7C, 0xC7, 0x61, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0x62, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0x88, 0x9C, 0x50, 0x64, 0xE2, 0x18, 0x70, 0xC4, 0x7C, 0x0B, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x18 }, -{ 0x42, 0x55, 0x3E, 0xEB, 0x24, 0xD4, 0x08, 0x0D, 0xA9, 0x71, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x18 }, -{ 0xC2, 0x00, 0x2B, 0x17, 0x51, 0xC2, 0x1E, 0x4D, 0x97, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x19 }, -{ 0xC6, 0x01, 0x2D, 0xA7, 0x44, 0xC2, 0x06, 0x0E, 0xA7, 0x79, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xC2, 0x0C, 0x06, 0x06, 0x55, 0xC2, 0x3F, 0x09, 0x86, 0x7D, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0A }, -{ 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0x59, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0x7F, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0x7D, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0B }, - // 0x20 -{ 0xC2, 0x40, 0x3C, 0x96, 0x58, 0xC4, 0xDE, 0x0E, 0xC7, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x20 }, -{ 0x31, 0x13, 0x2D, 0xD7, 0x3C, 0xE2, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x22, 0x86, 0x0D, 0xD7, 0x50, 0xE4, 0x18, 0x5E, 0xB8, 0x7C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x28 }, -{ 0xF2, 0x0A, 0x0D, 0xD7, 0x40, 0xE4, 0x1F, 0x5E, 0xB8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xF2, 0x09, 0x4B, 0xD6, 0x48, 0xE4, 0x1F, 0x1C, 0xB8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x28 }, -{ 0x62, 0x11, 0x0C, 0xE6, 0x3C, 0xE4, 0x1F, 0x0C, 0xC8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x12, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x7D, 0xB8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x13, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x5D, 0xB8, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xA2, 0x40, 0x5D, 0xBA, 0x3F, 0xE2, 0x00, 0x8F, 0xD8, 0x79, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x40, 0x3D, 0xDA, 0x3B, 0xE1, 0x00, 0x7E, 0xD8, 0x7A, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x62, 0x00, 0x6D, 0xFA, 0x5D, 0xE2, 0x00, 0x8F, 0xC8, 0x79, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE1, 0x00, 0x4E, 0xDB, 0x4A, 0xE3, 0x18, 0x6F, 0xE9, 0x7E, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE1, 0x00, 0x4E, 0xDB, 0x66, 0xE2, 0x00, 0x7F, 0xE9, 0x7E, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x02, 0x0F, 0x66, 0xAA, 0x51, 0x02, 0x64, 0x29, 0xF9, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 }, -{ 0x16, 0x4A, 0x04, 0xBA, 0x39, 0xC2, 0x58, 0x2D, 0xCA, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 }, -{ 0x02, 0x00, 0x01, 0x7A, 0x79, 0x02, 0x3F, 0x28, 0xEA, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, - // 0x30 -{ 0x62, 0x53, 0x9C, 0xBA, 0x31, 0x62, 0x5B, 0xAD, 0xC9, 0x55, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xF2, 0x40, 0x6E, 0xDA, 0x49, 0xE2, 0x13, 0x8F, 0xF9, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x40, 0x8F, 0xFA, 0x50, 0xF2, 0x04, 0x7F, 0xFA, 0x7D, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x3D, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE6, 0x80, 0x9C, 0x99, 0x42, 0xE2, 0x04, 0x7D, 0x78, 0x60, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0x7C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xC3, 0x3F, 0x4B, 0xE9, 0x7E, 0xC1, 0x3F, 0x9B, 0xF9, 0x7F, 0x0B, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x06 }, -{ 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0x68, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xF2, 0x00, 0x8F, 0xFB, 0x50, 0xF6, 0x47, 0x8F, 0xE9, 0x68, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xF2, 0x00, 0xAF, 0x88, 0x58, 0xF2, 0x54, 0x6E, 0xC9, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x84, 0x4E, 0x78, 0x6C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0x7C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0x7D, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0x7C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, - // 0x40 -{ 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0x7D, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x0F, 0x90, 0xF8, 0x78, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x62, 0x00, 0x8E, 0xC9, 0x3D, 0xE6, 0x00, 0x7E, 0xD8, 0x68, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x00, 0x5F, 0xF9, 0x48, 0xE6, 0x98, 0x8F, 0xF8, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x62, 0x0C, 0x6E, 0xD8, 0x3D, 0x2A, 0x06, 0x7D, 0xD8, 0x58, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0x00, 0x7E, 0x89, 0x38, 0xE6, 0x84, 0x80, 0xF8, 0x68, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0x80, 0x6C, 0xD9, 0x30, 0xE2, 0x00, 0x8D, 0xC8, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x80, 0x88, 0x48, 0x40, 0xE2, 0x0A, 0x7D, 0xA8, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0x00, 0x77, 0xC5, 0x54, 0xE2, 0x00, 0x9E, 0xD7, 0x70, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0x80, 0x86, 0xB9, 0x64, 0xE2, 0x05, 0x9F, 0xD7, 0x78, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x00, 0x68, 0x68, 0x56, 0xE2, 0x08, 0x9B, 0xB3, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0x00, 0xA6, 0x87, 0x41, 0xE2, 0x0A, 0x7E, 0xC9, 0x7C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0x80, 0x9A, 0xB8, 0x48, 0xE2, 0x00, 0x9E, 0xF9, 0x60, 0x09, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x80, 0x8E, 0x64, 0x68, 0xE2, 0x28, 0x6F, 0x73, 0x7C, 0x01, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, - // 0x50 -{ 0xE8, 0x00, 0x7D, 0x99, 0x54, 0xE6, 0x80, 0x80, 0xF8, 0x7C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE6, 0x00, 0x9F, 0xB9, 0x6D, 0xE1, 0x00, 0x8F, 0xC8, 0x7D, 0x02, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0x00, 0x09, 0x68, 0x4A, 0xE2, 0x2B, 0x9E, 0xF3, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xC4, 0x00, 0x99, 0xE8, 0x3B, 0xE2, 0x25, 0x6F, 0x93, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE6, 0x00, 0x6F, 0xDA, 0x69, 0xE2, 0x05, 0x2F, 0xD8, 0x6A, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0x7C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE3, 0x00, 0x0F, 0xF7, 0x7D, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x3C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE8, 0x40, 0x0D, 0x89, 0x7D, 0xE2, 0x17, 0x7E, 0xD9, 0x7C, 0x07, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE1, 0x00, 0xDF, 0x8A, 0x56, 0xE2, 0x5E, 0xCF, 0xBA, 0x7E, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE2, 0x00, 0x0B, 0x68, 0x60, 0xE2, 0x01, 0x9E, 0xB8, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xEA, 0x00, 0xAE, 0xAB, 0x49, 0xE2, 0x00, 0xAE, 0xBA, 0x6C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xEB, 0x80, 0x8C, 0xCB, 0x3A, 0xE2, 0x86, 0xAF, 0xCA, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE5, 0x40, 0xDB, 0x3B, 0x3C, 0xE2, 0x80, 0xBE, 0xCA, 0x71, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE4, 0x00, 0x9E, 0xAA, 0x3D, 0xE1, 0x43, 0x0F, 0xBA, 0x7E, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE7, 0x40, 0xEC, 0xCA, 0x44, 0xE2, 0x03, 0xBF, 0xBA, 0x66, 0x02, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, - // 0x60 -{ 0xEA, 0x00, 0x68, 0xB8, 0x48, 0xE2, 0x0A, 0x8E, 0xB8, 0x7C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x61, 0x00, 0xBE, 0x99, 0x7E, 0xE3, 0x40, 0xCF, 0xCA, 0x7D, 0x09, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xCD, 0x00, 0x0B, 0x00, 0x48, 0xC2, 0x58, 0x0C, 0x00, 0x7C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x1C }, -{ 0xE2, 0x00, 0x0E, 0x00, 0x52, 0xE2, 0x58, 0x5F, 0xD0, 0x7D, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xCC, 0x00, 0x7D, 0xDA, 0x40, 0xC2, 0x00, 0x5E, 0x9B, 0x58, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE9, 0xC0, 0xEE, 0xD8, 0x43, 0xE2, 0x05, 0xDD, 0xAA, 0x70, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xDA, 0x00, 0x8F, 0xAC, 0x4A, 0x22, 0x05, 0x8D, 0x8A, 0x75, 0x02, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x62, 0x8A, 0xCB, 0x7A, 0x74, 0xE6, 0x56, 0xAF, 0xDB, 0x70, 0x02, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xC2, 0x41, 0xAC, 0x5B, 0x5B, 0xC2, 0x80, 0x0D, 0xCB, 0x7D, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x12 }, -{ 0x75, 0x00, 0x0E, 0xCB, 0x5A, 0xE2, 0x1E, 0x0A, 0xC9, 0x7D, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x10 }, -{ 0x41, 0x00, 0x0E, 0xEA, 0x53, 0xC2, 0x00, 0x08, 0xCA, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x07 }, -{ 0xC1, 0x40, 0x0C, 0x59, 0x6A, 0xC2, 0x80, 0x3C, 0xAB, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0D }, -{ 0x4B, 0x00, 0x0A, 0xF5, 0x61, 0xC2, 0x19, 0x0C, 0xE9, 0x7C, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x07 }, -{ 0x62, 0x00, 0x7F, 0xD8, 0x54, 0xEA, 0x00, 0x8F, 0xD8, 0x7D, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, - // 0x70 -{ 0xCF, 0x40, 0x09, 0xEA, 0x54, 0xC4, 0x00, 0x0C, 0xDB, 0x64, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0xCF, 0x40, 0x0C, 0xAA, 0x54, 0xC4, 0x00, 0x18, 0xF9, 0x64, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0xC9, 0x0E, 0x88, 0xD9, 0x3E, 0xC2, 0x08, 0x1A, 0xEA, 0x6C, 0x0C, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 }, -{ 0x03, 0x00, 0x15, 0x00, 0x64, 0x02, 0x00, 0x08, 0x00, 0x7C, 0x09, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0x01, 0x00, 0x47, 0xD7, 0x6C, 0x01, 0x3F, 0x0C, 0xFB, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 }, -{ 0x00, 0x00, 0x36, 0x67, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 }, -{ 0x02, 0x00, 0x36, 0x68, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 }, -{ 0xCB, 0x00, 0xAF, 0x00, 0x7E, 0xC0, 0x00, 0xC0, 0x06, 0x7F, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0F }, -{ 0x05, 0x0D, 0x80, 0xA6, 0x7F, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 }, -{ 0x0F, 0x00, 0x90, 0xFA, 0x68, 0x06, 0x00, 0xA7, 0x39, 0x54, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x06 }, -{ 0xC9, 0x15, 0xDD, 0xFF, 0x7C, 0x00, 0x00, 0xE7, 0xFC, 0x6C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x38 }, -{ 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 }, -{ 0x07, 0x80, 0x0B, 0xC8, 0x65, 0x02, 0x3F, 0x0C, 0xEA, 0x7C, 0x0F, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 }, -{ 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0x08, 0x00, 0x0B, 0x3C, 0x7C, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 } -}; - -static byte gm_percussion_to_fm[39][30] = { -{ 0x1A, 0x3F, 0x15, 0x05, 0x7C, 0x02, 0x21, 0x2B, 0xE4, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x06 }, -{ 0x11, 0x12, 0x04, 0x07, 0x7C, 0x02, 0x23, 0x0B, 0xE5, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 }, -{ 0x0A, 0x3F, 0x0B, 0x01, 0x7C, 0x1F, 0x1C, 0x46, 0xD0, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x01 }, -{ 0x00, 0x3F, 0x0F, 0x00, 0x7C, 0x10, 0x12, 0x07, 0x00, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0x0F, 0x3F, 0x0B, 0x00, 0x7C, 0x1F, 0x0F, 0x19, 0xD0, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0x00, 0x3F, 0x1F, 0x00, 0x7E, 0x1F, 0x16, 0x07, 0x00, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 }, -{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x1F, 0x4A, 0xD9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 }, -{ 0xCF, 0x7F, 0x08, 0xFF, 0x7E, 0x00, 0xC7, 0x2D, 0xF7, 0x73, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x43, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 }, -{ 0xCF, 0x7F, 0x08, 0xCF, 0x7E, 0x00, 0x45, 0x2A, 0xF8, 0x4B, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0C }, -{ 0x12, 0x3F, 0x06, 0x17, 0x7C, 0x03, 0x27, 0x0B, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 }, -{ 0xCF, 0x7F, 0x08, 0xCD, 0x7E, 0x00, 0x40, 0x1A, 0x69, 0x63, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0C }, -{ 0x13, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x17, 0x0A, 0xD9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 }, -{ 0x15, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 }, -{ 0xCF, 0x3F, 0x2B, 0xFB, 0x7E, 0xC0, 0x1E, 0x1A, 0xCA, 0x7F, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x10 }, -{ 0x17, 0x3F, 0x04, 0x09, 0x7C, 0x03, 0x22, 0x0D, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 }, -{ 0xCF, 0x3F, 0x0F, 0x5E, 0x7C, 0xC6, 0x13, 0x00, 0xCA, 0x7F, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 }, -{ 0xCF, 0x3F, 0x7E, 0x9D, 0x7C, 0xC8, 0xC0, 0x0A, 0xBA, 0x74, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x06 }, -{ 0xCF, 0x3F, 0x4D, 0x9F, 0x7C, 0xC6, 0x00, 0x08, 0xDA, 0x5B, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 }, -{ 0xCF, 0x3F, 0x5D, 0xAA, 0x7A, 0xC0, 0xA4, 0x67, 0x99, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0xCF, 0x3F, 0x4A, 0xFD, 0x7C, 0xCF, 0x00, 0x59, 0xEA, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0x0F, 0x18, 0x0A, 0xFA, 0x57, 0x06, 0x07, 0x06, 0x39, 0x7C, 0x0A, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0xCF, 0x3F, 0x2B, 0xFC, 0x7C, 0xCC, 0xC6, 0x0B, 0xEA, 0x7F, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x10 }, -{ 0x05, 0x1A, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x0C, 0xEA, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x07 }, -{ 0x04, 0x19, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x2C, 0xEA, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x04 }, -{ 0x04, 0x0A, 0x04, 0x00, 0x6C, 0x01, 0x07, 0x0D, 0xFA, 0x74, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x07 }, -{ 0x15, 0x14, 0x05, 0x00, 0x7D, 0x01, 0x07, 0x5C, 0xE9, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 }, -{ 0x10, 0x10, 0x05, 0x08, 0x7C, 0x01, 0x08, 0x0D, 0xEA, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x05 }, -{ 0x11, 0x00, 0x06, 0x87, 0x7F, 0x02, 0x40, 0x09, 0x59, 0x68, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x08 }, -{ 0x13, 0x26, 0x04, 0x6A, 0x7F, 0x01, 0x00, 0x08, 0x5A, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x08 }, -{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC4, 0x00, 0x18, 0xF9, 0x54, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC3, 0x00, 0x18, 0xF8, 0x54, 0x04, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0xCB, 0x3F, 0x8F, 0x00, 0x7E, 0xC5, 0x00, 0x98, 0xD6, 0x5F, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x0D }, -{ 0x0C, 0x18, 0x87, 0xB3, 0x7F, 0x19, 0x10, 0x55, 0x75, 0x7C, 0x0E, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0x05, 0x11, 0x15, 0x00, 0x64, 0x02, 0x08, 0x08, 0x00, 0x5C, 0x09, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0x04, 0x08, 0x15, 0x00, 0x48, 0x01, 0x08, 0x08, 0x00, 0x60, 0x08, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x02 }, -{ 0xDA, 0x00, 0x53, 0x30, 0x68, 0x07, 0x1E, 0x49, 0xC4, 0x7E, 0x03, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 }, -{ 0x1C, 0x00, 0x07, 0xBC, 0x6C, 0x0C, 0x14, 0x0B, 0x6A, 0x7E, 0x0B, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x03 }, -{ 0x0A, 0x0E, 0x7F, 0x00, 0x7D, 0x13, 0x20, 0x28, 0x03, 0x7C, 0x06, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x00 } -}; - -static const byte gm_percussion_lookup[128] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, - 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0xFF, 0xFF, 0x17, 0x18, 0x19, 0x1A, - 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x21, 0x22, 0x23, 0xFF, 0xFF, - 0x24, 0x25, 0xFF, 0xFF, 0xFF, 0x26, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -}; - -static byte lookup_table[64][32]; -static const byte volume_table[] = { - 0, 4, 7, 11, - 13, 16, 18, 20, - 22, 24, 26, 27, - 29, 30, 31, 33, - 34, 35, 36, 37, - 38, 39, 40, 41, - 42, 43, 44, 44, - 45, 46, 47, 47, - 48, 49, 49, 50, - 51, 51, 52, 53, - 53, 54, 54, 55, - 55, 56, 56, 57, - 57, 58, 58, 59, - 59, 60, 60, 60, - 61, 61, 62, 62, - 62, 63, 63, 63 -}; - -static int lookup_volume(int a, int b) { - if (b == 0) - return 0; - - if (b == 31) - return a; - - if (a < -63 || a > 63) { - return b * (a + 1) >> 5; - } - - if (b < 0) { - if (a < 0) { - return lookup_table[-a][-b]; - } else { - return -lookup_table[a][-b]; - } - } else { - if (a < 0) { - return -lookup_table[-a][b]; - } else { - return lookup_table[a][b]; - } - } -} - -static void create_lookup_table() { - int i, j; - int sum; - - for (i = 0; i < 64; i++) { - sum = i; - for (j = 0; j < 32; j++) { - lookup_table[i][j] = sum >> 5; - sum += i; - } - } - for (i = 0; i < 64; i++) - lookup_table[i][0] = 0; -} - -//////////////////////////////////////// -// -// Adlib MIDI driver -// -//////////////////////////////////////// - -class MidiDriver_ADLIB : public MidiDriver_Emulated { - friend class AdlibPart; - friend class AdlibPercussionChannel; - -public: - MidiDriver_ADLIB(SoundMixer *mixer); - - int open(); - void close(); - void send(uint32 b); - void send(byte channel, uint32 b); // Supports higher than channel 15 - uint32 property(int prop, uint32 param); - - void setPitchBendRange(byte channel, uint range); - void sysEx_customInstrument(byte channel, uint32 type, byte *instr); - - MidiChannel *allocateChannel(); - MidiChannel *getPercussionChannel() { return &_percussion; } // Percussion partially supported - - - // AudioStream API - bool isStereo() const { return false; } - int getRate() const { return _mixer->getOutputRate(); } - -private: - bool _game_SmallHeader; - - FM_OPL *_opl; - byte *_adlib_reg_cache; - - int _adlib_timer_counter; - - uint16 channel_table_2[9]; - int _voice_index; - int _timer_p; - int _timer_q; - uint16 curnote_table[9]; - AdlibVoice _voices[9]; - AdlibPart _parts[32]; - AdlibPercussionChannel _percussion; - - void generateSamples(int16 *buf, int len); - void onTimer(); - void part_key_on(AdlibPart *part, AdlibInstrument *instr, byte note, byte velocity); - void part_key_off(AdlibPart *part, byte note); - - void adlib_key_off(int chan); - void adlib_note_on(int chan, byte note, int mod); - void adlib_note_on_ex(int chan, byte note, int mod); - int adlib_read_param(int chan, byte data); - void adlib_setup_channel(int chan, AdlibInstrument * instr, byte vol_1, byte vol_2); - byte adlib_read(byte port) { - return _adlib_reg_cache[port]; - } - void adlib_set_param(int channel, byte param, int value); - void adlib_key_onoff(int channel); - void adlib_write(byte port, byte value); - void adlib_playnote(int channel, int note); - - AdlibVoice *allocate_voice(byte pri); - - void mc_off(AdlibVoice * voice); - - static void link_mc(AdlibPart *part, AdlibVoice *voice); - void mc_inc_stuff(AdlibVoice *voice, Struct10 * s10, Struct11 * s11); - void mc_init_stuff(AdlibVoice *voice, Struct10 * s10, Struct11 * s11, byte flags, - InstrumentExtra * ie); - - void struct10_init(Struct10 * s10, InstrumentExtra * ie); - static byte struct10_ontimer(Struct10 * s10, Struct11 * s11); - static void struct10_setup(Struct10 * s10); - static int random_nr(int a); - void mc_key_on(AdlibVoice *voice, AdlibInstrument *instr, byte note, byte velocity); -}; - -// MidiChannel method implementations - -void AdlibPart::init(MidiDriver_ADLIB *owner, byte channel) { - _owner = owner; - _channel = channel; - _pri_eff = 127; -} - -MidiDriver *AdlibPart::device() { - return _owner; -} - -void AdlibPart::send(uint32 b) { - _owner->send(_channel, b); -} - -void AdlibPart::noteOff(byte note) { -#ifdef DEBUG_ADLIB - debug(6, "%10d: noteOff(%d)", tick, note); -#endif - _owner->part_key_off(this, note); -} - -void AdlibPart::noteOn(byte note, byte velocity) { -#ifdef DEBUG_ADLIB - debug(6, "%10d: noteOn(%d,%d)", tick, note, velocity); -#endif - _owner->part_key_on(this, &_part_instr, note, velocity); -} - -void AdlibPart::programChange(byte program) { - if (program > 127) - return; - - uint i; - uint count = 0; - for (i = 0; i < ARRAYSIZE(map_gm_to_fm[0]); ++i) - count += map_gm_to_fm[program][i]; - if (!count) - warning("No Adlib instrument defined for GM program %d", (int) program); - _program = program; - memcpy(&_part_instr, &map_gm_to_fm[program], sizeof(AdlibInstrument)); -} - -void AdlibPart::pitchBend(int16 bend) { - AdlibVoice *voice; - - _pitchbend = bend; - for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, - (_pitchbend * _pitchbend_factor >> 6) + _detune_eff); - } -} - -void AdlibPart::controlChange(byte control, byte value) { - switch (control) { - case 1: modulationWheel(value); break; - case 7: volume(value); break; - case 10: break; // Pan position. Not supported. - case 16: pitchBendFactor(value); break; - case 17: detune(value); break; - case 18: priority(value); break; - case 64: sustain(value > 0); break; - case 91: break; // Effects level. Not supported. - case 93: break; // Chorus level. Not supported. - case 119: break; // Unknown, used in Simon the Sorcerer 2 - case 121: break; // Unknown, used in Simon the Sorcerer 1 - case 123: allNotesOff(); break; - default: - warning("Adlib: Unknown control change message %d", (int) control); - } -} - -void AdlibPart::modulationWheel(byte value) { - AdlibVoice *voice; - - _modwheel = value; - for (voice = _voice; voice; voice = voice->_next) { - if (voice->_s10a.active && voice->_s11a.flag0x40) - voice->_s10a.modwheel = _modwheel >> 2; - if (voice->_s10b.active && voice->_s11b.flag0x40) - voice->_s10b.modwheel = _modwheel >> 2; - } -} - -void AdlibPart::volume(byte value) { - AdlibVoice *voice; - - _vol_eff = value; - for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_set_param(voice->_channel, 0, volume_table[lookup_table[voice->_vol_2][_vol_eff >> 2]]); - if (voice->_twochan) { - _owner->adlib_set_param(voice->_channel, 13, volume_table[lookup_table[voice->_vol_1][_vol_eff >> 2]]); - } - } -} - -void AdlibPart::pitchBendFactor(byte value) { - AdlibVoice *voice; - - _pitchbend_factor = value; - for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, - (_pitchbend * _pitchbend_factor >> 6) + _detune_eff); - } -} - -void AdlibPart::detune(byte value) { - AdlibVoice *voice; - - _detune_eff = value; - for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, - (_pitchbend * _pitchbend_factor >> 6) + _detune_eff); - } -} - -void AdlibPart::priority(byte value) { - _pri_eff = value; -} - -void AdlibPart::sustain(bool value) { - AdlibVoice *voice; - - _pedal = value; - if (!value) { - for (voice = _voice; voice; voice = voice->_next) { - if (voice->_waitforpedal) - _owner->mc_off(voice); - } - } -} - -void AdlibPart::allNotesOff() { - while (_voice) - _owner->mc_off(_voice); -} - -void AdlibPart::sysEx_customInstrument(uint32 type, byte *instr) { - if (type == 'ADL ') { - AdlibInstrument *i = &_part_instr; - memcpy(i, instr, sizeof(AdlibInstrument)); - } -} - -// MidiChannel method implementations for percussion - -void AdlibPercussionChannel::init(MidiDriver_ADLIB *owner, byte channel) { - AdlibPart::init(owner, channel); - _pri_eff = 0; - _vol_eff = 127; -} - -void AdlibPercussionChannel::noteOff(byte note) { - // Jamieson630: Unless I run into a specific instrument that - // may require a key off, I'm going to ignore this message. - // The rationale is that a percussion instrument should - // fade out of its own accord, and the Adlib instrument - // definitions used should follow this rule. Since - // percussion voices are allocated at the lowest priority - // anyway, we know that "hanging" percussion sounds will - // not prevent later musical instruments (or even other - // percussion sounds) from playing. -/* - _owner->part_key_off(this, note); -*/ -} - -void AdlibPercussionChannel::noteOn(byte note, byte velocity) { - byte key = gm_percussion_lookup[note]; - if (key == 0xFF) { - debug(2, "No FM map for GM percussion key %d", (int) note); - return; - } - _owner->part_key_on(this, (AdlibInstrument *) &gm_percussion_to_fm[key], note, velocity); -} - -// MidiDriver method implementations - -MidiDriver_ADLIB::MidiDriver_ADLIB(SoundMixer *mixer) - : MidiDriver_Emulated(mixer) { - uint i; - - _game_SmallHeader = false; - - _adlib_reg_cache = 0; - - _adlib_timer_counter = 0; - _voice_index = 0; - for (i = 0; i < ARRAYSIZE(curnote_table); ++i) { - curnote_table[i] = 0; - } - - for (i = 0; i < ARRAYSIZE(_parts); ++i) { - _parts[i].init(this, i + ((i >= 9) ? 1 : 0)); - } - _percussion.init(this, 9); - _timer_p = 0xD69; - _timer_q = 0x411B; -} - -int MidiDriver_ADLIB::open() { - if (_isOpen) - return MERR_ALREADY_OPEN; - - MidiDriver_Emulated::open(); - - int i; - AdlibVoice *voice; - - for (i = 0, voice = _voices; i != ARRAYSIZE(_voices); i++, voice++) { - voice->_channel = i; - voice->_s11a.s10 = &voice->_s10b; - voice->_s11b.s10 = &voice->_s10a; - } - - _adlib_reg_cache = (byte *)calloc(256, 1); - - _opl = makeAdlibOPL(getRate()); - - adlib_write(1, 0x20); - adlib_write(8, 0x40); - adlib_write(0xBD, 0x00); - create_lookup_table(); - - _mixer->setupPremix(this); - - return 0; -} - -void MidiDriver_ADLIB::close() { - if (!_isOpen) - return; - _isOpen = false; - - // Detach the premix callback handler - _mixer->setupPremix(0); - - uint i; - for (i = 0; i < ARRAYSIZE(_voices); ++i) { - if (_voices[i]._part) - mc_off(&_voices[i]); - } - - // Turn off the OPL emulation -// YM3812Shutdown(); - - free(_adlib_reg_cache); -} - -void MidiDriver_ADLIB::send(uint32 b) { - send(b & 0xF, b & 0xFFFFFFF0); -} - -void MidiDriver_ADLIB::send(byte chan, uint32 b) { - //byte param3 = (byte) ((b >> 24) & 0xFF); - byte param2 = (byte) ((b >> 16) & 0xFF); - byte param1 = (byte) ((b >> 8) & 0xFF); - byte cmd = (byte) (b & 0xF0); - - AdlibPart *part; - if (chan == 9) - part = &_percussion; - else - part = &_parts[chan]; - - switch (cmd) { - case 0x80:// Note Off - part->noteOff(param1); - break; - case 0x90: // Note On - part->noteOn(param1, param2); - break; - case 0xA0: // Aftertouch - break; // Not supported. - case 0xB0: // Control Change - part->controlChange(param1, param2); - break; - case 0xC0: // Program Change - part->programChange(param1); - break; - case 0xD0: // Channel Pressure - break; // Not supported. - case 0xE0: // Pitch Bend - part->pitchBend((param1 | (param2 << 7)) - 0x2000); - break; - case 0xF0: // SysEx - // We should never get here! SysEx information has to be - // sent via high-level semantic methods. - warning("MidiDriver_ADLIB: Receiving SysEx command on a send() call"); - break; - - default: - warning("MidiDriver_ADLIB: Unknown send() command 0x%02X", cmd); - } -} - -uint32 MidiDriver_ADLIB::property(int prop, uint32 param) { - switch (prop) { - case PROP_OLD_ADLIB: // Older games used a different operator volume algorithm - _game_SmallHeader = (param > 0); - if (_game_SmallHeader) { - _timer_p = 473; - _timer_q = 1000; - } else { - _timer_p = 0xD69; - _timer_q = 0x411B; - } - return 1; - } - - return 0; -} - -void MidiDriver_ADLIB::setPitchBendRange(byte channel, uint range) { - AdlibVoice *voice; - AdlibPart *part = &_parts[channel]; - - part->_pitchbend_factor = range; - for (voice = part->_voice; voice; voice = voice->_next) { - adlib_note_on(voice->_channel, voice->_note + part->_transpose_eff, - (part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff); - } -} - -void MidiDriver_ADLIB::sysEx_customInstrument(byte channel, uint32 type, byte *instr) { - _parts[channel].sysEx_customInstrument(type, instr); -} - -MidiChannel *MidiDriver_ADLIB::allocateChannel() { - AdlibPart *part; - uint i; - - for (i = 0; i < ARRAYSIZE(_parts); ++i) { - part = &_parts[i]; - if (!part->_allocated) { - part->allocate(); - return part; - } - } - return NULL; -} - -MidiDriver *MidiDriver_ADLIB_create(SoundMixer *mixer) { - return new MidiDriver_ADLIB(mixer); -} - -// All the code brought over from IMuseAdlib - -void MidiDriver_ADLIB::adlib_write(byte port, byte value) { - if (_adlib_reg_cache[port] == value) - return; -#ifdef DEBUG_ADLIB - debug(6, "%10d: adlib_write[%x] = %x", tick, port, value); -#endif - _adlib_reg_cache[port] = value; - - OPLWriteReg(_opl, port, value); -} - -void MidiDriver_ADLIB::generateSamples(int16 *data, int len) { - memset(data, 0, sizeof(int16) * len); - YM3812UpdateOne(_opl, data, len); -} - -void MidiDriver_ADLIB::onTimer() { - AdlibVoice *voice; - int i; - - _adlib_timer_counter += _timer_p; - while (_adlib_timer_counter >= _timer_q) { - _adlib_timer_counter -= _timer_q; -#ifdef DEBUG_ADLIB - tick++; -#endif - voice = _voices; - for (i = 0; i != ARRAYSIZE(_voices); i++, voice++) { - if (!voice->_part) - continue; - if (voice->_duration && (voice->_duration -= 0x11) <= 0) { - mc_off(voice); - return; - } - if (voice->_s10a.active) { - mc_inc_stuff(voice, &voice->_s10a, &voice->_s11a); - } - if (voice->_s10b.active) { - mc_inc_stuff(voice, &voice->_s10b, &voice->_s11b); - } - } - } -} - -void MidiDriver_ADLIB::mc_off(AdlibVoice *voice) { - AdlibVoice *tmp; - - adlib_key_off(voice->_channel); - - tmp = voice->_prev; - - if (voice->_next) - voice->_next->_prev = tmp; - if (tmp) - tmp->_next = voice->_next; - else - voice->_part->_voice = voice->_next; - voice->_part = NULL; -} - -void MidiDriver_ADLIB::mc_inc_stuff(AdlibVoice *voice, Struct10 *s10, Struct11 *s11) { - byte code; - AdlibPart *part = voice->_part; - - code = struct10_ontimer(s10, s11); - - if (code & 1) { - switch (s11->param) { - case 0: - voice->_vol_2 = s10->start_value + s11->modify_val; - if (!_game_SmallHeader) { - adlib_set_param(voice->_channel, 0, - volume_table[lookup_table[voice->_vol_2] - [part->_vol_eff >> 2]]); - } else { - adlib_set_param(voice->_channel, 0, voice->_vol_2); - } - break; - case 13: - voice->_vol_1 = s10->start_value + s11->modify_val; - if (voice->_twochan && !_game_SmallHeader) { - adlib_set_param(voice->_channel, 13, - volume_table[lookup_table[voice->_vol_1] - [part->_vol_eff >> 2]]); - } else { - adlib_set_param(voice->_channel, 13, voice->_vol_1); - } - break; - case 30: - s11->s10->modwheel = (char)s11->modify_val; - break; - case 31: - s11->s10->unk3 = (char)s11->modify_val; - break; - default: - adlib_set_param(voice->_channel, s11->param, - s10->start_value + s11->modify_val); - break; - } - } - - if (code & 2 && s11->flag0x10) - adlib_key_onoff(voice->_channel); -} - -void MidiDriver_ADLIB::adlib_key_off(int chan){ - byte port = chan + 0xB0; - adlib_write(port, adlib_read(port) & ~0x20); -} - -byte MidiDriver_ADLIB::struct10_ontimer(Struct10 *s10, Struct11 *s11) { - byte result = 0; - int i; - - if (s10->count && (s10->count -= 17) <= 0) { - s10->active = 0; - return 0; - } - - i = s10->cur_val + s10->speed_hi; - s10->speed_lo_counter += s10->speed_lo; - if (s10->speed_lo_counter >= s10->speed_lo_max) { - s10->speed_lo_counter -= s10->speed_lo_max; - i += s10->direction; - } - if (s10->cur_val != i || s10->modwheel != s10->modwheel_last) { - s10->cur_val = i; - s10->modwheel_last = s10->modwheel; - i = lookup_volume(i, s10->modwheel_last); - if (i != s11->modify_val) { - s11->modify_val = i; - result = 1; - } - } - - if (!--s10->num_steps) { - s10->active++; - if (s10->active > 4) { - if (s10->loop) { - s10->active = 1; - result |= 2; - struct10_setup(s10); - } else { - s10->active = 0; - } - } else { - struct10_setup(s10); - } - } - - return result; -} - -void MidiDriver_ADLIB::adlib_set_param(int channel, byte param, int value) { - const AdlibSetParams *as; - byte port; - - assert(channel >= 0 && channel < 9); - - if (param <= 12) { - port = channel_mappings_2[channel]; - } else if (param <= 25) { - param -= 13; - port = channel_mappings[channel]; - } else if (param <= 27) { - param -= 13; - port = channel; - } else if (param == 28 || param == 29) { - if (param == 28) - value -= 15; - else - value -= 383; - value <<= 4; - channel_table_2[channel] = value; - adlib_playnote(channel, curnote_table[channel] + value); - return; - } else { - return; - } - - as = &adlib_setparam_table[param]; - if (as->d) - value = as->d - value; - port += as->a; - adlib_write(port, (adlib_read(port) & ~as->c) | (((byte)value) << as->b)); -} - -void MidiDriver_ADLIB::adlib_key_onoff(int channel) { - byte val; - byte port = channel + 0xB0; - assert(channel >= 0 && channel < 9); - - val = adlib_read(port); - adlib_write(port, val & ~0x20); - adlib_write(port, val | 0x20); -} - -void MidiDriver_ADLIB::struct10_setup(Struct10 *s10) { - int b, c, d, e, f, g, h; - byte t; - - b = s10->unk3; - f = s10->active - 1; - - t = s10->table_a[f]; - e = num_steps_table[lookup_table[t & 0x7F][b]]; - if (t & 0x80) { - e = random_nr(e); - } - if (e == 0) - e++; - - s10->num_steps = s10->speed_lo_max = e; - - if (f != 2) { - c = s10->max_value; - g = s10->start_value; - t = s10->table_b[f]; - d = lookup_volume(c, (t & 0x7F) - 31); - if (t & 0x80) { - d = random_nr(d); - } - if (d + g > c) { - h = c - g; - } else { - h = d; - if (d + g < 0) - h = -g; - } - h -= s10->cur_val; - } else { - h = 0; - } - - s10->speed_hi = h / e; - if (h < 0) { - h = -h; - s10->direction = -1; - } else { - s10->direction = 1; - } - - s10->speed_lo = h % e; - s10->speed_lo_counter = 0; -} - -void MidiDriver_ADLIB::adlib_playnote(int channel, int note) { - byte old, oct, notex; - int note2; - int i; - - note2 = (note >> 7) - 4; - note2 = (note2 < 128) ? note2 : 0; - - oct = (note2 / 12); - if (oct > 7) - oct = 7 << 2; - else - oct <<= 2; - notex = note2 % 12 + 3; - - old = adlib_read(channel + 0xB0); - if (old & 0x20) { - old &= ~0x20; - if (oct > old) { - if (notex < 6) { - notex += 12; - oct -= 4; - } - } else if (oct < old) { - if (notex > 11) { - notex -= 12; - oct += 4; - } - } - } - - i = (notex << 3) + ((note >> 4) & 0x7); - adlib_write(channel + 0xA0, note_to_f_num[i]); - adlib_write(channel + 0xB0, oct | 0x20); -} - -// TODO: Replace this with RandomSource? But if so, please note that this -// function will be called with negative parameters - getRandomNumber(-1) will -// crash ScummVM, random_nr(-1) won't. - -int MidiDriver_ADLIB::random_nr(int a) { - static byte _rand_seed = 1; - if (_rand_seed & 1) { - _rand_seed >>= 1; - _rand_seed ^= 0xB8; - } else { - _rand_seed >>= 1; - } - return _rand_seed * a >> 8; -} - -void MidiDriver_ADLIB::part_key_off(AdlibPart *part, byte note) { - AdlibVoice *voice; - - for (voice = part->_voice; voice; voice = voice->_next) { - if (voice->_note == note) { - if (part->_pedal) - voice->_waitforpedal = true; - else - mc_off(voice); - } - } -} - -void MidiDriver_ADLIB::part_key_on(AdlibPart *part, AdlibInstrument *instr, byte note, byte velocity) { - AdlibVoice *voice; - - voice = allocate_voice(part->_pri_eff); - if (!voice) - return; - - link_mc(part, voice); - mc_key_on(voice, instr, note, velocity); -} - -AdlibVoice *MidiDriver_ADLIB::allocate_voice(byte pri) { - AdlibVoice *ac, *best = NULL; - int i; - - for (i = 0; i < 9; i++) { - if (++_voice_index >= 9) - _voice_index = 0; - ac = &_voices[_voice_index]; - if (!ac->_part) - return ac; - if (!ac->_next) { - if (ac->_part->_pri_eff <= pri) { - pri = ac->_part->_pri_eff; - best = ac; - } - } - } - - /* V3 games don't have note priorities, first comes wins. */ - if (_game_SmallHeader) - return NULL; - - if (best) - mc_off(best); - return best; -} - -void MidiDriver_ADLIB::link_mc(AdlibPart *part, AdlibVoice *voice) { - voice->_part = part; - voice->_next = (AdlibVoice *)part->_voice; - part->_voice = voice; - voice->_prev = NULL; - - if (voice->_next) - voice->_next->_prev = voice; -} - -void MidiDriver_ADLIB::mc_key_on(AdlibVoice *voice, AdlibInstrument *instr, byte note, byte velocity) { - AdlibPart *part = voice->_part; - int c; - byte vol_1, vol_2; - - voice->_twochan = instr->feedback & 1; - voice->_note = note; - voice->_waitforpedal = false; - voice->_duration = instr->duration; - if (voice->_duration != 0) - voice->_duration *= 63; - - if (!_game_SmallHeader) - vol_1 = (instr->oplvl_1 & 0x3F) + lookup_table[velocity >> 1][instr->waveform_1 >> 2]; - else - vol_1 = 0x3f - (instr->oplvl_1 & 0x3F); - if (vol_1 > 0x3F) - vol_1 = 0x3F; - voice->_vol_1 = vol_1; - - if (!_game_SmallHeader) - vol_2 = (instr->oplvl_2 & 0x3F) + lookup_table[velocity >> 1][instr->waveform_2 >> 2]; - else - vol_2 = 0x3f - (instr->oplvl_2 & 0x3F); - if (vol_2 > 0x3F) - vol_2 = 0x3F; - voice->_vol_2 = vol_2; - - c = part->_vol_eff >> 2; - - if (!_game_SmallHeader) { - vol_2 = volume_table[lookup_table[vol_2][c]]; - if (voice->_twochan) - vol_1 = volume_table[lookup_table[vol_1][c]]; - } - - adlib_setup_channel(voice->_channel, instr, vol_1, vol_2); - adlib_note_on_ex(voice->_channel, part->_transpose_eff + note, part->_detune_eff + (part->_pitchbend * part->_pitchbend_factor >> 6)); - - if (instr->flags_a & 0x80) { - mc_init_stuff(voice, &voice->_s10a, &voice->_s11a, instr->flags_a, &instr->extra_a); - } else { - voice->_s10a.active = 0; - } - - if (instr->flags_b & 0x80) { - mc_init_stuff(voice, &voice->_s10b, &voice->_s11b, instr->flags_b, &instr->extra_b); - } else { - voice->_s10b.active = 0; - } -} - -void MidiDriver_ADLIB::adlib_setup_channel(int chan, AdlibInstrument *instr, byte vol_1, byte vol_2) { - byte port; - - assert(chan >= 0 && chan < 9); - - port = channel_mappings[chan]; - adlib_write(port + 0x20, instr->flags_1); - adlib_write(port + 0x40, (instr->oplvl_1 | 0x3F) - vol_1 ); - adlib_write(port + 0x60, 0xff & (~instr->atdec_1)); - adlib_write(port + 0x80, 0xff & (~instr->sustrel_1)); - adlib_write(port + 0xE0, instr->waveform_1); - - port = channel_mappings_2[chan]; - adlib_write(port + 0x20, instr->flags_2); - adlib_write(port + 0x40, (instr->oplvl_2 | 0x3F) - vol_2 ); - adlib_write(port + 0x60, 0xff & (~instr->atdec_2)); - adlib_write(port + 0x80, 0xff & (~instr->sustrel_2)); - adlib_write(port + 0xE0, instr->waveform_2); - - adlib_write((byte)chan + 0xC0, instr->feedback); -} - -void MidiDriver_ADLIB::adlib_note_on_ex(int chan, byte note, int mod) -{ - int code; - assert(chan >= 0 && chan < 9); - code = (note << 7) + mod; - curnote_table[chan] = code; - channel_table_2[chan] = 0; - adlib_playnote(chan, code); -} - -void MidiDriver_ADLIB::mc_init_stuff(AdlibVoice *voice, Struct10 * s10, - Struct11 * s11, byte flags, InstrumentExtra * ie) { - AdlibPart *part = voice->_part; - s11->modify_val = 0; - s11->flag0x40 = flags & 0x40; - s10->loop = flags & 0x20; - s11->flag0x10 = flags & 0x10; - s11->param = param_table_1[flags & 0xF]; - s10->max_value = maxval_table[flags & 0xF]; - s10->unk3 = 31; - if (s11->flag0x40) { - s10->modwheel = part->_modwheel >> 2; - } else { - s10->modwheel = 31; - } - - switch (s11->param) { - case 0: - s10->start_value = voice->_vol_2; - break; - case 13: - s10->start_value = voice->_vol_1; - break; - case 30: - s10->start_value = 31; - s11->s10->modwheel = 0; - break; - case 31: - s10->start_value = 0; - s11->s10->unk3 = 0; - break; - default: - s10->start_value = adlib_read_param(voice->_channel, s11->param); - } - - struct10_init(s10, ie); -} - -void MidiDriver_ADLIB::struct10_init(Struct10 *s10, InstrumentExtra *ie) { - s10->active = 1; - if (!_game_SmallHeader) { - s10->cur_val = 0; - } else { - s10->cur_val = s10->start_value; - s10->start_value = 0; - } - s10->modwheel_last = 31; - s10->count = ie->a; - if (s10->count) - s10->count *= 63; - s10->table_a[0] = ie->b; - s10->table_a[1] = ie->d; - s10->table_a[2] = ie->f; - s10->table_a[3] = ie->g; - - s10->table_b[0] = ie->c; - s10->table_b[1] = ie->e; - s10->table_b[2] = 0; - s10->table_b[3] = ie->h; - - struct10_setup(s10); -} - -int MidiDriver_ADLIB::adlib_read_param(int chan, byte param) { - const AdlibSetParams *as; - byte val; - byte port; - - assert(chan >= 0 && chan < 9); - - if (param <= 12) { - port = channel_mappings_2[chan]; - } else if (param <= 25) { - param -= 13; - port = channel_mappings[chan]; - } else if (param <= 27) { - param -= 13; - port = chan; - } else if (param == 28) { - return 0xF; - } else if (param == 29) { - return 0x17F; - } else { - return 0; - } - - as = &adlib_setparam_table[param]; - val = adlib_read(port + as->a); - val &= as->c; - val >>= as->b; - if (as->d) - val = as->d - val; - - return val; -} - -void MidiDriver_ADLIB::adlib_note_on(int chan, byte note, int mod) { - int code; - assert(chan >= 0 && chan < 9); - code = (note << 7) + mod; - curnote_table[chan] = code; - adlib_playnote(chan, (int16) channel_table_2[chan] + code); -} diff --git a/backends/midi/emumidi.h b/backends/midi/emumidi.h deleted file mode 100644 index 34cd4b0983..0000000000 --- a/backends/midi/emumidi.h +++ /dev/null @@ -1,107 +0,0 @@ -/* ScummVM - Scumm Interpreter - * Copyright (C) 2001-2004 The ScummVM project - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * $Header$ - */ - -#include "stdafx.h" -#include "sound/audiostream.h" -#include "sound/mididrv.h" -#include "sound/mixer.h" - -#define FIXP_SHIFT 16 - -class MidiDriver_Emulated : public AudioStream, public MidiDriver { -protected: - bool _isOpen; - SoundMixer *_mixer; - -private: - Timer::TimerProc _timerProc; - void *_timerParam; - - int _nextTick; - int _samplesPerTick; - -protected: - virtual void generateSamples(int16 *buf, int len) = 0; - virtual void onTimer() {} - - int _baseFreq; - -public: - MidiDriver_Emulated(SoundMixer *mixer) : _mixer(mixer) { - _isOpen = false; - - _timerProc = 0; - _timerParam = 0; - - _nextTick = 0; - _samplesPerTick = 0; - - _baseFreq = 250; - } - - int open() { - _isOpen = true; - - int d = getRate() / _baseFreq; - int r = getRate() % _baseFreq; - - // This is equivalent to (getRate() << FIXP_SHIFT) / BASE_FREQ - // but less prone to arithmetic overflow. - - _samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq; - return 0; - } - - void setTimerCallback(void *timer_param, Timer::TimerProc timer_proc) { - _timerProc = timer_proc; - _timerParam = timer_param; - } - - uint32 getBaseTempo() { return 1000000 / _baseFreq; } - - - // AudioStream API - int readBuffer(int16 *data, const int numSamples) { - const int stereoFactor = isStereo() ? 2 : 1; - int len = numSamples / stereoFactor; - int step; - - do { - step = len; - if (step > (_nextTick >> FIXP_SHIFT)) - step = (_nextTick >> FIXP_SHIFT); - - generateSamples(data, step); - - _nextTick -= step << FIXP_SHIFT; - if (!(_nextTick >> FIXP_SHIFT)) { - if (_timerProc) - (*_timerProc)(_timerParam); - onTimer(); - _nextTick += _samplesPerTick; - } - data += step * stereoFactor; - len -= step; - } while (len); - - return numSamples; - } - bool endOfData() const { return false; } -}; diff --git a/backends/midi/mt32.cpp b/backends/midi/mt32.cpp deleted file mode 100644 index 8bf3807603..0000000000 --- a/backends/midi/mt32.cpp +++ /dev/null @@ -1,486 +0,0 @@ -/* ScummVM - Scumm Interpreter - * Copyright (C) 2001-2004 The ScummVM project - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * $Header$ - */ - -#include "stdafx.h" -#include "common/scummsys.h" - -#ifdef USE_MT32EMU - -#include "backends/midi/mt32/mt32emu.h" - -#include "backends/midi/emumidi.h" -#include "sound/mpu401.h" - -#include "common/util.h" -#include "common/file.h" -#include "common/config-manager.h" - -#include "graphics/font.h" -#include "graphics/surface.h" - -class MidiChannel_MT32 : public MidiChannel_MPU401 { - void effectLevel(byte value) { } - void chorusLevel(byte value) { } -}; - -class MidiDriver_MT32 : public MidiDriver_Emulated { -private: - PlayingSoundHandle _handle; - MidiChannel_MT32 _midiChannels[16]; - uint16 _channelMask; - MT32Emu::Synth *_synth; - - int _outputRate; - -protected: - void generateSamples(int16 *buf, int len); - -public: - bool _initialising; - - MidiDriver_MT32(SoundMixer *mixer); - virtual ~MidiDriver_MT32(); - - int open(); - void close(); - void send(uint32 b); - void setPitchBendRange (byte channel, uint range); - void sysEx(byte *msg, uint16 length); - - uint32 property(int prop, uint32 param); - MidiChannel *allocateChannel(); - MidiChannel *getPercussionChannel(); - - // AudioStream API - bool isStereo() const { return true; } - int getRate() const { return _outputRate; } -}; - -typedef File SFile; - -class MT32File: public MT32Emu::File { - SFile file; -public: - bool open(const char *filename, OpenMode mode) { - SFile::AccessMode accessMode = mode == OpenMode_read ? SFile::kFileReadMode : SFile::kFileWriteMode; - return file.open(filename, accessMode); - } - void close() { - return file.close(); - } - size_t read(void *in, size_t size) { - return file.read(in, size); - } - bool readLine(char *in, size_t size) { - return file.gets(in, size) != NULL; - } - bool readBit8u(MT32Emu::Bit8u *in) { - byte b = file.readByte(); - if (file.eof()) - return false; - *in = b; - return true; - } - size_t write(const void *in, size_t size) { - return file.write(in, size); - } - bool writeBit8u(MT32Emu::Bit8u out) { - file.writeByte(out); - return !file.ioFailed(); - } - bool isEOF() { - return file.eof(); - } -}; - -static int eatSystemEvents() { - OSystem::Event event; - while (g_system->pollEvent(event)) { - switch (event.type) { - case OSystem::EVENT_QUIT: - return 1; - default: - break; - } - } - return 0; -} - -static void drawProgress(float progress) { - Graphics::Surface surf; - uint32 borderColor = 0x2; - uint32 fillColor = 0x4; - surf.w = g_system->getWidth() / 7 * 5; - surf.h = Graphics::g_scummfont.getFontHeight(); - int x = g_system->getWidth() / 7; - int y = g_system->getHeight() / 2 - surf.h / 2; - surf.pitch = surf.w; - surf.bytesPerPixel = 1; - surf.pixels = calloc(surf.w, surf.h); - Common::Rect r(surf.w, surf.h); - surf.frameRect(r, borderColor); - r.grow(-1); - r.right = r.left + (uint16)(r.width() * progress); - surf.fillRect(r, fillColor); - g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, x, y, surf.w, surf.h); - g_system->updateScreen(); - free(surf.pixels); -} - -static void drawMessage(int offset, const Common::String &text) { - const Graphics::Font &font(Graphics::g_scummfont); - Graphics::Surface surf; - uint32 color = 0x2; - surf.w = g_system->getWidth(); - surf.h = font.getFontHeight(); - surf.pitch = surf.w; - surf.bytesPerPixel = 1; - surf.pixels = calloc(surf.w, surf.h); - font.drawString(&surf, text, 0, 0, surf.w, color, Graphics::kTextAlignCenter); - int y = g_system->getHeight() / 2 - font.getFontHeight() / 2 + offset * (font.getFontHeight() + 1); - g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, 0, y, surf.w, surf.h); - g_system->updateScreen(); - free(surf.pixels); -} - -static MT32Emu::File *MT32_OpenFile(void *userData, const char *filename, MT32Emu::File::OpenMode mode) { - MT32File *file = new MT32File(); - if (!file->open(filename, mode)) { - delete file; - return NULL; - } - return file; -} - -static void MT32_PrintDebug(void *userData, const char *fmt, va_list list) { - char buf[512]; - if (((MidiDriver_MT32 *)userData)->_initialising) { - vsprintf(buf, fmt, list); - buf[70] = 0; // Truncate to a reasonable length - drawMessage(1, buf); - } - //vdebug(0, fmt, list); // FIXME: Use a higher debug level -} - -static int MT32_Report(void *userData, MT32Emu::ReportType type, const void *reportData) { - switch(type) { - case MT32Emu::ReportType_lcdMessage: - g_system->displayMessageOnOSD((const char *)reportData); - break; - case MT32Emu::ReportType_errorControlROM: - error("Failed to load MT32_CONTROL.ROM"); - break; - case MT32Emu::ReportType_errorPCMROM: - error("Failed to load MT32_PCM.ROM"); - break; - case MT32Emu::ReportType_progressInit: - if (((MidiDriver_MT32 *)userData)->_initialising) { - drawProgress(*((const float *)reportData)); - return eatSystemEvents(); - } - break; - default: - break; - } - return 0; -} - -//////////////////////////////////////// -// -// MidiDriver_MT32 -// -//////////////////////////////////////// - -MidiDriver_MT32::MidiDriver_MT32(SoundMixer *mixer) : MidiDriver_Emulated(mixer) { - _channelMask = 0xFFFF; // Permit all 16 channels by default - uint i; - for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) { - _midiChannels[i].init(this, i); - } - _synth = NULL; - // A higher baseFreq reduces the length used in generateSamples(), - // and means that the timer callback will be called more often. - // That results in more accurate timing. - _baseFreq = 10000; - // Unfortunately bugs in the emulator cause inaccurate tuning - // at rates other than 32KHz, thus we produce data at 32KHz and - // rely on SoundMixer to convert. - _outputRate = 32000; //_mixer->getOutputRate(); - _initialising = false; -} - -MidiDriver_MT32::~MidiDriver_MT32() { - if (_synth != NULL) - delete _synth; -} - -int MidiDriver_MT32::open() { - MT32Emu::SynthProperties prop; - - if (_isOpen) - return MERR_ALREADY_OPEN; - - MidiDriver_Emulated::open(); - - memset(&prop, 0, sizeof(prop)); - prop.sampleRate = getRate(); - prop.useReverb = true; - prop.useDefaultReverb = false; - prop.reverbType = 0; - prop.reverbTime = 5; - prop.reverbLevel = 3; - prop.userData = this; - prop.printDebug = MT32_PrintDebug; - prop.report = MT32_Report; - prop.openFile = MT32_OpenFile; - _synth = new MT32Emu::Synth(); - _initialising = true; - const byte dummy_palette[] = { - 0, 0, 0, 0, - 0, 0, 171, 0, - 0, 171, 0, 0, - 0, 171, 171, 0, - 171, 0, 0, 0 - }; - - g_system->setPalette(dummy_palette, 0, 5); - drawMessage(-1, "Initialising MT-32 Emulator"); - if (!_synth->open(prop)) - return MERR_DEVICE_NOT_AVAILABLE; - _initialising = false; - g_system->clearScreen(); - g_system->updateScreen(); - _mixer->playInputStream(&_handle, this, false, -1, 255, 0, false, true); - return 0; -} - -void MidiDriver_MT32::send(uint32 b) { - _synth->playMsg(b); -} - -void MidiDriver_MT32::setPitchBendRange(byte channel, uint range) { - if (range > 24) { - printf("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); -} - -void MidiDriver_MT32::sysEx(byte *msg, uint16 length) { - if (msg[0] == 0xf0) { - _synth->playSysex(msg, length); - } else { - _synth->playSysexWithoutFraming(msg, length); - } -} - -void MidiDriver_MT32::close() { - if (!_isOpen) - return; - _isOpen = false; - - // Detach the player callback handler - setTimerCallback(NULL, NULL); - // Detach the mixer callback handler - _mixer->stopHandle(_handle); - - _synth->close(); - delete _synth; - _synth = NULL; -} - -void MidiDriver_MT32::generateSamples(int16 *data, int len) { - _synth->render(data, len); -} - -uint32 MidiDriver_MT32::property(int prop, uint32 param) { - switch (prop) { - case PROP_CHANNEL_MASK: - _channelMask = param & 0xFFFF; - return 1; - } - - return 0; -} - -MidiChannel *MidiDriver_MT32::allocateChannel() { - MidiChannel_MT32 *chan; - uint i; - - for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) { - if (i == 9 || !(_channelMask & (1 << i))) - continue; - chan = &_midiChannels[i]; - if (chan->allocate()) { - return chan; - } - } - return NULL; -} - -MidiChannel *MidiDriver_MT32::getPercussionChannel() { - return &_midiChannels[9]; -} - -// This code should be used when calling the timer callback from the mixer thread is undesirable. -// Note that it results in less accurate timing. -#if 0 -class MidiEvent_MT32 { -public: - MidiEvent_MT32 *_next; - uint32 _msg; // 0xFFFFFFFF indicates a sysex message - byte *_data; - uint32 _len; - - MidiEvent_MT32(uint32 msg, byte *data, uint32 len) { - _msg = msg; - if (len > 0) { - _data = new byte[len]; - memcpy(_data, data, len); - } - _len = len; - _next = NULL; - } - - MidiEvent_MT32() { - if (_len > 0) - delete _data; - } -}; - -class MidiDriver_ThreadedMT32 : public MidiDriver_MT32 { -private: - OSystem::MutexRef _eventMutex; - MidiEvent_MT32 *_events; - Timer::TimerProc _timer_proc; - - void pushMidiEvent(MidiEvent_MT32 *event); - MidiEvent_MT32 *popMidiEvent(); - -protected: - void send(uint32 b); - void sysEx(byte *msg, uint16 length); - -public: - MidiDriver_ThreadedMT32(SoundMixer *mixer); - virtual ~MidiDriver_ThreadedMT32(); - - void onTimer(); - void close(); - void setTimerCallback(void *timer_param, Timer::TimerProc timer_proc); -}; - - -MidiDriver_ThreadedMT32::MidiDriver_ThreadedMT32(SoundMixer *mixer) : MidiDriver_MT32(mixer) { - _eventMutex = g_system->createMutex(); - _events = NULL; - _timer_proc = NULL; -} - -MidiDriver_ThreadedMT32::~MidiDriver_ThreadedMT32() { - g_system->deleteMutex(_eventMutex); -} - -void MidiDriver_ThreadedMT32::close() { - MidiDriver_MT32::close(); - while ((popMidiEvent() != NULL)) { - // Just eat any leftover events - } -} - -void MidiDriver_ThreadedMT32::setTimerCallback(void *timer_param, Timer::TimerProc timer_proc) { - if (!_timer_proc || !timer_proc) { - if (_timer_proc) - g_timer->removeTimerProc(_timer_proc); - _timer_proc = timer_proc; - if (timer_proc) - g_timer->installTimerProc(timer_proc, getBaseTempo(), timer_param); - } -} - -void MidiDriver_ThreadedMT32::pushMidiEvent(MidiEvent_MT32 *event) { - g_system->lockMutex(_eventMutex); - if (_events == NULL) { - _events = event; - } else { - MidiEvent_MT32 *last = _events; - while (last->_next != NULL) - last = last->_next; - last->_next = event; - } - g_system->unlockMutex(_eventMutex); -} - -MidiEvent_MT32 *MidiDriver_ThreadedMT32::popMidiEvent() { - MidiEvent_MT32 *event; - g_system->lockMutex(_eventMutex); - event = _events; - if (event != NULL) - _events = event->_next; - g_system->unlockMutex(_eventMutex); - return event; -} - -void MidiDriver_ThreadedMT32::send(uint32 b) { - MidiEvent_MT32 *event = new MidiEvent_MT32(b, NULL, 0); - pushMidiEvent(event); -} - -void MidiDriver_ThreadedMT32::sysEx(byte *msg, uint16 length) { - MidiEvent_MT32 *event = new MidiEvent_MT32(0xFFFFFFFF, msg, length); - pushMidiEvent(event); -} - -void MidiDriver_ThreadedMT32::onTimer() { - MidiEvent_MT32 *event; - while ((event = popMidiEvent()) != NULL) { - if (event->_msg == 0xFFFFFFFF) { - MidiDriver_MT32::sysEx(event->_data, event->_len); - } else { - MidiDriver_MT32::send(event->_msg); - } - delete event; - } -} -#endif - -//////////////////////////////////////// -// -// MidiDriver_MT32 factory -// -//////////////////////////////////////// - -MidiDriver *MidiDriver_MT32_create(SoundMixer *mixer) { - // HACK: It will stay here until engine plugin loader overhaul - if (ConfMan.hasKey("extrapath")) - File::addDefaultDirectory(ConfMan.get("extrapath")); - return new MidiDriver_MT32(mixer); -} - -#endif diff --git a/backends/midi/mt32/.cvsignore b/backends/midi/mt32/.cvsignore deleted file mode 100644 index 39a06683b7..0000000000 --- a/backends/midi/mt32/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -.deps diff --git a/backends/midi/mt32/freeverb.cpp b/backends/midi/mt32/freeverb.cpp deleted file mode 100644 index df3e3bf2a2..0000000000 --- a/backends/midi/mt32/freeverb.cpp +++ /dev/null @@ -1,312 +0,0 @@ -/* ScummVM - Scumm Interpreter - * Copyright (C) 2004 The ScummVM project - * Copyright (C) 2000 Jezar at Dreampoint - * - * This code is public domain - * - * Parts of this code are: - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * 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 General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * $Header$ - * - */ - -// Comb filter implementation -// -// Written by -// http://www.dreampoint.co.uk -// This code is public domain - -#include "stdafx.h" -#include "backends/midi/mt32/freeverb.h" - -comb::comb() { - filterstore = 0; - bufidx = 0; -} - -void comb::setbuffer(float *buf, int size) { - buffer = buf; - bufsize = size; -} - -void comb::mute() { - for (int i = 0; i < bufsize; i++) - buffer[i] = 0; -} - -void comb::setdamp(float val) { - damp1 = val; - damp2 = 1 - val; -} - -float comb::getdamp() { - return damp1; -} - -void comb::setfeedback(float val) { - feedback = val; -} - -float comb::getfeedback() { - return feedback; -} - -// Allpass filter implementation - -allpass::allpass() { - bufidx = 0; -} - -void allpass::setbuffer(float *buf, int size) { - buffer = buf; - bufsize = size; -} - -void allpass::mute() { - for (int i = 0; i < bufsize; i++) - buffer[i] = 0; -} - -void allpass::setfeedback(float val) { - feedback = val; -} - -float allpass::getfeedback() { - return feedback; -} - -// Reverb model implementation - -revmodel::revmodel() { - // Tie the components to their buffers - combL[0].setbuffer(bufcombL1,combtuningL1); - combR[0].setbuffer(bufcombR1,combtuningR1); - combL[1].setbuffer(bufcombL2,combtuningL2); - combR[1].setbuffer(bufcombR2,combtuningR2); - combL[2].setbuffer(bufcombL3,combtuningL3); - combR[2].setbuffer(bufcombR3,combtuningR3); - combL[3].setbuffer(bufcombL4,combtuningL4); - combR[3].setbuffer(bufcombR4,combtuningR4); - combL[4].setbuffer(bufcombL5,combtuningL5); - combR[4].setbuffer(bufcombR5,combtuningR5); - combL[5].setbuffer(bufcombL6,combtuningL6); - combR[5].setbuffer(bufcombR6,combtuningR6); - combL[6].setbuffer(bufcombL7,combtuningL7); - combR[6].setbuffer(bufcombR7,combtuningR7); - combL[7].setbuffer(bufcombL8,combtuningL8); - combR[7].setbuffer(bufcombR8,combtuningR8); - allpassL[0].setbuffer(bufallpassL1,allpasstuningL1); - allpassR[0].setbuffer(bufallpassR1,allpasstuningR1); - allpassL[1].setbuffer(bufallpassL2,allpasstuningL2); - allpassR[1].setbuffer(bufallpassR2,allpasstuningR2); - allpassL[2].setbuffer(bufallpassL3,allpasstuningL3); - allpassR[2].setbuffer(bufallpassR3,allpasstuningR3); - allpassL[3].setbuffer(bufallpassL4,allpasstuningL4); - allpassR[3].setbuffer(bufallpassR4,allpasstuningR4); - - // Set default values - allpassL[0].setfeedback(0.5f); - allpassR[0].setfeedback(0.5f); - allpassL[1].setfeedback(0.5f); - allpassR[1].setfeedback(0.5f); - allpassL[2].setfeedback(0.5f); - allpassR[2].setfeedback(0.5f); - allpassL[3].setfeedback(0.5f); - allpassR[3].setfeedback(0.5f); - setwet(initialwet); - setroomsize(initialroom); - setdry(initialdry); - setdamp(initialdamp); - setwidth(initialwidth); - setmode(initialmode); - - // Buffer will be full of rubbish - so we MUST mute them - mute(); -} - -void revmodel::mute() { - int i; - - if (getmode() >= freezemode) - return; - - for (i = 0; i < numcombs; i++) { - combL[i].mute(); - combR[i].mute(); - } - - for (i = 0; i < numallpasses; i++) { - allpassL[i].mute(); - allpassR[i].mute(); - } -} - -void revmodel::processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) { - float outL, outR, input; - - while(numsamples-- > 0) { - int i; - - outL = outR = 0; - input = (*inputL + *inputR) * gain; - - // Accumulate comb filters in parallel - for (i = 0; i < numcombs; i++) { - outL += combL[i].process(input); - outR += combR[i].process(input); - } - - // Feed through allpasses in series - for (i = 0; i < numallpasses; i++) { - outL = allpassL[i].process(outL); - outR = allpassR[i].process(outR); - } - - // Calculate output REPLACING anything already there - *outputL = outL * wet1 + outR * wet2 + *inputL * dry; - *outputR = outR * wet1 + outL * wet2 + *inputR * dry; - - // Increment sample pointers, allowing for interleave (if any) - inputL += skip; - inputR += skip; - outputL += skip; - outputR += skip; - } -} - -void revmodel::processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) { - float outL, outR, input; - - while (numsamples-- > 0) { - int i; - - outL = outR = 0; - input = (*inputL + *inputR) * gain; - - // Accumulate comb filters in parallel - for (i = 0; i < numcombs; i++) { - outL += combL[i].process(input); - outR += combR[i].process(input); - } - - // Feed through allpasses in series - for (i = 0; i < numallpasses; i++) { - outL = allpassL[i].process(outL); - outR = allpassR[i].process(outR); - } - - // Calculate output MIXING with anything already there - *outputL += outL * wet1 + outR * wet2 + *inputL * dry; - *outputR += outR * wet1 + outL * wet2 + *inputR * dry; - - // Increment sample pointers, allowing for interleave (if any) - inputL += skip; - inputR += skip; - outputL += skip; - outputR += skip; - } -} - -void revmodel::update() { - // Recalculate internal values after parameter change - - int i; - - wet1 = wet * (width / 2 + 0.5f); - wet2 = wet * ((1 - width) / 2); - - if (mode >= freezemode) { - roomsize1 = 1; - damp1 = 0; - gain = muted; - } else { - roomsize1 = roomsize; - damp1 = damp; - gain = fixedgain; - } - - for (i = 0; i < numcombs; i++) { - combL[i].setfeedback(roomsize1); - combR[i].setfeedback(roomsize1); - } - - for (i = 0; i < numcombs; i++) { - combL[i].setdamp(damp1); - combR[i].setdamp(damp1); - } -} - -// The following get/set functions are not inlined, because -// speed is never an issue when calling them, and also -// because as you develop the reverb model, you may -// wish to take dynamic action when they are called. - -void revmodel::setroomsize(float value) { - roomsize = (value * scaleroom) + offsetroom; - update(); -} - -float revmodel::getroomsize() { - return (roomsize - offsetroom) / scaleroom; -} - -void revmodel::setdamp(float value) { - damp = value * scaledamp; - update(); -} - -float revmodel::getdamp() { - return damp / scaledamp; -} - -void revmodel::setwet(float value) { - wet = value * scalewet; - update(); -} - -float revmodel::getwet() { - return wet / scalewet; -} - -void revmodel::setdry(float value) { - dry = value * scaledry; -} - -float revmodel::getdry() { - return dry / scaledry; -} - -void revmodel::setwidth(float value) { - width = value; - update(); -} - -float revmodel::getwidth() { - return width; -} - -void revmodel::setmode(float value) { - mode = value; - update(); -} - -float revmodel::getmode() { - if (mode >= freezemode) - return 1; - else - return 0; -} diff --git a/backends/midi/mt32/freeverb.h b/backends/midi/mt32/freeverb.h deleted file mode 100644 index d618f79670..0000000000 --- a/backends/midi/mt32/freeverb.h +++ /dev/null @@ -1,240 +0,0 @@ -/* ScummVM - Scumm Interpreter - * Copyright (C) 2004 The ScummVM project - * Copyright (C) 2000 Jezar at Dreampoint - * - * This code is public domain - * - * Parts of this code are: - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * 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 General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * $Header$ - * - */ - -// Macro for killing denormalled numbers -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// Based on IS_DENORMAL macro by Jon Watte -// This code is public domain - -#ifndef FREEVERB_H -#define FREEVERB_H - -#define undenormalise(sample) if (((*(unsigned int*)&sample) & 0x7f800000) == 0) sample = 0.0f - -// Comb filter class declaration - -class comb { -public: - comb(); - void setbuffer(float *buf, int size); - inline float process(float inp); - void mute(); - void setdamp(float val); - float getdamp(); - void setfeedback(float val); - float getfeedback(); -private: - float feedback; - float filterstore; - float damp1; - float damp2; - float *buffer; - int bufsize; - int bufidx; -}; - - -// Big to inline - but crucial for speed - -inline float comb::process(float input) { - float output; - - output = buffer[bufidx]; - undenormalise(output); - - filterstore = (output * damp2) + (filterstore * damp1); - undenormalise(filterstore); - - buffer[bufidx] = input + (filterstore * feedback); - - if (++bufidx >= bufsize) - bufidx = 0; - - return output; -} - -// Allpass filter declaration - -class allpass { -public: - allpass(); - void setbuffer(float *buf, int size); - inline float process(float inp); - void mute(); - void setfeedback(float val); - float getfeedback(); -private: - float feedback; - float *buffer; - int bufsize; - int bufidx; -}; - - -// Big to inline - but crucial for speed - -inline float allpass::process(float input) { - float output; - float bufout; - - bufout = buffer[bufidx]; - undenormalise(bufout); - - output = -input + bufout; - buffer[bufidx] = input + (bufout * feedback); - - if (++bufidx >= bufsize) - bufidx = 0; - - return output; -} - - -// Reverb model tuning values - -const int numcombs = 8; -const int numallpasses = 4; -const float muted = 0; -const float fixedgain = 0.015f; -const float scalewet = 3; -const float scaledry = 2; -const float scaledamp = 0.4f; -const float scaleroom = 0.28f; -const float offsetroom = 0.7f; -const float initialroom = 0.5f; -const float initialdamp = 0.5f; -const float initialwet = 1 / scalewet; -const float initialdry = 0; -const float initialwidth = 1; -const float initialmode = 0; -const float freezemode = 0.5f; -const int stereospread = 23; - -// These values assume 44.1KHz sample rate -// they will probably be OK for 48KHz sample rate -// but would need scaling for 96KHz (or other) sample rates. -// The values were obtained by listening tests. -const int combtuningL1 = 1116; -const int combtuningR1 = 1116 + stereospread; -const int combtuningL2 = 1188; -const int combtuningR2 = 1188 + stereospread; -const int combtuningL3 = 1277; -const int combtuningR3 = 1277 + stereospread; -const int combtuningL4 = 1356; -const int combtuningR4 = 1356 + stereospread; -const int combtuningL5 = 1422; -const int combtuningR5 = 1422 + stereospread; -const int combtuningL6 = 1491; -const int combtuningR6 = 1491 + stereospread; -const int combtuningL7 = 1557; -const int combtuningR7 = 1557 + stereospread; -const int combtuningL8 = 1617; -const int combtuningR8 = 1617 + stereospread; -const int allpasstuningL1 = 556; -const int allpasstuningR1 = 556 + stereospread; -const int allpasstuningL2 = 441; -const int allpasstuningR2 = 441 + stereospread; -const int allpasstuningL3 = 341; -const int allpasstuningR3 = 341 + stereospread; -const int allpasstuningL4 = 225; -const int allpasstuningR4 = 225 + stereospread; - - -// Reverb model declaration - -class revmodel { -public: - revmodel(); - void mute(); - void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); - void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); - void setroomsize(float value); - float getroomsize(); - void setdamp(float value); - float getdamp(); - void setwet(float value); - float getwet(); - void setdry(float value); - float getdry(); - void setwidth(float value); - float getwidth(); - void setmode(float value); - float getmode(); -private: - void update(); - - float gain; - float roomsize, roomsize1; - float damp, damp1; - float wet, wet1, wet2; - float dry; - float width; - float mode; - - // The following are all declared inline - // to remove the need for dynamic allocation - // with its subsequent error-checking messiness - - // Comb filters - comb combL[numcombs]; - comb combR[numcombs]; - - // Allpass filters - allpass allpassL[numallpasses]; - allpass allpassR[numallpasses]; - - // Buffers for the combs - float bufcombL1[combtuningL1]; - float bufcombR1[combtuningR1]; - float bufcombL2[combtuningL2]; - float bufcombR2[combtuningR2]; - float bufcombL3[combtuningL3]; - float bufcombR3[combtuningR3]; - float bufcombL4[combtuningL4]; - float bufcombR4[combtuningR4]; - float bufcombL5[combtuningL5]; - float bufcombR5[combtuningR5]; - float bufcombL6[combtuningL6]; - float bufcombR6[combtuningR6]; - float bufcombL7[combtuningL7]; - float bufcombR7[combtuningR7]; - float bufcombL8[combtuningL8]; - float bufcombR8[combtuningR8]; - - // Buffers for the allpasses - float bufallpassL1[allpasstuningL1]; - float bufallpassR1[allpasstuningR1]; - float bufallpassL2[allpasstuningL2]; - float bufallpassR2[allpasstuningR2]; - float bufallpassL3[allpasstuningL3]; - float bufallpassR3[allpasstuningR3]; - float bufallpassL4[allpasstuningL4]; - float bufallpassR4[allpasstuningR4]; -}; - -#endif diff --git a/backends/midi/mt32/i386.cpp b/backends/midi/mt32/i386.cpp deleted file mode 100644 index e2e4b0f790..0000000000 --- a/backends/midi/mt32/i386.cpp +++ /dev/null @@ -1,817 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "mt32emu.h" - -#ifdef MT32EMU_HAVE_X86 - -namespace MT32Emu { - -#ifndef _MSC_VER - -#define eflag(value) __asm__ __volatile__("pushfl \n popfl \n" : : "a"(value)) -#define cpuid_flag (1 << 21) - -static inline bool atti386_DetectCPUID() { - unsigned int result; - - // Is there a cpuid? - result = cpuid_flag; // set test - eflag(result); - if (!(result & cpuid_flag)) - return false; - - result = 0; // clear test - eflag(result); - if (result & cpuid_flag) - return false; - - return true; -} - -static inline bool atti386_DetectSIMD() { - unsigned int result; - - if (atti386_DetectCPUID() == false) - return false; - - /* check cpuid */ - __asm__ __volatile__( - "movl $1, %%eax \n" \ - "cpuid \n" \ - "movl %%edx, %0 \n" \ - : "=r"(result) : : "eax", "ebx", "ecx", "edx"); - - if (result & (1 << 25)) - return true; - - return false; -} - -static inline bool atti386_Detect3DNow() { - unsigned int result; - - if (atti386_DetectCPUID() == false) - return false; - - // get cpuid - __asm__ __volatile__( - "movl $0x80000001, %%eax \n" \ - "cpuid \n" \ - "movl %%edx, %0 \n" \ - : "=r"(result) : : "eax", "ebx", "ecx", "edx"); - - if (result & 0x80000000) - return true; - - return false; -} - - -static inline float atti386_iir_filter_sse(float *output, float *hist1_ptr, float *coef_ptr) { - __asm__ __volatile__ ( - "pushl %1 \n" \ - "pushl %2 \n" \ - "movss 0(%0), %%xmm1 \n" \ - "movups 0(%1), %%xmm2 \n" \ - "movlps 0(%2), %%xmm3 \n" \ - " \n" \ - "shufps $0x44, %%xmm3, %%xmm3 \n" \ - " \n" \ - "mulps %%xmm3, %%xmm2 \n" \ - " \n" \ - "subss %%xmm2, %%xmm1 \n" \ - "shufps $0x39, %%xmm2, %%xmm2 \n" \ - "subss %%xmm2, %%xmm1 \n" \ - " \n" \ - "movss %%xmm1, 0(%2) \n" \ - " \n" \ - "shufps $0x39, %%xmm2, %%xmm2 \n" \ - "addss %%xmm2, %%xmm1 \n" \ - " \n" \ - "shufps $0x39, %%xmm2, %%xmm2 \n" \ - "addss %%xmm2, %%xmm1 \n" \ - " \n" \ - "movss %%xmm3, 4(%2) \n" \ - " \n" \ - "addl $16, %1 \n" \ - "addl $8, %2 \n" \ - " \n" \ - "movups 0(%1), %%xmm2 \n" \ - " \n" \ - "movlps 0(%2), %%xmm3 \n" \ - "shufps $0x44, %%xmm3, %%xmm3 \n" \ - " \n" \ - "mulps %%xmm3, %%xmm2 \n" \ - " \n" \ - "subss %%xmm2, %%xmm1 \n" \ - "shufps $0x39, %%xmm2, %%xmm2 \n" \ - "subss %%xmm2, %%xmm1 \n" \ - " \n" \ - "movss %%xmm1, 0(%2) \n" \ - " \n" \ - "shufps $0x39, %%xmm2, %%xmm2 \n" \ - "addss %%xmm2, %%xmm1 \n" \ - " \n" \ - "shufps $0x39, %%xmm2, %%xmm2 \n" \ - "addss %%xmm2, %%xmm1 \n" \ - " \n" \ - "movss %%xmm3, 4(%2) \n" \ - "movss %%xmm1, 0(%0) \n" \ - "popl %2 \n" \ - "popl %1 \n" \ - : : "r"(output), "r"(coef_ptr), "r"(hist1_ptr) - : "xmm1", "xmm2", "xmm3", "memory"); - - return *output; -} - -static inline float atti386_iir_filter_3DNow(float output, float *hist1_ptr, float *coef_ptr) { - float tmp; - - __asm__ __volatile__ ( - "movq %0, %%mm1 \n" \ - " \n" \ - "movl %1, %%ebx \n" \ - "movq 0(%%ebx), %%mm2 \n" \ - " \n" \ - "movl %2, %%eax; \n" \ - "movq 0(%%eax), %%mm3 \n" \ - " \n" \ - "pfmul %%mm3, %%mm2 \n" \ - "pfsub %%mm2, %%mm1 \n" \ - " \n" \ - "psrlq $32, %%mm2 \n" \ - "pfsub %%mm2, %%mm1 \n" \ - " \n" \ - "movd %%mm1, %3 \n" \ - " \n" \ - "addl $8, %%ebx \n" \ - "movq 0(%%ebx), %%mm2 \n" \ - "movq 0(%%eax), %%mm3 \n" \ - " \n" \ - "pfmul %%mm3, %%mm2 \n" \ - "pfadd %%mm2, %%mm1 \n" \ - " \n" \ - "psrlq $32, %%mm2 \n" \ - "pfadd %%mm2, %%mm1 \n" \ - " \n" \ - "pushl %3 \n" \ - "popl 0(%%eax) \n" \ - " \n" \ - "movd %%mm3, 4(%%eax) \n" \ - " \n" \ - "addl $8, %%ebx \n" \ - "addl $8, %%eax \n" \ - " \n" \ - "movq 0(%%ebx), %%mm2 \n" \ - "movq 0(%%eax), %%mm3 \n" \ - " \n" \ - "pfmul %%mm3, %%mm2 \n" \ - "pfsub %%mm2, %%mm1 \n" \ - " \n" \ - "psrlq $32, %%mm2 \n" \ - "pfsub %%mm2, %%mm1 \n" \ - " \n" \ - "movd %%mm1, %3 \n" \ - " \n" \ - "addl $8, %%ebx \n" \ - "movq 0(%%ebx), %%mm2 \n" \ - "movq 0(%%eax), %%mm3 \n" \ - " \n" \ - "pfmul %%mm3, %%mm2 \n" \ - "pfadd %%mm2, %%mm1 \n" \ - " \n" \ - "psrlq $32, %%mm2 \n" \ - "pfadd %%mm2, %%mm1 \n" \ - " \n" \ - "pushl %3 \n" \ - "popl 0(%%eax) \n" \ - "movd %%mm3, 4(%%eax) \n" \ - " \n" \ - "movd %%mm1, %0 \n" \ - "femms \n" \ - : "=m"(output) : "g"(coef_ptr), "g"(hist1_ptr), "m"(tmp) - : "eax", "ebx", "mm1", "mm2", "mm3", "memory"); - - return output; -} - -static inline void atti386_produceOutput1(int tmplen, Bit16s myvolume, Bit16s *useBuf, Bit16s *snd) { - __asm__ __volatile__( - "movl %0, %%ecx \n" \ - "movw %1, %%ax \n" \ - "shll $16, %%eax \n" \ - "movw %1, %%ax \n" \ - "movd %%eax, %%mm3 \n" \ - "movd %%eax, %%mm2 \n" \ - "psllq $32, %%mm3 \n" \ - "por %%mm2, %%mm3 \n" \ - "movl %2, %%esi \n" \ - "movl %3, %%edi \n" \ - "1: \n" \ - "movq 0(%%esi), %%mm1 \n" \ - "movq 0(%%edi), %%mm2 \n" \ - "pmulhw %%mm3, %%mm1 \n" \ - "paddw %%mm2, %%mm1 \n" \ - "movq %%mm1, 0(%%edi) \n" \ - " \n" \ - "addl $8, %%esi \n" \ - "addl $8, %%edi \n" \ - " \n" \ - "decl %%ecx \n" \ - "cmpl $0, %%ecx \n" \ - "jg 1b \n" \ - "emms \n" \ - : : "g"(tmplen), "g"(myvolume), "g"(useBuf), "g"(snd) - : "eax", "ecx", "edi", "esi", "mm1", "mm2", "mm3", "memory"); -} - -static inline void atti386_produceOutput2(Bit32u len, Bit16s *snd, float *sndbufl, float *sndbufr, float *multFactor) { - __asm__ __volatile__( - "movl %4, %%ecx \n" \ - "shrl $1, %%ecx \n" \ - "addl $4, %%ecx \n" \ - "pushl %%ecx \n" \ - " \n" \ - "movl %0, %%esi \n" \ - "movups 0(%%esi), %%xmm1 \n" \ - " \n" \ - "movl %1, %%esi \n" \ - "movl %2, %%edi \n" \ - "1: \n" \ - "xorl %%eax, %%eax \n" \ - "movw 0(%1), %%ax \n" \ - "cwde \n" \ - "incl %1 \n" \ - "incl %1 \n" \ - "movd %%eax, %%mm1 \n" \ - "psrlq $32, %%mm1 \n" \ - "movw 0(%1), %%ax \n" \ - "incl %1 \n" \ - "incl %1 \n" \ - "movd %%eax, %%mm2 \n" \ - "por %%mm2, %%mm1 \n" \ - " \n" \ - "decl %%ecx \n" \ - "jnz 1b \n" \ - " \n" \ - "popl %%ecx \n" \ - "movl %1, %%esi \n" \ - "movl %3, %%edi \n" \ - "incl %%esi \n" \ - "2: \n" \ - "decl %%ecx \n" \ - "jnz 2b \n" \ - : : "g"(multFactor), "r"(snd), "g"(sndbufl), "g"(sndbufr), "g"(len) - : "eax", "ecx", "edi", "esi", "mm1", "mm2", "xmm1", "memory"); -} - -static inline void atti386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) { - __asm__ __volatile__( - "movl %0, %%ecx \n" \ - "movl %1, %%esi \n" \ - "movl %2, %%edi \n" \ - "1: \n" \ - "movq 0(%%edi), %%mm1 \n" \ - "movq 0(%%esi), %%mm2 \n" \ - "paddw %%mm2, %%mm1 \n" \ - "movq %%mm1, 0(%%esi) \n" \ - "addl $8, %%edi \n" \ - "addl $8, %%esi \n" \ - "decl %%ecx \n" \ - "cmpl $0, %%ecx \n" \ - "jg 1b \n" \ - "emms \n" \ - : : "g"(len), "g"(buf1), "g"(buf2) - : "ecx", "edi", "esi", "mm1", "mm2", "memory"); -} - -static inline void atti386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) { - __asm__ __volatile__( - "movl %0, %%ecx \n" \ - "movl %1, %%esi \n" \ - "movl %2, %%edi \n" \ - "1: \n" \ - "movq 0(%%esi), %%mm1 \n" \ - "movq 0(%%edi), %%mm2 \n" \ - "movq %%mm1, %%mm3 \n" \ - "pmulhw %%mm2, %%mm1 \n" \ - "paddw %%mm3, %%mm1 \n" \ - "movq %%mm1, 0(%%esi) \n" \ - "addl $8, %%edi \n" \ - "addl $8, %%esi \n" \ - "decl %%ecx \n" \ - "cmpl $0, %%ecx \n" \ - "jg 1b \n" \ - "emms \n" \ - : : "g"(len), "g"(buf1), "g"(buf2) - : "ecx", "edi", "esi", "mm1", "mm2", "mm3", "memory"); -} - -static inline void atti386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) { - __asm__ __volatile__( - "movl %0, %%ecx \n" \ - "movl %1, %%esi \n" \ - "movl %2, %%edi \n" \ - "1: \n" \ - "movq 0(%%esi), %%mm1 \n" \ - "movq 0(%%edi), %%mm2 \n" \ - "pmulhw %%mm2, %%mm1 \n" \ - "movq %%mm1, 0(%%esi) \n" \ - "addl $8, %%edi \n" \ - "addl $8, %%esi \n" \ - "decl %%ecx \n" \ - "cmpl $0, %%ecx \n" \ - "jg 1b \n" \ - "emms \n" \ - : : "g"(len), "g"(buf1), "g"(buf2) - : "ecx", "edi", "esi", "mm1", "mm2", "memory"); -} - -static inline void atti386_partialProductOutput(int quadlen, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *p1buf) { - __asm__ __volatile__( - "movl %0, %%ecx \n" \ - "movw %1, %%ax \n" \ - "shll $16, %%eax \n" \ - "movw %2, %%ax \n" \ - "movd %%eax, %%mm1 \n" \ - "movd %%eax, %%mm2 \n" \ - "psllq $32, %%mm1 \n" \ - "por %%mm2, %%mm1 \n" \ - "movl %3, %%edi \n" \ - "movl %4, %%esi \n" \ - "1: \n" \ - "movw 0(%%esi), %%bx \n" \ - "addl $2, %%esi \n" \ - "movw 0(%%esi), %%dx \n" \ - "addl $2, %%esi \n" \ - "" \ - "movw %%dx, %%ax \n" \ - "shll $16, %%eax \n" \ - "movw %%dx, %%ax \n" \ - "movd %%eax, %%mm2 \n" \ - "psllq $32, %%mm2 \n" \ - "movw %%bx, %%ax \n" \ - "shll $16, %%eax \n" \ - "movw %%bx, %%ax \n" \ - "movd %%eax, %%mm3 \n" \ - "por %%mm3, %%mm2 \n" \ - "" \ - "pmulhw %%mm1, %%mm2 \n" \ - "movq %%mm2, 0(%%edi) \n" \ - "addl $8, %%edi \n" \ - "" \ - "decl %%ecx \n" \ - "cmpl $0, %%ecx \n" \ - "jg 1b \n" \ - "emms \n" \ - : : "g"(quadlen), "g"(leftvol), "g"(rightvol), "g"(partialBuf), "g"(p1buf) - : "eax", "ebx", "ecx", "edx", "edi", "esi", "mm1", "mm2", "mm3", "memory"); -} - -#endif - -bool DetectSIMD() { -#ifdef _MSC_VER - bool found_simd; - __asm { - pushfd - pop eax // get EFLAGS into eax - mov ebx,eax // keep a copy - xor eax,0x200000 - // toggle CPUID bit - - push eax - popfd // set new EFLAGS - pushfd - pop eax // EFLAGS back into eax - - xor eax,ebx - // have we changed the ID bit? - - je NO_SIMD - // No, no CPUID instruction - - // we could toggle the - // ID bit so CPUID is present - mov eax,1 - - cpuid // get processor features - test edx,1<<25 // check the SIMD bit - jz NO_SIMD - mov found_simd,1 - jmp DONE - NO_SIMD: - mov found_simd,0 - DONE: - } - return found_simd; -#else - return atti386_DetectSIMD(); -#endif -} - -bool Detect3DNow() { -#ifdef _MSC_VER - bool found3D = false; - __asm { - pushfd - pop eax - mov edx, eax - xor eax, 00200000h - push eax - popfd - pushfd - pop eax - xor eax, edx - jz NO_3DNOW - - mov eax, 80000000h - cpuid - - cmp eax, 80000000h - jbe NO_3DNOW - - mov eax, 80000001h - cpuid - test edx, 80000000h - jz NO_3DNOW - mov found3D, 1 -NO_3DNOW: - - } - return found3D; -#else - return atti386_Detect3DNow(); -#endif -} - -float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr, int revLevel) { - float output; - - // 1st number of coefficients array is overall input scale factor, or filter gain - output = input * (*coef_ptr++); - -#ifdef _MSC_VER - __asm { - - movss xmm1, output - - mov eax, coef_ptr - movups xmm2, [eax] - - mov eax, hist1_ptr - movlps xmm3, [eax] - shufps xmm3, xmm3, 44h - // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr - - mulps xmm2, xmm3 - - subss xmm1, xmm2 - // Rotate elements right - shufps xmm2, xmm2, 39h - subss xmm1, xmm2 - - // Store new_hist - movss DWORD PTR [eax], xmm1 - - // Rotate elements right - shufps xmm2, xmm2, 39h - addss xmm1, xmm2 - - // Rotate elements right - shufps xmm2, xmm2, 39h - addss xmm1, xmm2 - - // Store previous hist - movss DWORD PTR [eax+4], xmm3 - - add coef_ptr, 16 - add hist1_ptr, 8 - - mov eax, coef_ptr - movups xmm2, [eax] - - mov eax, hist1_ptr - movlps xmm3, [eax] - shufps xmm3, xmm3, 44h - // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr - - mulps xmm2, xmm3 - - subss xmm1, xmm2 - // Rotate elements right - shufps xmm2, xmm2, 39h - subss xmm1, xmm2 - - // Store new_hist - movss DWORD PTR [eax], xmm1 - - // Rotate elements right - shufps xmm2, xmm2, 39h - addss xmm1, xmm2 - - // Rotate elements right - shufps xmm2, xmm2, 39h - addss xmm1, xmm2 - - // Store previous hist - movss DWORD PTR [eax+4], xmm3 - - movss output, xmm1 - } -#else - output = atti386_iir_filter_sse(&output, hist1_ptr, coef_ptr); -#endif - output *= ResonInv[revLevel]; - return output; -} - -float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel) { - float output; - - // 1st number of coefficients array is overall input scale factor, or filter gain - output = input * (*coef_ptr++); - - // I find it very sad that 3DNow requires twice as many instructions as Intel's SSE - // Intel does have the upper hand here. -#ifdef _MSC_VER - float tmp; - __asm { - movq mm1, output - mov ebx, coef_ptr - movq mm2, [ebx] - - mov eax, hist1_ptr; - movq mm3, [eax] - - pfmul mm2, mm3 - pfsub mm1, mm2 - - psrlq mm2, 32 - pfsub mm1, mm2 - - // Store new hist - movd tmp, mm1 - - add ebx, 8 - movq mm2, [ebx] - movq mm3, [eax] - - pfmul mm2, mm3 - pfadd mm1, mm2 - - psrlq mm2, 32 - pfadd mm1, mm2 - - push tmp - pop DWORD PTR [eax] - - movd DWORD PTR [eax+4], mm3 - - add ebx, 8 - add eax, 8 - - movq mm2, [ebx] - movq mm3, [eax] - - pfmul mm2, mm3 - pfsub mm1, mm2 - - psrlq mm2, 32 - pfsub mm1, mm2 - - // Store new hist - movd tmp, mm1 - - add ebx, 8 - movq mm2, [ebx] - movq mm3, [eax] - - pfmul mm2, mm3 - pfadd mm1, mm2 - - psrlq mm2, 32 - pfadd mm1, mm2 - - push tmp - pop DWORD PTR [eax] - movd DWORD PTR [eax+4], mm3 - - movd output, mm1 - - femms - } -#else - output = atti386_iir_filter_3DNow(output, hist1_ptr, coef_ptr); -#endif - output *= ResonInv[revLevel]; - return output; -} - -#if MT32EMU_USE_MMX > 0 - -int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf) { - int tmplen = len >> 1; - if (tmplen == 0) { - return 0; - } -#ifdef _MSC_VER - __asm { - mov ecx,tmplen - mov ax, leftvol - shl eax,16 - mov ax, rightvol - movd mm1, eax - movd mm2, eax - psllq mm1, 32 - por mm1, mm2 - mov edi, partialBuf - mov esi, mixedBuf -mmxloop1: - mov bx, [esi] - add esi,2 - mov dx, [esi] - add esi,2 - - mov ax, dx - shl eax, 16 - mov ax, dx - movd mm2,eax - psllq mm2, 32 - mov ax, bx - shl eax, 16 - mov ax, bx - movd mm3,eax - por mm2,mm3 - - pmulhw mm2, mm1 - movq [edi], mm2 - add edi, 8 - - dec ecx - cmp ecx,0 - jg mmxloop1 - emms - } -#else - atti386_partialProductOutput(tmplen, leftvol, rightvol, partialBuf, mixedBuf); -#endif - return tmplen << 1; -} - -int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) { - int tmplen = len >> 2; - if (tmplen == 0) { - return 0; - } -#ifdef _MSC_VER - __asm { - mov ecx, tmplen - mov esi, buf1 - mov edi, buf2 - -mixloop1: - movq mm1, [edi] - movq mm2, [esi] - paddw mm1,mm2 - movq [esi],mm1 - add edi,8 - add esi,8 - - dec ecx - cmp ecx,0 - jg mixloop1 - emms - } -#else - atti386_mixBuffers(buf1, buf2, tmplen); -#endif - return tmplen << 2; -} - - -int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) { - int tmplen = len >> 2; - if (tmplen == 0) { - return 0; - } -#ifdef _MSC_VER - __asm { - mov ecx, tmplen - mov esi, buf1 - mov edi, buf2 - -mixloop2: - movq mm1, [esi] - movq mm2, [edi] - movq mm3, mm1 - pmulhw mm1, mm2 - paddw mm1,mm3 - movq [esi],mm1 - add edi,8 - add esi,8 - - dec ecx - cmp ecx,0 - jg mixloop2 - emms - } -#else - atti386_mixBuffersRingMix(buf1, buf2, tmplen); -#endif - return tmplen << 2; -} - -int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) { - int tmplen = len >> 2; - if (tmplen == 0) { - return 0; - } -#ifdef _MSC_VER - __asm { - mov ecx, tmplen - mov esi, buf1 - mov edi, buf2 - -mixloop3: - movq mm1, [esi] - movq mm2, [edi] - pmulhw mm1, mm2 - movq [esi],mm1 - add edi,8 - add esi,8 - - dec ecx - cmp ecx,0 - jg mixloop3 - emms - } -#else - atti386_mixBuffersRing(buf1, buf2, tmplen); -#endif - return tmplen << 2; -} - -int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) { - int tmplen = (len >> 1); - if (tmplen == 0) { - return 0; - } -#ifdef _MSC_VER - __asm { - mov ecx, tmplen - mov ax,volume - shl eax,16 - mov ax,volume - movd mm3,eax - movd mm2,eax - psllq mm3, 32 - por mm3,mm2 - mov esi, useBuf - mov edi, stream -mixloop4: - movq mm1, [esi] - movq mm2, [edi] - pmulhw mm1, mm3 - paddw mm1,mm2 - movq [edi], mm1 - - add esi,8 - add edi,8 - - dec ecx - cmp ecx,0 - jg mixloop4 - emms - } -#else - atti386_produceOutput1(tmplen, volume, useBuf, stream); -#endif - return tmplen << 1; -} - -#endif - -} - -#endif diff --git a/backends/midi/mt32/i386.h b/backends/midi/mt32/i386.h deleted file mode 100644 index c26fcb4110..0000000000 --- a/backends/midi/mt32/i386.h +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef MT32EMU_I386_H -#define MT32EMU_I386_H - -namespace MT32Emu { -#ifdef MT32EMU_HAVE_X86 - -// Function that detects the availablity of SSE SIMD instructions -bool DetectSIMD(); -// Function that detects the availablity of 3DNow instructions -bool Detect3DNow(); - -float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr, int revLevel); -float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel); -float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel); - -#if MT32EMU_USE_MMX > 0 -int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf); -int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len); -int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len); -int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len); -int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume); -#endif - -#endif - -} - -#endif diff --git a/backends/midi/mt32/module.mk b/backends/midi/mt32/module.mk deleted file mode 100644 index 9f0594190f..0000000000 --- a/backends/midi/mt32/module.mk +++ /dev/null @@ -1,17 +0,0 @@ -MODULE := backends/midi/mt32 - -MODULE_OBJS := \ - backends/midi/mt32/mt32_file.o \ - backends/midi/mt32/i386.o \ - backends/midi/mt32/part.o \ - backends/midi/mt32/partial.o \ - backends/midi/mt32/partialManager.o \ - backends/midi/mt32/synth.o \ - backends/midi/mt32/tables.o \ - backends/midi/mt32/freeverb.o - -MODULE_DIRS += \ - backends/midi/mt32 - -# Include common rules -include $(srcdir)/common.rules diff --git a/backends/midi/mt32/mt32_file.cpp b/backends/midi/mt32/mt32_file.cpp deleted file mode 100644 index 8cf4e14a7d..0000000000 --- a/backends/midi/mt32/mt32_file.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include <stdio.h> - -#include "mt32emu.h" - -namespace MT32Emu { - - bool ANSIFile::open(const char *filename, OpenMode mode) { - const char *fmode; - if (mode == OpenMode_read) { - fmode = "rb"; - } else { - fmode = "wb"; - } - fp = fopen(filename, fmode); - return (fp != NULL); - } - - void ANSIFile::close() { - fclose(fp); - } - - size_t ANSIFile::read(void *in, size_t size) { - return fread(in, 1, size, fp); - } - - bool ANSIFile::readLine(char *in, size_t size) { - return fgets(in, (int)size, fp) != NULL; - } - - bool ANSIFile::readBit8u(Bit8u *in) { - int c = fgetc(fp); - if (c == EOF) - return false; - *in = (Bit8u)c; - return true; - } - - bool File::readBit16u(Bit16u *in) { - Bit8u b[2]; - if (read(&b[0], 2) != 2) - return false; - *in = ((b[0] << 8) | b[1]); - return true; - } - - bool File::readBit32u(Bit32u *in) { - Bit8u b[4]; - if (read(&b[0], 4) != 4) - return false; - *in = ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]); - return true; - } - - size_t ANSIFile::write(const void *out, size_t size) { - return fwrite(out, 1, size, fp); - } - - bool ANSIFile::writeBit8u(Bit8u out) { - return fputc(out, fp) != EOF; - } - - bool File::writeBit16u(Bit16u out) { - if (!writeBit8u((Bit8u)((out & 0xFF00) >> 8))) { - return false; - } - if (!writeBit8u((Bit8u)(out & 0x00FF))) { - return false; - } - return true; - } - - bool File::writeBit32u(Bit32u out) { - if (!writeBit8u((Bit8u)((out & 0xFF000000) >> 24))) { - return false; - } - if (!writeBit8u((Bit8u)((out & 0x00FF0000) >> 16))) { - return false; - } - if (!writeBit8u((Bit8u)((out & 0x0000FF00) >> 8))) { - return false; - } - if (!writeBit8u((Bit8u)(out & 0x000000FF))) { - return false; - } - return true; - } - - bool ANSIFile::isEOF() { - return feof(fp) != 0; - } -} diff --git a/backends/midi/mt32/mt32_file.h b/backends/midi/mt32/mt32_file.h deleted file mode 100644 index 7ebb6449b6..0000000000 --- a/backends/midi/mt32/mt32_file.h +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef MT32EMU_FILE_H -#define MT32EMU_FILE_H - -#include <stdio.h> - -namespace MT32Emu { - -class File { -public: - enum OpenMode { - OpenMode_read = 0, - OpenMode_write = 1 - }; - virtual ~File() {} - virtual void close() = 0; - virtual size_t read(void *in, size_t size) = 0; - virtual bool readLine(char *in, size_t size) = 0; - virtual bool readBit8u(Bit8u *in) = 0; - virtual bool readBit16u(Bit16u *in); - virtual bool readBit32u(Bit32u *in); - virtual size_t write(const void *out, size_t size) = 0; - virtual bool writeBit8u(Bit8u out) = 0; - // Note: May write a single byte to the file before failing - virtual bool writeBit16u(Bit16u out); - // Note: May write some (<4) bytes to the file before failing - virtual bool writeBit32u(Bit32u out); - virtual bool isEOF() = 0; -}; - -class ANSIFile: public File { -private: - FILE *fp; -public: - bool open(const char *filename, OpenMode mode); - void close(); - size_t read(void *in, size_t size); - bool readLine(char *in, size_t size); - bool readBit8u(Bit8u *in); - size_t write(const void *out, size_t size); - bool writeBit8u(unsigned char out); - bool isEOF(); -}; - -} - -#endif diff --git a/backends/midi/mt32/mt32emu.h b/backends/midi/mt32/mt32emu.h deleted file mode 100644 index 9fffa721f5..0000000000 --- a/backends/midi/mt32/mt32emu.h +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef MT32EMU_MT32EMU_H -#define MT32EMU_MT32EMU_H - -// Debugging -// Show the instruments played -#define MT32EMU_MONITOR_INSTRUMENTS 1 -// Shows number of partials MT-32 is playing, and on which parts -#define MT32EMU_MONITOR_PARTIALS 0 -// Determines how the waveform cache file is handled (must be regenerated after sampling rate change) -#define MT32EMU_WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache -//#define MT32EMU_WAVECACHEMODE 1 // Load existing cache if possible, otherwise generage but don't save cache -//#define MT32EMU_WAVECACHEMODE 2 // Ignore existing cache, generate and save cache -//#define MT32EMU_WAVECACHEMODE 3 // Ignore existing cache, generate but don't save cache - -// Configuration -// The maximum number of partials playing simultaneously -#define MT32EMU_MAX_PARTIALS 32 -// The maximum number of notes playing simultaneously per part. -// No point making it more than MT32EMU_MAX_PARTIALS, since each note needs at least one partial. -#define MT32EMU_MAX_POLY 32 -// This calculates the exact frequencies of notes as they are played, instead of offsetting from pre-cached semitones. Potentially very slow. -#define MT32EMU_ACCURATENOTES 0 - -#if (defined (_MSC_VER) && defined(_M_IX86)) -#define MT32EMU_HAVE_X86 -#elif defined(__GNUC__) -#if __GNUC__ >= 3 && defined(__i386__) -#define MT32EMU_HAVE_X86 -#endif -#endif - -#ifdef MT32EMU_HAVE_X86 -#define MT32EMU_USE_MMX 1 -#else -#define MT32EMU_USE_MMX 0 -#endif - -#include "freeverb.h" - -#include "structures.h" -#include "i386.h" -#include "mt32_file.h" -#include "tables.h" -#include "partial.h" -#include "partialManager.h" -#include "part.h" -#include "synth.h" - -#endif diff --git a/backends/midi/mt32/part.cpp b/backends/midi/mt32/part.cpp deleted file mode 100644 index 5aee1202b2..0000000000 --- a/backends/midi/mt32/part.cpp +++ /dev/null @@ -1,603 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include <string.h> -#include <math.h> - -#include "mt32emu.h" - -namespace MT32Emu { - -static const Bit8u PartialStruct[13] = { - 0, 0, 2, 2, 1, 3, - 3, 0, 3, 0, 2, 1, 3 }; - -static const Bit8u PartialMixStruct[13] = { - 0, 1, 0, 1, 1, 0, - 1, 3, 3, 2, 2, 2, 2 }; - -static const float floatKeyfollow[17] = { - -1.0f, -1.0f/2.0f, -1.0f/4.0f, 0.0f, - 1.0f/8.0f, 1.0f/4.0f, 3.0f/8.0f, 1.0f/2.0f, 5.0f/8.0f, 3.0f/4.0f, 7.0f/8.0f, 1.0f, - 5.0f/4.0f, 3.0f/2.0f, 2.0f, - 1.0009765625f, 1.0048828125f -}; - -//FIXME:KG: Put this dpoly stuff somewhere better -bool dpoly::isActive() const { - return partials[0] != NULL || partials[1] != NULL || partials[2] != NULL || partials[3] != NULL; -} - -Bit32u dpoly::getAge() const { - for (int i = 0; i < 4; i++) { - if (partials[i] != NULL) { - return partials[i]->age; - } - } - return 0; -} - -RhythmPart::RhythmPart(Synth *useSynth, unsigned int usePartNum): Part(useSynth, usePartNum) { - strcpy(name, "Rhythm"); - rhythmTemp = &synth->mt32ram.rhythmSettings[0]; - refresh(); -} - -Part::Part(Synth *useSynth, unsigned int usePartNum) { - this->synth = useSynth; - this->partNum = usePartNum; - patchCache[0].dirty = true; - holdpedal = false; - if (usePartNum == 8) { - // Nasty hack for rhythm - patchTemp = NULL; - timbreTemp = NULL; - } else { - sprintf(name, "Part %d", partNum + 1); - patchTemp = &synth->mt32ram.patchSettings[partNum]; - timbreTemp = &synth->mt32ram.timbreSettings[partNum]; - } - currentInstr[0] = 0; - currentInstr[10] = 0; - volume = voltable[102]; //FIXME:KG: Original was just volume=102; I assume this is intended - volumesetting.leftvol = 32767; - volumesetting.rightvol = 32767; - bend = 0.0f; - memset(polyTable,0,sizeof(polyTable)); - memset(patchCache, 0, sizeof(patchCache)); -} - -void Part::setHoldPedal(bool pedalval) { - if (holdpedal && !pedalval) - stopPedalHold(); - holdpedal = pedalval; -} - -void RhythmPart::setBend(unsigned int midiBend) { - synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend); - return; -} - -void Part::setBend(unsigned int midiBend) { - // FIXME:KG: Slightly unbalanced increments, but I wanted min -1.0, centre 0.0 and max 1.0 - if (midiBend <= 0x2000) { - bend = ((signed int)midiBend - 0x2000) / (float)0x2000; - } else { - bend = ((signed int)midiBend - 0x2000) / (float)0x1FFF; - } - // Loop through all partials to update their bend - for (int i = 0; i < MT32EMU_MAX_POLY; i++) { - for (int j = 0; j < 4; j++) { - if (polyTable[i].partials[j] != NULL) { - polyTable[i].partials[j]->setBend(bend); - } - } - } -} - -void RhythmPart::setModulation(unsigned int midiModulation) { - synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, midiModulation); -} - -void Part::setModulation(unsigned int midiModulation) { - // Just a bloody guess, as always, before I get things figured out - for (int t = 0; t < 4; t++) { - if (patchCache[t].playPartial) { - int newrate = (patchCache[t].modsense * midiModulation) >> 7; - //patchCache[t].lfoperiod = lfotable[newrate]; - patchCache[t].lfodepth = newrate; - //FIXME:KG: timbreTemp->partial[t].lfo.depth = - } - } -} - -void RhythmPart::refresh() { - // (Re-)cache all the mapped timbres ahead of time - for (unsigned int drumNum = 0; drumNum < 64; drumNum++) { - int drumTimbreNum = rhythmTemp[drumNum].timbre; - if (drumTimbreNum >= 94) - continue; - Bit16s pan = rhythmTemp[drumNum].panpot; // They use R-L 0-14... - // FIXME:KG: Panning cache should be backed up to partials using it, too - // FIXME:KG: If I don't have left/right mixed up here, it's pure luck - if (pan < 7) { - drumPan[drumNum].leftvol = 32767; - drumPan[drumNum].rightvol = pan * 4681; - } else { - drumPan[drumNum].rightvol = 32767; - drumPan[drumNum].leftvol = (14 - pan) * 4681; - } - PatchCache *cache = drumCache[drumNum]; - backupCacheToPartials(cache); - for (int t = 0; t < 4; t++) { - // Common parameters, stored redundantly - cache[t].dirty = true; - cache[t].pitchShift = 0.0f; - cache[t].benderRange = 0.0f; - cache[t].pansetptr = &drumPan[drumNum]; - cache[t].reverb = rhythmTemp[drumNum].reverbSwitch > 0; - } - } -} - -void Part::refresh() { - backupCacheToPartials(patchCache); - for (int t = 0; t < 4; t++) { - // Common parameters, stored redundantly - patchCache[t].dirty = true; - patchCache[t].pitchShift = (patchTemp->patch.keyShift - 24) + (patchTemp->patch.fineTune - 50) / 100.0f; - patchCache[t].benderRange = patchTemp->patch.benderRange; - patchCache[t].pansetptr = &volumesetting; - patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0; - } - memcpy(currentInstr, timbreTemp->common.name, 10); -} - -void RhythmPart::refreshTimbre(unsigned int absTimbreNum) { - for (int m = 0; m < 64; m++) { - if (rhythmTemp[m].timbre == absTimbreNum - 128) - drumCache[m][0].dirty = true; - } -} - -void Part::refreshTimbre(unsigned int absTimbreNum) { - if (getAbsTimbreNum() == absTimbreNum) { - memcpy(currentInstr, timbreTemp->common.name, 10); - patchCache[0].dirty = true; - } -} - -int Part::fixBiaslevel(int srcpnt, int *dir) { - int noteat = srcpnt & 0x3F; - int outnote; - if (srcpnt < 64) - *dir = 0; - else - *dir = 1; - outnote = 33 + noteat; - //synth->printDebug("Bias note %d, dir %d", outnote, *dir); - - return outnote; -} - -int Part::fixKeyfollow(int srckey) { - if (srckey>=0 && srckey<=16) { - int keyfix[17] = { -256*16, -128*16, -64*16, 0, 32*16, 64*16, 96*16, 128*16, (128+32)*16, 192*16, (192+32)*16, 256*16, (256+64)*16, (256+128)*16, (512)*16, 4100, 4116}; - return keyfix[srckey]; - } else { - //LOG(LOG_ERROR|LOG_MISC,"Missed key: %d", srckey); - return 256; - } -} - -void Part::abortPoly(dpoly *poly) { - if (!poly->isPlaying) { - return; - } - for (int i = 0; i < 4; i++) { - Partial *partial = poly->partials[i]; - if (partial != NULL) { - partial->deactivate(); - } - } - poly->isPlaying = false; -} - -void Part::setPatch(const PatchParam *patch) { - patchTemp->patch = *patch; -} - -void Part::setTimbre(TimbreParam *timbre) { - *timbreTemp = *timbre; -} - -unsigned int RhythmPart::getAbsTimbreNum() const { - synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm"); - return 0; -} - -unsigned int Part::getAbsTimbreNum() const { - return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum; -} - -void RhythmPart::setProgram(unsigned int patchNum) { - synth->printDebug("%s: Attempt to set program (%d) on rhythm is invalid", name, patchNum); -} - -void Part::setProgram(unsigned int patchNum) { - setPatch(&synth->mt32ram.patches[patchNum]); - setTimbre(&synth->mt32ram.timbres[getAbsTimbreNum()].timbre); -#if 0 - // Immediately stop all partials on this part (this is apparently *not* the correct behaviour) - for (int m = 0; m < MT32EMU_MAX_POLY; m++) { - AbortPoly(poly); - } -#endif - - refresh(); - - allStop(); //FIXME:KG: Is this correct? -} - -void Part::backupCacheToPartials(PatchCache cache[4]) { - // check if any partials are still playing with the old patch cache - // if so then duplicate the cached data from the part to the partial so that - // we can change the part's cache without affecting the partial. - // We delay this until now to avoid a copy operation with every note played - for (int m = 0; m < MT32EMU_MAX_POLY; m++) { - for (int i = 0; i < 4; i++) { - Partial *partial = polyTable[m].partials[i]; - if (partial != NULL && partial->patchCache == &cache[i]) { - partial->cachebackup = cache[i]; - partial->patchCache = &partial->cachebackup; - } - } - } -} - -void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) { - backupCacheToPartials(cache); - int partialCount = 0; - for (int t = 0; t < 4; t++) { - if (((timbre->common.pmute >> t) & 0x1) == 1) { - cache[t].playPartial = true; - partialCount++; - } else { - cache[t].playPartial = false; - continue; - } - - // Calculate and cache common parameters - - cache[t].pcm = timbre->partial[t].wg.pcmwave; - cache[t].useBender = (timbre->partial[t].wg.bender == 1); - - switch (t) { - case 0: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x2) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12]; - cache[t].structurePosition = 0; - cache[t].structurePair = 1; - break; - case 1: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x1) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12]; - cache[t].structurePosition = 1; - cache[t].structurePair = 0; - break; - case 2: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x2) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34]; - cache[t].structurePosition = 0; - cache[t].structurePair = 3; - break; - case 3: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x1) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34]; - cache[t].structurePosition = 1; - cache[t].structurePair = 2; - break; - default: - break; - } - - cache[t].waveform = timbre->partial[t].wg.waveform; - cache[t].pulsewidth = timbre->partial[t].wg.pulsewid; - cache[t].pwsens = timbre->partial[t].wg.pwvelo; - if (timbre->partial[t].wg.keyfollow > 16) { - synth->printDebug("Bad keyfollow value in timbre!"); - cache[t].pitchKeyfollow = 1.0f; - } else { - cache[t].pitchKeyfollow = floatKeyfollow[timbre->partial[t].wg.keyfollow]; - } - - cache[t].pitch = timbre->partial[t].wg.coarse + (timbre->partial[t].wg.fine - 50) / 100.0f + 24.0f; - cache[t].pitchEnv = timbre->partial[t].env; - cache[t].pitchEnv.sensitivity = (char)((float)cache[t].pitchEnv.sensitivity * 1.27f); - cache[t].pitchsustain = cache[t].pitchEnv.level[3]; - - // Calculate and cache TVA envelope stuff - cache[t].ampEnv = timbre->partial[t].tva; - for (int i = 0; i < 4; i++) - cache[t].ampEnv.envlevel[i] = (char)((float)cache[t].ampEnv.envlevel[i] * 1.27f); - cache[t].ampEnv.level = (char)((float)cache[t].ampEnv.level * 1.27f); - float tvelo = ((float)timbre->partial[t].tva.velosens / 100.0f); - float velo = fabs(tvelo-0.5f) * 2.0f; - velo *= 63.0f; - cache[t].ampEnv.velosens = (char)velo; - if (tvelo<0.5f) - cache[t].ampenvdir = 1; - else - cache[t].ampenvdir = 0; - - cache[t].ampbias[0] = fixBiaslevel(cache[t].ampEnv.biaspoint1, &cache[t].ampdir[0]); - cache[t].ampblevel[0] = 12 - cache[t].ampEnv.biaslevel1; - cache[t].ampbias[1] = fixBiaslevel(cache[t].ampEnv.biaspoint2, &cache[t].ampdir[1]); - cache[t].ampblevel[1] = 12 - cache[t].ampEnv.biaslevel2; - cache[t].ampdepth = cache[t].ampEnv.envvkf * cache[t].ampEnv.envvkf; - cache[t].ampsustain = cache[t].ampEnv.envlevel[3]; - cache[t].amplevel = cache[t].ampEnv.level; - - // Calculate and cache filter stuff - cache[t].filtEnv = timbre->partial[t].tvf; - cache[t].tvfdepth = cache[t].filtEnv.envdkf; - cache[t].filtkeyfollow = fixKeyfollow(cache[t].filtEnv.keyfollow); - cache[t].filtEnv.envdepth = (char)((float)cache[t].filtEnv.envdepth * 1.27); - cache[t].tvfbias = fixBiaslevel(cache[t].filtEnv.biaspoint, &cache[t].tvfdir); - cache[t].tvfblevel = cache[t].filtEnv.biaslevel; - cache[t].filtsustain = cache[t].filtEnv.envlevel[3]; - - // Calculate and cache LFO stuff - cache[t].lfodepth = timbre->partial[t].lfo.depth; - cache[t].lfoperiod = lfotable[(int)timbre->partial[t].lfo.rate]; - cache[t].lforate = timbre->partial[t].lfo.rate; - cache[t].modsense = timbre->partial[t].lfo.modsense; - } - for (int t = 0; t < 4; t++) { - // Common parameters, stored redundantly - cache[t].dirty = false; - cache[t].partialCount = partialCount; - cache[t].sustain = (timbre->common.nosustain == 0); - } - //synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", cache[0].waveform, cache[1].waveform, cache[2].waveform, cache[3].waveform); - -#if MT32EMU_MONITOR_INSTRUMENTS == 1 - synth->printDebug("%s (%s): Recached timbre", name, currentInstr); - for (int i = 0; i < 4; i++) { - synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, cache[i].playPartial ? "YES" : "NO", cache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmwave, timbre->partial[i].wg.waveform); - } -#endif -} - -const char *Part::getName() const { - return name; -} - -void Part::setVolume(int vol) { - volume = voltable[vol]; -} - -void RhythmPart::setPan(unsigned int midiPan) -{ - // FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct? - synth->printDebug("%s: Setting pan (%d) not supported on rhythm", name, midiPan); -} - -void Part::setPan(unsigned int midiPan) { - // FIXME:KG: Tweaked this a bit so that we have a left 100%, centre and right 100% - // (But this makes the range somewhat skewed) - if (midiPan < 64) { - volumesetting.leftvol = 32767; - volumesetting.rightvol = (Bit16s)(midiPan * 512); - } else if (midiPan == 64) { - volumesetting.leftvol = 32767; - volumesetting.rightvol = 32767; - } else { - volumesetting.rightvol = 32767; - volumesetting.leftvol = (Bit16s)((127 - midiPan) * 520); - } - //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot); -} - -void RhythmPart::playNote(unsigned int key, int vel) { - if (key < 24 || key > 87) { - synth->printDebug("%s: Attempted to play invalid key %d", name, key); - return; - } - int drumNum = key - 24; - int drumTimbreNum = rhythmTemp[drumNum].timbre; - if (drumTimbreNum >= 94) { - synth->printDebug("%s: Attempted to play unmapped key %d", name, key); - return; - } - int absTimbreNum = drumTimbreNum + 128; - TimbreParam *timbre = &synth->mt32ram.timbres[absTimbreNum].timbre; - memcpy(currentInstr, timbre->common.name, 10); -#if MT32EMU_MONITOR_INSTRUMENTS == 1 - synth->printDebug("%s (%s): starting poly (drum %d, timbre %d) - Vel %d Key %d Vol %d", name, currentInstr, drumNum, absTimbreNum, vel, key, volume); -#endif - if (drumCache[drumNum][0].dirty) { - cacheTimbre(drumCache[drumNum], timbre); - } - playPoly(drumCache[drumNum], key, MIDDLEC, vel); -} - -void Part::playNote(unsigned int key, int vel) { - int freqNum = key; - if (freqNum < 12) { - synth->printDebug("%s (%s): Attempted to play invalid key %d < 12; moving up by octave", name, currentInstr, key); - freqNum += 12; - } else if (freqNum > 108) { - synth->printDebug("%s (%s): Attempted to play invalid key %d > 108; moving down by octave", name, currentInstr, key); - while (freqNum > 108) { - freqNum -= 12; - } - } - // POLY1 mode, Single Assign - // Haven't found any software that uses any of the other poly modes - // FIXME:KG: Should this also apply to rhythm? - for (unsigned int i = 0; i < MT32EMU_MAX_POLY; i++) { - if (polyTable[i].isActive() && (polyTable[i].key == key)) { - //AbortPoly(&polyTable[i]); - stopNote(key); - break; - } - } -#if MT32EMU_MONITOR_INSTRUMENTS == 1 - synth->printDebug("%s (%s): starting poly - Vel %d Key %d Vol %d", name, currentInstr, vel, key, volume); -#endif - if (patchCache[0].dirty) { - cacheTimbre(patchCache, timbreTemp); - } - playPoly(patchCache, key, freqNum, vel); -} - -void Part::playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel) { - unsigned int needPartials = cache[0].partialCount; - unsigned int freePartials = synth->partialManager->getFreePartialCount(); - - if (freePartials < needPartials) { - if (!synth->partialManager->freePartials(needPartials - freePartials, partNum)) { - synth->printDebug("%s (%s): Insufficient free partials to play key %d (vel=%d); needed=%d, free=%d", name, currentInstr, key, vel, needPartials, synth->partialManager->getFreePartialCount()); - return; - } - } - // Find free poly - int m; - for (m = 0; m < MT32EMU_MAX_POLY; m++) { - if (!polyTable[m].isActive()) { - break; - } - } - if (m == MT32EMU_MAX_POLY) { - synth->printDebug("%s (%s): No free poly to play key %d (vel %d)", name, currentInstr, key, vel); - return; - } - - dpoly *tpoly = &polyTable[m]; - - tpoly->isPlaying = true; - tpoly->key = key; - tpoly->isDecay = false; - tpoly->freqnum = freqNum; - tpoly->vel = vel; - tpoly->pedalhold = false; - - bool allnull = true; - for (int x = 0; x < 4; x++) { - if (cache[x].playPartial) { - tpoly->partials[x] = synth->partialManager->allocPartial(partNum); - allnull = false; - } else { - tpoly->partials[x] = NULL; - } - } - - if (allnull) - synth->printDebug("%s (%s): No partials to play for this instrument", name, this->currentInstr); - - tpoly->sustain = cache[0].sustain; - tpoly->volumeptr = &volume; - - for (int x = 0; x < 4; x++) { - if (tpoly->partials[x] != NULL) { - tpoly->partials[x]->startPartial(tpoly, &cache[x], tpoly->partials[cache[x].structurePair]); - tpoly->partials[x]->setBend(bend); - } - } -} - -static void startDecayPoly(dpoly *tpoly) { - if (tpoly->isDecay) { - return; - } - tpoly->isDecay = true; - - for (int t = 0; t < 4; t++) { - Partial *partial = tpoly->partials[t]; - if (partial == NULL) - continue; - partial->startDecayAll(); - } - tpoly->isPlaying = false; -} - -void Part::allStop() { - for (int q = 0; q < MT32EMU_MAX_POLY; q++) { - dpoly *tpoly = &polyTable[q]; - if (tpoly->isPlaying) { - startDecayPoly(tpoly); - } - } -} - -void Part::stopPedalHold() { - for (int q = 0; q < MT32EMU_MAX_POLY; q++) { - dpoly *tpoly; - tpoly = &polyTable[q]; - if (tpoly->isActive() && tpoly->pedalhold) - stopNote(tpoly->key); - } -} - -void Part::stopNote(unsigned int key) { - // Non-sustaining instruments ignore stop commands. - // They die away eventually anyway - -#if MT32EMU_MONITOR_INSTRUMENTS == 1 - synth->printDebug("%s (%s): stopping key %d", name, currentInstr, key); -#endif - - if (key != 255) { - for (int q = 0; q < MT32EMU_MAX_POLY; q++) { - dpoly *tpoly = &polyTable[q]; - if (tpoly->isPlaying && tpoly->key == key) { - if (holdpedal) - tpoly->pedalhold = true; - else if (tpoly->sustain) - startDecayPoly(tpoly); - } - } - return; - } - - // Find oldest poly... yes, the MT-32 can be reconfigured to kill different poly first - // This is simplest - int oldest = -1; - Bit32u oldage = 0; - - for (int q = 0; q < MT32EMU_MAX_POLY; q++) { - dpoly *tpoly = &polyTable[q]; - - if (tpoly->isPlaying && !tpoly->isDecay) { - if (tpoly->getAge() >= oldage) { - oldage = tpoly->getAge(); - oldest = q; - } - } - } - - if (oldest != -1) { - startDecayPoly(&polyTable[oldest]); - } -} - -} diff --git a/backends/midi/mt32/part.h b/backends/midi/mt32/part.h deleted file mode 100644 index 1214ec52f9..0000000000 --- a/backends/midi/mt32/part.h +++ /dev/null @@ -1,104 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef MT32EMU_PART_H -#define MT32EMU_PART_H - -namespace MT32Emu { - -class PartialManager; -class Synth; - -class Part { -private: - // Pointers to the areas of the MT-32's memory dedicated to this part (for parts 1-8) - MemParams::PatchTemp *patchTemp; - TimbreParam *timbreTemp; - - // 0=Part 1, .. 7=Part 8, 8=Rhythm - unsigned int partNum; - - bool holdpedal; - - StereoVolume volumesetting; - - PatchCache patchCache[4]; - - float bend; // -1.0 .. +1.0 - - dpoly polyTable[MT32EMU_MAX_POLY]; - - void abortPoly(dpoly *poly); - - static int fixKeyfollow(int srckey); - static int fixBiaslevel(int srcpnt, int *dir); - - void setPatch(const PatchParam *patch); - -protected: - Synth *synth; - char name[8]; // "Part 1".."Part 8", "Rhythm" - char currentInstr[11]; - Bit32u volume; - void backupCacheToPartials(PatchCache cache[4]); - void cacheTimbre(PatchCache cache[4], const TimbreParam *timbre); - void playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel); - const char *getName() const; - -public: - Part(Synth *synth, unsigned int usePartNum); - virtual void playNote(unsigned int key, int vel); - void stopNote(unsigned int key); - void allStop(); - void setVolume(int vol); - virtual void setPan(unsigned int midiPan); - virtual void setBend(unsigned int midiBend); - virtual void setModulation(unsigned int midiModulation); - virtual void setProgram(unsigned int patchNum); - void setHoldPedal(bool pedalval); - void stopPedalHold(); - virtual void refresh(); - virtual void refreshTimbre(unsigned int absTimbreNum); - void setTimbre(TimbreParam *timbre); - virtual unsigned int getAbsTimbreNum() const; -}; - -class RhythmPart: public Part { - // Pointer to the area of the MT-32's memory dedicated to rhythm - const MemParams::RhythmTemp *rhythmTemp; - - // This caches the timbres/settings in use by the rhythm part - PatchCache drumCache[64][4]; - StereoVolume drumPan[64]; -public: - RhythmPart(Synth *synth, unsigned int usePartNum); - void refreshTimbre(unsigned int timbreNum); - void refresh(); - void playNote(unsigned int key, int vel); - unsigned int getAbsTimbreNum() const; - void setPan(unsigned int midiPan); - void setBend(unsigned int midiBend); - void setModulation(unsigned int midiModulation); - void setProgram(unsigned int patchNum); -}; - -} -#endif diff --git a/backends/midi/mt32/partial.cpp b/backends/midi/mt32/partial.cpp deleted file mode 100644 index 32ff9e9af7..0000000000 --- a/backends/midi/mt32/partial.cpp +++ /dev/null @@ -1,912 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include <stdlib.h> -#include <math.h> -#include <string.h> - -#include "mt32emu.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)) - -using namespace MT32Emu; - -Partial::Partial(Synth *useSynth) { - this->synth = useSynth; - ownerPart = -1; - poly = NULL; - pair = NULL; -#if MT32EMU_ACCURATENOTES == 1 - for (int i = 0; i < 3; i++) { - noteLookupStorage.waveforms[i] = new Bit16s[65536]; - } - noteLookup = ¬eLookupStorage; -#endif -} - -Partial::~Partial() { -#if MT32EMU_ACCURATENOTES == 1 - for (int i = 0; i < 3; i++) { - delete[] noteLookupStorage.waveforms[i]; - } -#endif -} - -int Partial::getOwnerPart() { - return ownerPart; -} - -bool Partial::isActive() { - return ownerPart > -1; -} - -void Partial::activate(int part) { - // This just marks the partial as being assigned to a part - ownerPart = part; -} - -void Partial::deactivate() { - ownerPart = -1; - if (poly != NULL) { - for (int i = 0; i < 4; i++) { - if (poly->partials[i] == this) { - poly->partials[i] = NULL; - break; - } - } - if (pair != NULL) { - pair->pair = NULL; - } - } -} - -void Partial::initKeyFollow(int key) { - // Setup partial keyfollow - // Note follow relative to middle C - - // Calculate keyfollow for pitch -#if 1 - float rel = key == -1 ? 0.0f : (key - MIDDLEC); - float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch + patchCache->pitchShift; - //FIXME:KG: Does it truncate the keyfollowed pitch to a semitone (towards MIDDLEC)? - //int newKey = (int)(rel * patchCache->pitchKeyfollow); - //float newPitch = newKey + patchCache->pitch + patchCache->pitchShift; -#else - float rel = key == -1 ? 0.0f : (key + patchCache->pitchShift - MIDDLEC); - float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch; -#endif -#if MT32EMU_ACCURATENOTES == 1 - noteVal = newPitch; - synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch); -#else - float newPitchInt; - float newPitchFract = modff(newPitch, &newPitchInt); - if (newPitchFract > 0.5f) { - newPitchInt += 1.0f; - newPitchFract -= 1.0f; - } - noteVal = (int)newPitchInt; - fineShift = (int)(powf(2.0f, newPitchFract / 12.0f) * 4096.0f); - synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f, noteVal=%d, fineShift=%d", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch, noteVal, fineShift); -#endif - // FIXME:KG: Raise/lower by octaves until in the supported range. - while (noteVal > HIGHEST_NOTE) // FIXME:KG: see tables.cpp: >108? - noteVal -= 12; - while (noteVal < LOWEST_NOTE) // FIXME:KG: see tables.cpp: <12? - noteVal += 12; - // Calculate keyfollow for filter - int keyfollow = ((key - MIDDLEC) * patchCache->filtkeyfollow) / 4096; - if (keyfollow > 108) - keyfollow = 108; - if (keyfollow < -108) - keyfollow = -108; - filtVal = keytable[keyfollow + 108]; - realVal = keytable[(key - MIDDLEC) + 108]; -} - -void Partial::startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial) { - if (usePoly == NULL || useCache == NULL) { - synth->printDebug("*** Error: Starting partial for owner %d, usePoly=%s, useCache=%s", ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", useCache == NULL ? "*** NULL ***" : "OK"); - return; - } - patchCache = useCache; - poly = usePoly; - mixType = patchCache->structureMix; - structurePosition = patchCache->structurePosition; - - play = true; - initKeyFollow(poly->freqnum); // Initialises noteVal, filtVal and realVal -#if MT32EMU_ACCURATENOTES == 0 - noteLookup = ¬eLookups[noteVal - LOWEST_NOTE]; -#else - TableInitialiser::initNote(synth, ¬eLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->PCMList, NULL); -#endif - - lfoPos = 0; - pulsewidth = patchCache->pulsewidth + pwveltable[patchCache->pwsens][poly->vel]; - if (pulsewidth > 100) { - pulsewidth = 100; - } else if (pulsewidth < 0) { - pulsewidth = 0; - } - - for (int e = 0; e < 3; e++) { - envs[e].envpos = 0; - envs[e].envstat = -1; - envs[e].envbase = 0; - envs[e].envdist = 0; - envs[e].envsize = 0; - envs[e].sustaining = false; - envs[e].decaying = false; - envs[e].prevlevel = 0; - envs[e].counter = 0; - envs[e].count = 0; - } - ampEnvVal = 0; - pitchEnvVal = 0; - pitchSustain = false; - loopPos = 0; - partialOff.pcmoffset = partialOff.pcmplace = 0; - pair = pairPartial; - useNoisePair = pairPartial == NULL && (mixType == 1 || mixType == 2); - age = 0; - alreadyOutputed = false; - memset(history,0,sizeof(history)); -} - -Bit16s *Partial::generateSamples(long length) { - if (!isActive() || alreadyOutputed) { - return NULL; - } - if (poly == NULL) { - synth->printDebug("*** ERROR: poly is NULL at Partial::generateSamples()!"); - return NULL; - } - - alreadyOutputed = true; - - // Generate samples - - Bit16s *partialBuf = &myBuffer[0]; - while (length--) { - Bit32s envval; - Bit32s sample = 0; - if (!envs[EnvelopeType_amp].sustaining) { - if (envs[EnvelopeType_amp].count <= 0) { - Bit32u ampval = getAmpEnvelope(); - if (!play) { - deactivate(); - break; - } - if (ampval > 127) { - ampval = 127; - } - - ampval = voltable[ampval]; - int tmpvel; - if (patchCache->ampenvdir == 1) - tmpvel = 127 - poly->vel; - else - tmpvel = poly->vel; - ampval = (ampval * ampveltable[tmpvel][(int)patchCache->ampEnv.velosens]) >> 8; - //if (envs[EnvelopeType_amp].sustaining) - ampEnvVal = ampval; - } - --envs[EnvelopeType_amp].count; - } - - int lfoat = 0x1000; - if (pitchSustain) { - // Calculate LFO position - // LFO does not kick in completely until pitch envelope sustains - if (patchCache->lfodepth > 0) { - lfoPos++; - if (lfoPos >= patchCache->lfoperiod) - lfoPos = 0; - int lfoatm = FIXEDPOINT_UDIV(lfoPos, patchCache->lfoperiod, 16); - int lfoatr = sintable[lfoatm]; - lfoat = lfoptable[patchCache->lfodepth][lfoatr]; - } - } else { - // Calculate Pitch envelope - envval = getPitchEnvelope(); - int pd = patchCache->pitchEnv.depth; - pitchEnvVal = penvtable[pd][envval]; - } - - int delta; - // These two are only for PCM partials, obviously - PCMWaveEntry *pcmWave = NULL; // Initialise to please compiler - Bit32u pcmAddr = 0; // Initialise to please compiler - - // Wrap positions or end if necessary - if (patchCache->PCMPartial) { - // PCM partial - int len; - pcmWave = &synth->PCMList[patchCache->pcm]; - - delta = noteLookup->wavTable[patchCache->pcm]; - pcmAddr = pcmWave->addr; - len = pcmWave->len; - if (partialOff.pcmplace >= len) { - if (pcmWave->loop) { - //partialOff.pcmplace = partialOff.pcmoffset = 0; - partialOff.pcmplace %= len; - } else { - play = false; - deactivate(); - break; - } - } - } else { - // Synthesis partial - delta = 0x10707; - partialOff.pcmplace %= (Bit16u)(noteLookup->div << 1); - } - - // Build delta for position of next sample - // Fix delta code - Bit64u tdelta = (Bit64u)delta; -#if MT32EMU_ACCURATENOTES == 0 - tdelta = (tdelta * fineShift) >> 12; -#endif - tdelta = (tdelta * pitchEnvVal) >> 12; - tdelta = (tdelta * lfoat) >> 12; - tdelta = (tdelta * bendShift) >> 12; - delta = (int)tdelta; - Bit32u volume = *poly->volumeptr; - - // Get waveform - either PCM or synthesized sawtooth or square - if (ampEnvVal > 0) { - if (patchCache->PCMPartial) { - // Render PCM sample - int ra, rb, dist; - Bit32u taddr; - if (delta < 0x10000) { - // Linear sound interpolation - taddr = pcmAddr + partialOff.pcmplace; - ra = synth->romfile[taddr]; - taddr++; - if (taddr == pcmAddr + pcmWave->len) { - // Past end of PCM - if (pcmWave->loop) { - rb = synth->romfile[pcmAddr]; - } else { - rb = 0; - } - } else { - rb = synth->romfile[taddr]; - } - dist = rb - ra; - sample = (ra + ((dist * (Bit32s)(partialOff.pcmoffset >> 8)) >> 8)); - } else { - // Sound decimation - // The right way to do it is to use a lowpass filter on the waveform before selecting - // a point. This is too slow. The following approximates this as fast as possible - int idelta = delta >> 16; - taddr = pcmAddr + partialOff.pcmplace; - ra = synth->romfile[taddr++]; - for (int ix = 0; ix < idelta - 1; ix++) { - if (taddr == pcmAddr + pcmWave->len) { - // Past end of PCM - if (pcmWave->loop) { - taddr = pcmAddr; - } else { - // Behave as if all subsequent samples were 0 - break; - } - } - ra += synth->romfile[taddr++]; - } - sample = ra / idelta; - } - } else { - // Render synthesised sample - Bit32u div = noteLookup->div; - int wf = patchCache->waveform; - int toff = partialOff.pcmplace; - int minorplace = partialOff.pcmoffset >> 14; - - Bit32s filtval = getFiltEnvelope(); - - //synth->printDebug("Filtval: %d", filtval); - - if (wf==0) { - // Square waveform. Made by combining two pregenerated bandlimited - // sawtooth waveforms - // Pulse width is not yet correct - if (div == 0) { - synth->printDebug("ERROR: div=0 generating square wave, this should never happen!"); - div = 1; - } - Bit32u ofsA = toff % div; - Bit32u ofsB = toff + FIXEDPOINT_UMULT(div, pulsetable[pulsewidth], 8); - ofsB = ofsB % div; - Bit16s pa = noteLookup->waveforms[0][(ofsA << 2) + minorplace]; - Bit16s pb = noteLookup->waveforms[0][(ofsB << 2) + minorplace]; - sample = (pa - pb) * 4; - // Non-bandlimited squarewave - /* - ofs = ((div << 1) * pulsetable[patchCache->pulsewidth]) >> 8; - if (toff < ofs) - sample = 1 * WGAMP; - else - sample = -1 * WGAMP; - */ - } else { - // Sawtooth. Made by combining the full cosine and half cosine according - // to how it looks on the MT-32. What it really does it takes the - // square wave and multiplies it by a full cosine - int waveoff = (toff << 2) + minorplace; - if (toff < noteLookup->sawTable[pulsewidth]) - sample = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]]; - else - sample = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]]; - sample = sample * 4; - // This is the correct way - // Seems slow to me (though bandlimited) -- doesn't seem to - // sound any better though - /* - //int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8; - - Bit32u ofs = toff % div; - - Bit32u ofs3 = toff + FIXEDPOINT_UMULT(div, pulsetable[patchCache->pulsewidth], 8); - ofs3 = ofs3 % div; - - pa = noteLookup->waveforms[0][ofs]; - pb = noteLookup->waveforms[0][ofs3]; - sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / WGAMP; - sample = sample *4; - */ - } - - //Very exact filter - if (filtval > ((FILTERGRAN * 15) / 16)) - filtval = ((FILTERGRAN * 15) / 16); - sample = (Bit32s)floor((synth->iirFilter)((float)sample, &history[0], filtcoeff[filtval][(int)patchCache->filtEnv.resonance], patchCache->filtEnv.resonance)); - } - } - - // Add calculated delta to our waveform offset - Bit32u absOff = ((partialOff.pcmplace << 16) | partialOff.pcmoffset); - absOff += delta; - partialOff.pcmplace = (Bit16u)((absOff & 0xFFFF0000) >> 16); - partialOff.pcmoffset = (Bit16u)(absOff & 0xFFFF); - - // Put volume envelope over generated sample - sample = FIXEDPOINT_SMULT(sample, ampEnvVal, 9); - sample = FIXEDPOINT_SMULT(sample, volume, 7); - envs[EnvelopeType_amp].envpos++; - envs[EnvelopeType_pitch].envpos++; - envs[EnvelopeType_filt].envpos++; - - *partialBuf++ = (Bit16s)sample; - } - // We may have deactivated and broken out of the loop before the end of the buffer, - // if so then fill the remainder with 0s. - if (++length > 0) - memset(partialBuf, 0, length * 2); - return &myBuffer[0]; -} - -void Partial::setBend(float factor) { - if (!patchCache->useBender || factor == 0.0f) { - bendShift = 4096; - return; - } - // NOTE:KG: We can't do this smoothly with lookup tables, unless we use several MB. - // FIXME:KG: Bend should be influenced by pitch key-follow too, according to docs. - float bendSemitones = factor * patchCache->benderRange; // -24 .. 24 - float mult = powf(2.0f, bendSemitones / 12.0f); - synth->printDebug("setBend(): factor=%f, benderRange=%f, semitones=%f, mult=%f\n", factor, patchCache->benderRange, bendSemitones, mult); - bendShift = (int)(mult * 4096.0f); -} - -Bit16s *Partial::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) { - if (buf1 == NULL) - return buf2; - if (buf2 == NULL) - return buf1; - - Bit16s *outBuf = buf1; -#if MT32EMU_USE_MMX >= 1 - // KG: This seems to be fine - int donelen = i386_mixBuffers(buf1, buf2, len); - len -= donelen; - buf1 += donelen; - buf2 += donelen; -#endif - while (len--) { - *buf1 = *buf1 + *buf2; - buf1++, buf2++; - } - return outBuf; -} - -Bit16s *Partial::mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) { - if (buf1 == NULL) - return NULL; - if (buf2 == NULL) { - Bit16s *outBuf = buf1; - while (len--) { - if (*buf1 < -8192) - *buf1 = -8192; - else if (*buf1 > 8192) - *buf1 = 8192; - buf1++; - } - return outBuf; - } - - Bit16s *outBuf = buf1; -#if MT32EMU_USE_MMX >= 1 - // KG: This seems to be fine - int donelen = i386_mixBuffersRingMix(buf1, buf2, len); - len -= donelen; - buf1 += donelen; - buf2 += donelen; -#endif - while (len--) { - float a, b; - a = ((float)*buf1) / 8192.0f; - b = ((float)*buf2) / 8192.0f; - a = (a * b) + a; - if (a>1.0) - a = 1.0; - if (a<-1.0) - a = -1.0; - *buf1 = (Bit16s)(a * 8192.0f); - buf1++; - buf2++; - //buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10) + buf1[i]; - } - return outBuf; -} - -Bit16s *Partial::mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) { - if (buf1 == NULL) { - return NULL; - } - if (buf2 == NULL) { - return NULL; - } - - Bit16s *outBuf = buf1; -#if MT32EMU_USE_MMX >= 1 - // FIXME:KG: Not really checked as working - int donelen = i386_mixBuffersRing(buf1, buf2, len); - len -= donelen; - buf1 += donelen; - buf2 += donelen; -#endif - while (len--) { - float a, b; - a = ((float)*buf1) / 8192.0f; - b = ((float)*buf2) / 8192.0f; - a *= b; - if (a>1.0) - a = 1.0; - if (a<-1.0) - a = -1.0; - *buf1 = (Bit16s)(a * 8192.0f); - buf1++; - buf2++; - } - return outBuf; -} - -void Partial::mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len) { - if (buf2 == NULL) { - while (len--) { - *outBuf++ = *buf1++; - *outBuf++ = 0; - } - } else if (buf1 == NULL) { - while (len--) { - *outBuf++ = 0; - *outBuf++ = *buf2++; - } - } else { - while (len--) { - *outBuf++ = *buf1++; - *outBuf++ = *buf2++; - } - } -} - -bool Partial::produceOutput(Bit16s *partialBuf, long length) { - if (!isActive() || alreadyOutputed) - return false; - if (poly == NULL) { - synth->printDebug("*** ERROR: poly is NULL at Partial::produceOutput()!"); - return false; - } - - Bit16s *pairBuf = NULL; - // Check for dependant partial - if (pair != NULL) { - if (!pair->alreadyOutputed) { - // Note: pair may have become NULL after this - pairBuf = pair->generateSamples(length); - } - } else if (useNoisePair) { - // Generate noise for pairless ring mix - pairBuf = smallnoise; - } - - Bit16s *myBuf = generateSamples(length); - - if (myBuf == NULL && pairBuf == NULL) - return false; - - Bit16s * p1buf, * p2buf; - - if (structurePosition == 0 || pairBuf == NULL) { - p1buf = myBuf; - p2buf = pairBuf; - } else { - p2buf = myBuf; - p1buf = pairBuf; - } - - //synth->printDebug("mixType: %d", mixType); - - Bit16s *mixedBuf; - switch(mixType) { - case 0: - // Standard sound mix - mixedBuf = mixBuffers(p1buf, p2buf, length); - break; - - case 1: - // Ring modulation with sound mix - mixedBuf = mixBuffersRingMix(p1buf, p2buf, length); - break; - - case 2: - // Ring modulation alone - mixedBuf = mixBuffersRing(p1buf, p2buf, length); - break; - - case 3: - // Stereo mixing. One partial to one speaker channel, one to another. - // FIXME:KG: Surely we should be multiplying by the left/right volumes here? - mixBuffersStereo(p1buf, p2buf, partialBuf, length); - return true; - - default: - mixedBuf = mixBuffers(p1buf, p2buf, length); - break; - } - - if (mixedBuf == NULL) - return false; - - Bit16s leftvol, rightvol; - leftvol = patchCache->pansetptr->leftvol; - rightvol = patchCache->pansetptr->rightvol; - -#if MT32EMU_USE_MMX >= 2 - // FIXME:KG: This appears to introduce crackle - int donelen = i386_partialProductOutput(length, leftvol, rightvol, partialBuf, mixedBuf); - length -= donelen; - mixedBuf += donelen; - partialBuf += donelen * 2; -#endif - while (length--) { - *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)leftvol) >> 16); - *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)rightvol) >> 16); - mixedBuf++; - } - return true; -} - -Bit32s Partial::getFiltEnvelope() { - int reshigh; - - int cutoff,depth,keyfollow, realfollow; - - EnvelopeStatus *tStat = &envs[EnvelopeType_filt]; - - keyfollow = filtVal; - realfollow = realVal; - - if (tStat->decaying) { - reshigh = tStat->envbase; - reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize)); - if (tStat->envpos >= tStat->envsize) - reshigh = 0; - } else { - if (tStat->envstat==4) { - reshigh = patchCache->filtsustain; - if (!poly->sustain) { - startDecay(EnvelopeType_filt, reshigh); - } - } else { - if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) { - if (tStat->envstat==-1) - tStat->envbase = 0; - else - tStat->envbase = patchCache->filtEnv.envlevel[tStat->envstat]; - tStat->envstat++; - tStat->envpos = 0; - if (tStat->envstat == 3) { - tStat->envsize = lasttimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]]; - } else { - tStat->envsize = (envtimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8; - } - - tStat->envsize++; - tStat->envdist = patchCache->filtEnv.envlevel[tStat->envstat] - tStat->envbase; - } - - reshigh = tStat->envbase; - reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize)); - - } - tStat->prevlevel = reshigh; - } - - cutoff = patchCache->filtEnv.cutoff; - - //if (patchCache->waveform==1) reshigh = (reshigh * 3) >> 2; - - depth = patchCache->filtEnv.envdepth; - - //int sensedep = (depth * 127-patchCache->filtEnv.envsense) >> 7; - depth = (depth * filveltable[poly->vel][(int)patchCache->filtEnv.envsense]) >> 8; - - int bias = patchCache->tvfbias; - int dist; - - if (bias!=0) { - //FIXME:KG: Is this really based on pitch (as now), or key pressed? - //synth->printDebug("Cutoff before %d", cutoff); - if (patchCache->tvfdir == 0) { - if (noteVal < bias) { - dist = bias - noteVal; - cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8; - } - } else { - // > Bias - if (noteVal > bias) { - dist = noteVal - bias; - cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8; - } - - } - //synth->printDebug("Cutoff after %d", cutoff); - } - - depth = (depth * noteLookup->fildepTable[patchCache->tvfdepth]) >> 8; - reshigh = (reshigh * depth) >> 7; - - Bit32s tmp; - - cutoff *= keyfollow; - cutoff /= realfollow; - - reshigh *= keyfollow; - reshigh /= realfollow; - - if (cutoff>100) - cutoff = 100; - else if (cutoff<0) - cutoff = 0; - if (reshigh>100) - reshigh = 100; - else if (reshigh<0) - reshigh = 0; - tmp = noteLookup->nfiltTable[cutoff][reshigh]; - //tmp *= keyfollow; - //tmp /= realfollow; - - //synth->printDebug("Cutoff %d, tmp %d, freq %d", cutoff, tmp, tmp * 256); - return tmp; -} - -bool Partial::shouldReverb() { - if (!isActive()) - return false; - return patchCache->reverb; -} - -Bit32u Partial::getAmpEnvelope() { - Bit32s tc; - - EnvelopeStatus *tStat = &envs[EnvelopeType_amp]; - - if (!play) - return 0; - - if (tStat->decaying) { - tc = tStat->envbase; - tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); - if (tc < 0) - tc = 0; - if ((tStat->envpos >= tStat->envsize) || (tc == 0)) { - play = false; - // Don't have to worry about prevlevel storage or anything, this partial's about to die - return 0; - } - } else { - if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) { - if (tStat->envstat==-1) - tStat->envbase = 0; - else - tStat->envbase = patchCache->ampEnv.envlevel[tStat->envstat]; - tStat->envstat++; - tStat->envpos = 0; - - switch(tStat->envstat) { - case 0: - //Spot for velocity time follow - //Only used for first attack - tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * veltkeytable[(int)patchCache->ampEnv.envvkf][poly->vel]) >> 8; - //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize); - break; - case 3: - // Final attack envelope uses same time table as the decay - //tStat->envsize = decaytimetable[patchCache->ampEnv.envtime[tStat->envstat]]; - tStat->envsize = lasttimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]]; - //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize); - break; - case 4: - //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize); - tc = patchCache->ampsustain; - if (!poly->sustain) - startDecay(EnvelopeType_amp, tc); - else - tStat->sustaining = true; - - goto PastCalc; - default: - //Spot for timekey follow - //Only used in subsquent envelope parameters, including the decay - tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8; - - //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize); - break; - } - - tStat->envsize++; - tStat->envdist = patchCache->ampEnv.envlevel[tStat->envstat] - tStat->envbase; - - if (tStat->envdist != 0) { - tStat->counter = abs(tStat->envsize / tStat->envdist); - //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist); - } else { - tStat->counter = 0; - //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist); - } - } - tc = tStat->envbase; - tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); - tStat->count = tStat->counter; -PastCalc: - tc = (tc * (Bit32s)patchCache->amplevel) >> 7; - } - - // Prevlevel storage is bottle neck - tStat->prevlevel = tc; - - //Bias level crap stuff now - - for (int i = 0; i < 2; i++) { - if (patchCache->ampblevel[i]!=0) { - int bias = patchCache->ampbias[i]; - if (patchCache->ampdir[i]==0) { - // < Bias - if (noteVal < bias) { - int dist = bias - noteVal; - tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8; - } - } else { - // > Bias - if (noteVal > bias) { - int dist = noteVal - bias; - tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8; - } - } - } - } - if (tc < 0) { - synth->printDebug("*** ERROR: tc < 0 (%d) at getAmpEnvelope()", tc); - tc = 0; - } - return (Bit32u)tc; -} - -Bit32s Partial::getPitchEnvelope() { - EnvelopeStatus *tStat = &envs[EnvelopeType_pitch]; - - Bit32s tc; - pitchSustain = false; - if (tStat->decaying) { - if (tStat->envpos >= tStat->envsize) - tc = patchCache->pitchEnv.level[4]; - else { - tc = tStat->envbase; - tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); - } - } else { - if (tStat->envstat==3) { - tc = patchCache->pitchsustain; - if (poly->sustain) - pitchSustain = true; - else - startDecay(EnvelopeType_pitch, tc); - } else { - if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) { - tStat->envstat++; - - tStat->envbase = patchCache->pitchEnv.level[tStat->envstat]; - tStat->envsize = (envtimetable[(int)patchCache->pitchEnv.time[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8; - - tStat->envpos = 0; - tStat->envsize++; - tStat->envdist = patchCache->pitchEnv.level[tStat->envstat + 1] - tStat->envbase; - } - tc = tStat->envbase; - tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize)); - } - tStat->prevlevel = tc; - } - return tc; -} - -void Partial::startDecayAll() { - startDecay(EnvelopeType_amp, envs[EnvelopeType_amp].prevlevel); - startDecay(EnvelopeType_filt, envs[EnvelopeType_filt].prevlevel); - startDecay(EnvelopeType_pitch, envs[EnvelopeType_pitch].prevlevel); - pitchSustain = false; -} - -void Partial::startDecay(EnvelopeType envnum, Bit32s startval) { - EnvelopeStatus *tStat = &envs[envnum]; - - tStat->sustaining = false; - tStat->decaying = true; - tStat->envpos = 0; - tStat->envbase = startval; - - switch(envnum) { - case EnvelopeType_amp: - tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8; - tStat->envdist = -startval; - break; - case EnvelopeType_filt: - tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8; - tStat->envdist = -startval; - break; - case EnvelopeType_pitch: - tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * noteLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8 ; - tStat->envdist = patchCache->pitchEnv.level[4] - startval; - break; - default: - break; - } - tStat->envsize++; -} diff --git a/backends/midi/mt32/partial.h b/backends/midi/mt32/partial.h deleted file mode 100644 index 0834a64c15..0000000000 --- a/backends/midi/mt32/partial.h +++ /dev/null @@ -1,142 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef MT32EMU_PARTIAL_H -#define MT32EMU_PARTIAL_H - -namespace MT32Emu { - -class Synth; -struct NoteLookup; - -enum EnvelopeType { - EnvelopeType_amp = 0, - EnvelopeType_filt = 1, - EnvelopeType_pitch = 2 -}; - -struct EnvelopeStatus { - Bit32s envpos; - Bit32s envstat; - Bit32s envbase; - Bit32s envdist; - Bit32s envsize; - - bool sustaining; - bool decaying; - Bit32s prevlevel; - - Bit32s counter; - Bit32s count; -}; - -// Class definition of MT-32 partials. 32 in all. -class Partial { -private: - Synth *synth; - - int ownerPart; // -1 if unassigned - int mixType; - int structurePosition; // 0 or 1 of a structure pair - bool useNoisePair; - - Bit16s myBuffer[MAX_SAMPLE_OUTPUT]; - - bool play; - - // Keyfollowed note value -#if MT32EMU_ACCURATENOTES == 1 - NoteLookup noteLookupStorage; - float noteVal; -#else - int noteVal; - int fineShift; -#endif - const NoteLookup *noteLookup; // LUTs for this noteVal - - // Keyfollowed filter values - int realVal; - int filtVal; - - EnvelopeStatus envs[3]; - - int pulsewidth; - - Bit32u lfoPos; - soundaddr partialOff; - - Bit32u ampEnvVal; - Bit32u pitchEnvVal; - - float history[32]; - - bool pitchSustain; - - int loopPos; - - dpoly *poly; - - int bendShift; - - Bit16s *mixBuffers(Bit16s *buf1, Bit16s *buf2, int len); - Bit16s *mixBuffersRingMix(Bit16s *buf1, Bit16s *buf2, int len); - Bit16s *mixBuffersRing(Bit16s *buf1, Bit16s *buf2, int len); - void mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len); - - Bit32s getFiltEnvelope(); - Bit32u getAmpEnvelope(); - Bit32s getPitchEnvelope(); - - void initKeyFollow(int freqNum); - -public: - const PatchCache *patchCache; - PatchCache cachebackup; - - Partial *pair; - bool alreadyOutputed; - Bit32u age; - - Partial(Synth *synth); - ~Partial(); - - int getOwnerPart(); - bool isActive(); - void activate(int part); - void deactivate(void); - void startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial); - void startDecay(EnvelopeType envnum, Bit32s startval); - void startDecayAll(); - void setBend(float factor); - bool shouldReverb(); - - // 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(Bit16s * partialBuf, long length); - - // This function produces mono sample output using the partial's private internal buffer - Bit16s *generateSamples(long length); -}; - -} - -#endif diff --git a/backends/midi/mt32/partialManager.cpp b/backends/midi/mt32/partialManager.cpp deleted file mode 100644 index 776276edda..0000000000 --- a/backends/midi/mt32/partialManager.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include <memory.h> - -#include "mt32emu.h" - -using namespace MT32Emu; - -PartialManager::PartialManager(Synth *useSynth) { - this->synth = useSynth; - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) - partialTable[i] = new Partial(synth); -} - -PartialManager::~PartialManager(void) { - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) - delete partialTable[i]; -} - -void PartialManager::getPerPartPartialUsage(int usage[9]) { - memset(usage, 0, 9 * sizeof (int)); - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (partialTable[i]->isActive()) - usage[partialTable[i]->getOwnerPart()]++; - } -} - -void PartialManager::clearAlreadyOutputed() { - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) - partialTable[i]->alreadyOutputed = false; -} - -void PartialManager::ageAll() { - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) - partialTable[i]->age++; -} - -bool PartialManager::shouldReverb(int i) { - return partialTable[i]->shouldReverb(); -} - -bool PartialManager::produceOutput(int i, Bit16s *buffer, Bit32u bufferLength) { - return partialTable[i]->produceOutput(buffer, bufferLength); -} - -void PartialManager::deactivateAll() { - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - partialTable[i]->deactivate(); - } -} - -unsigned int PartialManager::setReserve(Bit8u *rset) { - unsigned int pr = 0; - for (int x = 0; x < 9; x++) { - for (int y = 0; y < rset[x]; y++) { - partialReserveTable[pr] = x; - pr++; - } - } - return pr; -} - -Partial *PartialManager::allocPartial(int partNum) { - Partial *outPartial = NULL; - - // Use the first inactive partial reserved for the specified part (if there are any) - // Otherwise, use the last inactive partial, if any - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (!partialTable[i]->isActive()) { - outPartial = partialTable[i]; - if (partialReserveTable[i] == partNum) - break; - } - } - if (outPartial != NULL) { - outPartial->activate(partNum); - outPartial->age = 0; - } - return outPartial; -} - -unsigned int PartialManager::getFreePartialCount(void) { - int count = 0; - memset(partialPart, 0, sizeof(partialPart)); - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (!partialTable[i]->isActive()) - count++; - else - partialPart[partialTable[i]->getOwnerPart()]++; - } - return count; -} - -/* -bool PartialManager::freePartials(unsigned int needed, int partNum) { - int i; - int myPartPrior = (int)mt32ram.system.reserveSettings[partNum]; - if (myPartPrior<partialPart[partNum]) { - //This can have more parts, must kill off those with less priority - int most, mostPart; - while (needed > 0) { - int selectPart = -1; - //Find the worst offender with more partials than allocated and kill them - most = -1; - mostPart = -1; - int diff; - - for (i=0;i<9;i++) { - diff = partialPart[i] - (int)mt32ram.system.reserveSettings[i]; - - if (diff>0) { - if (diff>most) { - most = diff; - mostPart = i; - } - } - } - selectPart = mostPart; - if (selectPart == -1) { - // All parts are within the allocated limits, you suck - // Look for first partial not of this part that's decaying perhaps? - return false; - } - bool found; - int oldest; - int oldnum; - while (partialPart[selectPart] > (int)mt32ram.system.reserveSettings[selectPart]) { - oldest = -1; - oldnum = -1; - found = false; - for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (partialTable[i]->isActive) { - if (partialTable[i]->ownerPart == selectPart) { - found = true; - if (partialTable[i]->age > oldest) { - oldest = partialTable[i]->age; - oldnum = i; - } - } - } - } - if (!found) break; - partialTable[oldnum]->deactivate(); - --partialPart[selectPart]; - --needed; - } - - } - return true; - - } else { - //This part has reached its max, must kill off its own - bool found; - int oldest; - int oldnum; - while (needed > 0) { - oldest = -1; - oldnum = -1; - found = false; - for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (partialTable[i]->isActive) { - if (partialTable[i]->ownerPart == partNum) { - found = true; - if (partialTable[i]->age > oldest) { - oldest = partialTable[i]->age; - oldnum = i; - } - } - } - } - if (!found) break; - partialTable[oldnum]->deactivate(); - --needed; - } - // Couldn't free enough partials, sorry - if (needed>0) return false; - return true; - } - -} -*/ -bool PartialManager::freePartials(unsigned int needed, int partNum) { - if (needed == 0) { - return true; - } - // Reclaim partials reserved for this part - // Kill those that are already decaying first - /* - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (partialReserveTable[i] == partNum) { - if (partialTable[i]->ownerPart != partNum) { - if (partialTable[i]->partCache->envs[AMPENV].decaying) { - partialTable[i]->isActive = false; - --needed; - if (needed == 0) - return true; - } - } - } - }*/ - // Then kill those with the lowest part priority -- oldest at the moment - while (needed > 0) { - Bit32u prior = 0; - int priornum = -1; - - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (partialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) { - /* - if (mt32ram.system.reserveSettings[partialTable[i]->ownerPart] < prior) { - prior = mt32ram.system.reserveSettings[partialTable[i]->ownerPart]; - priornum = i; - }*/ - if (partialTable[i]->age >= prior) { - prior = partialTable[i]->age; - priornum = i; - } - } - } - if (priornum != -1) { - partialTable[priornum]->deactivate(); - --needed; - } else { - break; - } - } - - // Kill off the oldest partials within this part - while (needed > 0) { - Bit32u oldest = 0; - int oldlist = -1; - for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (partialTable[i]->getOwnerPart() == partNum && partialTable[i]->isActive()) { - if (partialTable[i]->age >= oldest) { - oldest = partialTable[i]->age; - oldlist = i; - } - } - } - if (oldlist != -1) { - partialTable[oldlist]->deactivate(); - --needed; - } else { - break; - } - } - return needed == 0; -} diff --git a/backends/midi/mt32/partialManager.h b/backends/midi/mt32/partialManager.h deleted file mode 100644 index ee145d4c9f..0000000000 --- a/backends/midi/mt32/partialManager.h +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef MT32EMU_PARTIALMANAGER_H -#define MT32EMU_PARTIALMANAGER_H - -namespace MT32Emu { - -class Synth; - -class PartialManager { -private: - Synth *synth; // Only used for sending debug output - - Partial *partialTable[MT32EMU_MAX_PARTIALS]; - Bit32s partialReserveTable[MT32EMU_MAX_PARTIALS]; - Bit32s partialPart[9]; // The count of partials played per part - -public: - PartialManager(Synth *synth); - ~PartialManager(); - Partial *allocPartial(int partNum); - unsigned int getFreePartialCount(void); - bool freePartials(unsigned int needed, int partNum); - unsigned int setReserve(Bit8u *rset); - void deactivateAll(); - void ageAll(); - bool produceOutput(int i, Bit16s *buffer, Bit32u bufferLength); - bool shouldReverb(int i); - void clearAlreadyOutputed(); - void getPerPartPartialUsage(int usage[9]); -}; - -} - -#endif diff --git a/backends/midi/mt32/structures.h b/backends/midi/mt32/structures.h deleted file mode 100644 index b04d98b0fd..0000000000 --- a/backends/midi/mt32/structures.h +++ /dev/null @@ -1,282 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef MT32EMU_STRUCTURES_H -#define MT32EMU_STRUCTURES_H - -namespace MT32Emu { - -const unsigned int MAX_SAMPLE_OUTPUT = 4096; - -#ifdef _MSC_VER -#define MT32EMU_ALIGN_PACKED __declspec(align(1)) -typedef unsigned __int64 Bit64u; -typedef signed __int64 Bit64s; -#else -#define MT32EMU_ALIGN_PACKED __attribute__((packed)) -typedef unsigned long long Bit64u; -typedef signed long long Bit64s; -#endif - -typedef unsigned int Bit32u; -typedef signed int Bit32s; -typedef unsigned short int Bit16u; -typedef signed short int Bit16s; -typedef unsigned char Bit8u; -typedef signed char Bit8s; - -// The following structures represent the MT-32's memory -// Since sysex allows this memory to be written to in blocks of bytes, -// we keep this packed so that we can copy data into the various -// banks directly -#if defined(_MSC_VER) || defined (__MINGW32__) -#pragma pack(push, 1) -#else -#pragma pack(1) -#endif - -struct TimbreParam { - struct commonParam { - char name[10]; - Bit8u pstruct12; // 1&2 0-12 (1-13) - Bit8u pstruct34; // #3&4 0-12 (1-13) - Bit8u pmute; // 0-15 (0000-1111) - Bit8u nosustain; // 0-1(Normal, No sustain) - } MT32EMU_ALIGN_PACKED common; - - struct partialParam { - struct wgParam { - Bit8u coarse; // 0-96 (C1,C#1-C9) - Bit8u fine; // 0-100 (-50 to +50 (cents?)) - Bit8u keyfollow; // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2) - Bit8u bender; // 0,1 (ON/OFF) - Bit8u waveform; // 0-1 (SQU/SAW) - Bit8u pcmwave; // 0-127 (1-128) - Bit8u pulsewid; // 0-100 - Bit8u pwvelo; // 0-14 (-7 - +7) - } MT32EMU_ALIGN_PACKED wg; - - struct envParam { - Bit8u depth; // 0-10 - Bit8u sensitivity; // 1-100 - Bit8u timekeyfollow; // 0-4 - Bit8u time[4]; // 1-100 - Bit8u level[5]; // 1-100 (-50 - +50) - } MT32EMU_ALIGN_PACKED env; - - struct lfoParam { - Bit8u rate; // 0-100 - Bit8u depth; // 0-100 - Bit8u modsense; // 0-100 - } MT32EMU_ALIGN_PACKED lfo; - - struct tvfParam { - Bit8u cutoff; // 0-100 - Bit8u resonance; // 0-30 - Bit8u keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2) - Bit8u biaspoint; // 0-127 (<1A-<7C >1A-7C) - Bit8u biaslevel; // 0-14 (-7 - +7) - Bit8u envdepth; // 0-100 - Bit8u envsense; // 0-100 - Bit8u envdkf; // DEPTH KEY FOLL0W 0-4 - Bit8u envtkf; // TIME KEY FOLLOW 0-4 - Bit8u envtime[5]; // 1-100 - Bit8u envlevel[4]; // 1-100 - } MT32EMU_ALIGN_PACKED tvf; - - struct tvaParam { - Bit8u level; // 0-100 - Bit8u velosens; // 0-100 - Bit8u biaspoint1; // 0-127 (<1A-<7C >1A-7C) - Bit8u biaslevel1; // 0-12 (-12 - 0) - Bit8u biaspoint2; // 0-127 (<1A-<7C >1A-7C) - Bit8u biaslevel2; // 0-12 (-12 - 0) - Bit8u envtkf; // TIME KEY FOLLOW 0-4 - Bit8u envvkf; // VELOS KEY FOLL0W 0-4 - Bit8u envtime[5]; // 1-100 - Bit8u envlevel[4]; // 1-100 - } MT32EMU_ALIGN_PACKED tva; - } MT32EMU_ALIGN_PACKED partial[4]; -} MT32EMU_ALIGN_PACKED; - -struct PatchParam { - Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm) - Bit8u timbreNum; // TIMBRE NUMBER 0-63 - Bit8u keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones) - Bit8u fineTune; // FINE TUNE 0-100 (-50 - +50 cents) - Bit8u benderRange; // BENDER RANGE 0-24 - Bit8u assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4) - Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON) - Bit8u dummy; // (DUMMY) -} MT32EMU_ALIGN_PACKED; - -struct MemParams { - struct PatchTemp { - PatchParam patch; - Bit8u outlevel; // OUTPUT LEVEL 0-100 - Bit8u panpot; // PANPOT 0-14 (R-L) - Bit8u dummyv[6]; - } MT32EMU_ALIGN_PACKED patchSettings[8]; - - struct RhythmTemp { - Bit8u timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF) - Bit8u outlevel; // OUTPUT LEVEL 0-100 - Bit8u panpot; // PANPOT 0-14 (R-L) - Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON) - } MT32EMU_ALIGN_PACKED rhythmSettings[86]; // FIXME: Was 64, but let's support the next model... - - TimbreParam MT32EMU_ALIGN_PACKED timbreSettings[8]; - - PatchParam MT32EMU_ALIGN_PACKED patches[128]; - - struct PaddedTimbre { - TimbreParam timbre; - Bit8u padding[10]; - } MT32EMU_ALIGN_PACKED timbres[64 + 64 + 64 + 30]; // Group A, Group B, Memory, Rhythm - - struct SystemArea { - Bit8u masterTune; // MASTER TUNE 0-127 432.1-457.6Hz - Bit8u reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay) - Bit8u reverbTime; // REVERB TIME 0-7 (1-8) - Bit8u reverbLevel; // REVERB LEVEL 0-7 (1-8) - Bit8u reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32 - Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF) - Bit8u masterVol; // MASTER VOLUME 0-100 - } MT32EMU_ALIGN_PACKED system; -}; - -struct MemBanks { - Bit8u pTemp[8][sizeof(MemParams::PatchTemp)]; - Bit8u rTemp[86][sizeof(MemParams::RhythmTemp)]; - Bit8u tTemp[8][sizeof(TimbreParam)]; - Bit8u patchBank[128][sizeof(PatchParam)]; - Bit8u timbreBank[64 + 64 + 64 + 30][sizeof(MemParams::PaddedTimbre)]; - Bit8u systemBank[sizeof(MemParams::SystemArea)]; - // System memory 0x100000 - // Display 0x200000 - // Reset 0x7F0000 -}; - -#if defined(_MSC_VER) || defined (__MINGW32__) -#pragma pack(pop) -#else -#pragma pack() -#endif - -struct PCMWaveEntry { - Bit32u addr; - Bit32u len; - double tune; - bool loop; -}; - -struct soundaddr { - Bit16u pcmplace; - Bit16u pcmoffset; -}; - -struct StereoVolume { - Bit16s leftvol; - Bit16s rightvol; -}; - -// This is basically a per-partial, pre-processed combination of timbre and patch/rhythm settings -struct PatchCache { - bool playPartial; - bool PCMPartial; - int pcm; - char waveform; - int pulsewidth; - int pwsens; - - float pitch; - - int lfodepth; - int lforate; - Bit32u lfoperiod; - int modsense; - - float pitchKeyfollow; - - int filtkeyfollow; - - int tvfbias; - int tvfblevel; - int tvfdir; - - int ampbias[2]; - int ampblevel[2]; - int ampdir[2]; - - int ampdepth; - int ampenvdir; - int amplevel; - int tvfdepth; - - bool useBender; - float benderRange; // 0.0, 1.0, .., 24.0 (semitones) - - TimbreParam::partialParam::envParam pitchEnv; - TimbreParam::partialParam::tvaParam ampEnv; - TimbreParam::partialParam::tvfParam filtEnv; - - Bit32s ampsustain; - Bit32s pitchsustain; - Bit32s filtsustain; - - Bit32u structureMix; - int structurePosition; - int structurePair; - - // The following fields are actually common to all partials in the timbre - bool dirty; - Bit32u partialCount; - bool sustain; - float pitchShift; - bool reverb; - const StereoVolume *pansetptr; -}; - -class Partial; // Forward reference for class defined in partial.h - -struct dpoly { - bool isPlaying; - - unsigned int key; - int freqnum; - int vel; - - bool isDecay; - - const Bit32u *volumeptr; - - Partial *partials[4]; - - bool pedalhold; // This marks keys that have been released on the keyboard, but are being held by the pedal - bool sustain; - - bool isActive() const; - Bit32u getAge() const; -}; - -} - -#endif diff --git a/backends/midi/mt32/synth.cpp b/backends/midi/mt32/synth.cpp deleted file mode 100644 index 1ba543b581..0000000000 --- a/backends/midi/mt32/synth.cpp +++ /dev/null @@ -1,1048 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include <math.h> -#include <string.h> -#include <stdlib.h> -#include <errno.h> - -#include "mt32emu.h" - -namespace MT32Emu { - -const int MAX_SYSEX_SIZE = 512; - -float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel) { - float *hist2_ptr; - float output,new_hist; - - hist2_ptr = hist1_ptr + 1; // next history - - // 1st number of coefficients array is overall input scale factor, or filter gain - output = input * (*coef_ptr++); - - output = output - *hist1_ptr * (*coef_ptr++); - new_hist = output - *hist2_ptr * (*coef_ptr++); // poles - - output = new_hist + *hist1_ptr * (*coef_ptr++); - output = output + *hist2_ptr * (*coef_ptr++); // zeros - - *hist2_ptr++ = *hist1_ptr; - *hist1_ptr++ = new_hist; - hist1_ptr++; - hist2_ptr++; - - // i = 1 - output = output - *hist1_ptr * (*coef_ptr++); - new_hist = output - *hist2_ptr * (*coef_ptr++); // poles - - output = new_hist + *hist1_ptr * (*coef_ptr++); - output = output + *hist2_ptr * (*coef_ptr++); // zeros - - *hist2_ptr++ = *hist1_ptr; - *hist1_ptr++ = new_hist; - - output *= ResonInv[revLevel]; - - return(output); -} - -Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) { - for (unsigned int i = 0; i < len; i++) { - checksum = checksum + data[i]; - } - checksum = checksum & 0x7f; - if (checksum) - checksum = 0x80 - checksum; - return checksum; -} - -Synth::Synth() { - isOpen = false; - reverbModel = NULL; - partialManager = NULL; - memset(noteLookups, 0, sizeof(noteLookups)); - memset(parts, 0, sizeof(parts)); -} - -Synth::~Synth() { - close(); // Make sure we're closed and everything is freed -} - -int Synth::report(ReportType type, const void *data) { - if (myProp.report != NULL) { - return myProp.report(myProp.userData, type, data); - } - return 0; -} - -void Synth::printDebug(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - if (myProp.printDebug != NULL) { - myProp.printDebug(myProp.userData, fmt, ap); - } else { - vprintf(fmt, ap); - printf("\n"); - } - va_end(ap); -} - -void Synth::initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel) { - // FIXME:KG: I don't think it's necessary to recreate the reverbModel... Just set the parameters - if (reverbModel != NULL) - delete reverbModel; - reverbModel = new revmodel(); - - switch(newRevMode) { - case 0: - reverbModel->setroomsize(.1f); - reverbModel->setdamp(.75f); - break; - case 1: - reverbModel->setroomsize(.5f); - reverbModel->setdamp(.5f); - break; - case 2: - reverbModel->setroomsize(.5f); - reverbModel->setdamp(.1f); - break; - case 3: - reverbModel->setroomsize(1.0f); - reverbModel->setdamp(.75f); - break; - default: - reverbModel->setroomsize(.1f); - reverbModel->setdamp(.5f); - break; - } - reverbModel->setdry(1); - reverbModel->setwet((float)newRevLevel / 8.0f); - reverbModel->setwidth((float)newRevTime / 8.0f); -} - -File *Synth::openFile(const char *filename, File::OpenMode mode) { - if (myProp.openFile != NULL) { - return myProp.openFile(myProp.userData, filename, mode); - } - char pathBuf[2048]; - if (myProp.baseDir != NULL) { - strcpy(&pathBuf[0], myProp.baseDir); - strcat(&pathBuf[0], filename); - filename = pathBuf; - } - ANSIFile *file = new ANSIFile(); - if (!file->open(filename, mode)) { - delete file; - return NULL; - } - return file; -} - -void Synth::closeFile(File *file) { - if (myProp.closeFile != NULL) { - myProp.closeFile(myProp.userData, file); - } else { - file->close(); - delete file; - } -} - -bool Synth::loadPreset(File *file) { - bool inSys = false; - Bit8u sysexBuf[MAX_SYSEX_SIZE]; - Bit16u syslen = 0; - bool rc = true; - for (;;) { - Bit8u c; - if (!file->readBit8u(&c)) { - if (!file->isEOF()) { - rc = false; - } - break; - } - sysexBuf[syslen] = c; - if (inSys) { - syslen++; - if (c == 0xF7) { - playSysex(&sysexBuf[0], syslen); - inSys = false; - syslen = 0; - } else if (syslen == MAX_SYSEX_SIZE) { - printDebug("MAX_SYSEX_SIZE (%d) exceeded while processing preset, ignoring message", MAX_SYSEX_SIZE); - inSys = false; - syslen = 0; - } - } else if (c == 0xF0) { - syslen++; - inSys = true; - } - } - return rc; -} - -bool Synth::loadControlROM(const char *filename) { - File *file = openFile(filename, File::OpenMode_read); // ROM File - if (file == NULL) { - return false; - } - bool rc = (file->read(controlROMData, sizeof(controlROMData)) == sizeof(controlROMData)); - - closeFile(file); - return rc; -} - -bool Synth::loadPCMROM(const char *filename) { - File *file = openFile(filename, File::OpenMode_read); // ROM File - if (file == NULL) { - return false; - } - bool rc = true; - for (int i = 0; ; i++) { - Bit8u s; - if (!file->readBit8u(&s)) { - if (!file->isEOF()) { - rc = false; - } - break; - } - Bit8u c; - if (!file->readBit8u(&c)) { - if (!file->isEOF()) { - rc = false; - } else { - printDebug("ROM file has an odd number of bytes! Ignoring last"); - } - break; - } - - short e; - int bit; - int u; - - int order[16] = {0, 9, 1 ,2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8}; - - e = 0; - for (u = 0; u < 15; u++) { - if (order[u] < 8) - bit = (s >> (7 - order[u])) & 0x1; - else - bit = (c >> (7 - (order[u] - 8))) & 0x1; - e = e | (short)(bit << (15 - u)); - } - - /* - //Bit16s e = ( ((s & 0x7f) << 4) | ((c & 0x40) << 6) | ((s & 0x80) << 6) | ((c & 0x3f))) << 2; - if (e<0) - e = -32767 - e; - int ut = abs(e); - int dif = 0x7fff - ut; - x = exp(((float)((float)0x8000-(float)dif) / (float)0x1000)); - e = (int)((float)e * (x/3200)); - */ - - // File is encoded in dB, convert to PCM - // MINDB = -96 - // MAXDB = -15 - float testval; - testval = (float)((~e) & 0x7fff); - testval = -(testval / 400.00f); - //testval = -(testval / 341.32291666666666666666666666667); - float vol = powf(8, testval / 20) * 32767.0f; - - if (e > 0) - vol = -vol; - - romfile[i] = (Bit16s)vol; - - } - closeFile(file); - return rc; -} - -struct ControlROMPCMStruct -{ - Bit8u pos; - Bit8u len; - Bit8u pitchLSB; - Bit8u pitchMSB; -}; - -void Synth::initPCMList() { - ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[0x3000]; - for (int i = 0; i < 128; i++) { - int rAddr = tps[i].pos * 0x800; - int rLenExp = (tps[i].len & 0x70) >> 4; - int rLen = 0x800 << rLenExp; - bool rLoop = (tps[i].len & 0x80) != 0; - //Bit8u rFlag = tps[i].len & 0x0F; - Bit16u rTuneOffset = (tps[i].pitchMSB << 8) | tps[i].pitchLSB; - //FIXME:KG: Pick a number, any number. The one below sounded best to me in listening tests, but needs to be confirmed. - double STANDARDFREQ = 432.1; - float rTune = (float)(STANDARDFREQ * pow(2.0, (0x5000 - rTuneOffset) / 4096.0 - 9.0 / 12.0)); - //printDebug("%f,%d,%d", pTune, tps[i].pitchCoarse, tps[i].pitchFine); - PCMList[i].addr = rAddr; - PCMList[i].len = rLen; - PCMList[i].loop = rLoop; - PCMList[i].tune = rTune; - } -} - -void Synth::initRhythmTimbre(int timbreNum, const Bit8u *mem) { - TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre; - memcpy(&timbre->common, mem, 14); - mem += 14; - char drumname[11]; - strncpy(drumname, timbre->common.name, 10); - drumname[10] = 0; - for (int t = 0; t < 4; t++) { - if (((timbre->common.pmute >> t) & 0x1) == 0x1) { - memcpy(&timbre->partial[t], mem, 58); - mem += 58; - } - } -} - -void Synth::initRhythmTimbres() { - const Bit8u *drumMap = &controlROMData[0x3200]; - int timbreNum = 192; - for (Bit16u i = 0; i < 60; i += 2) { - Bit16u address = (drumMap[i + 1] << 8) | drumMap[i]; - initRhythmTimbre(timbreNum++, &controlROMData[address]); - } -} - -void Synth::initTimbres(Bit16u mapAddress, int startTimbre) { - for (Bit16u i = mapAddress; i < mapAddress + 0x80; i += 2) { - Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i]; - address = address + (mapAddress - 0x8000); - TimbreParam *timbre = &mt32ram.timbres[startTimbre++].timbre; - memcpy(timbre, &controlROMData[address], sizeof(TimbreParam)); - } -} - -bool Synth::open(SynthProperties &useProp) { - if (isOpen) - return false; - - myProp = useProp; - if (useProp.baseDir != NULL) { - myProp.baseDir = new char[strlen(useProp.baseDir) + 1]; - strcpy(myProp.baseDir, useProp.baseDir); - } - - // This is to help detect bugs - memset(&mt32ram, '?', sizeof(mt32ram)); - for (int i = 128; i < 192; i++) { - // If something sets a patch to point to an uninitialised memory timbre, don't play anything - mt32ram.timbres[i].timbre.common.pmute = 0; - } - - printDebug("Loading Control ROM"); - if (!loadControlROM("MT32_CONTROL.ROM")) { - printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM"); - report(ReportType_errorControlROM, &errno); - return false; - } - - printDebug("Loading PCM ROM"); - if (!loadPCMROM("MT32_PCM.ROM")) { - printDebug("Init Error - Missing MT32_PCM.ROM"); - report(ReportType_errorPCMROM, &errno); - return false; - } - - partialManager = new PartialManager(this); - - printDebug("Initialising PCM List"); - initPCMList(); - - printDebug("Initialising Timbre Bank A"); - initTimbres(0x8000, 0); - - printDebug("Initialising Timbre Bank B"); - initTimbres(0xC000, 64); - - printDebug("Initialising Timbre Bank R"); - initRhythmTimbres(); - - printDebug("Initialising Rhythm Temp"); - memcpy(mt32ram.rhythmSettings, &controlROMData[0x741C], 344); - - printDebug("Initialising Patches"); - for (Bit8u i = 0; i < 128; i++) { - PatchParam *patch = &mt32ram.patches[i]; - patch->timbreGroup = i / 64; - patch->timbreNum = i % 64; - patch->keyShift = 24; - patch->fineTune = 50; - patch->benderRange = 12; - patch->assignMode = 0; - patch->reverbSwitch = 1; - patch->dummy = 0; - } - - printDebug("Initialising System"); - //FIXME: Confirm that these are all correct - // The MT-32 manual claims that "Standard pitch" is 442Hz. - // I assume they mean this is the MT-32 default pitch, and not concert pitch, - // since the latter has been internationally defined as 440Hz for decades. - // Regardless, I'm setting the default masterTune to 440Hz - mt32ram.system.masterTune = 0x40; - mt32ram.system.reverbMode = 0; - mt32ram.system.reverbTime = 5; - mt32ram.system.reverbLevel = 3; - memcpy(mt32ram.system.reserveSettings, &controlROMData[0x57E5], 9); - for (Bit8u i = 0; i < 9; i++) { - // This is the default: {1, 2, 3, 4, 5, 6, 7, 8, 9} - // An alternative configuration can be selected by holding "Master Volume" - // and pressing "PART button 1" on the real MT-32's frontpanel. - // The channel assignment is then {0, 1, 2, 3, 4, 5, 6, 7, 9} - mt32ram.system.chanAssign[i] = i + 1; - } - mt32ram.system.masterVol = 100; - if (!refreshSystem()) - return false; - - for (int i = 0; i < 8; i++) { - mt32ram.patchSettings[i].outlevel = 80; - mt32ram.patchSettings[i].panpot = controlROMData[0x5800 + i]; - memset(mt32ram.patchSettings[i].dummyv, 0, sizeof(mt32ram.patchSettings[i].dummyv)); - parts[i] = new Part(this, i); - parts[i]->setProgram(controlROMData[0x57EE + i]); - } - parts[8] = new RhythmPart(this, 8); - - // For resetting mt32 mid-execution - mt32default = mt32ram; - - iirFilter = &iir_filter_normal; - -#ifdef MT32EMU_HAVE_X86 - bool availableSSE = DetectSIMD(); - bool available3DNow = Detect3DNow(); - - if (availableSSE) - report(ReportType_availableSSE, NULL); - if (available3DNow) - report(ReportType_available3DNow, NULL); - - if (available3DNow) { - printDebug("Detected and using SIMD (AMD 3DNow) extensions"); - iirFilter = &iir_filter_3dnow; - report(ReportType_using3DNow, NULL); - } else if (availableSSE) { - printDebug("Detected and using SIMD (Intel SSE) extensions"); - iirFilter = &iir_filter_sse; - report(ReportType_usingSSE, NULL); - } -#endif - - isOpen = true; - isEnabled = false; - - printDebug("*** Initialisation complete ***"); - return true; -} - -void Synth::close(void) { - if (!isOpen) - return; - - TableInitialiser::freeNotes(); - if (partialManager != NULL) { - delete partialManager; - partialManager = NULL; - } - - if (reverbModel != NULL) { - delete reverbModel; - reverbModel = NULL; - } - - for (int i = 0; i < 9; i++) { - if (parts[i] != NULL) { - delete parts[i]; - parts[i] = NULL; - } - } - if (myProp.baseDir != NULL) { - delete myProp.baseDir; - myProp.baseDir = NULL; - } - isOpen = false; -} - -void Synth::playMsg(Bit32u msg) { - unsigned char code = (unsigned char)((msg & 0xf0) >> 4); - unsigned char chan = (unsigned char)(msg & 0xf); - unsigned char note = (unsigned char)((msg & 0xff00) >> 8); - unsigned char velocity = (unsigned char)((msg & 0xff0000) >> 16); - isEnabled = true; - - //printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note); - - char part = chantable[chan]; - if (part < 0 || part > 8) { - printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity); - return; - } - playMsgOnPart(part, code, note, velocity); -} - -void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) { - Bit32u bend; - - //printDebug("Synth::playMsg(0x%02x)",msg); - switch (code) { - case 0x8: - //printDebug("Note OFF - Part %d", part); - // The MT-32 ignores velocity for note off - parts[part]->stopNote(note); - break; - case 0x9: - //printDebug("Note ON - Part %d, Note %d Vel %d", part, note, velocity); - if (velocity == 0) { - // MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40 - parts[part]->stopNote(note); - } else { - parts[part]->playNote(note, velocity); - } - break; - case 0xB: // Control change - switch (note) { - case 0x01: // Modulation - //printDebug("Modulation: %d", velocity); - parts[part]->setModulation(velocity); - break; - case 0x07: // Set volume - //if (part!=3) return; - //printDebug("Volume set: %d", velocity); - parts[part]->setVolume(velocity); - break; - case 0x0A: // Pan - //printDebug("Pan set: %d", velocity); - parts[part]->setPan(velocity); - break; - case 0x0B: - //printDebug("Expression set: %d", velocity); - parts[part]->setVolume(velocity); - break; - case 0x40: // Hold pedal - //printDebug("Hold pedal set: %d", velocity); - parts[part]->setHoldPedal(velocity>=64); - break; - - case 0x79: // Reset all controllers - printDebug("Reset all controllers (NYI)"); - break; - - case 0x7B: // All notes off - //printDebug("All notes off"); - parts[part]->allStop(); - break; - - default: - printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity); - break; - } - - break; - case 0xC: // Program change - //printDebug("Program change %01x", note); - parts[part]->setProgram(note); - break; - case 0xE: // Pitch bender - bend = (velocity << 7) | (note); - //printDebug("Pitch bender %02x", bend); - parts[part]->setBend(bend); - break; - default: - printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity); - break; - } - - //midiOutShortMsg(m_out, msg); -} - -void Synth::playSysex(const Bit8u * sysex,Bit32u len) { - if (len < 3) { - printDebug("playSysex: Message is too short for sysex (%d bytes)", len); - } - if (sysex[0] != 0xf0) { - printDebug("playSysex: Message lacks start-of-sysex (0xf0)"); - return; - } - if (sysex[len - 1] != 0xf7) { - printDebug("playSysex: Message lacks end-of-sysex (0xf7)"); - return; - } - playSysexWithoutFraming(sysex + 1, len - 2); -} - -void Synth::playSysexWithoutFraming(const Bit8u * sysex, Bit32u len) { - if (len < 4) { - printDebug("playSysexWithoutFraming: Message is too short (%d bytes)!", len); - return; - } - if (sysex[0] != 0x41) { - printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); - return; - } - if (sysex[2] == 0x14) { - printDebug("playSysexWithoutFraming: Header is intended for Roland D-50 (not yet supported): %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); - return; - } - else if (sysex[2] != 0x16) { - printDebug("playSysexWithoutFraming: Header not intended for MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); - return; - } - if (sysex[3] != 0x12) { - printDebug("playSysexWithoutFraming: Unsupported command %02x", sysex[3]); - return; - } - playSysexWithoutHeader(sysex[1], sysex + 4, len - 4); -} - -// MEMADDR() converts from sysex-padded, SYSEXMEMADDR converts to it -// Roland provides documentation using the sysex-padded addresses, so we tend to use that int code and output -#define MEMADDR(x) ((((x) & 0x7f0000) >> 2) | (((x) & 0x7f00) >> 1) | ((x) & 0x7f)) -#define SYSEXMEMADDR(x) ((((x) & 0x1FC000) << 2) | (((x) & 0x3F80) << 1) | ((x) & 0x7f)) - -#define NUMTOUCHED(x,y) (((x) + sizeof(y) - 1) / sizeof(y)) - -void Synth::playSysexWithoutHeader(unsigned char device, const Bit8u *sysex, Bit32u len) { - if (device > 0x10) { - // We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels - printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", (int)device); - return; - } - if (len < 4) { - printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len); - return; - } - unsigned char checksum = calcSysexChecksum(sysex, len - 1, 0); - if (checksum != sysex[len - 1]) { - printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum); - return; - } - len -= 1; // Exclude checksum - Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]); - addr = MEMADDR(addr); - sysex += 3; - len -= 3; - //printDebug("Sysex addr: 0x%06x", SYSEXMEMADDR(addr)); - // NOTE: Please keep both lower and upper bounds in each check, for ease of reading - if (device < 0x10) { - printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, SYSEXMEMADDR(addr)); - if (/*addr >= MEMADDR(0x000000) && */addr < MEMADDR(0x010000)) { - int offset; - if (chantable[device] == -1) { - printDebug(" (Channel not mapped to a partial... 0 offset)"); - offset = 0; - } else if (chantable[device] == 8) { - printDebug(" (Channel mapped to rhythm... 0 offset)"); - offset = 0; - } else { - offset = chantable[device] * sizeof(MemParams::PatchTemp); - printDebug(" (Setting extra offset to %d)", offset); - } - addr += MEMADDR(0x030000) + offset; - } else if (/*addr >= 0x010000 && */ addr < MEMADDR(0x020000)) { - addr += MEMADDR(0x030110) - MEMADDR(0x010000); - } else if (/*addr >= 0x020000 && */ addr < MEMADDR(0x030000)) { - int offset; - if (chantable[device] == -1) { - printDebug(" (Channel not mapped to a partial... 0 offset)"); - offset = 0; - } else if (chantable[device] == 8) { - printDebug(" (Channel mapped to rhythm... 0 offset)"); - offset = 0; - } else { - offset = chantable[device] * sizeof(TimbreParam); - printDebug(" (Setting extra offset to %d)", offset); - } - addr += MEMADDR(0x040000) - MEMADDR(0x020000) + offset; - } else { - printDebug("PlaySysexWithoutHeader: Invalid channel %d address 0x%06x", device, SYSEXMEMADDR(addr)); - return; - } - } - if (addr >= MEMADDR(0x030000) && addr < MEMADDR(0x030110)) { - int off = addr - MEMADDR(0x030000); - if (off + len > sizeof(mt32ram.patchSettings)) { - printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len); - return; - } - int firstPart = off / sizeof(MemParams::PatchTemp); - off %= sizeof(MemParams::PatchTemp); - for (unsigned int m = 0; m < len; m++) - ((Bit8u *)&mt32ram.patchSettings[firstPart])[off + m] = sysex[m]; - //printDebug("Patch temp: Patch %d, offset %x, len %d", off/16, off % 16, len); - - int lastPart = firstPart + NUMTOUCHED(off + len, MemParams::PatchTemp) - 1; - for (int i = firstPart; i <= lastPart; i++) { - int absTimbreNum = mt32ram.patchSettings[i].patch.timbreGroup * 64 + mt32ram.patchSettings[i].patch.timbreNum; - char timbreName[11]; - memcpy(timbreName, mt32ram.timbres[absTimbreNum].timbre.common.name, 10); - timbreName[10] = 0; - printDebug("WRITE-PARTPATCH (%d-%d@%d..%d): %d; timbre=%d (%s)", firstPart, lastPart, off, off + len, i, absTimbreNum, timbreName); - if (parts[i] != NULL) { - if (i == firstPart && off > 2) { - printDebug(" (Not updating timbre, since those values weren't touched)"); - } else { - // Not sure whether we should do this at all, really. - parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre); - } - parts[i]->refresh(); - } - } - } else if (addr >= MEMADDR(0x030110) && addr < MEMADDR(0x040000)) { - int off = addr - MEMADDR(0x030110); - if (off + len > sizeof(mt32ram.rhythmSettings)) { - printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len); - return; - } - int firstDrum = off / sizeof(MemParams::RhythmTemp); - off %= sizeof(MemParams::RhythmTemp); - for (unsigned int m = 0; m < len; m++) - ((Bit8u *)&mt32ram.rhythmSettings[firstDrum])[off + m] = sysex[m]; - int lastDrum = firstDrum + NUMTOUCHED(off + len, MemParams::RhythmTemp) - 1; - for (int i = firstDrum; i <= lastDrum; i++) { - int timbreNum = mt32ram.rhythmSettings[i].timbre; - char timbreName[11]; - if (timbreNum < 94) { - memcpy(timbreName, mt32ram.timbres[128 + timbreNum].timbre.common.name, 10); - timbreName[10] = 0; - } else { - strcpy(timbreName, "[None]"); - } - printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", firstDrum, lastDrum, off, off + len, i, mt32ram.rhythmSettings[i].outlevel, mt32ram.rhythmSettings[i].panpot, mt32ram.rhythmSettings[i].reverbSwitch, mt32ram.rhythmSettings[i].timbre, timbreName); - } - if (parts[8] != NULL) { - parts[8]->refresh(); - } - } else if (addr >= MEMADDR(0x040000) && addr < MEMADDR(0x050000)) { - int off = addr - MEMADDR(0x040000); - if (off + len > sizeof(mt32ram.timbreSettings)) { - printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len); - return; - } - int firstPart = off / sizeof(TimbreParam); - off %= sizeof(TimbreParam); - for (unsigned int m = 0; m < len; m++) - ((Bit8u *)&mt32ram.timbreSettings[firstPart])[off + m] = sysex[m]; - int lastPart = firstPart + NUMTOUCHED(off + len, TimbreParam) - 1; - for (int i = firstPart; i <= lastPart; i++) { - char instrumentName[11]; - memcpy(instrumentName, mt32ram.timbreSettings[i].common.name, 10); - instrumentName[10] = 0; - printDebug("WRITE-PARTTIMBRE (%d-%d@%d..%d): timbre=%d (%s)", firstPart, lastPart, off, off + len, i, instrumentName); - if (parts[i] != NULL) { - parts[i]->refresh(); - } - } - } - else if (addr >= MEMADDR(0x050000) && addr < MEMADDR(0x060000)) { - int off = addr - MEMADDR(0x050000); - if (off + len > sizeof(mt32ram.patches)) { - printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len); - return; - } - int firstPatch = off / sizeof(PatchParam); - off %= sizeof(PatchParam); - for (unsigned int m = 0; m < len; m++) - ((Bit8u *)&mt32ram.patches[firstPatch])[off + m] = sysex[m]; - int lastPatch = firstPatch + NUMTOUCHED(off + len, PatchParam) - 1; - for (int i = firstPatch; i <= lastPatch; i++) { - PatchParam *patch = &mt32ram.patches[i]; - int patchAbsTimbreNum = patch->timbreGroup * 64 + patch->timbreNum; - char instrumentName[11]; - memcpy(instrumentName, mt32ram.timbres[patchAbsTimbreNum].timbre.common.name, 10); - instrumentName[10] = 0; - Bit8u *n = (Bit8u *)patch; - printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s) %02X%02X%02X%02X%02X%02X%02X%02X", firstPatch, lastPatch, off, off + len, i, patchAbsTimbreNum, instrumentName, n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]); - // FIXME:KG: The below is definitely dodgy. We just guess that this is the patch that the part was using - // based on a timbre match (but many patches could have the same timbre!) - // If this refresh is really correct, we should store the patch number in use by each part. - /* - for (int part = 0; part < 8; part++) { - if (parts[part] != NULL) { - int partPatchAbsTimbreNum = mt32ram.patchSettings[part].patch.timbreGroup * 64 + mt32ram.patchSettings[part].patch.timbreNum; - if (parts[part]->getAbsTimbreNum() == patchAbsTimbreNum) { - parts[part]->setPatch(patch); - parts[part]->RefreshPatch(); - } - } - } - */ - } - } else if (addr >= MEMADDR(0x080000) && addr < MEMADDR(0x090000)) { - // Timbres - int off = addr - MEMADDR(0x080000); - if (off + len > sizeof(MemParams::PaddedTimbre) * 64) { - // You can only write to one group at a time - printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len); - return; - } - unsigned int firstTimbre = off / sizeof (MemParams::PaddedTimbre); - off %= sizeof (MemParams::PaddedTimbre); - firstTimbre += 128; - for (unsigned int m = 0; m < len; m++) - ((Bit8u *)&mt32ram.timbres[firstTimbre])[off + m] = sysex[m]; - unsigned int lastTimbre = firstTimbre + NUMTOUCHED(len + off, MemParams::PaddedTimbre) - 1; - for (unsigned int i = firstTimbre; i <= lastTimbre; i++) { - char instrumentName[11]; - memcpy(instrumentName, mt32ram.timbres[i].timbre.common.name, 10); - instrumentName[10] = 0; - printDebug("WRITE-TIMBRE (%d-%d@%d..%d): %d; name=\"%s\"", firstTimbre, lastTimbre, off, off + len, i, instrumentName); - // FIXME:KG: Not sure if the stuff below should be done (for rhythm and/or parts)... - // Does the real MT-32 automatically do this? - for (unsigned int part = 0; part < 9; part++) { - if (parts[part] != NULL) { - parts[part]->refreshTimbre(i); - } - } - } - } else if (addr >= MEMADDR(0x100000) && addr < MEMADDR(0x200000)) { - int off = addr - MEMADDR(0x100000); - if (off + len > sizeof(mt32ram.system)) { - printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len); - return; - } - for (unsigned int m = 0; m < len; m++) - ((Bit8u *)&mt32ram.system)[m + off] = sysex[m]; - - report(ReportType_devReconfig, NULL); - - printDebug("WRITE-SYSTEM:"); - refreshSystem(); - } else if (addr == MEMADDR(0x200000)) { - char buf[MAX_SYSEX_SIZE]; - if (len > MAX_SYSEX_SIZE - 1) { - printDebug("WRITE-LCD sysex length (%d) exceeded MAX_SYSEX_SIZE (%d) - 1; truncating", len, MAX_SYSEX_SIZE); - len = MAX_SYSEX_SIZE - 1; - } - memcpy(&buf, &sysex[0], len); - buf[len] = 0; - printDebug("WRITE-LCD: %s", buf); - report(ReportType_lcdMessage, buf); - } else if (addr >= MEMADDR(0x7f0000)) { - printDebug("Reset"); - report(ReportType_devReset, NULL); - partialManager->deactivateAll(); - mt32ram = mt32default; - for (int i = 0; i < 9; i++) { - parts[i]->refresh(); - } - isEnabled = false; - } else { - printDebug("Sysex write to unrecognised address %06x", SYSEXMEMADDR(addr)); - } -} - -bool Synth::refreshSystem() { - memset(chantable,-1,sizeof(chantable)); - - for (unsigned int i = 0; i < 9; i++) { - //LOG(LOG_MISC|LOG_ERROR,"Part %d set to MIDI channel %d",i,mt32ram.system.chanAssign[i]); - if (mt32ram.system.chanAssign[i] == 16 && parts[i] != NULL) { - parts[i]->allStop(); - } else { - chantable[(int)mt32ram.system.chanAssign[i]] = (char)i; - } - } - //FIXME:KG: This is just an educated guess. - // The LAPC-I documentation claims a range of 427.5Hz-452.6Hz (similar to what we have here) - // The MT-32 documentation claims a range of 432.1Hz-457.6Hz - masterTune = 440.0f * powf(2.0f, (mt32ram.system.masterTune - 64.0f) / (128.0f * 12.0f)); - printDebug(" Master Tune: %f", masterTune); - printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel); - report(ReportType_newReverbMode, &mt32ram.system.reverbMode); - report(ReportType_newReverbTime, &mt32ram.system.reverbTime); - report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel); - - if (myProp.useDefaultReverb) { - initReverb(mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel); - } else { - initReverb(myProp.reverbType, myProp.reverbTime, mt32ram.system.reverbLevel); - } - - Bit8u *rset = mt32ram.system.reserveSettings; - printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]); - int pr = partialManager->setReserve(rset); - if (pr != 32) - printDebug(" (Partial Reserve Table with less than 32 partials reserved!)"); - rset = mt32ram.system.chanAssign; - printDebug(" Part assign: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]); - printDebug(" Master volume: %d", mt32ram.system.masterVol); - masterVolume = (Bit16u)(mt32ram.system.masterVol * 327); - if (!TableInitialiser::initMT32Tables(this, PCMList, (float)myProp.sampleRate, masterTune)) { - report(ReportType_errorSampleRate, NULL); - return false; - } - return true; -} - -bool Synth::dumpTimbre(File *file, const TimbreParam *timbre, Bit32u address) { - // Sysex header - if (!file->writeBit8u(0xF0)) - return false; - if (!file->writeBit8u(0x41)) - return false; - if (!file->writeBit8u(0x10)) - return false; - if (!file->writeBit8u(0x16)) - return false; - if (!file->writeBit8u(0x12)) - return false; - - char lsb = (char)(address & 0x7f); - char isb = (char)((address >> 7) & 0x7f); - char msb = (char)(((address >> 14) & 0x7f) | 0x08); - - //Address - if (!file->writeBit8u(msb)) - return false; - if (!file->writeBit8u(isb)) - return false; - if (!file->writeBit8u(lsb)) - return false; - - //Data - if (file->write(timbre, 246) != 246) - return false; - - //Checksum - unsigned char checksum = calcSysexChecksum((const Bit8u *)timbre, 246, msb + isb + lsb); - if (!file->writeBit8u(checksum)) - return false; - - //End of sysex - if (!file->writeBit8u(0xF7)) - return false; - return true; -} - -int Synth::dumpTimbres(const char *filename, int start, int len) { - File *file = openFile(filename, File::OpenMode_write); - if (file == NULL) - return -1; - - for (int timbreNum = start; timbreNum < start + len; timbreNum++) { - int useaddr = (timbreNum - start) * 256; - TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre; - if (!dumpTimbre(file, timbre, useaddr)) - break; - } - closeFile(file); - return 0; -} - -void ProduceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) { -#if MT32EMU_USE_MMX > 2 - //FIXME:KG: This appears to introduce crackle - int donelen = i386_produceOutput1(useBuf, stream, len, volume); - len -= donelen; - stream += donelen * 2; - useBuf += donelen * 2; -#endif - int end = len * 2; - while (end--) { - *stream = *stream + (Bit16s)(((Bit32s)*useBuf++ * (Bit32s)volume)>>15); - stream++; - } -} - -void Synth::render(Bit16s *stream, Bit32u len) { - memset(stream, 0, len * sizeof (Bit16s) * 2); - if (!isEnabled) - return; - while (len > 0) { - Bit32u thisLen = len > MAX_SAMPLE_OUTPUT ? MAX_SAMPLE_OUTPUT : len; - doRender(stream, thisLen); - len -= thisLen; - stream += 2 * thisLen; - } -} - -void Synth::doRender(Bit16s * stream,Bit32u len) { - Bit32u m; - - partialManager->ageAll(); - - if (myProp.useReverb) { - bool hasOutput = false; - for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (partialManager->shouldReverb(i)) { - if (partialManager->produceOutput(i, &tmpBuffer[0], len)) { - ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume); - hasOutput = true; - } - } - } - // No point in doing reverb on a mute buffer... - if (hasOutput) { - m=0; - for (unsigned int i = 0; i < len; i++) { - sndbufl[i] = (float)stream[m] / 32767.0f; - m++; - sndbufr[i] = (float)stream[m] / 32767.0f; - m++; - } - reverbModel->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1); - m=0; - for (unsigned int i = 0; i < len; i++) { - stream[m] = (Bit16s)(outbufl[i] * 32767.0f); - m++; - stream[m] = (Bit16s)(outbufr[i] * 32767.0f); - m++; - } - } - for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (!partialManager->shouldReverb(i)) { - if (partialManager->produceOutput(i, &tmpBuffer[0], len)) { - ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume); - } - } - } - } else { - for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { - if (partialManager->produceOutput(i, &tmpBuffer[0], len)) - ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume); - } - } - - partialManager->clearAlreadyOutputed(); - -#if MT32EMU_MONITOR_PARTIALS == 1 - samplepos += len; - if (samplepos > myProp.SampleRate * 5) { - samplepos = 0; - int partialUsage[9]; - partialManager->GetPerPartPartialUsage(partialUsage); - printDebug("1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7]); - printDebug("Rhythm: %02d TOTAL: %02d", partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->GetFreePartialCount()); - } -#endif -} - -} diff --git a/backends/midi/mt32/synth.h b/backends/midi/mt32/synth.h deleted file mode 100644 index 1501593cbd..0000000000 --- a/backends/midi/mt32/synth.h +++ /dev/null @@ -1,200 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef MT32EMU_SYNTH_H -#define MT32EMU_SYNTH_H - -#include <stdarg.h> - -class revmodel; - -namespace MT32Emu { - -const int ROMSIZE = 512 * 1024; -const int PCMSIZE = ROMSIZE / 2; -const int GRAN = 512; - -class File; -class TableInitialiser; -class Partial; -class PartialManager; -class Part; - -enum ReportType { - // Errors - ReportType_errorControlROM = 1, - ReportType_errorPCMROM, - ReportType_errorSampleRate, - - // Progress - ReportType_progressInit, - - // HW spec - ReportType_availableSSE, - ReportType_available3DNow, - ReportType_usingSSE, - ReportType_using3DNow, - - // General info - ReportType_lcdMessage, - ReportType_devReset, - ReportType_devReconfig, - ReportType_newReverbMode, - ReportType_newReverbTime, - ReportType_newReverbLevel -}; - -struct SynthProperties { - // Sample rate to use in mixing - int sampleRate; - - // Flag to activate reverb. True = use reverb, False = no reverb - bool useReverb; - // True to use software set reverb settings, False to set reverb settings in - // following parameters - bool useDefaultReverb; - // When not using the default settings, this specifies one of the 4 reverb types - // 1 = Room 2 = Hall 3 = Plate 4 = Tap - unsigned char reverbType; - // This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement) - unsigned char reverbTime; - // This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement) - unsigned char reverbLevel; - // The name of the directory in which the ROM and data files are stored (with trailing slash/backslash) - // Not used if "openFile" is set. May be NULL in any case. - char *baseDir; - // This is used as the first argument to all callbacks - void *userData; - // Callback for reporting various errors and information. May be NULL - int (*report)(void *userData, ReportType type, const void *reportData); - // Callback for debug messages, in vprintf() format - void (*printDebug)(void *userData, const char *fmt, va_list list); - // Callback for providing an implementation of File, opened and ready for use - // May be NULL, in which case a default implementation will be used. - File *(*openFile)(void *userData, const char *filename, File::OpenMode mode); - // Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted. - void (*closeFile)(void *userData, File *file); -}; - -// This is the specification of the Callback routine used when calling the RecalcWaveforms -// function -typedef void (*recalcStatusCallback)(int percDone); - -// This external function recreates the base waveform file (waveforms.raw) using a specifed -// sampling rate. The callback routine provides interactivity to let the user know what -// percentage is complete in regenerating the waveforms. When a NULL pointer is used as the -// callback routine, no status is reported. -bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack); - -typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr, int revLevel); - -class Synth { -friend class Part; -friend class RhythmPart; -friend class Partial; -friend class TableInitialiser; -private: - bool isEnabled; - - iir_filter_type iirFilter; - - PCMWaveEntry PCMList[128]; - - Bit8u controlROMData[64 * 1024]; - Bit16s romfile[PCMSIZE + GRAN]; - Bit8s chantable[32]; - - #if MT32EMU_MONITOR_PARTIALS == 1 - static Bit32s samplepos = 0; - #endif - - MemParams mt32ram, mt32default; - - revmodel *reverbModel; - - float masterTune; - Bit16u masterVolume; - - bool isOpen; - - PartialManager *partialManager; - Part *parts[9]; - - Bit16s tmpBuffer[MAX_SAMPLE_OUTPUT * 2]; - float sndbufl[MAX_SAMPLE_OUTPUT]; - float sndbufr[MAX_SAMPLE_OUTPUT]; - float outbufl[MAX_SAMPLE_OUTPUT]; - float outbufr[MAX_SAMPLE_OUTPUT]; - - SynthProperties myProp; - - bool loadPreset(File *file); - void initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel); - void doRender(Bit16s * stream, Bit32u len); - void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity); - void playSysexWithoutHeader(unsigned char channel, const Bit8u *sysex, Bit32u len); - - bool loadControlROM(const char *filename); - bool loadPCMROM(const char *filename); - bool dumpTimbre(File *file, const TimbreParam *timbre, Bit32u addr); - int dumpTimbres(const char *filename, int start, int len); - - void initPCMList(); - void initRhythmTimbres(); - void initTimbres(Bit16u mapAddress, int startTimbre); - void initRhythmTimbre(int drumNum, const Bit8u *mem); - bool refreshSystem(); -protected: - int report(ReportType type, const void *reportData); - File *openFile(const char *filename, File::OpenMode mode); - void closeFile(File *file); - void printDebug(const char *fmt, ...); - -public: - static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum); - - Synth(); - ~Synth(); - - // Used to initialise the MT-32. Must be called before any other function. - // Returns true if initialization was sucessful, otherwise returns false. - bool open(SynthProperties &useProp); - - // Closes the MT-32 and deallocates any memory used by the synthesizer - void close(void); - - // Sends a 4-byte MIDI message to the MT-32 for immediate playback - void playMsg(Bit32u msg); - - // Sends a string of Sysex commands to the MT-32 for immediate interpretation - // The length is in bytes - void playSysex(const Bit8u *sysex, Bit32u len); - void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len); - - // This callback routine is used to have the MT-32 generate samples to the specified - // output stream. The length is in whole samples, not bytes. (I.E. in 16-bit stereo, - // one sample is 4 bytes) - void render(Bit16s * stream, Bit32u len); -}; - -} - -#endif diff --git a/backends/midi/mt32/tables.cpp b/backends/midi/mt32/tables.cpp deleted file mode 100644 index 4a17165733..0000000000 --- a/backends/midi/mt32/tables.cpp +++ /dev/null @@ -1,729 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include <stdlib.h> -#include <string.h> -#include <math.h> - -#include "mt32emu.h" - -#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x)) - -namespace MT32Emu { - -//Amplitude time velocity follow exponential coefficients -const double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637}; -const double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539}; - -// These are division constants for the TVF depth key follow -static const Bit32u depexp[5] = {3000, 950, 485, 255, 138}; - -//Envelope time keyfollow exponential coefficients -static const double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143, 0.043333215}; -static const double tkcatmult[5] = {1.0, 1.058245688, 1.048488989, 1.016049301, 1.097538067}; - -static float initialisedSampleRate = 0.0f; -static float initialisedMasterTune = 0.0f; - -Bit16s smallnoise[MAX_SAMPLE_OUTPUT]; - -// Some optimization stuff -Bit32s keytable[217]; -Bit16s sintable[65536]; -Bit32u lfotable[101]; -Bit32s penvtable[16][101]; -Bit32s filveltable[128][101]; -Bit32s veltkeytable[5][128]; -Bit32s pulsetable[101]; -Bit32s pulseoffset[101]; -Bit32s ampbiastable[13][128]; -Bit32s fbiastable[15][128]; -float filtcoeff[FILTERGRAN][31][8]; -Bit32s finetable[201]; -Bit32u lfoptable[101][101]; -Bit32s ampveltable[128][64]; -Bit32s pwveltable[15][128]; -Bit32s envtimetable[101]; -Bit32s decaytimetable[101]; -Bit32s lasttimetable[101]; -Bit32s voltable[128]; -float ResonFactor[31]; -float ResonInv[31]; - -NoteLookup noteLookups[NUM_NOTES]; - -// Begin filter stuff - -// Pre-warp the coefficients of a numerator or denominator. -// Note that a0 is assumed to be 1, so there is no wrapping -// of it. -static void prewarp(double *a1, double *a2, double fc, double fs) { - double wp; - - wp = 2.0 * fs * tan(DOUBLE_PI * fc / fs); - - *a2 = *a2 / (wp * wp); - *a1 = *a1 / wp; -} - -// Transform the numerator and denominator coefficients -// of s-domain biquad section into corresponding -// z-domain coefficients. -// -// Store the 4 IIR coefficients in array pointed by coef -// in following order: -// beta1, beta2 (denominator) -// alpha1, alpha2 (numerator) -// -// Arguments: -// a0-a2 - s-domain numerator coefficients -// b0-b2 - s-domain denominator coefficients -// k - filter gain factor. initially set to 1 -// and modified by each biquad section in such -// a way, as to make it the coefficient by -// which to multiply the overall filter gain -// in order to achieve a desired overall filter gain, -// specified in initial value of k. -// fs - sampling rate (Hz) -// coef - array of z-domain coefficients to be filled in. -// -// Return: -// On return, set coef z-domain coefficients -static void bilinear(double a0, double a1, double a2, double b0, double b1, double b2, double *k, double fs, float *coef) { - double ad, bd; - - // alpha (Numerator in s-domain) - ad = 4. * a2 * fs * fs + 2. * a1 * fs + a0; - // beta (Denominator in s-domain) - bd = 4. * b2 * fs * fs + 2. * b1* fs + b0; - - // update gain constant for this section - *k *= ad/bd; - - // Denominator - *coef++ = (float)((2. * b0 - 8. * b2 * fs * fs) / bd); // beta1 - *coef++ = (float)((4. * b2 * fs * fs - 2. * b1 * fs + b0) / bd); // beta2 - - // Nominator - *coef++ = (float)((2. * a0 - 8. * a2 * fs * fs) / ad); // alpha1 - *coef = (float)((4. * a2 * fs * fs - 2. * a1 * fs + a0) / ad); // alpha2 -} - -// a0-a2: numerator coefficients -// b0-b2: denominator coefficients -// fc: Filter cutoff frequency -// fs: sampling rate -// k: overall gain factor -// coef: pointer to 4 iir coefficients -static void szxform(double *a0, double *a1, double *a2, double *b0, double *b1, double *b2, double fc, double fs, double *k, float *coef) { - // Calculate a1 and a2 and overwrite the original values - prewarp(a1, a2, fc, fs); - prewarp(b1, b2, fc, fs); - bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef); -} - -static void initFilter(float fs, float fc, float *icoeff, float Q) { - float *coef; - double a0, a1, a2, b0, b1, b2; - - double k = 1.5; // Set overall filter gain factor - coef = icoeff + 1; // Skip k, or gain - - // Section 1 - a0 = 1.0; - a1 = 0; - a2 = 0; - b0 = 1.0; - b1 = 0.765367 / Q; // Divide by resonance or Q - b2 = 1.0; - szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef); - coef += 4; // Point to next filter section - - // Section 2 - a0 = 1.0; - a1 = 0; - a2 = 0; - b0 = 1.0; - b1 = 1.847759 / Q; - b2 = 1.0; - szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef); - - icoeff[0] = (float)k; -} - -static void initFiltCoeff(float samplerate) { - for (int j = 0; j < FILTERGRAN; j++) { - for (int res = 0; res < 31; res++) { - float tres = ResonFactor[res]; - initFilter((float)samplerate, (((float)(j+1.0)/FILTERGRAN)) * ((float)samplerate/2), filtcoeff[j][res], tres); - } - } -} - -static void initEnvelopes(float samplerate) { - for (int lf = 0; lf <= 100; lf++) { - float elf = (float)lf; - - // General envelope - float logtime = elf * 0.088362939f; - envtimetable[lf] = (int)((exp(logtime)/312.12) * (float)samplerate); - - // Decay envelope -- shorter for some reason - // This is also the timing for the envelope right before the - // amp and filter envelope sustains - - lasttimetable[lf] = decaytimetable[lf] = (int)((exp(logtime)/(312.12*2)) * (float)samplerate); - //lasttimetable[lf] = (int)((exp(logtime)/(312.12*6)) * (float)samplerate); - - float mv = (float)lf / 100.0f; - float pt = mv - 0.5f; - if (pt < 0) - pt = 0; - - pulsetable[lf] = (int)(pt * 215.04f) + 128; - - // I am certain of this: Verified by hand LFO log - lfotable[lf] = (Bit32u)(((float)samplerate) / (powf(1.088883372f,(float)lf) * 0.021236044f)); - - //LOG(LOG_ERROR|LOG_MISC,"lf %d = lfo %d pulsetable %d", lf, lfotable[lf], pulsetable[lf]); - } -} - -void TableInitialiser::initMT32ConstantTables(Synth *synth) { - if (initialisedSampleRate > 0.0f) { - return; - } - int lf; - synth->printDebug("Initialising Pitch Tables"); - for (lf = -108; lf <= 108; lf++) { - keytable[lf + 108] = (int)(256 * powf(2.0f, (float)(lf / 24.0f))); - //synth->printDebug("KT %d = %d", f, keytable[f+108]); - } - - int res; - float fres; - for (res = 0; res < 31; res++) { - fres = (float)res / 30.0f; - ResonFactor[res] = (powf(2.0f, logf(powf(fres, 16.0f))) * 2.5f) + 1.0f; - ResonInv[res] = 1 / ResonFactor[res]; - } - - int period = 65536; - - for (int ang = 0; ang < period; ang++) { - int halfang = (period / 2); - int angval = ang % halfang; - float tval = (((float)angval / (float)halfang) - 0.5f) * 2; - if (ang >= halfang) - tval = -tval; - sintable[ang] = (Bit16s)(tval * 50.0f) + 50; - } - - int velt, dep; - float tempdep; - for (velt = 0; velt < 128; velt++) { - for (dep = 0; dep < 5; dep++) { - if (dep > 0) { - float ff = (float)(exp(3.5f*tvcatconst[dep] * (59.0f-(float)velt)) * tvcatmult[dep]); - tempdep = 256.0f * ff; - veltkeytable[dep][velt] = (int)tempdep; - //if ((velt % 16) == 0) { - // synth->printDebug("Key %d, depth %d, factor %d", velt, dep, (int)tempdep); - //} - } else - veltkeytable[dep][velt] = 256; - } - - for (dep = -7; dep < 8; dep++) { - float fldep = (float)abs(dep) / 7.0f; - fldep = powf(fldep,2.5f); - if (dep < 0) - fldep = fldep * -1.0f; - pwveltable[dep+7][velt] = Bit32s((fldep * (float)velt * 100) / 128.0); - } - } - - for (dep = 0; dep <= 100; dep++) { - for (velt = 0; velt < 128; velt++) { - float fdep = (float)dep * 0.000347013f; // Another MT-32 constant - float fv = ((float)velt - 64.0f)/7.26f; - float flogdep = powf(10, fdep * fv); - float fbase; - - if (velt > 64) - filveltable[velt][dep] = (int)(flogdep * 256.0); - else { - //lff = 1 - (pow(((128.0 - (float)lf) / 64.0),.25) * ((float)velt / 96)); - fbase = 1 - (powf(((float)dep / 100.0f),.25f) * ((float)(64-velt) / 96.0f)); - filveltable[velt][dep] = (int)(fbase * 256.0); - } - //synth->printDebug("Filvel dep %d velt %d = %x", dep, velt, filveltable[velt][dep]); - } - } - - for (lf = 0; lf <= 200; lf++) { - //FIXME:KG: I'm fairly sure this is wrong... lf=100 should yield no fine-tuning (4096)? - finetable[lf] = (int)((powf(2.0f, (((float)lf / 200.0f) - 1.0f) / 12.0f)) * 4096.0f); - - // FIXME:KG: This now gives a range of -1 .. 1 semitone. Should be correct, but check - //finetable[lf] = (int)((powf(2.0f, (((float)lf / 100.0f) - 1.0f) / 12.0f)) * 4096.0f); - } - - float lff; - for (lf = 0; lf < 128; lf++) { - for (velt = 0; velt < 64; velt++) { - lff = 1 - (powf(((128.0f - (float)lf) / 64.0f), 0.25f) * ((float)velt / 96)); - ampveltable[lf][velt] = (int)(lff * 256.0); - //synth->printDebug("Ampveltable: %d, %d = %d", lf, velt, ampveltable[lf][velt]); - } - } - - for (lf = 0; lf < 128; lf++) { - // Converts MIDI velocity to volume. - voltable[lf] = FIXEDPOINT_MAKE(powf((float)lf / 127.0f, FLOAT_LN), 7); - } - for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) { - int myRand; - myRand = rand(); - int origRand = myRand; - //myRand = ((myRand - 16383) * WGAMP) >> 16; - // This one is slower but works with all values of RAND_MAX - myRand = (int)((origRand - RAND_MAX / 2) / (float)RAND_MAX * (WGAMP / 2)); - //FIXME:KG: Original ultimately set the lowest two bits to 0, for no obvious reason - smallnoise[i] = (Bit16s)myRand; - } - - float tdist; - float padjtable[51]; - for (lf = 0; lf <= 50; lf++) { - if (lf == 0) - padjtable[lf] = 7; - else if (lf == 1) - padjtable[lf] = 6; - else if (lf == 2) - padjtable[lf] = 5; - else if (lf == 3) - padjtable[lf] = 4; - else if (lf == 4) - padjtable[lf] = 4 - (0.333333f); - else if (lf == 5) - padjtable[lf] = 4 - (0.333333f * 2); - else if (lf == 6) - padjtable[lf] = 3; - else if ((lf > 6) && (lf <= 12)) { - tdist = (lf-6.0f) / 6.0f; - padjtable[lf] = 3.0f - tdist; - } else if ((lf > 12) && (lf <= 25)) { - tdist = (lf - 12.0f) / 13.0f; - padjtable[lf] = 2.0f - tdist; - } else { - tdist = (lf - 25.0f) / 25.0f; - padjtable[lf] = 1.0f - tdist; - } - //synth->printDebug("lf %d = padj %f", lf, padjtable[lf]); - } - - float lfp, depf, finalval, tlf; - int depat, pval, depti; - for (lf = 0; lf <= 10; lf++) { - // I believe the depth is cubed or something - - for (depat = 0; depat <= 100; depat++) { - if (lf > 0) { - depti = abs(depat - 50); - tlf = (float)lf - padjtable[depti]; - if (tlf < 0) - tlf = 0; - lfp = expf(0.713619942f * tlf) / 407.4945111f; - - if (depat < 50) - finalval = 4096.0f * powf(2, -lfp); - else - finalval = 4096.0f * powf(2, lfp); - pval = (int)finalval; - - penvtable[lf][depat] = pval; - //synth->printDebug("lf %d depat %d pval %d tlf %f lfp %f", lf,depat,pval, tlf, lfp); - } else { - penvtable[lf][depat] = 4096; - //synth->printDebug("lf %d depat %d pval 4096", lf, depat); - } - } - } - for (lf = 0; lf <= 100; lf++) { - // It's linear - verified on MT-32 - one of the few things linear - lfp = ((float)lf * 0.1904f) / 310.55f; - - for (depat = 0; depat <= 100; depat++) { - depf = ((float)depat - 50.0f) / 50.0f; - //finalval = pow(2, lfp * depf * .5); - finalval = 4096.0f + (4096.0f * lfp * depf); - - pval = (int)finalval; - - lfoptable[lf][depat] = pval; - - //synth->printDebug("lf %d depat %d pval %x", lf,depat,pval); - } - } - - for (lf = 0; lf <= 12; lf++) { - for (int distval = 0; distval < 128; distval++) { - float amplog, dval; - if (lf == 0) { - amplog = 0; - dval = 1; - ampbiastable[lf][distval] = 256; - } else { - amplog = powf(1.431817011f, (float)lf) / FLOAT_PI; - dval = ((128.0f - (float)distval) / 128.0f); - amplog = expf(amplog); - dval = powf(amplog, dval) / amplog; - ampbiastable[lf][distval] = (int)(dval * 256.0); - } - //synth->printDebug("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, dval, ampbiastable[lf][distval],amplog); - } - } - - for (lf = 0; lf <= 14; lf++) { - for (int distval = 0; distval < 128; distval++) { - float filval = fabsf((float)((lf - 7) * 12) / 7.0f); - float amplog, dval; - if (lf == 7) { - amplog = 0; - dval = 1; - fbiastable[lf][distval] = 256; - } else { - //amplog = pow(1.431817011, filval) / FLOAT_PI; - amplog = powf(1.531817011f, filval) / FLOAT_PI; - dval = (128.0f - (float)distval) / 128.0f; - amplog = expf(amplog); - dval = powf(amplog,dval)/amplog; - if (lf < 8) { - fbiastable[lf][distval] = (int)(dval * 256.0f); - } else { - dval = powf(dval, 0.3333333f); - if (dval < 0.01f) - dval = 0.01f; - dval = 1 / dval; - fbiastable[lf][distval] = (int)(dval * 256.0f); - } - } - //synth->printDebug("Fbias lf %d distval %d = %f (%x) %f", lf, distval, dval, fbiastable[lf][distval],amplog); - } - } -} - -// Per-note table initialisation follows - -static void initSaw(NoteLookup *noteLookup, Bit32s div) { - for (int rsaw = 0; rsaw <= 100; rsaw++) { - float fsaw; - if (rsaw < 50) - fsaw = 50.0f; - else - fsaw = (float)rsaw; - int tmpdiv = div << 17; - - //(66 - (((A8 - 50) / 50) ^ 0.63) * 50) / 132 - float sawfact = (66.0f - (powf((fsaw - 50.0f) / 50.0f, 0.63f) * 50.0f)) / 132.0f; - noteLookup->sawTable[rsaw] = (int)(sawfact * (float)tmpdiv) >> 16; - //synth->printDebug("F %d divtable %d saw %d sawtable %d", f, div, rsaw, sawtable[f][rsaw]); - } -} - -static void initDep(NoteLookup *noteLookup, float f) { - for (int dep = 0; dep < 5; dep++) { - if (dep == 0) { - noteLookup->fildepTable[dep] = 256; - noteLookup->timekeyTable[dep] = 256; - } else { - float depfac = 3000.0f; - float ff, tempdep; - depfac = (float)depexp[dep]; - - ff = (f - (float)MIDDLEC) / depfac; - tempdep = powf(2, ff) * 256.0f; - noteLookup->fildepTable[dep] = (int)tempdep; - - ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC - f)) * tkcatmult[dep]); - noteLookup->timekeyTable[dep] = (int)(ff * 256.0f); - } - } - //synth->printDebug("F %f d1 %x d2 %x d3 %x d4 %x d5 %x", f, noteLookup->fildepTable[0], noteLookup->fildepTable[1], noteLookup->fildepTable[2], noteLookup->fildepTable[3], noteLookup->fildepTable[4]); -} - -File *TableInitialiser::initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file) { - int iDiv = (int)div; - noteLookup->waveformSize[0] = iDiv << 2; - noteLookup->waveformSize[1] = iDiv << 2; - noteLookup->waveformSize[2] = iDiv << 3; - for (int i = 0; i < 3; i++) { - if (noteLookup->waveforms[i] == NULL) { - noteLookup->waveforms[i] = new Bit16s[noteLookup->waveformSize[i]]; - } - } - if (file != NULL) { - for (int i = 0; i < 3 && file != NULL; i++) { - size_t len = noteLookup->waveformSize[i]; - for (unsigned int j = 0; j < len; j++) { - if (!file->readBit16u((Bit16u *)¬eLookup->waveforms[i][j])) { - synth->printDebug("Error reading wave file cache!"); - file->close(); - file = NULL; - break; - } - } - } - } - if (file == NULL) { - double sd = DOUBLE_PI / (div * 2.0); - - for (int fa = 0; fa < (iDiv << 2); fa++) { - double sa = fa * sd; - -#if 0 - //FIXME:KG: Credit Timo Strunk (bastardo on #scummvm) for help with this! - double saw = 0.5 * DOUBLE_PI - sa / 2; -#else - double saw = 0.0; - for (int sinus = 1; sinus < div; sinus++) { - double fsinus = (double)sinus; - saw += sin(fsinus * sa) / fsinus; - } -#endif - - // This works pretty well - noteLookup->waveforms[0][fa] = (Bit16s)(saw * -ampsize / 2); - noteLookup->waveforms[1][fa] = (Bit16s)(cos(sa / 2.0) * -ampsize); - noteLookup->waveforms[2][fa * 2] = (Bit16s)(cos(sa - DOUBLE_PI) * -ampsize); - noteLookup->waveforms[2][fa * 2 + 1] = (Bit16s)(cos((sa + (sd / 2)) - DOUBLE_PI) * -ampsize); - } - } - return file; -} - -static void initFiltTable(NoteLookup *noteLookup, float freq, float rate) { - for (int tr = 0; tr <= 200; tr++) { - float ftr = (float)tr; - - // Verified exact on MT-32 - if (tr > 100) - ftr = 100.0f + (powf((ftr - 100.0f) / 100.0f, 3.0f) * 100.0f); - - // I think this is the one - float brsq = powf(10.0f, (tr / 50.0f) - 1.0f); - noteLookup->filtTable[0][tr] = (int)((freq * brsq) / (rate / 2) * FILTERGRAN); - if (noteLookup->filtTable[0][tr]>=((FILTERGRAN*15)/16)) - noteLookup->filtTable[0][tr] = ((FILTERGRAN*15)/16); - - float brsa = powf(10.0f, ((tr / 55.0f) - 1.0f)) / 2.0f; - noteLookup->filtTable[1][tr] = (int)((freq * brsa) / (rate / 2) * FILTERGRAN); - if (noteLookup->filtTable[1][tr]>=((FILTERGRAN*15)/16)) - noteLookup->filtTable[1][tr] = ((FILTERGRAN*15)/16); - } -} - -static void initNFiltTable(NoteLookup *noteLookup, float freq, float rate) { - for (int cf = 0; cf <= 100; cf++) { - float cfmult = (float)cf; - - for (int tf = 0;tf <= 100; tf++) { - float tfadd = (float)(tf - 0); - if (tfadd < 0) - tfadd = 0; - - float freqsum = expf((cfmult + tfadd) / 30.0f) / 4.0f; - - noteLookup->nfiltTable[cf][tf] = (int)((freq * freqsum) / (rate / 2) * FILTERGRAN); - if (noteLookup->nfiltTable[cf][tf] >= ((FILTERGRAN * 15) / 16)) - noteLookup->nfiltTable[cf][tf] = ((FILTERGRAN * 15) / 16); - } - } -} - -File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry pcmWaves[128], File *file) { - float ampsize = WGAMP; - float freq = (float)(masterTune * pow(2.0, ((double)note - MIDDLEA) / 12.0)); - float div = rate / freq; - noteLookup->div = (int)div; - - if (noteLookup->div == 0) - noteLookup->div = 1; - - initSaw(noteLookup, noteLookup->div); - initDep(noteLookup, note); - - //synth->printDebug("Note %f; freq=%f, div=%f", note, freq, rate / freq); - file = initWave(synth, noteLookup, ampsize, div, file); - - // Create the pitch tables - - double rateMult = 32000.0 / rate; - double tuner = freq * 65536.0f; - for (int pc = 0; pc < 128; pc++) { - noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune * rateMult); - } - - initFiltTable(noteLookup, freq, rate); - initNFiltTable(noteLookup, freq, rate); - return file; -} - -bool TableInitialiser::initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], float rate, float masterTune) { - const char *NoteNames[12] = { - "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B " - }; - char filename[64]; - int intRate = (int)rate; - char version[4] = {0, 0, 0, 3}; - sprintf(filename, "waveformcache-%d-%.2f.raw", intRate, masterTune); - - File *file = NULL; - char header[20]; - strncpy(header, "MT32WAVE", 8); - int pos = 8; - // Version... - for (int i = 0; i < 4; i++) - header[pos++] = version[i]; - header[pos++] = (char)((intRate >> 24) & 0xFF); - header[pos++] = (char)((intRate >> 16) & 0xFF); - header[pos++] = (char)((intRate >> 8) & 0xFF); - header[pos++] = (char)(intRate & 0xFF); - int intTuning = (int)masterTune; - header[pos++] = (char)((intTuning >> 8) & 0xFF); - header[pos++] = (char)(intTuning & 0xFF); - header[pos++] = 0; - header[pos] = (char)((masterTune - intTuning) * 10); -#if MT32EMU_WAVECACHEMODE < 2 - bool reading = false; - file = synth->openFile(filename, File::OpenMode_read); - if (file != NULL) { - char fileHeader[20]; - if (file->read(fileHeader, 20) == 20) { - if (memcmp(fileHeader, header, 20) == 0) { - Bit16u endianCheck; - if (file->readBit16u(&endianCheck)) { - if (endianCheck == 1) { - reading = true; - } else { - synth->printDebug("Endian check in %s does not match expected", filename); - } - } else { - synth->printDebug("Unable to read endian check in %s", filename); - } - } else { - synth->printDebug("Header of %s does not match expected", filename); - } - } else { - synth->printDebug("Error reading 16 bytes of %s", filename); - } - if (!reading) { - file->close(); - file = NULL; - } - } else { - synth->printDebug("Unable to open %s for reading", filename); - } -#endif - - float progress = 0.0f; - bool abort = false; - synth->report(ReportType_progressInit, &progress); - for (int f = LOWEST_NOTE; f <= HIGHEST_NOTE; f++) { - synth->printDebug("Initialising note %s%d", NoteNames[f % 12], (f / 12) - 1); - NoteLookup *noteLookup = ¬eLookups[f - LOWEST_NOTE]; - file = initNote(synth, noteLookup, (float)f, rate, masterTune, pcmWaves, file); - progress = (f - LOWEST_NOTE + 1) / (float)NUM_NOTES; - abort = synth->report(ReportType_progressInit, &progress) != 0; - if (abort) - break; - } - -#if MT32EMU_WAVECACHEMODE == 0 || MT32EMU_WAVECACHEMODE == 2 - if (file == NULL) { - file = synth->openFile(filename, File::OpenMode_write); - if (file != NULL) { - if (file->write(header, 20) == 20 && file->writeBit16u(1)) { - for (int f = 0; f < NUM_NOTES; f++) { - for (int i = 0; i < 3 && file != NULL; i++) { - int len = noteLookups[f].waveformSize[i]; - for (int j = 0; j < len; j++) { - if (!file->writeBit16u(noteLookups[f].waveforms[i][j])) { - synth->printDebug("Error writing waveform cache file"); - file->close(); - file = NULL; - break; - } - } - } - } - } else { - synth->printDebug("Error writing 16-byte header to %s - won't continue saving", filename); - } - } else { - synth->printDebug("Unable to open %s for writing - won't be created", filename); - } - } -#endif - - if (file != NULL) - synth->closeFile(file); - return !abort; -} - -void TableInitialiser::freeNotes() { - for (int t = 0; t < 3; t++) { - for (int m = 0; m < NUM_NOTES; m++) { - if (noteLookups[m].waveforms[t] != NULL) { - delete[] noteLookups[m].waveforms[t]; - noteLookups[m].waveforms[t] = NULL; - noteLookups[m].waveformSize[t] = 0; - } - } - } - initialisedMasterTune = 0.0f; -} - -bool TableInitialiser::initMT32Tables(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune) { - if (sampleRate <= 0.0f) { - synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate); - return false; - } - if (initialisedSampleRate == 0.0f) { - initMT32ConstantTables(synth); - } - if (initialisedSampleRate != sampleRate) { - initFiltCoeff(sampleRate); - initEnvelopes(sampleRate); - } - if (initialisedSampleRate != sampleRate || initialisedMasterTune != masterTune) { - freeNotes(); - if (!initNotes(synth, pcmWaves, sampleRate, masterTune)) { - return false; - } - initialisedSampleRate = sampleRate; - initialisedMasterTune = masterTune; - } - return true; -} - -} diff --git a/backends/midi/mt32/tables.h b/backends/midi/mt32/tables.h deleted file mode 100644 index 0c85796f4f..0000000000 --- a/backends/midi/mt32/tables.h +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright (c) 2003-2004 Various contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef MT32EMU_TABLES_H -#define MT32EMU_TABLES_H - -namespace MT32Emu { - -// Mathematical constants -const double DOUBLE_PI = 3.1415926535897932384626433832795; -const double DOUBLE_LN = 2.3025850929940456840179914546844; -const float FLOAT_PI = 3.1415926535897932384626433832795f; -const float FLOAT_LN = 2.3025850929940456840179914546844f; - -// Filter settings -const int FILTERGRAN = 512; - -const int MIDDLEC = 60; -const int MIDDLEA = 69; // By this I mean "A above middle C" - -//FIXME:KG: may only need to do 12 to 108 -//12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow -//and adjustment for timbre pitch, so the results can be outside that range. Do move it (by octave) into -// the 12..108 range, or keep it in 0..127 range, or something else altogether? -const int LOWEST_NOTE = 12; -const int HIGHEST_NOTE = 127; -const int NUM_NOTES = HIGHEST_NOTE - LOWEST_NOTE + 1; // Number of slots for note LUT - -// Amplitude of waveform generator -const int WGAMP = 7168; // 8192? - -class Synth; - -extern Bit16s smallnoise[MAX_SAMPLE_OUTPUT]; - -// Some optimization stuff -extern Bit32s keytable[217]; -extern Bit16s sintable[65536]; -extern Bit32u lfotable[101]; -extern Bit32s penvtable[16][101]; -extern Bit32s filveltable[128][101]; -extern Bit32s veltkeytable[5][128]; -extern Bit32s pulsetable[101]; -extern Bit32s ampbiastable[13][128]; -extern Bit32s fbiastable[15][128]; -extern float filtcoeff[FILTERGRAN][31][8]; -extern Bit32s finetable[201]; -extern Bit32u lfoptable[101][101]; -extern Bit32s ampveltable[128][64]; -extern Bit32s pwveltable[15][128]; -extern Bit32s envtimetable[101]; -extern Bit32s decaytimetable[101]; -extern Bit32s lasttimetable[101]; -extern Bit32s voltable[128]; -extern float ResonInv[31]; - -struct NoteLookup { - Bit32u div; - Bit32u wavTable[128]; - Bit32s sawTable[101]; - Bit32s fildepTable[5]; - Bit32s timekeyTable[5]; - int filtTable[2][201]; - int nfiltTable[101][101]; - Bit16s *waveforms[3]; - Bit32u waveformSize[3]; -}; - -extern NoteLookup noteLookups[NUM_NOTES]; - -class TableInitialiser { - static void initMT32ConstantTables(Synth *synth); - static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file); - static bool initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], float rate, float tuning); -public: - static bool initMT32Tables(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune); - static File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWaveEntry pcmWaves[128], File *file); - static void freeNotes(); -}; - -} - -#endif diff --git a/backends/midi/ym2612.cpp b/backends/midi/ym2612.cpp deleted file mode 100644 index b4af3c6854..0000000000 --- a/backends/midi/ym2612.cpp +++ /dev/null @@ -1,907 +0,0 @@ -/* ScummVM - Scumm Interpreter - * Copyright (C) 2001-2004 The ScummVM project - * - * YM2612 tone generation code written by Tomoaki Hayasaka. - * Used under the terms of the GNU General Public License. - * Adpated to ScummVM by Jamieson Christian. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * $Header$ - */ - -#include "emumidi.h" - -#include <math.h> - - -//////////////////////////////////////// -// -// Miscellaneous -// -//////////////////////////////////////// - -static int *sintbl = 0; -static int *powtbl = 0; -static int *frequencyTable = 0; -static int *keycodeTable = 0; -static int *keyscaleTable = 0; -static int *attackOut = 0; - -//////////////////////////////////////// -// -// Class declarations -// -//////////////////////////////////////// - -class Operator2612; -class Voice2612; -class MidiChannel_YM2612; -class MidiDriver_YM2612; - -class Operator2612 { -protected: - Voice2612 *_owner; - enum State { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing }; - State _state; - int32 _currentLevel; - int _frequency; - uint32 _phase; - int _lastOutput; - int _feedbackLevel; - int _detune; - int _multiple; - int32 _totalLevel; - int _keyScale; - int _velocity; - int _specifiedTotalLevel; - int _specifiedAttackRate; - int _specifiedDecayRate; - int _specifiedSustainLevel; - int _specifiedSustainRate; - int _specifiedReleaseRate; - int _tickCount; - int _attackTime; - int32 _decayRate; - int32 _sustainLevel; - int32 _sustainRate; - int32 _releaseRate; - -public: - Operator2612 (Voice2612 *owner); - ~Operator2612(); - void feedbackLevel(int level); - void setInstrument(byte const *instrument); - void velocity(int velo); - void keyOn(); - void keyOff(); - void frequency(int freq); - void nextTick(const int *phaseShift, int *outbuf, int buflen); - bool inUse() { return (_state != _s_ready); } -}; - -class Voice2612 { -public: - Voice2612 *next; - uint16 _rate; - -protected: - Operator2612 *_opr[4]; - int _velocity; - int _control7; - int _note; - int _frequencyOffs; - int _frequency; - int _algorithm; - - int *_buffer; - int _buflen; - -public: - Voice2612(); - ~Voice2612(); - void setControlParameter(int control, int value); - void setInstrument(byte const *instrument); - void velocity(int velo); - void nextTick(int *outbuf, int buflen); - void noteOn(int n, int onVelo); - bool noteOff(int note); - void pitchBend(int value); - void recalculateFrequency(); -}; - -class MidiChannel_YM2612 : public MidiChannel { -protected: - uint16 _rate; - Voice2612 *_voices; - Voice2612 *_next_voice; - -public: - void removeAllVoices(); - void nextTick(int *outbuf, int buflen); - void rate(uint16 r); - -public: - MidiChannel_YM2612(); - virtual ~MidiChannel_YM2612(); - - // MidiChannel interface - MidiDriver *device() { return 0; } - byte getNumber() { return 0; } - void release() { } - void send(uint32 b) { } - void noteOff(byte note); - void noteOn(byte note, byte onVelo); - void programChange(byte program) { } - void pitchBend(int16 value); - void controlChange(byte control, byte value); - void pitchBendFactor(byte value) { } - void sysEx_customInstrument(uint32 type, byte *instr); -}; - -class MidiDriver_YM2612 : public MidiDriver_Emulated { -protected: - MidiChannel_YM2612 *_channel[16]; - - int _next_voice; - int _volume; - -protected: - static void createLookupTables(); - void nextTick(int16 *buf1, int buflen); - int volume(int val = -1) { if (val >= 0) _volume = val; return _volume; } - void rate(uint16 r); - - void generateSamples(int16 *buf, int len); - -public: - MidiDriver_YM2612(SoundMixer *mixer); - virtual ~MidiDriver_YM2612(); - - int open(); - void close(); - void send(uint32 b); - void send(byte channel, uint32 b); // Supports higher than channel 15 - uint32 property(int prop, uint32 param) { return 0; } - - void setPitchBendRange(byte channel, uint range) { } - void sysEx(byte *msg, uint16 length); - - MidiChannel *allocateChannel() { return 0; } - MidiChannel *getPercussionChannel() { return 0; } - - - // AudioStream API - bool isStereo() const { return true; } - int getRate() const { return _mixer->getOutputRate(); } -}; - -//////////////////////////////////////// -// -// Operator2612 implementation -// -//////////////////////////////////////// - -Operator2612::Operator2612 (Voice2612 *owner) : - _owner (owner), - _state (_s_ready), - _currentLevel ((int32)0x7f << 15), - _phase (0), - _lastOutput (0), - _feedbackLevel (0), - _detune (0), - _multiple (1), - _keyScale (0), - _specifiedTotalLevel (127), - _specifiedAttackRate (0), - _specifiedDecayRate (0), - _specifiedSustainRate (0), - _specifiedReleaseRate (15) { - velocity(0); -} - -Operator2612::~Operator2612() -{ } - -void Operator2612::velocity(int velo) { - _velocity = velo; - _totalLevel = ((int32)_specifiedTotalLevel << 15) + - ((int32)(127-_velocity) << 13); - _sustainLevel = ((int32)_specifiedSustainLevel << 17); -} - -void Operator2612::feedbackLevel(int level) { - _feedbackLevel = level; -} - -void Operator2612::setInstrument(byte const *instrument) { - _detune = (instrument[8] >> 4) & 7; - _multiple = instrument[8] & 15; - _specifiedTotalLevel = instrument[12] & 127; - _keyScale = (instrument[16] >> 6) & 3; - _specifiedAttackRate = instrument[16] & 31; - _specifiedDecayRate = instrument[20] & 31; - _specifiedSustainRate = instrument[24] & 31; - _specifiedSustainLevel = (instrument[28] >> 4) & 15; - _specifiedReleaseRate = instrument[28] & 15; - _state = _s_ready; // 本物ではどうなのかな? - velocity(_velocity); -} - -void Operator2612::keyOn() { - _state = _s_attacking; - _tickCount = 0; - _phase = 0; // どうも、実際こうらしい - _currentLevel = ((int32)0x7f << 15); // これも、実際こうらしい -} - -void Operator2612::keyOff() { - if (_state != _s_ready) - _state = _s_releasing; -} - -void Operator2612::frequency(int freq) { - double value; // Use for intermediate computations to avoid int64 arithmetic - int r; - - _frequency = freq / _owner->_rate; - - r = _specifiedAttackRate; - if (r != 0) { - r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale)); - if (r >= 64) - r = 63; // するべきなんだろうとは思うんだけど (赤p.207) - } - - r = 63 - r; - if (_specifiedTotalLevel >= 128) - value = 0; - else { - value = powtbl[(r&3) << 7]; - value *= 1 << (r >> 2); - value *= 41; // r == 20 のとき、0-96[db] が 10.01[ms] == 41.00096 - value /= 1 << (15 + 5); - value *= 127 - _specifiedTotalLevel; - value /= 127; - } - _attackTime = (int32) value; // 1 秒 == (1 << 12) - if (_attackTime > 0) - _attackTime = (1 << (12+10)) / (_owner->_rate * _attackTime); - - r = _specifiedDecayRate; - if (r != 0) { - r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale)); - if (r >= 64) - r = 63; - } - value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31; - _decayRate = (int32) value / _owner->_rate; - - r = _specifiedSustainRate; - if (r != 0) { - r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale)); - if (r >= 64) - r = 63; - } - value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31; - _sustainRate = (int32) value / _owner->_rate; - - r = _specifiedReleaseRate; - if (r != 0) { - r = r * 2 + 1; // (Translated) I cannot know whether the timing is a good choice or not - r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale)); - // KS による補正はあるらしい。赤p.206 では記述されてないけど。 - if (r >= 64) - r = 63; - } - value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31; - _releaseRate = (int32) value / _owner->_rate; -} - -void Operator2612::nextTick(const int *phasebuf, int *outbuf, int buflen) { - if (_state == _s_ready) - return; - if (_state == _s_attacking && _attackTime <= 0) { - _currentLevel = 0; - _state = _s_decaying; - } - - int32 levelIncrement = 0; - int32 target = 0; - State next_state = _s_ready; - const int32 zero_level = ((int32)0x7f << 15); - const int phaseIncrement = (_multiple > 0) ? (_frequency * _multiple) : (_frequency / 2); - - int32 output = _lastOutput; - int32 level = _currentLevel + _totalLevel; - - while (buflen) { - switch (_state) { - case _s_ready: - return; - break; - case _s_attacking: - next_state = _s_attacking; - break; - case _s_decaying: - levelIncrement = _decayRate; - target = _sustainLevel + _totalLevel; - next_state = _s_sustaining; - break; - case _s_sustaining: - levelIncrement = _sustainRate; - target = zero_level + _totalLevel; - next_state = _s_ready; - break; - case _s_releasing: - levelIncrement = _releaseRate; - target = zero_level + _totalLevel; - next_state = _s_ready; - break; - } - - bool switching = false; - do { - if (next_state == _s_attacking) { - // Attack phase - ++_tickCount; - int i = (int) (_tickCount * _attackTime); - if (i >= 1024) { - level = _totalLevel; - _state = _s_decaying; - switching = true; - } else { - level = (attackOut[i] << (31 - 8 - 16)) + _totalLevel; - } - } else { - // Decay, Sustain and Release phases - level += levelIncrement; - if (level >= target) { - level = target; - _state = next_state; - switching = true; - } - } - - if (level < zero_level) { - int phaseShift = *phasebuf >> 2; // 正しい変調量は? 3 じゃ小さすぎで 2 じゃ大きいような。 - if (_feedbackLevel) - phaseShift += (output << (_feedbackLevel - 1)) / 1024; - output = sintbl[((_phase >> 7) + phaseShift) & 0x7ff]; - output >>= (level >> 18); // 正しい減衰量は? - // Here is the original code, which requires 64-bit ints -// output *= powtbl[511 - ((level>>25)&511)]; -// output >>= 16; -// output >>= 1; - // And here's our 32-bit trick for doing it. (Props to Fingolfin!) - // Result varies from original code by max of 1. -// int powVal = powtbl[511 - ((level>>9)&511)]; -// int outputHI = output / 256; -// int powHI = powVal / 256; -// output = (outputHI * powHI) / 2 + (outputHI * (powVal % 256) + powHI * (output % 256)) / 512; - // And here's the even faster code. - // Result varies from original code by max of 8. - output = ((output >> 4) * (powtbl[511-((level>>9)&511)] >> 3)) / 1024; - - _phase += phaseIncrement; - _phase &= 0x3ffff; - } else - output = 0; - - *outbuf += output; - --buflen; - ++phasebuf; - ++outbuf; - } while (buflen && !switching); - } - _lastOutput = output; - _currentLevel = level - _totalLevel; -} - -//////////////////////////////////////// -// -// Voice2612 implementation -// -//////////////////////////////////////// - -Voice2612::Voice2612() { - next = 0; - _control7 = 127; - _note = 40; - _frequency = 440; - _frequencyOffs = 0x2000; - _algorithm = 7; - - _buffer = 0; - _buflen = 0; - - int i; - for (i = 0; i < ARRAYSIZE(_opr); ++i) - _opr[i] = new Operator2612 (this); - velocity(0); -} - -Voice2612::~Voice2612() { - int i; - for (i = 0; i < ARRAYSIZE(_opr); ++i) - delete _opr[i]; - free(_buffer); -} - -void Voice2612::velocity(int velo) { - _velocity = velo; -#if 0 - int v = (velo * _control7) >> 7; // これだと精度良くないですね -#else - int v = velo + (_control7 - 127) * 4; -#endif - bool iscarrier[8][4] = { - { false, false, false, true, }, //0 - { false, false, false, true, }, //1 - { false, false, false, true, }, //2 - { false, false, false, true, }, //3 - { false, true, false, true, }, //4 - { false, true, true, true, }, //5 - { false, true, true, true, }, //6 - { true, true, true, true, }, //7 - }; - int opr; - for (opr = 0; opr < 4; opr++) - if (iscarrier[_algorithm][opr]) - _opr[opr]->velocity(v); - else - _opr[opr]->velocity(127); -} - -void Voice2612::setControlParameter(int control, int value) { - switch (control) { - case 7: - _control7 = value; - velocity(_velocity); - break; - case 123: - // All notes off - noteOff(_note); - }; -} - -void Voice2612::setInstrument(byte const *instrument) { - if (instrument == NULL) - return; - - _algorithm = instrument[32] & 7; - _opr[0]->feedbackLevel((instrument[32] >> 3) & 7); - _opr[1]->feedbackLevel(0); - _opr[2]->feedbackLevel(0); - _opr[3]->feedbackLevel(0); - _opr[0]->setInstrument(instrument + 0); - _opr[1]->setInstrument(instrument + 2); - _opr[2]->setInstrument(instrument + 1); - _opr[3]->setInstrument(instrument + 3); -} - -void Voice2612::nextTick(int *outbuf, int buflen) { - if (_velocity == 0) - return; - - if (_buflen < buflen) { - free(_buffer); - _buflen = buflen; - _buffer = (int *) malloc(sizeof(int) * buflen * 2); - } - - int *buf1 = _buffer; - int *buf2 = _buffer + buflen; - memset(_buffer, 0, sizeof(int) * buflen * 2); - - switch (_algorithm) { - case 0: - _opr[0]->nextTick(buf1, buf2, buflen); - _opr[1]->nextTick(buf2, buf1, buflen); - memset (buf2, 0, sizeof (int) * buflen); - _opr[2]->nextTick(buf1, buf2, buflen); - _opr[3]->nextTick(buf2, outbuf, buflen); - break; - case 1: - _opr[0]->nextTick(buf1, buf2, buflen); - _opr[1]->nextTick(buf1, buf2, buflen); - _opr[2]->nextTick(buf2, buf1, buflen); - _opr[3]->nextTick(buf1, outbuf, buflen); - break; - case 2: - _opr[1]->nextTick(buf1, buf2, buflen); - _opr[2]->nextTick(buf2, buf1, buflen); - memset(buf2, 0, sizeof(int) * buflen); - _opr[0]->nextTick(buf2, buf1, buflen); - _opr[3]->nextTick(buf1, outbuf, buflen); - break; - case 3: - _opr[0]->nextTick(buf1, buf2, buflen); - _opr[1]->nextTick(buf2, buf1, buflen); - memset(buf2, 0, sizeof(int) * buflen); - _opr[2]->nextTick(buf2, buf1, buflen); - _opr[3]->nextTick(buf1, outbuf, buflen); - break; - case 4: - _opr[0]->nextTick(buf1, buf2, buflen); - _opr[1]->nextTick(buf2, outbuf, buflen); - _opr[2]->nextTick(buf1, buf1, buflen); - _opr[3]->nextTick(buf1, outbuf, buflen); - break; - case 5: - _opr[0]->nextTick(buf1, buf2, buflen); - _opr[1]->nextTick(buf2, outbuf, buflen); - _opr[2]->nextTick(buf2, outbuf, buflen); - _opr[3]->nextTick(buf2, outbuf, buflen); - break; - case 6: - _opr[0]->nextTick(buf1, buf2, buflen); - _opr[1]->nextTick(buf2, outbuf, buflen); - _opr[2]->nextTick(buf1, outbuf, buflen); - _opr[3]->nextTick(buf1, outbuf, buflen); - break; - case 7: - _opr[0]->nextTick(buf1, outbuf, buflen); - _opr[1]->nextTick(buf1, outbuf, buflen); - _opr[2]->nextTick(buf1, outbuf, buflen); - _opr[3]->nextTick(buf1, outbuf, buflen); - break; - }; -} - -void Voice2612::noteOn(int n, int onVelo) { - _note = n; - velocity(onVelo); - recalculateFrequency(); - int i; - for (i = 0; i < ARRAYSIZE(_opr); i++) - _opr[i]->keyOn(); -} - -bool Voice2612::noteOff(int note) { - if (_note != note) - return false; - int i; - for (i = 0; i < ARRAYSIZE(_opr); i++) - _opr[i]->keyOff(); - return true; -} - -void Voice2612::pitchBend(int value) { - _frequencyOffs = value; - recalculateFrequency(); -} - -void Voice2612::recalculateFrequency() { - // MIDI とも違うし.... - // どういう仕様なんだろうか? - // と思ったら、なんと、これ (↓) が正解らしい。 - int32 basefreq = frequencyTable[_note]; - int cfreq = frequencyTable[_note - (_note % 12)]; - int oct = _note / 12; - int fnum = (int) (((double)basefreq * (1 << 13)) / cfreq); // OPL の fnum と同じようなもの。 - fnum += _frequencyOffs - 0x2000; - if (fnum < 0x2000) { - fnum += 0x2000; - oct--; - } - if (fnum >= 0x4000) { - fnum -= 0x2000; - oct++; - } - - // _frequency は最終的にバイアス 256*1024 倍 - _frequency = (int) ((frequencyTable[oct*12] * (double)fnum) / 8); - - int i; - for (i = 0; i < ARRAYSIZE(_opr); i++) - _opr[i]->frequency(_frequency); -} - -//////////////////////////////////////// -// -// MidiChannel_YM2612 -// -//////////////////////////////////////// - -MidiChannel_YM2612::MidiChannel_YM2612() { - _voices = 0; - _next_voice = 0; -} - -MidiChannel_YM2612::~MidiChannel_YM2612() { - removeAllVoices(); -} - -void MidiChannel_YM2612::removeAllVoices() { - if (!_voices) - return; - Voice2612 *last, *voice = _voices; - for (; voice; voice = last) { - last = voice->next; - delete voice; - } - _voices = _next_voice = 0; -} - -void MidiChannel_YM2612::noteOn(byte note, byte onVelo) { - if (!_voices) - return; - _next_voice = _next_voice ? _next_voice : _voices; - _next_voice->noteOn(note, onVelo); - _next_voice = _next_voice->next; -} - -void MidiChannel_YM2612::noteOff(byte note) { - if (!_voices) - return; - if (_next_voice == _voices) - _next_voice = 0; - Voice2612 *voice = _next_voice; - do { - if (!voice) - voice = _voices; - if (voice->noteOff(note)) { - _next_voice = voice; - break; - } - voice = voice->next; - } while (voice != _next_voice); -} - -void MidiChannel_YM2612::controlChange(byte control, byte value) { - // いいのかこれで? - if (control == 121) { - // Reset controller - removeAllVoices(); - } else { - Voice2612 *voice = _voices; - for (; voice; voice = voice->next) - voice->setControlParameter(control, value); - } -} - -void MidiChannel_YM2612::sysEx_customInstrument(uint32 type, byte *fmInst) { - if (type != 'EUP ') - return; - Voice2612 *voice = new Voice2612; - voice->next = _voices; - _voices = voice; - voice->_rate = _rate; - voice->setInstrument(fmInst); -} - -void MidiChannel_YM2612::pitchBend(int16 value) { - // いいのかこれで? - Voice2612 *voice = _voices; - for (; voice; voice = voice->next) - voice->pitchBend(value); -} - -void MidiChannel_YM2612::nextTick(int *outbuf, int buflen) { - Voice2612 *voice = _voices; - for (; voice; voice = voice->next) - voice->nextTick(outbuf, buflen); -} - -void MidiChannel_YM2612::rate(uint16 r) { - _rate = r; - Voice2612 *voice = _voices; - for (; voice; voice = voice->next) - voice->_rate = r; -} - -//////////////////////////////////////// -// -// MidiDriver_YM2612 -// -//////////////////////////////////////// - -MidiDriver_YM2612::MidiDriver_YM2612(SoundMixer *mixer) - : MidiDriver_Emulated(mixer) { - _next_voice = 0; - - createLookupTables(); - _volume = 256; - int i; - for (i = 0; i < ARRAYSIZE(_channel); i++) - _channel[i] = new MidiChannel_YM2612; - rate(getRate()); -} - -MidiDriver_YM2612::~MidiDriver_YM2612() { - int i; - for (i = 0; i < ARRAYSIZE(_channel); i++) - delete _channel[i]; - delete sintbl; - delete powtbl; - delete frequencyTable; - delete keycodeTable; - delete keyscaleTable; - delete attackOut; - sintbl = powtbl = frequencyTable = keycodeTable = keyscaleTable = attackOut = 0; -} - -int MidiDriver_YM2612::open() { - if (_isOpen) - return MERR_ALREADY_OPEN; - - MidiDriver_Emulated::open(); - - _mixer->setupPremix(this); - return 0; -} - -void MidiDriver_YM2612::close() { - if (!_isOpen) - return; - _isOpen = false; - - // Detach the premix callback handler - _mixer->setupPremix(0); -} - -void MidiDriver_YM2612::send(uint32 b) { - send(b & 0xF, b & 0xFFFFFFF0); -} - -void MidiDriver_YM2612::send(byte chan, uint32 b) { - //byte param3 = (byte) ((b >> 24) & 0xFF); - byte param2 = (byte) ((b >> 16) & 0xFF); - byte param1 = (byte) ((b >> 8) & 0xFF); - byte cmd = (byte) (b & 0xF0); - if (chan > ARRAYSIZE(_channel)) - return; - - switch (cmd) { - case 0x80:// Note Off - _channel[chan]->noteOff(param1); - break; - case 0x90: // Note On - _channel[chan]->noteOn(param1, param2); - break; - case 0xA0: // Aftertouch - break; // Not supported. - case 0xB0: // Control Change - _channel[chan]->controlChange(param1, param2); - break; - case 0xC0: // Program Change - _channel[chan]->programChange(param1); - break; - case 0xD0: // Channel Pressure - break; // Not supported. - case 0xE0: // Pitch Bend - _channel[chan]->pitchBend((param1 | (param2 << 7)) - 0x2000); - break; - case 0xF0: // SysEx - // We should never get here! SysEx information has to be - // sent via high-level semantic methods. - warning("MidiDriver_YM2612: Receiving SysEx command on a send() call"); - break; - - default: - warning("MidiDriver_YM2612: Unknown send() command 0x%02X", cmd); - } -} - -void MidiDriver_YM2612::sysEx(byte *msg, uint16 length) { - if (msg[0] != 0x7C || msg[1] >= ARRAYSIZE(_channel)) - return; - _channel[msg[1]]->sysEx_customInstrument('EUP ', &msg[2]); -} - -void MidiDriver_YM2612::generateSamples(int16 *data, int len) { - memset(data, 0, 2 * sizeof(int16) * len); - nextTick(data, len); -} - -void MidiDriver_YM2612::nextTick(int16 *buf1, int buflen) { - int *buf0 = (int *)buf1; - - int i; - for (i = 0; i < ARRAYSIZE(_channel); i++) - _channel[i]->nextTick(buf0, buflen); - - for (i = 0; i < buflen; ++i) - buf1[i*2+1] = buf1[i*2] = ((buf0[i] * volume()) >> 10) & 0xffff; -} - -void MidiDriver_YM2612::rate(uint16 r) -{ - int i; - for (i = 0; i < ARRAYSIZE(_channel); i++) - _channel[i]->rate(r); -} - -void MidiDriver_YM2612::createLookupTables() { - { - int i; - sintbl = new int [2048]; - for (i = 0; i < 2048; i++) - sintbl[i] = (int)(0xffff * sin(i/2048.0*2.0*PI)); - } - - { - int i; - powtbl = new int [1025]; - for (i = 0; i <= 1024; i++) - powtbl[i] = (int)(0x10000 * pow(2.0, (i-512)/512.0)); - } - - { - int i; - int block; - - static int fnum[] = { - 0x026a, 0x028f, 0x02b6, 0x02df, - 0x030b, 0x0339, 0x036a, 0x039e, - 0x03d5, 0x0410, 0x044e, 0x048f, - }; - - // (int)(880.0 * 256.0 * pow(2.0, (note-0x51)/12.0)); // バイアス 256 倍 - // 0x45 が 440Hz (a4)、0x51 が 880Hz (a5) らしい - frequencyTable = new int [120]; - for (block = -1; block < 9; block++) { - for (i = 0; i < 12; i++) { - double freq = fnum[i] * (166400.0 / 3) * pow(2.0, block-21); - frequencyTable[(block+1)*12+i] = (int)(256.0 * freq); - } - } - - keycodeTable = new int [120]; - // detune 量の計算や KS による rate 変換に使うんじゃないかな - for (block = -1; block < 9; block++) { - for (i = 0; i < 12; i++) { - // see p.204 - int f8 = (fnum[i] >> 7) & 1; - int f9 = (fnum[i] >> 8) & 1; - int f10 = (fnum[i] >> 9) & 1; - int f11 = (fnum[i] >> 10) & 1; - int n4 = f11; - int n3 = f11&(f10|f9|f8) | (~f11&f10&f9&f8); - int note = n4*2 + n3; - // see p.207 - keycodeTable[(block+1)*12+i] = block*4 + note; - } - } - } - - { - int freq; - keyscaleTable = new int [8192]; - keyscaleTable[0] = 0; - for (freq = 1; freq < 8192; freq++) { - keyscaleTable[freq] = (int)(log((double)freq) / 9.03 * 32.0) - 1; - // 8368[Hz] (o9c) で 32くらい。9.03 =:= ln 8368 - } - } - - { - int i; - attackOut = new int [1024]; - for (i = 0; i < 1024; i++) - attackOut[i] = (int)(((0x7fff+0x03a5)*30.0) / (30.0+i)) - 0x03a5; - } -} - -//////////////////////////////////////// -// -// MidiDriver_YM2612 factory -// -//////////////////////////////////////// - -MidiDriver *MidiDriver_YM2612_create(SoundMixer *mixer) { - return new MidiDriver_YM2612(mixer); -} |