diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/adlib.cpp | 938 | ||||
-rw-r--r-- | sound/fmopl.cpp | 1106 | ||||
-rw-r--r-- | sound/fmopl.h | 155 | ||||
-rw-r--r-- | sound/gmidi.cpp | 414 |
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 |