diff options
-rw-r--r-- | actor.cpp | 10 | ||||
-rw-r--r-- | gui.cpp | 20 | ||||
-rw-r--r-- | resource.cpp | 46 | ||||
-rw-r--r-- | saveload.cpp | 4 | ||||
-rw-r--r-- | script.cpp | 13 | ||||
-rw-r--r-- | scumm.h | 8 | ||||
-rw-r--r-- | scummvm.cpp | 6 | ||||
-rw-r--r-- | scummvm.dsp | 38 | ||||
-rw-r--r-- | sdl.cpp | 12 | ||||
-rw-r--r-- | sound.cpp | 31 | ||||
-rw-r--r-- | sound.h | 310 | ||||
-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 | ||||
-rw-r--r-- | sys.cpp | 4 | ||||
-rw-r--r-- | windows.cpp | 21 |
17 files changed, 2998 insertions, 138 deletions
@@ -168,13 +168,9 @@ int Scumm::remapDirection(Actor *a, int dir) { switch(flags & 7) { case 1: - if (dir >= 180) - return 270; - return 90; - case 2: - if (dir <= 90 || dir>270) - return 0; - return 180; + return a->walkdata.XYFactor >0 ? 90 : 270; + case 2: + return a->walkdata.YXFactor >0 ? 180 : 0; case 3: return 270; case 4: return 90; case 5: return 0; @@ -1,3 +1,23 @@ +/* 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 "gui.h" diff --git a/resource.cpp b/resource.cpp index 4c71656532..7927c24b51 100644 --- a/resource.cpp +++ b/resource.cpp @@ -457,16 +457,17 @@ int Scumm::loadResource(int type, int index) { } int Scumm::readSoundResource(int type, int index) { - uint32 resStart, size, tag, size2, basetag; - byte *ptr; + uint32 pos, total_size, size, tag,basetag; int i; + int pri, best_pri; + uint32 best_size, best_offs; debug(9, "readSoundResource(%d,%d)", type, index); - resStart = 0; + pos = 0; basetag = fileReadDwordLE(); - size = fileReadDwordBE(); + total_size = fileReadDwordBE(); #if defined(SAMNMAX) || defined(FULL_THROTTLE) if (basetag == MKID('MIDI')) { @@ -475,23 +476,36 @@ int Scumm::readSoundResource(int type, int index) { return 1; } #else - while (size>resStart) { + while (pos < total_size) { tag = fileReadDword(); - size2 = fileReadDwordBE(); - - resStart += size2 + 8; + size = fileReadDwordBE() + 8; + pos += size; - for (i=0,ptr=_soundTagTable; i<_numSoundTags; i++,ptr+=4) { -/* endian OK, tags are in native format */ - if (READ_UINT32_UNALIGNED(ptr) == tag) { - fileSeek(_fileHandle, -8, SEEK_CUR); - fileRead(_fileHandle,createResource(type, index, size2+8), size2+8); - return 1; - } + switch(tag) { +#ifdef USE_ADLIB + case MKID('ADL '): pri = 10; break; +#else + case MKID('ROL '): pri = 1; break; + case MKID('GMD '): pri = 2; break; +#endif + default: pri = -1; } - fileSeek(_fileHandle, size2, SEEK_CUR); + if (pri > best_pri) { + best_pri = pri; + best_size = size; + best_offs = filePos(_fileHandle); + } + + fileSeek(_fileHandle, size - 8, SEEK_CUR); + } + + if (best_pri != -1) { + fileSeek(_fileHandle, best_offs - 8, SEEK_SET); + fileRead(_fileHandle,createResource(type, index, best_size), best_size); + return 1; } + #endif res.roomoffs[type][index] = 0xFFFFFFFF; return 0; diff --git a/saveload.cpp b/saveload.cpp index f0289634eb..5b6ff31caf 100644 --- a/saveload.cpp +++ b/saveload.cpp @@ -499,8 +499,8 @@ void Scumm::saveOrLoad(Serializer *s) { } } - if (_soundDriver) - ((SoundEngine*)_soundDriver)->save_or_load(s); + if (_soundEngine) + ((SoundEngine*)_soundEngine)->save_or_load(s); } diff --git a/script.cpp b/script.cpp index 28f9f7f1c7..07343e8ae3 100644 --- a/script.cpp +++ b/script.cpp @@ -293,6 +293,12 @@ int Scumm::readVar(uint var) { #endif debug(9, "readvar=%d", var); if (!(var&0xF000)) { +#if defined(BYPASS_COPY_PROT) + if (var==490 && _gameId == GID_MONKEY2 && !copyprotbypassed) { + copyprotbypassed = true; + var = 518; + } +#endif checkRange(_numVariables-1, 0, var, "Variable %d out of range(r)"); return _vars[var]; } @@ -317,13 +323,6 @@ int Scumm::readVar(uint var) { if (var&0x4000) { var &= 0xFFF; checkRange(0x10, 0, var, "Local variable %d out of range(r)"); - -#if defined(BYPASS_COPY_PROT) - if (!copyprotbypassed && _currentScript==1 && _gameId==GID_MONKEY2 && var==0) { - copyprotbypassed=1; - return 1; - } -#endif return vm.localvar[_currentScript][var]; } @@ -22,6 +22,7 @@ #include "scummsys.h" #define SWAP(a,b) do{int tmp=a; a=b; b=tmp; } while(0) +#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) struct Scumm; struct Actor; @@ -936,7 +937,7 @@ struct Scumm { int _keyPressed; - void *_soundDriver; + void *_soundEngine; uint16 *_inventory; byte *_arrays; @@ -1128,8 +1129,8 @@ struct Scumm { byte *_shadowPalette; - int _numSoundTags; - byte *_soundTagTable; +// int _numSoundTags; +// byte *_soundTagTable; int16 _bootParam; @@ -1220,6 +1221,7 @@ struct Scumm { void fileSeek(void *file, long offs, int whence); void fileRead(void *handle, void *ptr, uint32 size); bool fileEof(void *handle); + uint32 filePos(void *handle); int fileReadByte(); uint32 fileReadDwordLE(); diff --git a/scummvm.cpp b/scummvm.cpp index cfad282df9..479282d8d7 100644 --- a/scummvm.cpp +++ b/scummvm.cpp @@ -196,9 +196,9 @@ void Scumm::scummMain(int argc, char **argv) { _bootParam = -7873; } - if (_gameId==GID_MONKEY2 && _bootParam==0) { - _bootParam = 10001; - } +// if (_gameId==GID_MONKEY2 && _bootParam==0) { +// _bootParam = 10001; +// } initGraphics(this, _fullScreen); diff --git a/scummvm.dsp b/scummvm.dsp index 4049781556..41e6e89593 100644 --- a/scummvm.dsp +++ b/scummvm.dsp @@ -66,7 +66,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
-# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "ALLOW_GDI" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "./sound" /I "./" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "ALLOW_GDI" /D "BYPASS_COPY_PROT" /D "USE_ADLIB" /Yu"stdafx.h" /FD /GZ /c
# SUBTRACT CPP /Fr
# ADD BASE RSC /l 0x41d /d "_DEBUG"
# ADD RSC /l 0x41d /d "_DEBUG"
@@ -86,6 +86,26 @@ LINK32=link.exe # Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Group "sound"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\sound\adlib.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\sound\fmopl.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\sound\gmidi.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\imuse.cpp
+# End Source File
+# End Group
# Begin Source File
SOURCE=.\actor.cpp
@@ -152,10 +172,6 @@ SOURCE=.\gui.cpp # End Source File
# Begin Source File
-SOURCE=.\imuse.cpp
-# End Source File
-# Begin Source File
-
SOURCE=.\object.cpp
!IF "$(CFG)" == "scummvm - Win32 Release"
@@ -247,6 +263,10 @@ SOURCE=.\scummvm.cpp # End Source File
# Begin Source File
+SOURCE=.\sdl.cpp
+# End Source File
+# Begin Source File
+
SOURCE=.\sound.cpp
!IF "$(CFG)" == "scummvm - Win32 Release"
@@ -312,16 +332,16 @@ SOURCE=.\verbs.cpp !ENDIF
# End Source File
-# Begin Source File
-
-SOURCE=.\windows.cpp
-# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
+SOURCE=.\sound\fmopl.h
+# End Source File
+# Begin Source File
+
SOURCE=.\gui.h
# End Source File
# Begin Source File
@@ -35,6 +35,7 @@ ScummDebugger debugger; Gui gui; SoundEngine sound; +SOUND_DRIVER_TYPE snd_driv; static SDL_Surface *screen; @@ -479,7 +480,6 @@ void drawMouse(Scumm *s, int xdraw, int ydraw, int color, byte *mask, bool visib static uint32 midi_counter; void fill_sound(void *userdata, Uint8 *stream, int len) { - memset(stream, 0, len); scumm.mixWaves((int16*)stream, len>>1); } @@ -501,6 +501,7 @@ int music_thread(Scumm *s) { return 0; } + void initGraphics(Scumm *s, bool fullScreen) { SDL_AudioSpec desired; @@ -528,8 +529,10 @@ void initGraphics(Scumm *s, bool fullScreen) { SDL_WM_SetCaption(buf,buf); SDL_ShowCursor(SDL_DISABLE); - /* Create Music Thread */ - SDL_CreateThread((int (*)(void *))&music_thread, &scumm); + if (!snd_driv.wave_based()) { + /* Create Music Thread */ + SDL_CreateThread((int (*)(void *))&music_thread, &scumm); + } #if !defined(SCALEUP_2x2) screen = SDL_SetVideoMode(320, 200, 8, fullScreen ? (SDL_SWSURFACE | SDL_FULLSCREEN) : SDL_SWSURFACE); @@ -557,8 +560,7 @@ int main(int argc, char* argv[]) { int delta,tmp; int last_time, new_time; - sound.initialize(&scumm); - scumm._soundDriver = &sound; + sound.initialize(&scumm, &snd_driv); scumm._gui = &gui; scumm.scummMain(argc, argv); @@ -65,7 +65,7 @@ void Scumm::processSoundQues() { data[j] = _soundQue[i+j]; i += num; - se = (SoundEngine*)_soundDriver; + se = (SoundEngine*)_soundEngine; #if 0 debug(1,"processSoundQues(%d,%d,%d,%d,%d,%d,%d,%d,%d)", data[0]>>8, @@ -89,7 +89,7 @@ void Scumm::processSoundQues() { } void Scumm::playSound(int sound) { - SoundEngine *se = (SoundEngine*)_soundDriver; + SoundEngine *se = (SoundEngine*)_soundEngine; if (se) { getResourceAddress(rtSound, sound); se->start_sound(sound); @@ -202,7 +202,7 @@ int Scumm::isSoundRunning(int sound) { if (!isResourceLoaded(rtSound, sound)) return 0; - se = (SoundEngine*)_soundDriver; + se = (SoundEngine*)_soundEngine; if (!se) return 0; return se->get_sound_status(sound); @@ -232,7 +232,7 @@ void Scumm::stopSound(int a) { SoundEngine *se; int i; - se = (SoundEngine*)_soundDriver; + se = (SoundEngine*)_soundEngine; if (se) se->stop_sound(a); @@ -242,7 +242,7 @@ void Scumm::stopSound(int a) { } void Scumm::stopAllSounds() { - SoundEngine *se = (SoundEngine*)_soundDriver; + SoundEngine *se = (SoundEngine*)_soundEngine; if (se) { se->stop_all_sounds(); se->clear_queue(); @@ -288,22 +288,16 @@ void Scumm::talkSound(uint32 a, uint32 b, int mode) { * is needed. */ -static const uint32 sound_tags[] = { - MKID('GMD ') -}; - void Scumm::setupSound() { - SoundEngine *se = (SoundEngine*)_soundDriver; + SoundEngine *se = (SoundEngine*)_soundEngine; if (se) - se->_base_sounds = res.address[rtSound]; + se->setBase(res.address[rtSound]); - _soundTagTable = (byte*)sound_tags; - _numSoundTags = 1; _sfxFile = openSfxFile(); } void Scumm::pauseSounds(bool pause) { - SoundEngine *se = (SoundEngine*)_soundDriver; + SoundEngine *se = (SoundEngine*)_soundEngine; if (se) se->pause(pause); _soundsPaused = pause; @@ -466,9 +460,16 @@ void MixerChannel::clear() { void Scumm::mixWaves(int16 *sounds, int len) { int i; + memset(sounds, 0,len * sizeof(int16)); + if (_soundsPaused) return; - + + SoundEngine *se = (SoundEngine*)_soundEngine; + if (se) { + se->driver()->generate_samples(sounds, len); + } + for(i=NUM_MIXER-1; i>=0;i--) { _mixer_channel[i].mix(sounds, len); } @@ -17,21 +17,89 @@ * * Change Log: * $Log$ + * Revision 1.2 2001/12/01 17:06:13 strigeus + * adlib sound support, use USE_ADLIB + * * Revision 1.1 2001/11/14 18:37:38 strigeus * music support, * fixed timing bugs * */ +int clamp(int val, int min, int max); + +struct FM_OPL; struct Part; struct MidiChannel; +struct MidiChannelAdl; +struct MidiChannelGM; struct VolumeFader; struct Player; struct HookDatas; struct SoundEngine; +struct SoundDriver; +struct Instrument; +struct AdlibSoundDriver; +struct MidiSoundDriver; + +#if defined(USE_ADLIB) +#define SOUND_DRIVER_TYPE AdlibSoundDriver +#else +#define SOUND_DRIVER_TYPE MidiSoundDriver +#endif + +struct Struct10 { + byte active; + int16 cur_val; + int16 count; + uint16 param; + int16 start_value; + byte loop; + byte table_a[4]; + byte table_b[4]; + int8 unk3; + int8 modwheel; + int8 modwheel_last; + uint16 speed_lo_max; + uint16 num_steps; + int16 speed_hi; + int8 direction; + uint16 speed_lo; + uint16 speed_lo_counter; +}; + +struct Struct11 { + int16 modify_val; + byte param,flag0x40,flag0x10; + Struct10 *s10; +}; + +struct InstrumentExtra { + byte a,b,c,d,e,f,g,h; +}; + +struct Instrument { + byte flags_1; + byte oplvl_1; + byte atdec_1; + byte sustrel_1; + byte waveform_1; + byte flags_2; + byte oplvl_2; + byte atdec_2; + byte sustrel_2; + byte waveform_2; + byte feedback; + byte flags_a; + InstrumentExtra extra_a; + byte flags_b; + InstrumentExtra extra_b; + byte duration; +}; struct Part { - SoundEngine *_se; + int _slot; + SOUND_DRIVER_TYPE *_drv; Part *_next, *_prev; MidiChannel *_mc; Player *_player; @@ -50,24 +118,24 @@ struct Part { byte _chan; byte _effect_level; byte _chorus; - byte _gmidi_5; - byte _gmidi_1; + byte _percussion; + byte _bank; void key_on(byte note, byte velocity); void key_off(byte note); - void set_param(int b, byte c) {} - void init(SoundEngine *se); + void set_param(byte param, int value); + void init(SoundDriver *_driver); void setup(Player *player); void uninit(); void off(); void silence(); void set_instrument(uint b); - void set_instrument(byte *data) {} + void set_instrument(Instrument *data); void set_transpose(int8 transpose); void set_vol(uint8 volume); void set_detune(int8 detune); - void set_pri(int8 pri, bool recalc); + void set_pri(int8 pri); void set_pan(int8 pan); void set_modwheel(uint value); void set_pedal(bool value); @@ -76,28 +144,42 @@ struct Part { void set_program(byte program); void set_chorus(uint chorus); void set_effect_level(uint level); - void set_chan_param(int b, int c) {} - void mod_changed(); - void vol_changed(); - void pedal_changed(); - void modwheel_changed(); - void pan_changed(); - void effect_level_changed(); - void program_changed(); - void chorus_changed(); + int update_actives(uint16 *active); void set_pitchbend_factor(uint8 value); void set_onoff(bool on); - void fix_after_load(); + + void update_pris(); + + void changed(byte what); }; + struct MidiChannel { Part *_part; + MidiChannelAdl *adl() { return (MidiChannelAdl*)this; } + MidiChannelGM *gm() { return (MidiChannelGM*)this; } +}; + +struct MidiChannelAdl : MidiChannel { + MidiChannelAdl *_next,*_prev; + byte _waitforpedal; + byte _note; + byte _channel; + byte _twochan; + byte _vol_1,_vol_2; + int16 _duration; + + Struct10 _s10a; + Struct11 _s11a; + Struct10 _s10b; + Struct11 _s11b; +}; + +struct MidiChannelGM : MidiChannel { byte _chan; uint16 _actives[8]; - - void init(byte chan); }; struct VolumeFader { @@ -188,7 +270,7 @@ struct Player { void set_priority(int pri); void set_pan(int pan); void set_detune(int detune); - void silence_parts(); + void turn_off_parts(); void play_active_notes(); void cancel_volume_fade(); @@ -235,8 +317,133 @@ struct IsNoteCmdData { byte vel; }; +struct SoundDriver { + enum { + pcMod = 1, + pcVolume = 2, + pcPedal = 4, + pcModwheel = 8, + pcPan = 16, + pcEffectLevel = 32, + pcProgram = 64, + pcChorus = 128, + pcAll = 255, + }; +}; + +struct AdlibSoundDriver : SoundDriver { +private: + FM_OPL *_opl; + byte *_adlib_reg_cache; + SoundEngine *_se; + + int _adlib_timer_counter; + + uint16 channel_table_2[9]; + int _midichan_index; + int _next_tick; + uint16 curnote_table[9]; + MidiChannelAdl _midi_channels[9]; + + Instrument _part_instr[32]; + Instrument _glob_instr[32]; + + void adlib_key_off(int chan); + void adlib_note_on(int chan, byte note, int mod); + void adlib_note_on_ex(int chan, byte note, int mod); + int adlib_read_param(int chan, byte data); + void adlib_setup_channel(int chan, Instrument *instr, byte vol_1, byte vol_2); + byte adlib_read(byte port) { return _adlib_reg_cache[port]; } + void adlib_set_param(int channel, byte param, int value); + void adlib_key_onoff(int channel); + void adlib_write(byte port, byte value); + void adlib_playnote(int channel, int note); + + MidiChannelAdl *allocate_midichan(byte pri); + + void reset_tick(); + void mc_off(MidiChannel *mc); + + static void link_mc(Part *part, MidiChannelAdl *mc); + static void mc_inc_stuff(MidiChannelAdl *mc, Struct10 *s10, Struct11 *s11); + static void mc_init_stuff(MidiChannelAdl *mc, Struct10 *s10, Struct11 *s11, byte flags, InstrumentExtra *ie); + static void struct10_init(Struct10 *s10, InstrumentExtra *ie); + static byte struct10_ontimer(Struct10 *s10, Struct11 *s11); + static void struct10_setup(Struct10 *s10); + static int random_nr(int a); + void mc_key_on(MidiChannel *mc, byte note, byte velocity); + +public: + void uninit(); + void init(SoundEngine *eng); + void update_pris() { } + void generate_samples(int16 *buf, int len); + void on_timer(); + void set_instrument(uint slot, byte *instr); + void part_set_instrument(Part *part, Instrument *instr); + void part_key_on(Part *part, byte note, byte velocity); + void part_key_off(Part *part, byte note); + void part_set_param(Part *part, byte param, int value); + void part_changed(Part *part,byte what); + void part_off(Part *part); + int part_update_active(Part *part,uint16 *active); + void adjust_priorities() {} + + bool wave_based() { return true; } +}; + +struct MidiSoundDriver : SoundDriver { + HMIDIOUT *_mo; + bool _mt32emulate; + SoundEngine *_se; + + MidiChannelGM _midi_channels[9]; + + int16 _midi_pitchbend_last[16]; + uint8 _midi_volume_last[16]; + bool _midi_pedal_last[16]; + byte _midi_modwheel_last[16]; + byte _midi_effectlevel_last[16]; + byte _midi_chorus_last[16]; + int8 _midi_pan_last[16]; + + void midiPitchBend(byte chan, int16 pitchbend); + void midiVolume(byte chan, byte volume); + void midiPedal(byte chan, bool pedal); + void midiModWheel(byte chan, byte modwheel); + void midiEffectLevel(byte chan, byte level); + void midiChorus(byte chan, byte chorus); + void midiControl0(byte chan, byte value); + void midiProgram(byte chan, byte program); + void midiPan(byte chan, int8 pan); + void midiNoteOn(byte chan, byte note, byte velocity); + void midiNoteOff(byte chan, byte note); + void midiSilence(byte chan); + void midiInit(); + +public: + void uninit(); + void init(SoundEngine *eng); + void update_pris(); + void part_off(Part *part); + int part_update_active(Part *part,uint16 *active); + + void generate_samples(int16 *buf, int len) {} + void on_timer() {} + void set_instrument(uint slot, byte *instr) {} + void part_set_instrument(Part *part, Instrument *instr) {} + void part_set_param(Part *part, byte param, int value) {} + void part_key_on(Part *part, byte note, byte velocity); + void part_key_off(Part *part, byte note); + void part_changed(Part *part,byte what); + + bool wave_based() { return false; } +}; + struct SoundEngine { - void *_mo; /* midi out */ +friend struct Player; +private: + SOUND_DRIVER_TYPE *_driver; byte **_base_sounds; @@ -256,8 +463,6 @@ struct SoundEngine { SustainingNotes *_sustain_notes_free; SustainingNotes *_sustain_notes_head; - uint16 _timer_counter_1; - byte _queue_marker; byte _queue_cleared; byte _master_volume; @@ -272,28 +477,11 @@ struct SoundEngine { SustainingNotes _sustaining_notes[24]; VolumeFader _volume_fader[8]; Part _parts[32]; - MidiChannel _midi_channels[9]; + uint16 _active_notes[128]; CommandQueue _cmd_queue[64]; - int16 _midi_pitchbend_last[16]; - uint8 _midi_volume_last[16]; - bool _midi_pedal_last[16]; - byte _midi_modwheel_last[16]; - byte _midi_effectlevel_last[16]; - byte _midi_chorus_last[16]; - int8 _midi_pan_last[16]; - byte *findTag(int sound, char *tag, int index); - int initialize(Scumm *scumm); - int terminate(); - int save_or_load(Serializer *ser); - int set_master_volume(uint vol); - int get_master_volume(); - bool start_sound(int sound); - int stop_sound(int sound); - int stop_all_sounds(); - int get_sound_status(int sound); int get_queue_sound_status(int sound); Player *allocate_player(byte priority); void handle_marker(uint id, byte data); @@ -304,17 +492,12 @@ struct SoundEngine { void init_sustaining_notes(); void init_queue(); - void on_timer(); void sequencer_timers(); void expire_sustain_notes(); void expire_volume_faders(); - void set_instrument(uint slot, byte *data) {} - - int32 do_command(int a, int b, int c, int d, int e, int f, int g, int h); Part *allocate_part(byte pri); - int clear_queue(); int enqueue_command(int a, int b, int c, int d, int e, int f, int g); int enqueue_trigger(int sound, int marker); int query_queue(int param); @@ -329,22 +512,6 @@ struct SoundEngine { int set_volchan(int sound, int volchan); - void midiPitchBend(byte chan, int16 pitchbend); - void midiVolume(byte chan, byte volume); - void midiPedal(byte chan, bool pedal); - void midiModWheel(byte chan, byte modwheel); - void midiEffectLevel(byte chan, byte level); - void midiChorus(byte chan, byte chorus); - void midiControl0(byte chan, byte value); - void midiProgram(byte chan, byte program); - void midiPan(byte chan, int8 pan); - void midiNoteOn(byte chan, byte note, byte velocity); - void midiNoteOff(byte chan, byte note); - void midiSilence(byte chan); - void midiInit(); - - void adjust_priorities(); - void fix_parts_after_load(); void fix_players_after_load(); @@ -354,5 +521,24 @@ struct SoundEngine { void lock(); void unlock(); +public: + void on_timer(); + Part *parts_ptr() { return _parts; } void pause(bool paused); + int initialize(Scumm *scumm, SoundDriver *driver); + int terminate(); + int save_or_load(Serializer *ser); + int set_master_volume(uint vol); + int get_master_volume(); + bool start_sound(int sound); + int stop_sound(int sound); + int stop_all_sounds(); + int get_sound_status(int sound); + int32 do_command(int a, int b, int c, int d, int e, int f, int g, int h); + int clear_queue(); + void setBase(byte **base) { _base_sounds = base; } + + SOUND_DRIVER_TYPE *driver() { return _driver; } }; + + 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 @@ -55,6 +55,10 @@ bool Scumm::fileEof(void *file) { return feof((FILE*)file) != 0; } +uint32 Scumm::filePos(void *handle) { + return ftell((FILE*)handle); +} + void Scumm::fileSeek(void *file, long offs, int whence) { switch(_fileMode) { case 1: case 2: diff --git a/windows.cpp b/windows.cpp index c0753bb35e..e764e722a4 100644 --- a/windows.cpp +++ b/windows.cpp @@ -132,6 +132,7 @@ Scumm scumm; ScummDebugger debugger; Gui gui; SoundEngine sound; +SOUND_DRIVER_TYPE snd_driv; WndMan wm[1]; byte veryFastMode; @@ -846,7 +847,6 @@ void drawMouse(Scumm *s, int x, int y, int w, int h, byte *buf, bool visible) { } void fill_buffer(int16 *buf, int len) { - memset(buf, 0, len*2); scumm.mixWaves(buf, len); } @@ -891,10 +891,13 @@ DWORD _stdcall WndMan::sound_thread(WndMan *wm) { int time = GetTickCount(), cur; while (1) { - cur = GetTickCount(); - while (time < cur) { - sound.on_timer(); - time += 10; + + if (!snd_driv.wave_based()) { + cur = GetTickCount(); + while (time < cur) { + sound.on_timer(); + time += 10; + } } signaled = WaitForSingleObject(wm->_event, time - cur) == WAIT_OBJECT_0; @@ -917,12 +920,12 @@ int main(int argc, char* argv[]) { int delta; wm->init(); - wm->sound_init(); wm->_vgabuf = (byte*)calloc(320,200); wm->_scumm = &scumm; - - sound.initialize(&scumm); - scumm._soundDriver = &sound; + + sound.initialize(&scumm,&snd_driv); + + wm->sound_init(); scumm._gui = &gui; scumm.scummMain(argc, argv); |