aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/adlib.cpp938
-rw-r--r--sound/fmopl.cpp1106
-rw-r--r--sound/fmopl.h155
-rw-r--r--sound/gmidi.cpp414
4 files changed, 2613 insertions, 0 deletions
diff --git a/sound/adlib.cpp b/sound/adlib.cpp
new file mode 100644
index 0000000000..7f74cef00c
--- /dev/null
+++ b/sound/adlib.cpp
@@ -0,0 +1,938 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 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 "scumm.h"
+#include "sound.h"
+#include "fmopl.h"
+
+#if defined USE_ADLIB
+
+static byte lookup_table[64][32];
+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
+};
+
+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];
+ }
+ }
+}
+
+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;
+}
+
+MidiChannelAdl *AdlibSoundDriver::allocate_midichan(byte pri) {
+ MidiChannelAdl *ac,*best=NULL;
+ int i;
+
+ for (i=0; i<9; i++) {
+ if (++_midichan_index >= 9)
+ _midichan_index = 0;
+ ac = &_midi_channels[_midichan_index];
+ if (!ac->_part)
+ return ac;
+ if (!ac->_next) {
+ if (ac->_part->_pri_eff <= pri) {
+ pri = ac->_part->_pri_eff;
+ best = ac;
+ }
+ }
+ }
+
+ if (best)
+ mc_off(best);
+ else
+ ;//debug(1, "Denying adlib channel request");
+ return best;
+}
+
+void AdlibSoundDriver::init(SoundEngine *eng) {
+ int i;
+ MidiChannelAdl *mc;
+
+ _se = eng;
+
+ for(i=0,mc=_midi_channels; i!=ARRAYSIZE(_midi_channels);i++,mc++)
+ mc->_channel = i;
+
+ _adlib_reg_cache = (byte*)calloc(256,1);
+ _opl = OPLCreate(OPL_TYPE_YM3812,3579545,22050);
+ adlib_write(1,0x20);
+ adlib_write(8,0x40);
+ adlib_write(0xBD, 0x00);
+ create_lookup_table();
+}
+
+void AdlibSoundDriver::adlib_write(byte port, byte value) {
+ if (_adlib_reg_cache[port] == value)
+ return;
+ _adlib_reg_cache[port] = value;
+
+ static FILE *myout;
+ if (!myout)
+ myout = fopen("d:\\old.ims", "w");
+ fprintf(myout, "[%x]=%x\n", port, value);
+
+
+ OPLWriteReg(_opl, port, value);
+}
+
+void AdlibSoundDriver::adlib_key_off(int chan) {
+ byte port = chan + 0xB0;
+ adlib_write(port, adlib_read(port)&~0x20);
+}
+
+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 */
+};
+
+void AdlibSoundDriver::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));
+}
+
+static const byte octave_numbers[] = {
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 1, 1, 1, 1,
+1, 1, 1, 1, 1, 1, 1, 1,
+2, 2, 2, 2, 2, 2, 2, 2,
+2, 2, 2, 2, 3, 3, 3, 3,
+3, 3, 3, 3, 3, 3, 3, 3,
+4, 4, 4, 4, 4, 4, 4, 4,
+4, 4, 4, 4, 5, 5, 5, 5,
+5, 5, 5, 5, 5, 5, 5, 5,
+6, 6, 6, 6, 6, 6, 6, 6,
+6, 6, 6, 6, 7, 7, 7, 7,
+7, 7, 7, 7, 7, 7, 7, 7,
+7, 7, 7, 7, 7, 7, 7, 7,
+7, 7, 7, 7, 7, 7, 7, 7,
+7, 7, 7, 7, 7, 7, 7, 7,
+7, 7, 7, 7, 7, 7, 7, 7
+};
+
+static const byte note_numbers[]= {
+ 3, 4, 5, 6, 7, 8, 9, 10,
+11, 12, 13, 14, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 3, 4, 5, 6, 7, 8, 9, 10,
+11, 12, 13, 14, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 3, 4, 5, 6, 7, 8, 9, 10,
+11, 12, 13, 14, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 3, 4, 5, 6, 7, 8, 9, 10,
+11, 12, 13, 14, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 3, 4, 5, 6, 7, 8, 9, 10,
+11, 12, 13, 14, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 3, 4, 5, 6, 7, 8, 9, 10
+};
+
+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,
+};
+
+void AdlibSoundDriver::adlib_playnote(int channel, int note) {
+ byte old,oct,notex;
+ int note2;
+ int i;
+
+ note2 = (note>>7) - 4;
+
+ oct = octave_numbers[note2]<<2;
+ notex = note_numbers[note2];
+
+ 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);
+}
+
+void AdlibSoundDriver::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, channel_table_2[chan] + code);
+}
+
+void AdlibSoundDriver::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 AdlibSoundDriver::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 AdlibSoundDriver::adlib_setup_channel(int chan, Instrument *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, ~instr->atdec_1);
+ adlib_write(port + 0x80, ~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, ~instr->atdec_2);
+ adlib_write(port + 0x80, ~instr->sustrel_2);
+ adlib_write(port + 0xE0, instr->waveform_2);
+
+ adlib_write((byte)chan + 0xC0, instr->feedback);
+}
+
+int AdlibSoundDriver::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 AdlibSoundDriver::generate_samples(int16 *data, int len) {
+ int step;
+
+ if (!_opl) {
+ memset(data, 0, len*sizeof(int16));
+ return;
+ }
+
+ do {
+ step = len;
+ if (step > _next_tick)
+ step = _next_tick;
+ YM3812UpdateOne(_opl,data,step);
+
+ if(!(_next_tick -= step)) {
+ _se->on_timer();
+ reset_tick();
+ }
+ data += step;
+ } while (len-=step);
+}
+
+void AdlibSoundDriver::reset_tick() {
+ _next_tick = 88;
+}
+
+void AdlibSoundDriver::on_timer() {
+ MidiChannelAdl *mc;
+ int i;
+
+ _adlib_timer_counter += 0xD69;
+ while (_adlib_timer_counter >= 0x411B) {
+ _adlib_timer_counter -= 0x411B;
+ mc = _midi_channels;
+ for (i=0; i!=ARRAYSIZE(_midi_channels); i++,mc++) {
+ if (!mc->_part)
+ continue;
+ if (mc->_duration && (mc->_duration -= 0x11) <= 0) {
+ mc_off(mc);
+ return;
+ }
+ if (mc->_s10a.active) {
+ mc_inc_stuff(mc, &mc->_s10a, &mc->_s11a);
+ }
+ if (mc->_s10b.active) {
+ mc_inc_stuff(mc, &mc->_s10b, &mc->_s11b);
+ }
+ }
+ }
+}
+
+const byte param_table_1[16] = {
+29,28,27,0,
+3,4,7,8,
+13,16,17,20,
+21,30,31,0
+};
+
+const uint16 param_table_2[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
+};
+
+int AdlibSoundDriver::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 AdlibSoundDriver::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->param;
+ 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;
+}
+
+byte AdlibSoundDriver::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;
+ }
+ }
+ assert(s10->num_steps>=0);
+ 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 AdlibSoundDriver::struct10_init(Struct10 *s10, InstrumentExtra *ie) {
+ s10->active = 1;
+ s10->cur_val = 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);
+}
+
+void AdlibSoundDriver::mc_init_stuff(MidiChannelAdl *mc, Struct10 *s10, Struct11 *s11, byte flags, InstrumentExtra *ie) {
+ Part *part = mc->_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->param = param_table_2[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 = mc->_vol_2;
+ break;
+ case 13:
+ s10->start_value = mc->_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 = part->_drv->adlib_read_param(mc->_channel, s11->param);
+ }
+
+ struct10_init(s10, ie);
+}
+
+void AdlibSoundDriver::mc_inc_stuff(MidiChannelAdl *mc, Struct10 *s10, Struct11 *s11) {
+ byte code;
+ Part *part= mc->_part;
+
+ code = struct10_ontimer(s10,s11);
+
+ if (code&1) {
+ switch(s11->param) {
+ case 0:
+ mc->_vol_2 = s10->start_value + s11->modify_val;
+ part->_drv->adlib_set_param(mc->_channel, 0, volume_table[lookup_table[mc->_vol_2][part->_vol_eff>>2]]);
+ break;
+ case 13:
+ mc->_vol_1 = s10->start_value + s11->modify_val;
+ if (mc->_twochan) {
+ part->_drv->adlib_set_param(mc->_channel, 13, volume_table[lookup_table[mc->_vol_1][part->_vol_eff>>2]]);
+ } else {
+ part->_drv->adlib_set_param(mc->_channel, 13, mc->_vol_1);
+ }
+ break;
+ case 30:
+ s11->s10->modwheel = s11->modify_val;
+ break;
+ case 31:
+ s11->s10->unk3 = s11->modify_val;
+ break;
+ default:
+ part->_drv->adlib_set_param(mc->_channel, s11->param, s10->start_value + s11->modify_val);
+ break;
+ }
+ }
+
+ if (code&2 && s11->flag0x10)
+ part->_drv->adlib_key_onoff(mc->_channel);
+}
+
+void AdlibSoundDriver::part_changed(Part *part,byte what) {
+ MidiChannelAdl *mc;
+
+ if (what & pcProgram) {
+ if (part->_program < 32) {
+ part_set_instrument(part, &_glob_instr[part->_program]);
+ }
+ }
+
+ if (what & pcMod) {
+ for(mc=part->_mc->adl(); mc; mc=mc->_next) {
+ adlib_note_on(mc->_channel, mc->_note + part->_transpose_eff, part->_pitchbend + part->_detune_eff);
+ }
+ }
+
+ if (what & pcVolume) {
+ for(mc=part->_mc->adl(); mc; mc=mc->_next) {
+ adlib_set_param(mc->_channel, 0, volume_table[lookup_table[mc->_vol_2][part->_vol_eff>>2]]);
+ if (mc->_twochan) {
+ adlib_set_param(mc->_channel, 13, volume_table[lookup_table[mc->_vol_1][part->_vol_eff>>2]]);
+ }
+ }
+ }
+
+ if (what & pcPedal) {
+ if (!part->_pedal) {
+ for(mc=(MidiChannelAdl*)part->_mc; mc; mc=mc->_next) {
+ if (mc->_waitforpedal)
+ mc_off(mc);
+ }
+ }
+ }
+
+ if (what & pcModwheel) {
+ for(mc=(MidiChannelAdl*)part->_mc; mc; mc=mc->_next) {
+ if (mc->_s10a.active && mc->_s11a.flag0x40)
+ mc->_s10a.modwheel = part->_modwheel>>2;
+ if (mc->_s10b.active && mc->_s11b.flag0x40)
+ mc->_s10b.modwheel = part->_modwheel>>2;
+ }
+ }
+}
+
+void AdlibSoundDriver::mc_key_on(MidiChannel *mc2, byte note, byte velocity) {
+ MidiChannelAdl *mc = (MidiChannelAdl*)mc2;
+ Part *part = mc->_part;
+ Instrument *instr = &_part_instr[part->_slot];
+ int c;
+ byte vol_1,vol_2;
+
+ mc->_twochan = instr->feedback&1;
+ mc->_note = note;
+ mc->_waitforpedal = false;
+ mc->_duration = instr->duration;
+ if (mc->_duration != 0)
+ mc->_duration *= 63;
+
+ vol_1 = (instr->oplvl_1&0x3F) + lookup_table[velocity>>1][instr->waveform_1>>2];
+ if (vol_1 > 0x3F)
+ vol_1 = 0x3F;
+ mc->_vol_1 = vol_1;
+
+ vol_2 = (instr->oplvl_2&0x3F) + lookup_table[velocity>>1][instr->waveform_2>>2];
+ if (vol_2 > 0x3F)
+ vol_2 = 0x3F;
+ mc->_vol_2 = vol_2;
+
+ c = part->_vol_eff >> 2;
+
+ vol_2 = volume_table[lookup_table[vol_2][c]];
+ if (mc->_twochan)
+ vol_1 = volume_table[lookup_table[vol_1][c]];
+
+ adlib_setup_channel(mc->_channel, instr, vol_1, vol_2);
+ adlib_note_on_ex(mc->_channel, part->_transpose_eff + note, part->_detune_eff + part->_pitchbend);
+
+ if (instr->flags_a & 0x80) {
+ mc_init_stuff(mc, &mc->_s10a, &mc->_s11a, instr->flags_a, &instr->extra_a);
+ } else {
+ mc->_s10a.active = 0;
+ }
+
+ if (instr->flags_b & 0x80) {
+ mc_init_stuff(mc, &mc->_s10b, &mc->_s11b, instr->flags_b, &instr->extra_b);
+ } else {
+ mc->_s10b.active = 0;
+ }
+}
+
+void AdlibSoundDriver::set_instrument(uint slot, byte *data) {
+ if (slot < 32) {
+ memcpy(&_glob_instr[slot], data, sizeof(Instrument));
+ }
+}
+
+
+void AdlibSoundDriver::link_mc(Part *part, MidiChannelAdl *mc) {
+ mc->_part = part;
+ mc->_next = (MidiChannelAdl*)part->_mc;
+ part->_mc = mc;
+ mc->_prev = NULL;
+
+ if (mc->_next)
+ mc->_next->_prev = mc;
+}
+
+void AdlibSoundDriver::part_key_on(Part *part, byte note, byte velocity) {
+ MidiChannelAdl *mc;
+
+ mc = allocate_midichan(part->_pri_eff);
+ if (!mc)
+ return;
+
+ link_mc(part, mc);
+ mc_key_on(mc,note, velocity);
+}
+
+void AdlibSoundDriver::part_key_off(Part *part, byte note) {
+ MidiChannelAdl *mc;
+
+ for(mc=(MidiChannelAdl*)part->_mc; mc; mc=mc->_next) {
+ if (mc->_note==note) {
+ if (part->_pedal)
+ mc->_waitforpedal = true;
+ else
+ mc_off(mc);
+ }
+ }
+}
+
+struct AdlibInstrSetParams {
+ byte param;
+ byte shl;
+ byte mask;
+};
+
+#define MKLINE(_a_,_b_,_c_) { (int)&((Instrument*)0)->_a_, _b_, ((1<<(_c_))-1)<<(_b_) }
+static const AdlibInstrSetParams adlib_instr_params[69] = {
+ MKLINE(oplvl_2,0,6),
+ MKLINE(waveform_2,2,5),
+ MKLINE(oplvl_2,6,2),
+ MKLINE(flags_2,0,4),
+ MKLINE(atdec_2,4,4),
+ MKLINE(atdec_2,0,4),
+ MKLINE(sustrel_2,4,4),
+ MKLINE(sustrel_2,0,4),
+ MKLINE(waveform_2,0,2),
+ MKLINE(flags_2,7,1),
+ MKLINE(flags_2,6,1),
+ MKLINE(flags_2,5,1),
+ MKLINE(flags_2,4,1),
+
+ MKLINE(oplvl_1,0,6),
+ MKLINE(waveform_1,2,5),
+ MKLINE(oplvl_1,6,2),
+ MKLINE(flags_1,0,4),
+ MKLINE(atdec_1,4,4),
+ MKLINE(atdec_1,0,4),
+ MKLINE(sustrel_1,4,4),
+ MKLINE(sustrel_1,0,4),
+ MKLINE(waveform_1,0,2),
+ MKLINE(flags_1,7,1),
+ MKLINE(flags_1,6,1),
+ MKLINE(flags_1,5,1),
+ MKLINE(flags_1,4,1),
+
+ MKLINE(feedback,0,1),
+ MKLINE(feedback,1,3),
+
+ MKLINE(flags_a,7,1),
+ MKLINE(flags_a,6,1),
+ MKLINE(flags_a,5,1),
+ MKLINE(flags_a,4,1),
+ MKLINE(flags_a,0,4),
+ MKLINE(extra_a.a,0,8),
+ MKLINE(extra_a.b,0,7),
+ MKLINE(extra_a.c,0,7),
+ MKLINE(extra_a.d,0,7),
+ MKLINE(extra_a.e,0,7),
+ MKLINE(extra_a.f,0,7),
+ MKLINE(extra_a.g,0,7),
+ MKLINE(extra_a.h,0,7),
+ MKLINE(extra_a.b,7,1),
+ MKLINE(extra_a.c,7,1),
+ MKLINE(extra_a.d,7,1),
+ MKLINE(extra_a.e,7,1),
+ MKLINE(extra_a.f,7,1),
+ MKLINE(extra_a.g,7,1),
+ MKLINE(extra_a.h,7,1),
+
+ MKLINE(flags_b,7,1),
+ MKLINE(flags_b,6,1),
+ MKLINE(flags_b,5,1),
+ MKLINE(flags_b,4,1),
+ MKLINE(flags_b,0,4),
+ MKLINE(extra_b.a,0,8),
+ MKLINE(extra_b.b,0,7),
+ MKLINE(extra_b.c,0,7),
+ MKLINE(extra_b.d,0,7),
+ MKLINE(extra_b.e,0,7),
+ MKLINE(extra_b.f,0,7),
+ MKLINE(extra_b.g,0,7),
+ MKLINE(extra_b.h,0,7),
+ MKLINE(extra_b.b,7,1),
+ MKLINE(extra_b.c,7,1),
+ MKLINE(extra_b.d,7,1),
+ MKLINE(extra_b.e,7,1),
+ MKLINE(extra_b.f,7,1),
+ MKLINE(extra_b.g,7,1),
+ MKLINE(extra_b.h,7,1),
+
+ MKLINE(duration,0,8),
+};
+#undef MKLINE
+
+void AdlibSoundDriver::part_set_param(Part *part, byte param, int value) {
+ const AdlibInstrSetParams *sp = &adlib_instr_params[param];
+ byte *p = (byte*)&_part_instr[part->_slot] + sp->param;
+ *p = (*p&~sp->mask) | (value<<sp->shl);
+
+ if (param < 28) {
+ MidiChannelAdl *mc;
+
+ for(mc=(MidiChannelAdl*)part->_mc; mc; mc=mc->_next) {
+ adlib_set_param(mc->_channel, param, value);
+ }
+ }
+}
+
+void AdlibSoundDriver::part_off(Part *part) {
+ MidiChannelAdl *mc = (MidiChannelAdl*)part->_mc;
+ part->_mc = NULL;
+ for(; mc; mc=mc->_next) {
+ mc_off(mc);
+ }
+}
+
+void AdlibSoundDriver::mc_off(MidiChannel *mc2) {
+ MidiChannelAdl *mc = (MidiChannelAdl*)mc2, *tmp;
+
+ adlib_key_off(mc->_channel);
+
+ tmp = mc->_prev;
+
+ if (mc->_next)
+ mc->_next->_prev = tmp;
+ if (tmp)
+ tmp->_next = mc->_next;
+ else
+ mc->_part->_mc = mc->_next;
+ mc->_part = NULL;
+}
+
+void AdlibSoundDriver::part_set_instrument(Part *part, Instrument *instr) {
+ Instrument *i = &_part_instr[part->_slot];
+ memcpy(i, instr, sizeof(Instrument));
+}
+
+int AdlibSoundDriver::part_update_active(Part *part,uint16 *active) {
+ int i;
+ uint16 bits;
+ int count = 0;
+ MidiChannelAdl *mc;
+
+ bits = 1<<part->_chan;
+
+ for(mc=part->_mc->adl(); mc; mc=mc->_next) {
+ if (!(active[mc->_note] & bits)) {
+ active[mc->_note] |= bits;
+ count++;
+ }
+ }
+ return count;
+}
+
+#endif \ No newline at end of file
diff --git a/sound/fmopl.cpp b/sound/fmopl.cpp
new file mode 100644
index 0000000000..167e323a9c
--- /dev/null
+++ b/sound/fmopl.cpp
@@ -0,0 +1,1106 @@
+/*
+**
+** File: fmopl.c -- software implementation of FM sound generator
+**
+** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmurator development
+** Modified for ScummVM by Ludvig Strigeus
+** Version 0.37a (modified)
+**
+*/
+
+#include "stdafx.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h>
+#include "fmopl.h"
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+#define INLINE inline
+
+/* -------------------- for debug --------------------- */
+
+/* -------------------- preliminary define section --------------------- */
+/* attack/decay rate time rate */
+#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */
+#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */
+
+#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */
+
+#define FREQ_BITS 24 /* frequency turn */
+
+/* counter bits = 20 , octerve 7 */
+#define FREQ_RATE (1<<(FREQ_BITS-20))
+#define TL_BITS (FREQ_BITS+2)
+
+/* final output shift , limit minimum and maximum */
+#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */
+#define OPL_MAXOUT (0x7fff<<OPL_OUTSB)
+#define OPL_MINOUT (-0x8000<<OPL_OUTSB)
+
+/* -------------------- quality selection --------------------- */
+
+/* sinwave entries */
+/* used static memory = SIN_ENT * 4 (byte) */
+#define SIN_ENT 2048
+
+/* output level entries (envelope,sinwave) */
+/* envelope counter lower bits */
+#define ENV_BITS 16
+/* envelope output entries */
+#define EG_ENT 4096
+/* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */
+/* used static memory = EG_ENT*4 (byte) */
+
+#define EG_OFF ((2*EG_ENT)<<ENV_BITS) /* OFF */
+#define EG_DED EG_OFF
+#define EG_DST (EG_ENT<<ENV_BITS) /* DECAY START */
+#define EG_AED EG_DST
+#define EG_AST 0 /* ATTACK START */
+
+#define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step */
+
+/* LFO table entries */
+#define VIB_ENT 512
+#define VIB_SHIFT (32-9)
+#define AMS_ENT 512
+#define AMS_SHIFT (32-9)
+
+#define VIB_RATE 256
+
+/* -------------------- local defines , macros --------------------- */
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* envelope phase */
+#define ENV_MOD_RR 0x00
+#define ENV_MOD_DR 0x01
+#define ENV_MOD_AR 0x02
+
+/* -------------------- tables --------------------- */
+static const int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1
+};
+
+#pragma warning (disable: 4244)
+
+/* key scale level */
+/* table is 3dB/OCT , DV converts this in TL step at 6dB/OCT */
+#define DV (EG_STEP/2)
+static const UINT32 KSL_TABLE[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* sustain lebel table (3db per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST
+static const INT32 SL_TABLE[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sinwave */
+/* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */
+/* TL_TABLE[ 0 to TL_MAX ] : plus section */
+/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
+static INT32 *TL_TABLE;
+
+/* pointers to TL_TABLE with sinwave output offset */
+static INT32 **SIN_TABLE;
+
+/* LFO table */
+static INT32 *AMS_TABLE;
+static INT32 *VIB_TABLE;
+
+/* envelope output curve table */
+/* attack + decay + OFF */
+static INT32 ENV_CURVE[2*EG_ENT+1];
+
+/* multiple table */
+#define ML(x) (UINT32)(2*(x))
+static const UINT32 MUL_TABLE[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
+ ML(0.50), ML(1.00), ML(2.00), ML(3.00), ML(4.00), ML(5.00), ML(6.00), ML(7.00),
+ ML(8.00), ML(9.00),ML(10.00),ML(10.00),ML(12.00),ML(12.00),ML(15.00),ML(15.00)
+};
+#undef ML
+
+/* dummy attack / decay rate ( when rate == 0 ) */
+static INT32 RATE_0[16]=
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/* -------------------- static state --------------------- */
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+static void *cur_chip = NULL; /* current chip point */
+/* currenct chip state */
+/* static OPLSAMPLE *bufL,*bufR; */
+static OPL_CH *S_CH;
+static OPL_CH *E_CH;
+OPL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2;
+
+static INT32 outd[1];
+static INT32 ams;
+static INT32 vib;
+INT32 *ams_table;
+INT32 *vib_table;
+static INT32 amsIncr;
+static INT32 vibIncr;
+static INT32 feedback2; /* connect for SLOT 2 */
+
+/* --------------------- subroutines --------------------- */
+
+INLINE int Limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)
+{
+ /* set status flag */
+ OPL->status |= flag;
+ if(!(OPL->status & 0x80))
+ {
+ if(OPL->status & OPL->statusmask)
+ { /* IRQ on */
+ OPL->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
+{
+ /* reset status flag */
+ OPL->status &=~flag;
+ if((OPL->status & 0x80))
+ {
+ if (!(OPL->status & OPL->statusmask) )
+ {
+ OPL->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
+{
+ OPL->statusmask = flag;
+ /* IRQ handling check */
+ OPL_STATUS_SET(OPL,0);
+ OPL_STATUS_RESET(OPL,0);
+}
+
+/* ----- key on ----- */
+INLINE void OPL_KEYON(OPL_SLOT *SLOT)
+{
+ /* sin wave restart */
+ SLOT->Cnt = 0;
+ /* set attack */
+ SLOT->evm = ENV_MOD_AR;
+ SLOT->evs = SLOT->evsa;
+ SLOT->evc = EG_AST;
+ SLOT->eve = EG_AED;
+}
+/* ----- key off ----- */
+INLINE void OPL_KEYOFF(OPL_SLOT *SLOT)
+{
+ if( SLOT->evm > ENV_MOD_RR)
+ {
+ /* set envelope counter from envleope output */
+ SLOT->evm = ENV_MOD_RR;
+ if( !(SLOT->evc&EG_DST) )
+ //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST;
+ SLOT->evc = EG_DST;
+ SLOT->eve = EG_DED;
+ SLOT->evs = SLOT->evsr;
+ }
+}
+
+/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
+/* return : envelope output */
+INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT )
+{
+ /* calcrate envelope generator */
+ if( (SLOT->evc+=SLOT->evs) >= SLOT->eve )
+ {
+ switch( SLOT->evm ){
+ case ENV_MOD_AR: /* ATTACK -> DECAY1 */
+ /* next DR */
+ SLOT->evm = ENV_MOD_DR;
+ SLOT->evc = EG_DST;
+ SLOT->eve = SLOT->SL;
+ SLOT->evs = SLOT->evsd;
+ break;
+ case ENV_MOD_DR: /* DECAY -> SL or RR */
+ SLOT->evc = SLOT->SL;
+ SLOT->eve = EG_DED;
+ if(SLOT->eg_typ)
+ {
+ SLOT->evs = 0;
+ }
+ else
+ {
+ SLOT->evm = ENV_MOD_RR;
+ SLOT->evs = SLOT->evsr;
+ }
+ break;
+ case ENV_MOD_RR: /* RR -> OFF */
+ SLOT->evc = EG_OFF;
+ SLOT->eve = EG_OFF+1;
+ SLOT->evs = 0;
+ break;
+ }
+ }
+ /* calcrate envelope */
+ return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0);
+}
+
+/* set algorythm connection */
+static void set_algorythm( OPL_CH *CH)
+{
+ INT32 *carrier = &outd[0];
+ CH->connect1 = CH->CON ? carrier : &feedback2;
+ CH->connect2 = carrier;
+}
+
+/* ---------- frequency counter for operater update ---------- */
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+ int ksr;
+
+ /* frequency step counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+ /* attack , decay rate recalcration */
+ SLOT->evsa = SLOT->AR[ksr];
+ SLOT->evsd = SLOT->DR[ksr];
+ SLOT->evsr = SLOT->RR[ksr];
+ }
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = MUL_TABLE[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_typ = (v&0x20)>>5;
+ SLOT->vib = (v&0x40);
+ SLOT->ams = (v&0x80);
+ CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TL = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */
+
+ if( !(OPL->mode&0x80) )
+ { /* not CSM latch total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ar = v>>4;
+ int dr = v&0x0f;
+
+ SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0;
+ SLOT->evsa = SLOT->AR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa;
+
+ SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0;
+ SLOT->evsd = SLOT->DR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd;
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int sl = v>>4;
+ int rr = v & 0x0f;
+
+ SLOT->SL = SL_TABLE[sl];
+ if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL;
+ SLOT->RR = &OPL->DR_TABLE[rr<<2];
+ SLOT->evsr = SLOT->RR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr;
+}
+
+/* operator output calcrator */
+#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env]
+/* ---------- calcrate one of channel ---------- */
+INLINE void OPL_CALC_CH( OPL_CH *CH )
+{
+ UINT32 env_out;
+ OPL_SLOT *SLOT;
+
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH->FB)
+ {
+ int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB;
+ CH->op1_out[1] = CH->op1_out[0];
+ *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+ }
+ else
+ {
+ *CH->connect1 += OP_OUT(SLOT,env_out,0);
+ }
+ }else
+ {
+ CH->op1_out[1] = CH->op1_out[0];
+ CH->op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH->SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT,env_out, feedback2);
+ }
+}
+
+/* ---------- calcrate rythm block ---------- */
+#define WHITE_NOISE_db 6.0
+INLINE void OPL_CALC_RH( OPL_CH *CH )
+{
+ UINT32 env_tam,env_sd,env_top,env_hh;
+ int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP);
+ INT32 tone8;
+
+ OPL_SLOT *SLOT;
+ int env_out;
+
+ /* BD : same as FM serial mode and output level is large */
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH[6].FB)
+ {
+ int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+ }
+ else
+ {
+ feedback2 = OP_OUT(SLOT,env_out,0);
+ }
+ }else
+ {
+ feedback2 = 0;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ CH[6].op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH[6].SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT,env_out, feedback2)*2;
+ }
+
+ // SD (17) = mul14[fnum7] + white noise
+ // TAM (15) = mul15[fnum8]
+ // TOP (18) = fnum6(mul18[fnum8]+whitenoise)
+ // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise
+ env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise;
+ env_tam=OPL_CALC_SLOT(SLOT8_1);
+ env_top=OPL_CALC_SLOT(SLOT8_2);
+ env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise;
+
+ /* PG */
+ if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE);
+ else SLOT7_1->Cnt += 2*SLOT7_1->Incr;
+ if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE);
+ else SLOT7_2->Cnt += (CH[7].fc*8);
+ if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE);
+ else SLOT8_1->Cnt += SLOT8_1->Incr;
+ if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE);
+ else SLOT8_2->Cnt += (CH[8].fc*48);
+
+ tone8 = OP_OUT(SLOT8_2,whitenoise,0 );
+
+ /* SD */
+ if( env_sd < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8;
+ /* TAM */
+ if( env_tam < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2;
+ /* TOP-CY */
+ if( env_top < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2;
+ /* HH */
+ if( env_hh < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2;
+}
+
+/* ----------- initialize time tabls ----------- */
+static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE )
+{
+ int i;
+ double rate;
+
+ /* make attack rate & decay rate tables */
+ for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0;
+ for (i = 4;i <= 60;i++){
+ rate = OPL->freqbase; /* frequency rate */
+ if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */
+ rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */
+ rate *= (double)(EG_ENT<<ENV_BITS);
+ OPL->AR_TABLE[i] = rate / ARRATE;
+ OPL->DR_TABLE[i] = rate / DRRATE;
+ }
+ for (i = 60;i < 76;i++)
+ {
+ OPL->AR_TABLE[i] = EG_AED-1;
+ OPL->DR_TABLE[i] = OPL->DR_TABLE[60];
+ }
+}
+
+/* ---------- generic table initialize ---------- */
+static int OPLOpenTable( void )
+{
+ int s,t;
+ double rate;
+ int i,j;
+ double pom;
+
+ /* allocate dynamic tables */
+ if( (TL_TABLE = (INT32*)malloc(TL_MAX*2*sizeof(INT32))) == NULL)
+ return 0;
+ if( (SIN_TABLE = (INT32**)malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL)
+ {
+ free(TL_TABLE);
+ return 0;
+ }
+ if( (AMS_TABLE = (INT32*)malloc(AMS_ENT*2 *sizeof(INT32))) == NULL)
+ {
+ free(SIN_TABLE);
+ free(TL_TABLE);
+ return 0;
+ }
+ if( (VIB_TABLE = (INT32*)malloc(VIB_ENT*2 *sizeof(INT32))) == NULL)
+ {
+ free(AMS_TABLE);
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ return 0;
+ }
+ /* make total level table */
+ for (t = 0;t < EG_ENT-1 ;t++){
+ rate = ((1<<TL_BITS)-1)/pow(10,EG_STEP*t/20); /* dB -> voltage */
+ TL_TABLE[ t] = (int)rate;
+ TL_TABLE[TL_MAX+t] = -TL_TABLE[t];
+/* LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL_TABLE[t]));*/
+ }
+ /* fill volume off area */
+ for ( t = EG_ENT-1; t < TL_MAX ;t++){
+ TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0;
+ }
+
+ /* make sinwave table (total level offet) */
+ /* degree 0 = degree 180 = off */
+ SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1];
+ for (s = 1;s <= SIN_ENT/4;s++){
+ pom = sin(2*PI*s/SIN_ENT); /* sin */
+ pom = 20*log10(1/pom); /* decibel */
+ j = pom / EG_STEP; /* TL_TABLE steps */
+
+ /* degree 0 - 90 , degree 180 - 90 : plus section */
+ SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j];
+ /* degree 180 - 270 , degree 360 - 270 : minus section */
+ SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j];
+/* LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/
+ }
+ for (s = 0;s < SIN_ENT;s++)
+ {
+ SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT];
+ SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)];
+ SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s];
+ }
+
+ /* envelope counter -> envelope output table */
+ for (i=0; i<EG_ENT; i++)
+ {
+ /* ATTACK curve */
+ pom = pow( ((double)(EG_ENT-1-i)/EG_ENT) , 8 ) * EG_ENT;
+ /* if( pom >= EG_ENT ) pom = EG_ENT-1; */
+ ENV_CURVE[i] = (int)pom;
+ /* DECAY ,RELEASE curve */
+ ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i;
+ }
+ /* off */
+ ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1;
+ /* make LFO ams table */
+ for (i=0; i<AMS_ENT; i++)
+ {
+ pom = (1.0+sin(2*PI*i/AMS_ENT))/2; /* sin */
+ AMS_TABLE[i] = (1.0/EG_STEP)*pom; /* 1dB */
+ AMS_TABLE[AMS_ENT+i] = (4.8/EG_STEP)*pom; /* 4.8dB */
+ }
+ /* make LFO vibrate table */
+ for (i=0; i<VIB_ENT; i++)
+ {
+ /* 100cent = 1seminote = 6% ?? */
+ pom = (double)VIB_RATE*0.06*sin(2*PI*i/VIB_ENT); /* +-100sect step */
+ VIB_TABLE[i] = VIB_RATE + (pom*0.07); /* +- 7cent */
+ VIB_TABLE[VIB_ENT+i] = VIB_RATE + (pom*0.14); /* +-14cent */
+ /* LOG(LOG_INF,("vib %d=%d\n",i,VIB_TABLE[VIB_ENT+i])); */
+ }
+ return 1;
+}
+
+
+static void OPLCloseTable( void )
+{
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ free(VIB_TABLE);
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH)
+{
+ OPL_SLOT *slot1 = &CH->SLOT[SLOT1];
+ OPL_SLOT *slot2 = &CH->SLOT[SLOT2];
+ /* all key off */
+ OPL_KEYOFF(slot1);
+ OPL_KEYOFF(slot2);
+ /* total level latch */
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ /* key on */
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(slot1);
+ OPL_KEYON(slot2);
+}
+
+/* ---------- opl initialize ---------- */
+static void OPL_initalize(FM_OPL *OPL)
+{
+ int fn;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0;
+ /* Timer base time */
+ OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 );
+ /* make time tables */
+ init_timetables( OPL , OPL_ARRATE , OPL_DRRATE );
+ /* make fnumber -> increment counter table */
+ for( fn=0 ; fn < 1024 ; fn++ )
+ {
+ OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2;
+ }
+ /* LFO freq.table */
+ OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<<AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0;
+ OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<<VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0;
+}
+
+/* ---------- write a OPL registers ---------- */
+void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+ OPL_CH *CH;
+ int slot;
+ int block_fnum;
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:controll */
+ switch(r&0x1f)
+ {
+ case 0x01:
+ /* wave selector enable */
+ if(OPL->type&OPL_TYPE_WAVESEL)
+ {
+ OPL->wavesel = v&0x20;
+ if(!OPL->wavesel)
+ {
+ /* preset compatible mode */
+ int c;
+ for(c=0;c<OPL->max_ch;c++)
+ {
+ OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0];
+ OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0];
+ }
+ }
+ }
+ return;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v)*16;
+ return;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flag clear */
+ OPL_STATUS_RESET(OPL,0x7f);
+ }
+ else
+ { /* set IRQ mask ,timer enable*/
+ UINT8 st1 = v&1;
+ UINT8 st2 = (v>>1)&1;
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ OPL_STATUS_RESET(OPL,v&0x78);
+ OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01);
+ /* timer 2 */
+ if(OPL->st[1] != st2)
+ {
+ double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0;
+ OPL->st[1] = st2;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval);
+ }
+ /* timer 1 */
+ if(OPL->st[0] != st1)
+ {
+ double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0;
+ OPL->st[0] = st1;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval);
+ }
+ }
+ return;
+ }
+ break;
+ case 0x20: /* am,vib,ksr,eg type,mul */
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_mul(OPL,slot,v);
+ return;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_ksl_tl(OPL,slot,v);
+ return;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_ar_dr(OPL,slot,v);
+ return;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_sl_rr(OPL,slot,v);
+ return;
+ case 0xa0:
+ switch(r)
+ {
+ case 0xbd:
+ /* amsep,vibdep,r,bd,sd,tom,tc,hh */
+ {
+ UINT8 rkey = OPL->rythm^v;
+ OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0];
+ OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0];
+ OPL->rythm = v&0x3f;
+ if(OPL->rythm&0x20)
+ {
+#if 0
+ usrintf_showmessage("OPL Rythm mode select");
+#endif
+ /* BD key on/off */
+ if(rkey&0x10)
+ {
+ if(v&0x10)
+ {
+ OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0;
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ else
+ {
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ }
+ /* SD key on/off */
+ if(rkey&0x08)
+ {
+ if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]);
+ else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]);
+ }/* TAM key on/off */
+ if(rkey&0x04)
+ {
+ if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]);
+ else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]);
+ }
+ /* TOP-CY key on/off */
+ if(rkey&0x02)
+ {
+ if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]);
+ else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]);
+ }
+ /* HH key on/off */
+ if(rkey&0x01)
+ {
+ if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]);
+ else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]);
+ }
+ }
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ int keyon = (v>>5)&1;
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+ if(CH->keyon != keyon)
+ {
+ if( (CH->keyon=keyon) )
+ {
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(&CH->SLOT[SLOT1]);
+ OPL_KEYON(&CH->SLOT[SLOT2]);
+ }
+ else
+ {
+ OPL_KEYOFF(&CH->SLOT[SLOT1]);
+ OPL_KEYOFF(&CH->SLOT[SLOT2]);
+ }
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ int blockRv = 7-(block_fnum>>10);
+ int fnum = block_fnum&0x3ff;
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = KSL_TABLE[block_fnum>>6];
+ CH->fc = OPL->FN_TABLE[fnum]>>blockRv;
+ CH->kcode = CH->block_fnum>>9;
+ if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1;
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ return;
+ case 0xc0:
+ /* FB,C */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ {
+ int feedback = (v>>1)&7;
+ CH->FB = feedback ? (8+1) - feedback : 0;
+ CH->CON = v&1;
+ set_algorythm(CH);
+ }
+ return;
+ case 0xe0: /* wave type */
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ CH = &OPL->P_CH[slot/2];
+ if(OPL->wavesel)
+ {
+ /* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */
+ CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT];
+ }
+ return;
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable(void)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+ /* first time */
+ cur_chip = NULL;
+ /* allocate total level table (128kb space) */
+ if( !OPLOpenTable() )
+ {
+ num_lock--;
+ return -1;
+ }
+ return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+ /* last time */
+ cur_chip = NULL;
+ OPLCloseTable();
+}
+
+/*******************************************************************************/
+/* YM3812 local section */
+/*******************************************************************************/
+
+/* ---------- update one of chip ----------- */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+ int i;
+ int data;
+ OPLSAMPLE *buf = buffer;
+ UINT32 amsCnt = OPL->amsCnt;
+ UINT32 vibCnt = OPL->vibCnt;
+ UINT8 rythm = OPL->rythm&0x20;
+ OPL_CH *CH,*R_CH;
+
+ if( (void *)OPL != cur_chip ){
+ cur_chip = (void *)OPL;
+ /* channel pointers */
+ S_CH = OPL->P_CH;
+ E_CH = &S_CH[9];
+ /* rythm slot */
+ SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+ SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+ SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+ SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+ /* LFO state */
+ amsIncr = OPL->amsIncr;
+ vibIncr = OPL->vibIncr;
+ ams_table = OPL->ams_table;
+ vib_table = OPL->vib_table;
+ }
+ R_CH = rythm ? &S_CH[6] : E_CH;
+ for( i=0; i < length ; i++ )
+ {
+ /* channel A channel B channel C */
+ /* LFO */
+ ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+ vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+ outd[0] = 0;
+ /* FM part */
+ for(CH=S_CH ; CH < R_CH ; CH++)
+ OPL_CALC_CH(CH);
+ /* Rythn part */
+ if(rythm)
+ OPL_CALC_RH(S_CH);
+ /* limit check */
+ data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+ /* store to sound buffer */
+ buf[i] = data >> OPL_OUTSB;
+ }
+
+ OPL->amsCnt = amsCnt;
+ OPL->vibCnt = vibCnt;
+}
+
+
+/* ---------- reset one of chip ---------- */
+void OPLResetChip(FM_OPL *OPL)
+{
+ int c,s;
+ int i;
+
+ /* reset chip */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL,0x7f);
+ /* reset with register write */
+ OPLWriteReg(OPL,0x01,0); /* wabesel disable */
+ OPLWriteReg(OPL,0x02,0); /* Timer1 */
+ OPLWriteReg(OPL,0x03,0); /* Timer2 */
+ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
+ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
+ /* reset OPerator paramater */
+ for( c = 0 ; c < OPL->max_ch ; c++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[c];
+ /* OPL->P_CH[c].PAN = OPN_CENTER; */
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = &SIN_TABLE[0];
+ /* CH->SLOT[s].evm = ENV_MOD_RR; */
+ CH->SLOT[s].evc = EG_OFF;
+ CH->SLOT[s].eve = EG_OFF+1;
+ CH->SLOT[s].evs = 0;
+ }
+ }
+}
+
+/* ---------- Create one of vietual YM3812 ---------- */
+/* 'rate' is sampling rate and 'bufsiz' is the size of the */
+FM_OPL *OPLCreate(int type, int clock, int rate)
+{
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+ int max_ch = 9; /* normaly 9 channels */
+
+ if( OPL_LockTable() ==-1) return NULL;
+ /* allocate OPL state space */
+ state_size = sizeof(FM_OPL);
+ state_size += sizeof(OPL_CH)*max_ch;
+ /* allocate memory block */
+ ptr = (char*)malloc(state_size);
+ if(ptr==NULL) return NULL;
+ /* clear */
+ memset(ptr,0,state_size);
+ OPL = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL);
+ OPL->P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch;
+ /* set channel state pointer */
+ OPL->type = type;
+ OPL->clock = clock;
+ OPL->rate = rate;
+ OPL->max_ch = max_ch;
+ /* init grobal tables */
+ OPL_initalize(OPL);
+ /* reset chip */
+ OPLResetChip(OPL);
+ return OPL;
+}
+
+/* ---------- Destroy one of vietual YM3812 ---------- */
+void OPLDestroy(FM_OPL *OPL)
+{
+ OPL_UnLockTable();
+ free(OPL);
+}
+
+/* ---------- Option handlers ---------- */
+
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset)
+{
+ OPL->TimerHandler = TimerHandler;
+ OPL->TimerParam = channelOffset;
+}
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param)
+{
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param)
+{
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+
+int OPLTimerOver(FM_OPL *OPL,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL_STATUS_SET(OPL,0x20);
+ }
+ else
+ { /* Timer A */
+ OPL_STATUS_SET(OPL,0x40);
+ /* CSM mode key,TL controll */
+ if( OPL->mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ int ch;
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ for(ch=0;ch<9;ch++)
+ CSMKeyControll( &OPL->P_CH[ch] );
+ }
+ }
+ /* reload timer */
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase);
+ return OPL->status>>7;
+}
diff --git a/sound/fmopl.h b/sound/fmopl.h
new file mode 100644
index 0000000000..d331ff14c4
--- /dev/null
+++ b/sound/fmopl.h
@@ -0,0 +1,155 @@
+
+#ifndef __FMOPL_H_
+#define __FMOPL_H_
+
+/* --- select emulation chips --- */
+#define BUILD_YM3812 (HAS_YM3812)
+#define BUILD_YM3526 (HAS_YM3526)
+#define BUILD_Y8950 (HAS_Y8950)
+
+/* --- system optimize --- */
+/* select bit size of output : 8 or 16 */
+#define OPL_OUTPUT_BIT 16
+
+/* compiler dependence */
+#ifndef OSD_CPU_H
+#define OSD_CPU_H
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+#endif
+
+#if (OPL_OUTPUT_BIT==16)
+typedef INT16 OPLSAMPLE;
+#endif
+#if (OPL_OUTPUT_BIT==8)
+typedef unsigned char OPLSAMPLE;
+#endif
+
+
+typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
+typedef void (*OPL_IRQHANDLER)(int param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
+typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
+
+/* !!!!! here is private section , do not access there member direct !!!!! */
+
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
+#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
+#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
+#define OPL_TYPE_IO 0x08 /* I/O port */
+
+/* Saving is necessary for member of the 'R' mark for suspend/resume */
+/* ---------- OPL one of slot ---------- */
+typedef struct fm_opl_slot {
+ INT32 TL; /* total level :TL << 8 */
+ INT32 TLL; /* adjusted now TL */
+ UINT8 KSR; /* key scale rate :(shift down bit) */
+ INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */
+ INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */
+ INT32 SL; /* sustin level :SL_TALBE[SL] */
+ INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */
+ UINT8 ksl; /* keyscale level :(shift down bits) */
+ UINT8 ksr; /* key scale rate :kcode>>KSR */
+ UINT32 mul; /* multiple :ML_TABLE[ML] */
+ UINT32 Cnt; /* frequency count : */
+ UINT32 Incr; /* frequency step : */
+ /* envelope generator state */
+ UINT8 eg_typ; /* envelope type flag */
+ UINT8 evm; /* envelope phase */
+ INT32 evc; /* envelope counter */
+ INT32 eve; /* envelope counter end point */
+ INT32 evs; /* envelope counter step */
+ INT32 evsa; /* envelope step for AR :AR[ksr] */
+ INT32 evsd; /* envelope step for DR :DR[ksr] */
+ INT32 evsr; /* envelope step for RR :RR[ksr] */
+ /* LFO */
+ UINT8 ams; /* ams flag */
+ UINT8 vib; /* vibrate flag */
+ /* wave selector */
+ INT32 **wavetable;
+}OPL_SLOT;
+
+/* ---------- OPL one of channel ---------- */
+typedef struct fm_opl_channel {
+ OPL_SLOT SLOT[2];
+ UINT8 CON; /* connection type */
+ UINT8 FB; /* feed back :(shift down bit) */
+ INT32 *connect1; /* slot1 output pointer */
+ INT32 *connect2; /* slot2 output pointer */
+ INT32 op1_out[2]; /* slot1 output for selfeedback */
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum : */
+ UINT8 kcode; /* key code : KeyScaleCode */
+ UINT32 fc; /* Freq. Increment base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 keyon; /* key on/off flag */
+} OPL_CH;
+
+/* OPL state */
+struct FM_OPL {
+ UINT8 type; /* chip type */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ double TimerBase; /* Timer base time (==sampling time) */
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT8 statusmask; /* status mask */
+ UINT32 mode; /* Reg.08 : CSM , notesel,etc. */
+ /* Timer */
+ int T[2]; /* timer counter */
+ UINT8 st[2]; /* timer enable */
+ /* FM channel slots */
+ OPL_CH *P_CH; /* pointer of CH */
+ int max_ch; /* maximum channel */
+ /* Rythm sention */
+ UINT8 rythm; /* Rythm mode , key flag */
+ /* time tables */
+ /* LFO */
+ INT32 *ams_table;
+ INT32 *vib_table;
+ INT32 amsCnt;
+ INT32 amsIncr;
+ INT32 vibCnt;
+ INT32 vibIncr;
+ /* wave selector enable flag */
+ UINT8 wavesel;
+ /* external event callback handler */
+ OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
+ int TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ int IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
+ int UpdateParam; /* stream update parameter */
+ INT32 AR_TABLE[75]; /* atttack rate tables */
+ INT32 DR_TABLE[75]; /* decay rate tables */
+ UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */
+};
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
+
+FM_OPL *OPLCreate(int type, int clock, int rate);
+void OPLDestroy(FM_OPL *OPL);
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
+
+void OPLResetChip(FM_OPL *OPL);
+int OPLWrite(FM_OPL *OPL,int a,int v);
+unsigned char OPLRead(FM_OPL *OPL,int a);
+int OPLTimerOver(FM_OPL *OPL,int c);
+
+void OPLWriteReg(FM_OPL *OPL, int r, int v);
+
+/* YM3626/YM3812 local section */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+
+#endif
diff --git a/sound/gmidi.cpp b/sound/gmidi.cpp
new file mode 100644
index 0000000000..f76a00d708
--- /dev/null
+++ b/sound/gmidi.cpp
@@ -0,0 +1,414 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 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$
+ */
+
+/*
+ * Timidity support by Lionel Ulmer <lionel.ulmer@free.fr>
+ */
+
+
+#include "stdafx.h"
+
+#if !defined USE_ADLIB
+
+#include "scumm.h"
+#include "sound.h"
+
+#ifdef USE_TIMIDITY
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Copy-pasted from Timidity */
+#define SEQ_MIDIPUTC 5
+
+#endif /* USE_TIMIDITY */
+
+
+#define SPECIAL_CHANNEL 9
+
+#if defined(WIN32)
+
+void MidiSoundDriver::midiInit() {
+ if (midiOutOpen((HMIDIOUT*)&_mo, MIDI_MAPPER, NULL, NULL, 0) != MMSYSERR_NOERROR)
+ error("midiOutOpen failed");
+}
+
+#define MIDI_OUT(a,b) midiOutShortMsg((HMIDIOUT)(a), (b))
+
+#elif defined(USE_TIMIDITY)
+
+static int connect_to_timidity(int port)
+{
+ struct hostent *serverhost;
+ struct sockaddr_in sadd;
+ int s;
+
+ serverhost = gethostbyname("localhost");
+ if (serverhost == NULL)
+ error("Could not resolve host");
+ sadd.sin_family = serverhost->h_addrtype;
+ sadd.sin_port = htons(port);
+ memcpy(&(sadd.sin_addr), serverhost->h_addr_list[0], serverhost->h_length);
+
+ s = socket(AF_INET,SOCK_STREAM,0);
+ if (s < 0)
+ error("Could not open socket");
+ if (connect(s, (struct sockaddr *) &sadd, sizeof(struct sockaddr_in)) < 0)
+ error("Could not connect to server");
+
+ return s;
+}
+
+void SoundEngine::midiInit() {
+ int s, s2;
+ int len;
+ int dummy, newport;
+ char buf[256];
+
+ s = connect_to_timidity(7777);
+ len = read(s, buf, 256);
+ buf[len] = '\0';
+ printf("%s", buf);
+
+ sprintf(buf, "SETBUF %f %f\n", 0.1, 0.15);
+ write(s, buf, strlen(buf));
+ len = read(s, buf, 256);
+ buf[len] = '\0';
+ printf("%s", buf);
+
+ sprintf(buf, "OPEN lsb\n");
+ write(s, buf, strlen(buf));
+ len = read(s, buf, 256);
+ buf[len] = '\0';
+ printf("%s", buf);
+
+ sscanf(buf, "%d %d", &dummy, &newport);
+ printf(" => port = %d\n", newport);
+
+ s2 = connect_to_timidity(newport);
+ _mo = (void *) s2;
+}
+
+#define DEVICE_NUM 0
+
+static inline void MIDI_OUT(void *a, int b) {
+ int s = (int) a;
+ unsigned char buf[256];
+ int position = 0;
+
+ switch (b & 0xF0) {
+ case 0x80:
+ case 0x90:
+ case 0xA0:
+ case 0xB0:
+ case 0xE0:
+ buf[position++] = SEQ_MIDIPUTC;
+ buf[position++] = b;
+ buf[position++] = DEVICE_NUM;
+ buf[position++] = 0;
+ buf[position++] = SEQ_MIDIPUTC;
+ buf[position++] = (b >> 8) & 0x7F;
+ buf[position++] = DEVICE_NUM;
+ buf[position++] = 0;
+ buf[position++] = SEQ_MIDIPUTC;
+ buf[position++] = (b >> 16) & 0x7F;
+ buf[position++] = DEVICE_NUM;
+ buf[position++] = 0;
+ break;
+ case 0xC0:
+ case 0xD0:
+ buf[position++] = SEQ_MIDIPUTC;
+ buf[position++] = b;
+ buf[position++] = DEVICE_NUM;
+ buf[position++] = 0;
+ buf[position++] = SEQ_MIDIPUTC;
+ buf[position++] = (b >> 8) & 0x7F;
+ buf[position++] = DEVICE_NUM;
+ buf[position++] = 0;
+ break;
+ default:
+ fprintf(stderr, "Unknown : %08x\n", b);
+ break;
+ }
+ write(s, buf, position);
+}
+
+#else
+#define MIDI_OUT(a,b)
+void SoundEngine::midiInit() { }
+#endif
+
+void MidiSoundDriver::midiPitchBend(byte chan, int16 pitchbend) {
+ uint16 tmp;
+
+ if (_midi_pitchbend_last[chan] != pitchbend) {
+ _midi_pitchbend_last[chan] = pitchbend;
+ tmp = (pitchbend<<2) + 0x2000;
+ MIDI_OUT(_mo, ((tmp>>7)&0x7F)<<16 | (tmp&0x7F)<<8 | 0xE0 | chan);
+ }
+}
+
+void MidiSoundDriver::midiVolume(byte chan, byte volume) {
+ if (_midi_volume_last[chan] != volume) {
+ _midi_volume_last[chan] = volume;
+ MIDI_OUT(_mo, volume<<16 | 7<<8 | 0xB0 | chan);
+ }
+}
+void MidiSoundDriver::midiPedal(byte chan, bool pedal) {
+ if (_midi_pedal_last[chan] != pedal) {
+ _midi_pedal_last[chan] = pedal;
+ MIDI_OUT(_mo, pedal<<16 | 64<<8 | 0xB0 | chan);
+ }
+}
+
+void MidiSoundDriver::midiModWheel(byte chan, byte modwheel) {
+ if (_midi_modwheel_last[chan] != modwheel) {
+ _midi_modwheel_last[chan] = modwheel;
+ MIDI_OUT(_mo, modwheel<<16 | 1<<8 | 0xB0 | chan);
+ }
+}
+
+void MidiSoundDriver::midiEffectLevel(byte chan, byte level) {
+ if (_midi_effectlevel_last[chan] != level) {
+ _midi_effectlevel_last[chan] = level;
+ MIDI_OUT(_mo, level<<16 | 91<<8 | 0xB0 | chan);
+ }
+}
+
+void MidiSoundDriver::midiChorus(byte chan, byte chorus) {
+ if (_midi_chorus_last[chan] != chorus) {
+ _midi_chorus_last[chan] = chorus;
+ MIDI_OUT(_mo, chorus<<16 | 93<<8 | 0xB0 | chan);
+ }
+}
+
+void MidiSoundDriver::midiControl0(byte chan, byte value) {
+ MIDI_OUT(_mo, value<<16 | 0<<8 | 0xB0 | chan);
+}
+
+static const byte mt32_to_gmidi[128] = {
+ 0, 1, 2, 4, 4, 5, 5, 3, 16, 17, 18, 18, 19,
+ 19, 20, 21, 6, 6, 6, 7, 7, 7, 8, 8, 62, 63,
+ 62, 63, 38, 39, 38, 39, 88, 89, 52, 113, 97, 96, 91,
+ 85, 14, 101, 68, 95, 86, 103, 88, 80, 48, 49, 51, 45,
+ 40, 40, 42, 42, 43, 46, 46, 24, 25, 26, 27, 104, 32,
+ 33, 34, 39, 36, 37, 38, 35, 79, 73, 72, 72, 74, 75,
+ 64, 65, 66, 67, 71, 71, 68, 69, 70, 22, 56, 59, 57,
+ 63, 60, 60, 58, 61, 61, 11, 11, 12, 88, 9, 14, 13,
+ 12, 107, 111, 77, 78, 78, 76, 121, 47, 117, 127, 115, 118,
+ 116, 118, 94, 115, 9, 55, 124, 123, 125, 126, 127
+};
+
+
+void MidiSoundDriver::midiProgram(byte chan, byte program) {
+ if (_mt32emulate)
+ program=mt32_to_gmidi[program];
+ MIDI_OUT(_mo, program<<8 | 0xC0 | chan);
+}
+
+void MidiSoundDriver::midiPan(byte chan, int8 pan) {
+ if (_midi_pan_last[chan] != pan) {
+ _midi_pan_last[chan] = pan;
+ MIDI_OUT(_mo, ((pan-64)&0x7F)<<16 | 10<<8 | 0xB0 | chan);
+ }
+}
+
+void MidiSoundDriver::midiNoteOn(byte chan, byte note, byte velocity) {
+ MIDI_OUT(_mo, velocity<<16 | note<<8 | 0x90 | chan);
+}
+
+void MidiSoundDriver::midiNoteOff(byte chan, byte note) {
+ MIDI_OUT(_mo, note<<8 | 0x80 | chan);
+}
+
+void MidiSoundDriver::midiSilence(byte chan) {
+ MIDI_OUT(_mo, (64<<8)|0xB0|chan);
+ MIDI_OUT(_mo, (123<<8)|0xB0|chan);
+}
+
+
+void MidiSoundDriver::part_key_on(Part *part, byte note, byte velocity) {
+ MidiChannelGM *mc = part->_mc->gm();
+
+ if (mc) {
+ mc->_actives[note>>4] |= (1<<(note&0xF));
+ midiNoteOn(mc->_chan, note, velocity);
+ } else if (part->_percussion) {
+ midiVolume(SPECIAL_CHANNEL, part->_vol_eff);
+ midiProgram(SPECIAL_CHANNEL, part->_bank);
+ midiNoteOn(SPECIAL_CHANNEL, note, velocity);
+ }
+}
+
+void MidiSoundDriver::part_key_off(Part *part, byte note) {
+ MidiChannelGM *mc = part->_mc->gm();
+
+ if (mc) {
+ mc->_actives[note>>4] &= ~(1<<(note&0xF));
+ midiNoteOff(mc->_chan, note);
+ } else if (part->_percussion) {
+ midiNoteOff(SPECIAL_CHANNEL, note);
+ }
+}
+
+void MidiSoundDriver::init(SoundEngine *eng) {
+ int i;
+ MidiChannelGM *mc;
+
+ _se = eng;
+
+ for(i=0,mc=_midi_channels; i!=ARRAYSIZE(_midi_channels);i++,mc++)
+ mc->_chan = i;
+
+ midiInit();
+}
+
+void MidiSoundDriver::update_pris() {
+ Part *part,*hipart;
+ int i;
+ byte hipri,lopri;
+ MidiChannelGM *mc,*lomc;
+
+ while(true) {
+ hipri = 0;
+ hipart = NULL;
+ for(i=32,part=_se->parts_ptr(); i; i--,part++) {
+ if (part->_player && !part->_percussion && part->_on && !part->_mc && part->_pri_eff>=hipri) {
+ hipri = part->_pri_eff;
+ hipart = part;
+ }
+ }
+
+ if (!hipart)
+ return;
+
+ lopri = 255;
+ lomc = NULL;
+ for(i=ARRAYSIZE(_midi_channels),mc=_midi_channels;;mc++) {
+ if (!mc->_part) {
+ lomc = mc;
+ break;
+ }
+ if (mc->_part->_pri_eff<=lopri) {
+ lopri = mc->_part->_pri_eff;
+ lomc = mc;
+ }
+
+ if (!--i) {
+ if (lopri >= hipri)
+ return;
+ lomc->_part->off();
+ break;
+ }
+ }
+
+ hipart->_mc = lomc;
+ lomc->_part = hipart;
+ hipart->changed(pcAll);
+ }
+}
+
+int MidiSoundDriver::part_update_active(Part *part, uint16 *active) {
+ int i,j;
+ uint16 *act,mask,bits;
+ int count = 0;
+
+ bits = 1<<part->_chan;
+
+ act = part->_mc->gm()->_actives;
+
+ for(i=8; i; i--) {
+ mask = *act++;
+ if (mask) {
+ for(j=16; j; j--,mask>>=1,active++) {
+ if (mask&1 && !(*active&bits)) {
+ *active|=bits;
+ count++;
+ }
+ }
+ } else {
+ active += 16;
+ }
+ }
+ return count;
+}
+
+void MidiSoundDriver::part_changed(Part *part, byte what) {
+ MidiChannelGM *mc;
+
+ /* Mark for re-schedule if program changed when in pre-state */
+ if (what&pcProgram && part->_percussion) {
+ part->_percussion = false;
+ update_pris();
+ }
+
+ if (!(mc = part->_mc->gm()))
+ return;
+
+ if (what & pcMod)
+ midiPitchBend(mc->_chan, clamp(part->_pitchbend + part->_detune_eff + (part->_transpose_eff<<7), -2048, 2047));
+
+ if (what & pcVolume)
+ midiVolume(mc->_chan, part->_vol_eff);
+
+ if (what & pcPedal)
+ midiPedal(mc->_chan, part->_pedal);
+
+ if (what & pcModwheel)
+ midiModWheel(mc->_chan, part->_modwheel);
+
+ if (what & pcPan)
+ midiPan(mc->_chan, part->_pan_eff);
+
+ if (what & pcEffectLevel)
+ midiEffectLevel(mc->_chan, part->_effect_level);
+
+ if (what & pcProgram) {
+ if (part->_bank) {
+ midiControl0(mc->_chan, part->_bank);
+ midiProgram(mc->_chan, part->_program);
+ midiControl0(mc->_chan, 0);
+ } else {
+ midiProgram(mc->_chan, part->_program);
+ }
+ }
+
+ if (what & pcChorus)
+ midiChorus(mc->_chan, part->_effect_level);
+}
+
+
+void MidiSoundDriver::part_off(Part *part) {
+ MidiChannelGM *mc = part->_mc->gm();
+ if (mc) {
+ part->_mc = NULL;
+ mc->_part = NULL;
+ memset(mc->_actives, 0, sizeof(mc->_actives));
+ midiSilence(mc->_chan);
+ }
+}
+
+#endif \ No newline at end of file