diff options
author | Ludvig Strigeus | 2001-11-14 18:40:39 +0000 |
---|---|---|
committer | Ludvig Strigeus | 2001-11-14 18:40:39 +0000 |
commit | b8d259d60eed2f8edc321dcba6116bddabbc35e8 (patch) | |
tree | 84d98395311081717250ea496df496fe88b5707d | |
parent | 279d5b2fd73057dc48b9f18a8753a2a5cff1c2e6 (diff) | |
download | scummvm-rg350-b8d259d60eed2f8edc321dcba6116bddabbc35e8.tar.gz scummvm-rg350-b8d259d60eed2f8edc321dcba6116bddabbc35e8.tar.bz2 scummvm-rg350-b8d259d60eed2f8edc321dcba6116bddabbc35e8.zip |
music support,
fixed timing bugs
svn-id: r3491
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | actor.cpp | 2 | ||||
-rw-r--r-- | gfx.cpp | 2 | ||||
-rw-r--r-- | gui.cpp | 69 | ||||
-rw-r--r-- | gui.h | 2 | ||||
-rw-r--r-- | imuse.cpp | 2529 | ||||
-rw-r--r-- | resource.cpp | 4 | ||||
-rw-r--r-- | saveload.cpp | 44 | ||||
-rw-r--r-- | script_v1.cpp | 2 | ||||
-rw-r--r-- | script_v2.cpp | 2 | ||||
-rw-r--r-- | scumm.h | 43 | ||||
-rw-r--r-- | scummvm.cpp | 20 | ||||
-rw-r--r-- | scummvm.dsp | 10 | ||||
-rw-r--r-- | sdl.cpp | 287 | ||||
-rw-r--r-- | sound.cpp | 140 | ||||
-rw-r--r-- | sound.h | 358 | ||||
-rw-r--r-- | string.cpp | 2 | ||||
-rw-r--r-- | windows.cpp | 143 |
18 files changed, 3292 insertions, 370 deletions
@@ -13,7 +13,8 @@ INCS = scumm.h scummsys.h stdafx.h OBJS = actor.o boxes.o costume.o gfx.o object.o resource.o \ saveload.o script.o scummvm.o sound.o string.o \ - sys.o verbs.o sdl.o script_v1.o script_v2.o debug.o gui.o + sys.o verbs.o sdl.o script_v1.o script_v2.o debug.o gui.o \ + imuse.o DISTFILES=actor.cpp boxes.cpp costume.cpp gfx.cpp object.cpp resource.cpp \ saveload.cpp script.cpp scummvm.cpp sound.cpp string.cpp \ @@ -585,6 +585,8 @@ void Scumm::showActors() { void Scumm::stopTalk() { int act; + stopTalkSound(); + _haveMsg = 0; _talkDelay = 0; @@ -1396,7 +1396,7 @@ void Scumm::unkScreenEffect7(int a) { tab_2[i] += tab_1[i]; updateScreen(this); - waitForTimer(this,3); + waitForTimer(this,30); } } @@ -2,6 +2,11 @@ #include "scumm.h" #include "gui.h" +enum { + SAVELOAD_DIALOG, + PAUSE_DIALOG +}; + void Gui::draw(int start,int end) { int i; @@ -249,6 +254,12 @@ const GuiWidget save_load_dialog[] = { {0} }; +const GuiWidget pause_dialog[] = { + {GUI_TEXT,0x01,GWF_DEFAULT,50,80,220,16,0,10}, + {0}, +}; + + void Gui::handleCommand(int cmd) { int lastEdit = _editString; showCaret(false); @@ -332,18 +343,20 @@ const byte string_map_table_v6[] = { 99, /* Cancel */ 100, /* Quit */ 101, /* Ok */ + 93, /* Game paused */ }; const byte string_map_table_v5[] = { 0, /* How may I serve you? */ 20, /* Select a game to LOAD */ - 21, /* Name your SAVE game */ + 19, /* Name your SAVE game */ 7, /* Save */ 8, /* Load */ 9, /* Play */ 10, /* Cancel */ 11, /* Quit */ 12, /* Ok */ + 4, /* Game paused */ }; const char *Gui::queryString(int string, int id) { @@ -399,27 +412,28 @@ void Gui::editString(int i) { } void Gui::addLetter(byte letter) { - if (_editString==-1) - return; + switch(_dialog) { + case SAVELOAD_DIALOG: + if (_editString==-1) + return; - if (letter==13) { - handleCommand(8); - return; - } + if (letter==13) { + handleCommand(8); + return; + } - if (letter>=32 && letter<128 && _editLen < SAVEGAME_NAME_LEN-1) { - game_names[_editString][_editLen++] = letter; - } else if (letter==8 && _editLen>0) { - _editLen--; + if (letter>=32 && letter<128 && _editLen < SAVEGAME_NAME_LEN-1) { + game_names[_editString][_editLen++] = letter; + } else if (letter==8 && _editLen>0) { + _editLen--; + } + showCaret(true); + break; + case PAUSE_DIALOG: + if (letter==32) + close(); + break; } - showCaret(true); -} - -void Gui::saveLoadDialog() { - _widgets[0] = save_load_dialog; - _editString = -1; - _cur_page = 0; - _active = 1; } byte Gui::getDefaultColor(int color) { @@ -431,7 +445,6 @@ byte Gui::getDefaultColor(int color) { } } - void Gui::init(Scumm *s) { _s = s; _bgcolor = getDefaultColor(0); @@ -447,6 +460,7 @@ void Gui::loop() { draw(0,100); _s->_cursorAnimate++; _s->gdi._cursorActive = 1; + _s->pauseSounds(true); } _s->getKeyInput(0); @@ -471,5 +485,20 @@ void Gui::close() { _s->_fullRedraw = true; _s->_completeScreenRedraw = true; _s->_cursorAnimate--; + _s->pauseSounds(false); _active = false; } + +void Gui::saveLoadDialog() { + _widgets[0] = save_load_dialog; + _editString = -1; + _cur_page = 0; + _active = true; + _dialog = SAVELOAD_DIALOG; +} + +void Gui::pause() { + _widgets[0] = pause_dialog; + _active = true; + _dialog = PAUSE_DIALOG; +} @@ -43,6 +43,7 @@ struct Gui { byte _active; byte _clickTimer; byte _cur_page; + byte _dialog; int _clickWidget; char *_queryMess; @@ -77,6 +78,7 @@ struct Gui { void saveLoadDialog(); void queryMessage(const char *msg, const char *alts); byte getDefaultColor(int color); + void pause(); }; #endif
\ No newline at end of file diff --git a/imuse.cpp b/imuse.cpp new file mode 100644 index 0000000000..1ae3de9ae3 --- /dev/null +++ b/imuse.cpp @@ -0,0 +1,2529 @@ +/* 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" + +#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) + +#define TICKS_PER_BEAT 480 +#define TEMPO_BASE 0x400000 +#define HARDWARE_TYPE 5 +#define SYSEX_ID 0x7D +#define SPECIAL_CHANNEL 9 + +#define TRIGGER_ID 0 +#define COMMAND_ID 1 + +#ifdef SAMNMAX +#define MDHD_TAG "MDpg" +#else +#define MDHD_TAG "MDhd" +#endif + +static int clamp(int val, int min, int max) { + if (val<min) + return min; + if (val>max) + return max; + return val; +} + +static int transpose_clamp(int a, int b, int c) { + if (b>a) a += (b - a + 11) / 12 * 12; + if (c<a) a -= (a - c + 11) / 12 * 12; + return a; +} + +uint32 get_delta_time(byte **s) { + byte *d = *s,b; + uint32 time = 0; + do { + b = *d++; + time = (time<<7) | (b&0x7F); + } while (b&0x80); + *s = d; + return time; +} + +uint read_word(byte *a) { + return (a[0]<<8) + a[1]; +} + +void skip_midi_cmd(byte **song_ptr) { + byte *s, code; + + const byte num_skip[] = { + 2,2,2,2,1,1,2 + }; + + s = *song_ptr; + + code = *s++; + + if (code<0x80) { + s = NULL; + } else if (code<0xF0) { + s += num_skip[(code&0x70)>>4]; + } else { + if (code==0xF0 || code==0xF7 || code==0xFF && *s++ != 0x2F) { + s += get_delta_time(&s); + } else { + s = NULL; + } + } + *song_ptr = s; +} + +int is_note_cmd(byte **a, IsNoteCmdData *isnote) { + byte *s = *a; + byte code; + + code = *s++; + + switch(code>>4) { + case 8: /* key off */ + isnote->chan = code&0xF; + isnote->note = *s++; + isnote->vel = *s++; + *a = s; + return 1; + case 9: /* key on */ + isnote->chan = code&0xF; + isnote->note = *s++; + isnote->vel = *s++; + *a = s; + if (isnote->vel) + return 2; + return 1; + case 0xA: + case 0xB: + case 0xE: + s++; + case 0xC: + case 0xD: + s++; + break; + case 0xF: + if (code==0xF0 || code==0xF7 || code==0xFF && *s++ != 0x2F) { + s += get_delta_time(&s); + break; + } + return -1; + default: + return -1; + } + *a = s; + return 0; +} + +/**********************************************************************/ + +byte *SoundEngine::findTag(int sound, char *tag, int index) { + byte *ptr = _base_sounds[sound]; + int32 size,pos; + + if (ptr==NULL) { + debug(1, "SoundEngine::findTag completely failed finding sound %d", sound); + return 0; + } + + ptr += 8; + size = READ_BE_UINT32_UNALIGNED(ptr); + ptr += 4; + + pos = 0; + while (pos < size) { + if (!memcmp(ptr + pos, tag, 4) && !index--) + return ptr + pos + 8; + pos += READ_BE_UINT32_UNALIGNED(ptr + pos + 4) + 8; + } + debug(1, "SoundEngine::findTag failed finding sound %d", sound); + return NULL; + +} + +bool SoundEngine::start_sound(int sound) { + Player *player; + void *mdhd; + + mdhd = findTag(sound, MDHD_TAG, 0); + if (!mdhd) + return false; + + player = allocate_player(128); + if (!player) + return false; + + player->clear(); + return player->start_sound(sound); +} + + +Player *SoundEngine::allocate_player(byte priority) { + Player *player = _players, *best = NULL; + int i; + byte bestpri = 255; + + for (i=ARRAYSIZE(_players); i!=0; i--, player++) { + if (!player->_active) + return player; + if (player->_priority < bestpri) { + best = player; + bestpri = player->_priority; + } + } + + if (bestpri < priority) + return best; + + debug(1, "Denying player request"); + return NULL; +} + +void SoundEngine::init_players() { + Player *player = _players; + int i; + + for (i=ARRAYSIZE(_players); i!=0; i--, player++) { + player->_active = false; + player->_se = this; + } +} + +void SoundEngine::init_sustaining_notes() { + SustainingNotes *next = NULL, *sn = _sustaining_notes; + int i; + + _sustain_notes_used = NULL; + _sustain_notes_head = NULL; + + for (i=ARRAYSIZE(_sustaining_notes);i!=0; i--,sn++) { + sn->next = next; + next = sn; + } + _sustain_notes_free = next; +} + +void SoundEngine::init_volume_fader() { + VolumeFader *vf = _volume_fader; + int i; + + for (i=ARRAYSIZE(_volume_fader); i!=0; i--, vf++) + vf->initialize(); + + _active_volume_faders = false; +} + +void SoundEngine::init_parts() { + Part *part; + int i; + MidiChannel *mc; + + for (i=ARRAYSIZE(_parts),part=_parts; i!=0; i--, part++) + part->init(this); + + for(i=0,mc=_midi_channels; i!=ARRAYSIZE(_midi_channels);i++,mc++) + mc->init(i); +} + +int SoundEngine::stop_sound(int sound) { + Player *player = _players; + int i; + int r = -1; + + for (i=ARRAYSIZE(_players); i!=0; i--,player++) { + if (player->_active && player->_id==sound) { + player->clear(); + r = 0; + } + } + return r; +} + +int SoundEngine::stop_all_sounds() { + Player *player = _players; + int i; + + for (i=ARRAYSIZE(_players); i!=0; i--,player++) { + if (player->_active) + player->clear(); + } + return 0; +} + +void SoundEngine::on_timer() { + if (_locked || _paused) + return; + lock(); + sequencer_timers(); + expire_sustain_notes(); + expire_volume_faders(); + unlock(); +} + +void SoundEngine::sequencer_timers() { + Player *player = _players; + int i; + + for (i=ARRAYSIZE(_players); i!=0; i--,player++) { + if (player->_active) + player->sequencer_timer(); + } +} + +void Player::sequencer_timer() { + byte *mtrk; + uint32 counter; + byte *song_ptr; + + counter = _timer_counter + _timer_speed; + _timer_counter = counter&0xFFFF; + _cur_pos += counter>>16; + _tick_index += counter>>16; + + if (_tick_index>=_ticks_per_beat) { + _beat_index += _tick_index/_ticks_per_beat; + _tick_index %= _ticks_per_beat; + } + if (_loop_counter && _beat_index >= _loop_from_beat && _tick_index >= _loop_from_tick) { + _loop_counter--; + jump(_track_index, _loop_to_beat, _loop_to_tick); + } + if (_next_pos <= _cur_pos) { + mtrk = _se->findTag(_song_index, "MTrk", _track_index); + if (!mtrk) { + warning("Sound %d was unloaded while active", _song_index); + clear(); + } else { + song_ptr = mtrk + _song_offset; + _abort = false; + + while (_next_pos <= _cur_pos) { + song_ptr = parse_midi(song_ptr); + if (!song_ptr || _abort) + return; + _next_pos += get_delta_time(&song_ptr); + } + _song_offset = song_ptr - mtrk; + } + } +} + +void SoundEngine::handle_marker(uint id, byte data) { + uint16 *p; + uint pos; + int a; + + pos = _queue_end; + if (pos == _queue_pos) + return; + + if (_queue_adding && _queue_sound==id && data==_queue_marker) + return; + + p = _cmd_queue[pos].array; + + if (p[0] != TRIGGER_ID || p[1] != id || p[2] != data) + return; + + _trigger_count--; + _queue_cleared = false; + do { + pos = (++pos) & (ARRAYSIZE(_cmd_queue)-1); + if (_queue_pos == pos) + break; + p = _cmd_queue[pos].array; + if (*p++ != COMMAND_ID) + break; + _queue_end = pos; + + do_command(p[0],p[1],p[2],p[3],p[4],p[5],p[6],0); + + if (_queue_cleared) + return; + pos = _queue_end; + } while (1); + + _queue_end = pos; +} + +int SoundEngine::get_channel_volume(uint a) { + if (a<8) + return _channel_volume_eff[a]; + return _master_volume; +} + +Part *SoundEngine::allocate_part(byte pri) { + Part *part,*best=NULL; + int i; + + for (i=ARRAYSIZE(_parts),part=_parts; i!=0; i--,part++) { + if (!part->_player) + return part; + if (pri >= part->_pri_eff) { + pri = part->_pri_eff; + best = part; + } + } + + if (best) + best->uninit(); + else + debug(1, "Denying part request"); + return best; +} + +void SoundEngine::expire_sustain_notes() { + SustainingNotes *sn,*next; + Player *player; + uint32 counter; + + for(sn=_sustain_notes_head; sn; sn = next) { + next = sn->next; + player = sn->player; + + counter = sn->counter + player->_timer_speed; + sn->pos += counter>>16; + sn->counter = counter & 0xFFFF; + + if (sn->pos >= sn->off_pos) { + player->key_off(sn->chan, sn->note); + + /* Unlink the node */ + if (next) + next->prev = sn->prev; + if (sn->prev) + sn->prev->next = next; + else + _sustain_notes_head = next; + + /* And put it in the free list */ + sn->next = _sustain_notes_free; + _sustain_notes_free = sn; + } + } +} + +void SoundEngine::expire_volume_faders() { + VolumeFader *vf; + int i; + + if (++_volume_fader_counter & 7) + return; + + if (!_active_volume_faders) + return; + + _active_volume_faders = false; + vf = _volume_fader; + for (i=ARRAYSIZE(_volume_fader); i!=0; i--,vf++) { + if (vf->active) { + _active_volume_faders = true; + vf->on_timer(); + } + } +} + +void VolumeFader::on_timer() { + byte newvol; + + newvol = curvol + speed_hi; + speed_lo_counter += speed_lo; + + if (speed_lo_counter >= speed_lo_max) { + speed_lo_counter -= speed_lo_max; + newvol += direction; + } + + if (curvol!=newvol) { + if (!newvol) { + player->clear(); + active = false; + return; + } + curvol = newvol; + player->set_vol(newvol); + } + + if (!--num_steps) { + active = false; + } +} + +int SoundEngine::get_sound_status(int sound) { + int i; + Player *player; + + for (i=ARRAYSIZE(_players),player=_players; i!=0; i--,player++) { + if (player->_active && player->_id==(uint16)sound) + return 1; + } + return get_queue_sound_status(sound); +} + +int SoundEngine::get_queue_sound_status(int sound) { + uint16 *a; + int i,j; + + j = _queue_pos; + i = _queue_end; + + while (i!=j) { + a = _cmd_queue[i].array; + if (a[0] == COMMAND_ID && a[1] == 8 && a[2] == (uint16)sound) + return 2; + i = (++i) & (ARRAYSIZE(_cmd_queue)-1); + } + return 0; +} + +int SoundEngine::set_volchan(int sound, int volchan) { + int r; + int i; + int num; + Player *player,*best,*sameid; + + r = get_volchan_entry(volchan); + if (r==-1) + return -1; + + if (r >= 8) { + for(i=ARRAYSIZE(_players),player=_players; i!=0; i--,player++) { + if (player->_active && player->_id==(uint16)sound && player->_vol_chan!=(uint16)volchan) { + player->_vol_chan = volchan; + player->set_vol(player->_volume); + return 0; + } + } + return -1; + } else { + best = NULL; + num = 0; + sameid = NULL; + for(i=ARRAYSIZE(_players),player=_players; i!=0; i--,player++) { + if (player->_active) { + if (player->_vol_chan==(uint16)volchan) { + num++; + if (!best || player->_priority <= best->_priority) + best = player; + } else if (player->_id == (uint16)sound) { + sameid = player; + } + } + } + if (sameid==NULL) + return -1; + if (num >= r) + best->clear(); + player->_vol_chan = volchan; + player->set_vol(player->_volume); + return 0; + } +} + +int SoundEngine::clear_queue() { + _queue_adding = false; + _queue_cleared = true; + _queue_pos = 0; + _queue_end = 0; + _trigger_count = 0; + return 0; +} + +int SoundEngine::enqueue_command(int a, int b, int c, int d, int e, int f, int g) { + uint16 *p; + uint32 r; + uint i; + + i = _queue_pos; + + if (i == _queue_end) + return -1; + + if (a==-1) { + _queue_adding = false; + _trigger_count++; + return 0; + } + + p = _cmd_queue[_queue_pos].array; + p[0] = COMMAND_ID; + p[1] = a; + p[2] = b; + p[3] = c; + p[4] = d; + p[5] = e; + p[6] = f; + p[7] = g; + + i = (++i) & (ARRAYSIZE(_cmd_queue)-1); + + if (_queue_end!=i) { + _queue_pos = i; + return 0; + } else { + _queue_pos = (--i) & (ARRAYSIZE(_cmd_queue)-1); + return -1; + } +} + +int SoundEngine::query_queue(int param) { + switch(param) { + case 0: /* get trigger count */ + return _trigger_count; + case 1: /* get trigger type */ + if (_queue_end==_queue_pos) + return -1; + return _cmd_queue[_queue_end].array[1]; + case 2: /* get trigger sound */ + if (_queue_end==_queue_pos) + return 0xFF; + return _cmd_queue[_queue_end].array[2]; + default: + return -1; + } +} + +int SoundEngine::set_master_volume(uint vol) { + int i; + if (vol > 127) + return -1; + _master_volume = vol; + for (i=0; i!=8; i++) + _channel_volume_eff[i] = (_channel_volume[i]+1) * vol >> 7; + update_volumes(); + return 0; +} + +int SoundEngine::get_master_volume() { + return _master_volume; +} + +int SoundEngine::terminate() { + return 0; + /* not implemented */ +} + + +int SoundEngine::enqueue_trigger(int sound, int marker) { + uint16 *p; + uint pos; + + pos = _queue_pos; + + p = _cmd_queue[pos].array; + p[0] = TRIGGER_ID; + p[1] = sound; + p[2] = marker; + + pos = (++pos) & (ARRAYSIZE(_cmd_queue)-1); + if (_queue_end==pos) { + _queue_pos = (--pos) & (ARRAYSIZE(_cmd_queue)-1); + return -1; + } + + _queue_pos = pos; + _queue_adding = true; + _queue_sound = sound; + _queue_marker = marker; + return 0; +} + +int32 SoundEngine::do_command(int a, int b, int c, int d, int e, int f, int g, int h) { + byte cmd = a&0xFF; + byte param = a>>8; + Player *player; + + if (!_initialized && (cmd || param)) + return -1; + + if (param==0) { + switch(cmd) { + case 6: + return set_master_volume(b); + case 7: + return get_master_volume(); + case 8: + return start_sound(b) ? 0 : -1; + case 9: + return stop_sound(b); + case 11: + return stop_all_sounds(); + case 13: + return get_sound_status(b); + case 16: + return set_volchan(b,c); + case 17: + return set_channel_volume(b,c); + case 18: + return set_volchan_entry(b,c); + default: + warning("SoundEngine::do_command invalid command %d", cmd); + } + } else if (param==1) { + + if ( (1<<cmd) & (0x783FFF)) { + player = get_player_byid(b); + if (!player) + return -1; + } + + if ( (1<<cmd) & (1<<11 | 1<<22) ) { + assert(c>=0 && c<=15); + player = (Player*)player->get_part(c); + if(!player) + return -1; + } + + switch(cmd) { + case 0: + return player->get_param(c, d); + case 1: + player->set_priority(c); + return 0; + case 2: + return player->set_vol(c); + case 3: + player->set_pan(c); + return 0; + case 4: + return player->set_transpose(c, d); + case 5: + player->set_detune(c); + return 0; + case 6: + player->set_speed(c); + return 0; + case 7: + return player->jump(c,d,e) ? 0 : -1; + case 8: + return player->scan(c,d,e); + case 9: + return player->set_loop(c,d,e,f,g) ? 0 : -1; + case 10: + player->clear_loop(); + return 0; + case 11: + ((Part*)player)->set_onoff(d!=0); + return 0; + case 12: + return player->_hook.set(c, d, e); + case 13: + return player->fade_vol(c,d); + case 14: + return enqueue_trigger(b,c); + case 15: + return enqueue_command(b,c,d,e,f,g,h); + case 16: + return clear_queue(); + case 19: + return player->get_param(c,d); + case 20: + return player->_hook.set(c,d,e); + case 21: + return -1; + case 22: + ((Part*)player)->set_vol(d); + return 0; + case 23: + return query_queue(b); + case 24: + return 0; + default: + warning("SoundEngine::do_command default midi command %d", cmd); + return -1; + } + } + + return -1; +} + +int SoundEngine::set_channel_volume(uint chan, uint vol) { + if (chan>=8 || vol>127) + return -1; + + _channel_volume[chan] = vol; + _channel_volume_eff[chan] = _master_volume * (vol+1) >> 7; + update_volumes(); + return 0; +} + +void SoundEngine::update_volumes() { + Player *player; + int i; + + for(i=ARRAYSIZE(_players),player=_players; i!=0; i--,player++) { + if (player->_active) + player->set_vol(player->_volume); + } +} + +int SoundEngine::set_volchan_entry(uint a, uint b) { + if (a >= 8) + return -1; + _volchan_table[a] = b; + return 0; +} + +int HookDatas::query_param(int param, byte chan) { + switch(param) { + case 18: + return _jump; + case 19: + return _transpose; + case 20: + return _part_onoff[chan]; + case 21: + return _part_volume[chan]; + case 22: + return _part_program[chan]; + case 23: + return _part_transpose[chan]; + default: + return -1; + } +} + +int HookDatas::set(byte cls, byte value, byte chan) { + switch(cls) { + case 0: + _jump = value; + break; + case 1: + _transpose = value; + break; + case 2: + if (chan<16) + _part_onoff[chan] = value; + else if (chan==16) + memset(_part_onoff, value, 16); + break; + case 3: + if (chan<16) + _part_volume[chan] = value; + else if (chan==16) + memset(_part_volume, value, 16); + break; + case 4: + if (chan<16) + _part_program[chan] = value; + else if (chan==16) + memset(_part_program, value, 16); + break; + case 5: + if (chan<16) + _part_transpose[chan] = value; + else if (chan==16) + memset(_part_transpose, value, 16); + break; + default: + return -1; + } + return 0; +} + + +VolumeFader *SoundEngine::allocate_volume_fader() { + VolumeFader *vf; + int i; + + vf = _volume_fader; + for(i=ARRAYSIZE(_volume_fader); vf->active; ) { + vf++; + if (!--i) + return NULL; + } + + vf->active = true; + _active_volume_faders = true; + return vf; +} + +Player *SoundEngine::get_player_byid(int id) { + int i; + Player *player,*found=NULL; + + for(i=ARRAYSIZE(_players),player=_players; i!=0; i--,player++) { + if (player->_active && player->_id==(uint16)id) { + if(found) + return NULL; + found = player; + } + } + return found; +} + +int SoundEngine::get_volchan_entry(uint a) { + if (a<8) + return _volchan_table[a]; + return -1; +} + +int SoundEngine::initialize(Scumm *scumm) { + int i; + if (_initialized) + return -1; + + _s = scumm; + + _master_volume = 127; + for (i=0; i!=8; i++) + _channel_volume[i] = _channel_volume_eff[i] = _volchan_table[i] = 127; + + init_players(); + init_sustaining_notes(); + init_volume_fader(); + init_queue(); + init_parts(); + midiInit(); + + _initialized = true; + + return 0; +} + +void SoundEngine::init_queue() { + _queue_adding = false; + _queue_pos = 0; + _queue_end = 0; + _trigger_count = 0; +} + +void SoundEngine::pause(bool paused) { + Part *part; + int i; + MidiChannel *mc; + + lock(); + + for (i=ARRAYSIZE(_parts),part=_parts; i!=0; i--, part++) { + if (part->_player) { + if (paused) { + part->_vol_eff = 0; + } else { + part->set_vol(part->_vol); + } + part->vol_changed(); + } + } + + _paused = paused; + + unlock(); +} + + +/*************************************************************************/ + +int Player::fade_vol(byte vol, int time) { + VolumeFader *vf; + int i; + + cancel_volume_fade(); + if (time==0) { + set_vol(vol); + return 0; + } + + vf = _se->allocate_volume_fader(); + if (vf==NULL) + return -1; + + vf->player = this; + vf->num_steps = vf->speed_lo_max = time; + vf->curvol = _volume; + i = (vol - vf->curvol); + vf->speed_hi = i / time; + if (i<0) { + i = -i; + vf->direction = -1; + } else { + vf->direction = 1; + } + vf->speed_lo = i % time; + vf->speed_lo_counter = 0; + return 0; +} + +void Player::clear() { + uninit_seq(); + cancel_volume_fade(); + uninit_parts(); + _active = false; + _ticks_per_beat = TICKS_PER_BEAT; +} + +bool Player::start_sound(int sound) { + void *mdhd; + + mdhd = _se->findTag(sound, MDHD_TAG, 0); + if (mdhd==NULL) + return false; + + _parts = NULL; + _active = true; + _id = sound; + _priority = 0x80; + _volume = 0x7F; + _vol_chan = 0xFFFF; + + _vol_eff = (_se->get_channel_volume(0xFFFF)<<7)>>7; + + _pan = 0; + _transpose = 0; + _detune = 0; + + hook_clear(); + if (start_seq_sound(sound) != 0) { + _active = false; + return false; + } + return true; +} + +void Player::hook_clear() { + memset(&_hook, 0, sizeof(_hook)); +} + +int Player::start_seq_sound(int sound) { + byte *ptr, *track_ptr; + + _song_index = sound; + _timer_counter = 0; + _loop_to_beat = 1; + _loop_from_beat = 1; + _track_index = 0; + _loop_counter = 0; + _loop_to_tick = 0; + _loop_from_tick = 0; + + set_tempo(500000); + set_speed(128); + ptr = _se->findTag(sound, "MTrk", _track_index); + if (ptr==NULL) + return -1; + + track_ptr = ptr; + _cur_pos = _next_pos = get_delta_time(&track_ptr); + _song_offset = track_ptr - ptr; + + _tick_index = _cur_pos; + _beat_index = 1; + + if (_tick_index >= _ticks_per_beat) { + _beat_index += _tick_index/_ticks_per_beat; + _tick_index %= _ticks_per_beat; + } + + return 0; +} + +void Player::set_tempo(uint32 b) { + uint32 i,j; + + i = TEMPO_BASE; + j = _tempo = b; + + while (i&0xFFFF0000 || j&0xFFFF0000) { i>>=1; j>>=1; } + + _tempo_eff = (i<<16) / j; + + set_speed(_speed); +} + +void Player::cancel_volume_fade() { + VolumeFader *vf = _se->_volume_fader; + int i; + + for (i=0; i<8; i++,vf++) { + if (vf->active && vf->player==this) + vf->active = false; + } +} + +void Player::uninit_parts() { + if (_parts && _parts->_player != this) + error("asd"); + while(_parts) + _parts->uninit(); +} + +void Player::uninit_seq() { + _abort = true; +} + +void Player::set_speed(byte speed) { + _speed = speed; + _timer_speed = (_tempo_eff * speed >> 7); +} + +byte *Player::parse_midi(byte *s) { + byte cmd,chan,note,velocity,control; + uint value; + Part *part; + + cmd = *s++; + + chan = cmd&0xF; + + switch(cmd>>4) { + case 0x8: /* key off */ + note = *s++; + if (!_scanning) { + key_off(chan, note); + } else { + clear_active_note(chan, note); + } + s++; /* skip velocity */ + break; + + case 0x9: /* key on */ + note = *s++; + velocity = *s++; + if (velocity) { + if (!_scanning) + key_on(chan, note,velocity); + else + set_active_note(chan,note); + } else { + if (!_scanning) + key_off(chan, note); + else + clear_active_note(chan,note); + } + break; + + case 0xA: /* aftertouch */ + s += 2; + break; + + case 0xB: /* control change */ + control = *s++; + value = *s++; + part = get_part(chan); + if (!part) + break; + + switch(control) { + case 1: /* modulation wheel */ + part->set_modwheel(value); + break; + case 7: /* volume */ + part->set_vol(value); + break; + case 10: /* pan position */ + part->set_pan(value - 0x40); + break; + case 16: /* pitchbend factor */ + part->set_pitchbend_factor(value); + break; + case 17: /* gp slider 2 */ + part->set_detune(value - 0x40); + break; + case 18: /* gp slider 3 */ + part->set_pri(value - 0x40, true); + break; + case 64: /* hold pedal */ + part->set_pedal(value!=0); + break; + case 91: /* effects level */ + part->set_effect_level(value); + break; + case 93: /* chorus */ + part->set_chorus(value); + break; + default: + warning("parse_midi: invalid control %d", control); + } + break; + + case 0xC: /* program change */ + value = *s++; + part = get_part(chan); + if (part) + part->set_program(value); + break; + + case 0xD: /* channel pressure */ + s++; + break; + + case 0xE: /* pitch bend */ + part = get_part(chan); + if (part) + part->set_pitchbend(((s[1]-0x40)<<7)|s[0]); + s+=2; + break; + + case 0xF: + if (chan==0) { + uint size = get_delta_time(&s); + if (*s==SYSEX_ID) + parse_sysex(s,size); + s += size; + } else if (chan==0xF) { + cmd = *s++; + if (cmd==47) + goto Error; /* end of song */ + if (cmd==81) { + set_tempo((s[1]<<16) | (s[2]<<8) | s[3]); + s+=4; + break; + } + s += get_delta_time(&s); + } else if (chan==0x7) { + s += get_delta_time(&s); + } else { + goto Error; + } + break; + + default: +Error:; + if(!_scanning) + clear(); + return NULL; + } + return s; +} + +void Player::parse_sysex(byte *p, uint len) { + byte code; + byte a; + uint b; + byte buf[128]; + Part *part; + + /* too big? */ + if (len>=sizeof(buf)*2) + return; + + /* skip sysex manufacturer */ + p++; + len -= 2; + + switch(code=*p++) { + case 16: + a = *p++ & 0x0F; + if (HARDWARE_TYPE != *p++) + break; + decode_sysex_bytes(p, buf, len - 3); + part = get_part(a); + if(part) + part->set_instrument(buf); + break; + + case 17: + p++; + if (HARDWARE_TYPE != *p++) + break; + a=*p++; + decode_sysex_bytes(p, buf, len - 4); + _se->set_instrument(a, buf); + break; + + case 33: /* param adjust */ + a = *p++ & 0x0F; + if (HARDWARE_TYPE != *p++) + break; + decode_sysex_bytes(p, buf, len - 3); + part = get_part(a); + if (part) + part->set_chan_param(read_word(buf),read_word(buf+2)); + break; + + case 48: /* hook - jump */ + if (_scanning) + break; + decode_sysex_bytes(p+1,buf,len-2); + maybe_jump(buf); + break; + + case 49: /* hook - global transpose */ + decode_sysex_bytes(p+1,buf,len-2); + maybe_set_transpose(buf); + break; + + case 50: /* hook - part on/off */ + buf[0] = *p++ & 0x0F; + decode_sysex_bytes(p,buf+1,len-2); + maybe_part_onoff(buf); + break; + + case 51: /* hook - set volume */ + buf[0] = *p++ & 0x0F; + decode_sysex_bytes(p,buf+1,len-2); + maybe_set_volume(buf); + break; + + case 52: /* hook - set program */ + buf[0] = *p++ & 0x0F; + decode_sysex_bytes(p,buf+1,len-2); + maybe_set_program(buf); + break; + + case 53: /* hook - set transpose */ + buf[0] = *p++ & 0x0F; + decode_sysex_bytes(p,buf+1,len-2); + maybe_set_transpose_part(buf); + break; + + case 64: /* marker */ + p++; + len -= 2; + while (len--) { + _se->handle_marker(_id, *p++); + } + break; + + case 80: /* loop */ + decode_sysex_bytes(p+1,buf,len-2); + set_loop( + read_word(buf), + read_word(buf+2), + read_word(buf+4), + read_word(buf+6), + read_word(buf+8) + ); + break; + + case 81: /* end loop */ + clear_loop(); + break; + + case 96: /* set instrument */ + part = get_part(p[0] & 0x0F); + b = (p[1]&0x0F)<<12 | (p[2]&0x0F)<<8 | (p[4]&0x0F)<<4 | (p[4]&0x0F); + if(part) + part->set_instrument(b); + break; + + default: + debug(6,"unknown sysex %d", code); + } +} + +void Player::decode_sysex_bytes(byte *src, byte *dst, int len) { + while(len>=0) { + *dst++ = (src[0]<<4)|(src[1]&0xF); + src += 2; + len -= 2; + } +} + +void Player::maybe_jump(byte *data) { + byte cmd; + + cmd = data[0]; + + /* is this the hook i'm waiting for? */ + if (cmd && _hook._jump!=cmd) + return; + + /* reset hook? */ + if(cmd!=0 && cmd<0x80) + _hook._jump = 0; + + jump(read_word(data+1), read_word(data+3), read_word(data+5)); +} + +void Player::maybe_set_transpose(byte *data) { + byte cmd; + + cmd = data[0]; + + /* is this the hook i'm waiting for? */ + if (cmd && _hook._transpose!=cmd) + return; + + /* reset hook? */ + if(cmd!=0 && cmd<0x80) + _hook._transpose = 0; + + set_transpose(data[1], (int8)data[2]); +} + +void Player::maybe_part_onoff(byte *data) { + byte cmd,*p; + uint chan; + Part *part; + + cmd = data[1]; + chan = data[0]; + + p = &_hook._part_onoff[chan]; + + /* is this the hook i'm waiting for? */ + if (cmd && *p!=cmd) + return; + + if (cmd!=0 && cmd<0x80) + *p = 0; + + part = get_part(chan); + if (part) + part->set_onoff(data[2]!=0); +} + +void Player::maybe_set_volume(byte *data) { + byte cmd; + byte *p; + uint chan; + Part *part; + + cmd = data[1]; + chan = data[0]; + + p = &_hook._part_volume[chan]; + + /* is this the hook i'm waiting for? */ + if (cmd && *p!=cmd) + return; + + /* reset hook? */ + if (cmd!=0 && cmd<0x80) + *p = 0; + + part = get_part(chan); + if (part) + part->set_vol(data[2]); +} + +void Player::maybe_set_program(byte *data) { + byte cmd; + byte *p; + uint chan; + Part *part; + + cmd = data[1]; + chan = data[0]; + + /* is this the hook i'm waiting for? */ + p = &_hook._part_program[chan]; + + if (cmd && *p!=cmd) + return; + + if (cmd!=0 && cmd<0x80) + *p = 0; + + part = get_part(chan); + if (part) + part->set_program(data[2]); +} + +void Player::maybe_set_transpose_part(byte *data) { + byte cmd; + byte *p; + uint chan; + + cmd = data[1]; + chan = data[0]; + + /* is this the hook i'm waiting for? */ + p = &_hook._part_transpose[chan]; + + if (cmd && *p!=cmd) + return; + + /* reset hook? */ + if (cmd!=0 && cmd<0x80) + *p = 0; + + part_set_transpose(chan, data[2], (int8)data[3]); +} + +int Player::set_transpose(byte relative, int b) { + Part *part; + + if (b>24 || b<-24 || relative>1) + return -1; + if (relative) + b = transpose_clamp(_transpose + b, -7, 7); + + _transpose = b; + + for(part=_parts; part; part=part->_next) { + part->set_transpose(part->_transpose); + } + + return 0; +} + +void Player::clear_active_notes() { + memset(_se->_active_notes, 0, sizeof(_se->_active_notes)); +} + +void Player::clear_active_note(int chan, byte note) { + _se->_active_notes[note] &= ~(1<<chan); +} + +void Player::set_active_note(int chan, byte note) { + _se->_active_notes[note] |= (1<<chan); +} + +void Player::part_set_transpose(uint8 chan, byte relative, int8 b) { + Part *part; + + if (b>24 || b<-24) + return; + + part = get_part(chan); + if (!part) + return; + if (relative) + b = transpose_clamp(b + part->_transpose, -7, 7); + part->set_transpose(b); +} + +void Player::key_on(uint8 chan, uint8 note, uint8 velocity) { + Part *part; + + part = get_part(chan); + if (!part || !part->_on) + return; + + part->key_on(note, velocity); +} + +void Player::key_off(uint8 chan, uint8 note) { + Part *part; + + for(part=_parts; part; part = part->_next) { + if (part->_chan==(byte)chan && part->_on) + part->key_off(note); + } +} + +bool Player::jump(uint track, uint beat, uint tick) { + byte *mtrk, *cur_mtrk, *scanpos; + uint32 topos,curpos,track_offs; + + if (!_active) + return false; + + mtrk = _se->findTag(_song_index, "MTrk", track); + if (!mtrk) + return false; + + cur_mtrk = _se->findTag(_song_index,"MTrk", _track_index); + if (!cur_mtrk) + return false; + + _se->lock(); + + if (beat==0) + beat=1; + + topos = (beat-1) * _ticks_per_beat + tick; + + if (track == _track_index && topos >= _cur_pos) { + scanpos = _song_offset + mtrk; + curpos = _next_pos; + } else { + scanpos = mtrk; + curpos = get_delta_time(&scanpos); + } + + while (curpos < topos) { + skip_midi_cmd(&scanpos); + if (!scanpos) { + _se->unlock(); + return false; + } + curpos += get_delta_time(&scanpos); + } + + track_offs = scanpos - mtrk; + + turn_off_pedals(); + + find_sustaining_notes(cur_mtrk+_song_offset, mtrk+track_offs, curpos-topos); + + _beat_index = beat; + _tick_index = tick; + _cur_pos = topos; + _next_pos = curpos; + _timer_counter = 0; + _song_offset = track_offs; + if (track != _track_index) { + _track_index = track; + _loop_counter = 0; + } + _abort = true; + _se->unlock(); + return true; +} + +bool Player::set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick) { + if (tobeat+1 >= frombeat) + return false; + + if (tobeat==0) + tobeat=1; + + _loop_counter = 0; /* because of possible interrupts */ + _loop_to_beat = tobeat; + _loop_to_tick = totick; + _loop_from_beat = frombeat; + _loop_from_tick = fromtick; + _loop_counter = count; + + return true; +} + +void Player::clear_loop() { + _loop_counter = 0; +} + +void Player::turn_off_pedals() { + Part *part; + + for(part=_parts; part; part = part->_next) { + if (part->_pedal) + part->set_pedal(false); + } +} + +void Player::find_sustaining_notes(byte *a, byte *b, uint32 l) { + uint32 pos; + uint16 mask; + uint16 *bitlist_ptr; + SustainingNotes *sn,*next; + IsNoteCmdData isnote; + int j; + uint num_active; + uint max_off_pos; + + num_active = update_actives(); + + /* pos contains number of ticks since current position */ + pos = _next_pos - _cur_pos; + if ((int32)pos<0) + pos = 0; + + /* locate the positions where the notes are turned off. + * remember each note that was turned off + */ + while (num_active != 0) { + /* is note off? */ + j = is_note_cmd(&a,&isnote); + if (j==-1) + break; + if (j == 1) { + mask = 1<<isnote.chan; + bitlist_ptr = _se->_active_notes + isnote.note; + if (*bitlist_ptr & mask) { + *bitlist_ptr &= ~mask; + num_active--; + /* Get a node from the free list */ + if ((sn=_se->_sustain_notes_free) == NULL) + return; + _se->_sustain_notes_free = sn->next; + + /* Insert it in the beginning of the used list */ + sn->next = _se->_sustain_notes_used; + _se->_sustain_notes_used = sn; + sn->prev = NULL; + if (sn->next) + sn->next->prev = sn; + + sn->note = isnote.note; + sn->chan = isnote.chan; + sn->player = this; + sn->off_pos = pos; + sn->pos = 0; + sn->counter = 0; + } + } + pos += get_delta_time(&a); + } + + /* find the maximum position where a note was turned off */ + max_off_pos = 0; + for(sn=_se->_sustain_notes_used; sn; sn = sn->next) { + _se->_active_notes[sn->note] |= (1<<sn->chan); + if (sn->off_pos > max_off_pos) { + max_off_pos = sn->off_pos; + } + } + + /* locate positions where notes are turned on */ + pos = l; + while (pos < max_off_pos) { + j = is_note_cmd(&b,&isnote); + if (j==-1) + break; + if (j == 2) { + mask = 1<<isnote.chan; + bitlist_ptr = _se->_active_notes + isnote.note; + + if (*bitlist_ptr&mask) { + sn = _se->_sustain_notes_used; + while (sn) { + next = sn->next; + if (sn->note==isnote.note && sn->chan==isnote.chan && pos < sn->off_pos) { + *bitlist_ptr &= ~mask; + /* Unlink from the sustain list */ + if (next) + next->prev = sn->prev; + if (sn->prev) + sn->prev->next = next; + else + _se->_sustain_notes_used = next; + /* Insert into the free list */ + sn->next = _se->_sustain_notes_free; + _se->_sustain_notes_free = sn; + } + sn = next; + } + } + } + pos += get_delta_time(&b); + } + + /* Concatenate head and used list */ + if (!_se->_sustain_notes_head) { + _se->_sustain_notes_head = _se->_sustain_notes_used; + _se->_sustain_notes_used = NULL; + return; + } + sn = _se->_sustain_notes_head; + while (sn->next) sn = sn->next; + sn->next = _se->_sustain_notes_used; + _se->_sustain_notes_used = NULL; + if (sn->next) + sn->next->prev = sn; +} + +Part *Player::get_part(uint8 chan) { + Part *part; + + part = _parts; + while (part) { + if (part->_chan == chan) + return part; + part = part->_next; + } + + part = _se->allocate_part(_priority); + if (!part) { + warning("no parts available"); + return NULL; + } + + part->_chan = chan; + part->setup(this); + + return part; +} + +uint Player::update_actives() { + Part *part; + MidiChannel *mc; + uint16 *active; + int count = 0; + + clear_active_notes(); + active = _se->_active_notes; + for(part=_parts; part; part = part->_next) { + if (part->_mc) + count += part->update_actives(active); + } + return count; +} + +void Player::set_priority(int pri) { + Part *part; + + _priority = pri; + for(part=_parts; part; part = part->_next) { + part->set_pri(part->_pri, false); + } + _se->adjust_priorities(); +} + +void Player::set_pan(int pan) { + Part *part; + + _pan = pan; + for(part=_parts; part; part = part->_next) { + part->set_pan(part->_pan); + } +} + +void Player::set_detune(int detune) { + Part *part; + + _detune = detune; + for(part=_parts; part; part = part->_next) { + part->set_detune(part->_detune); + } +} + +int Player::scan(uint totrack, uint tobeat, uint totick) { + byte *mtrk,*scanptr; + uint32 curpos,topos; + uint32 pos; + + assert(totrack>=0 && tobeat>=0 && totick>=0); + + if (!_active) + return -1; + + mtrk = _se->findTag(_song_index, "MTrk", totrack); + if (!mtrk) + return -1; + + _se->lock(); + if (tobeat==0) + tobeat++; + + silence_parts(); + clear_active_notes(); + scanptr = mtrk; + curpos = get_delta_time(&scanptr); + _scanning=true; + + topos = (tobeat-1) * _ticks_per_beat + totick; + + while (curpos < topos) { + scanptr = parse_midi(scanptr); + if (!scanptr) { + _scanning=false; + _se->unlock(); + return -1; + } + curpos += get_delta_time(&scanptr); + } + pos = scanptr - mtrk; + + _scanning=false; + play_active_notes(); + _beat_index = tobeat; + _tick_index = totick; + _cur_pos = topos; + _next_pos = curpos; + _timer_counter = 0; + _song_offset = pos; + if (_track_index != totrack) { + _track_index = totrack; + _loop_counter = 0; + } + _se->unlock(); + return 0; +} + +void Player::silence_parts() { + Part *part; + + for(part=_parts; part; part = part->_next) + if (part->_mc) + part->silence(); +} + +void Player::play_active_notes() { + int i,j; + uint mask; + + for (i=0; i!=128; i++) { + mask = _se->_active_notes[i]; + for (j=0; j!=16; j++,mask>>=1) { + if (mask&1) { + key_on(j, i, 80); + } + } + } +} + +int Player::set_vol(byte vol) { + Part *part; + + if (vol > 127) + return -1; + + _volume = vol; + _vol_eff = _se->get_channel_volume(_vol_chan) * (vol + 1) >> 7; + + for(part=_parts; part; part=part->_next) { + part->set_vol(part->_vol); + } + + return 0; +} + +int Player::get_param(int param, byte chan) { + switch(param) { + case 0: + return (byte)_priority; + case 1: + return (byte)_volume; + case 2: + return (byte)_pan; + case 3: + return (byte)_transpose; + case 4: + return (byte)_detune; + case 5: + return _speed; + case 6: + return _track_index; + case 7: + return _beat_index; + case 8: + return _tick_index; + case 9: + return _loop_counter; + case 10: + return _loop_to_beat; + case 11: + return _loop_to_tick; + case 12: + return _loop_from_beat; + case 13: + return _loop_from_tick; + case 14: case 15: case 16: case 17: + return query_part_param(param, chan); + case 18: case 19: case 20: case 21: case 22: case 23: + return _hook.query_param(param, chan); + default: + return -1; + } +} + +int Player::query_part_param(int param, byte chan) { + Part *part; + + part = _parts; + while (part) { + if(part->_chan==chan) { + switch(param) { + case 14: + return part->_on; + case 15: + return part->_vol; + case 16: + return part->_program; + case 17: + return part->_transpose; + default: + return -1; + } + } + part = part->_next; + } + return 129; +} + +/*******************************************************************/ + +#define OFFS(type,item) ((int)(&((type*)0)->item)) +#define SIZE(type,item) sizeof(((type*)0)->item) +#define MKLINE(type,item,saveas) {OFFS(type,item),saveas,SIZE(type,item)} +#define MKARRAY(type,item,saveas,num) {OFFS(type,item),128|saveas,SIZE(type,item)}, {num,0,0} +#define MKEND() {0xFFFF,0xFF,0xFF} + +#define MKREF(type,item,refid) {OFFS(type,item),refid,0xFF} + +enum { + TYPE_PART = 1, + TYPE_PLAYER = 2, +}; + +int SoundEngine::saveReference(SoundEngine *me, byte type, void*ref) { + switch(type) { + case TYPE_PART: return (Part*)ref - me->_parts; + case TYPE_PLAYER: return (Player*)ref - me->_players; + default: + error("saveReference: invalid type"); + } +} + +void *SoundEngine::loadReference(SoundEngine *me, byte type, int ref) { + switch(type) { + case TYPE_PART: return &me->_parts[ref]; + case TYPE_PLAYER: return &me->_players[ref]; + default: + error("loadReference: invalid type"); + } +} + +int SoundEngine::save_or_load(Serializer *ser) { + const SaveLoadEntry mainEntries[] = { + MKLINE(SoundEngine,_queue_end, sleUint8), + MKLINE(SoundEngine,_queue_pos, sleUint8), + MKLINE(SoundEngine,_queue_sound, sleUint16), + MKLINE(SoundEngine,_queue_adding, sleByte), + MKLINE(SoundEngine,_queue_marker, sleByte), + MKLINE(SoundEngine,_queue_cleared, sleByte), + MKLINE(SoundEngine,_master_volume, sleByte), + MKLINE(SoundEngine,_trigger_count, sleUint16), + MKARRAY(SoundEngine,_channel_volume[0], sleUint16, 8), + MKARRAY(SoundEngine,_volchan_table[0], sleUint16, 8), + MKEND() + }; + + const SaveLoadEntry playerEntries[] = { + MKREF(Player,_parts,TYPE_PART), + MKLINE(Player,_active,sleByte), + MKLINE(Player,_id,sleUint16), + MKLINE(Player,_priority,sleByte), + MKLINE(Player,_volume,sleByte), + MKLINE(Player,_pan,sleInt8), + MKLINE(Player,_transpose,sleByte), + MKLINE(Player,_detune,sleInt8), + MKLINE(Player,_vol_chan,sleUint16), + MKLINE(Player,_vol_eff,sleByte), + MKLINE(Player,_speed,sleByte), + MKLINE(Player,_song_index,sleUint16), + MKLINE(Player,_track_index,sleUint16), + MKLINE(Player,_timer_counter,sleUint16), + MKLINE(Player,_loop_to_beat,sleUint16), + MKLINE(Player,_loop_from_beat,sleUint16), + MKLINE(Player,_loop_counter,sleUint16), + MKLINE(Player,_loop_to_tick,sleUint16), + MKLINE(Player,_loop_from_tick,sleUint16), + MKLINE(Player,_tempo,sleUint32), + MKLINE(Player,_cur_pos,sleUint32), + MKLINE(Player,_next_pos,sleUint32), + MKLINE(Player,_song_offset,sleUint32), + MKLINE(Player,_tick_index,sleUint16), + MKLINE(Player,_beat_index,sleUint16), + MKLINE(Player,_ticks_per_beat,sleUint16), + MKLINE(Player,_hook._jump,sleByte), + MKLINE(Player,_hook._transpose,sleByte), + MKARRAY(Player,_hook._part_onoff[0],sleByte,16), + MKARRAY(Player,_hook._part_volume[0],sleByte,16), + MKARRAY(Player,_hook._part_program[0],sleByte,16), + MKARRAY(Player,_hook._part_transpose[0],sleByte,16), + MKEND() + }; + + const SaveLoadEntry volumeFaderEntries[] = { + MKREF(VolumeFader,player,TYPE_PLAYER), + MKLINE(VolumeFader,active,sleUint8), + MKLINE(VolumeFader,curvol,sleUint8), + MKLINE(VolumeFader,speed_lo_max,sleUint16), + MKLINE(VolumeFader,num_steps,sleUint16), + MKLINE(VolumeFader,speed_hi,sleInt8), + MKLINE(VolumeFader,direction,sleInt8), + MKLINE(VolumeFader,speed_lo,sleInt8), + MKLINE(VolumeFader,speed_lo_counter,sleUint16), + MKEND() + }; + + const SaveLoadEntry partEntries[] = { + MKREF(Part,_next,TYPE_PART), + MKREF(Part,_prev,TYPE_PART), + MKREF(Part,_player,TYPE_PLAYER), + MKLINE(Part,_pitchbend,sleInt16), + MKLINE(Part,_pitchbend_factor,sleUint8), + MKLINE(Part,_transpose,sleInt8), + MKLINE(Part,_vol,sleUint8), + MKLINE(Part,_detune,sleInt8), + MKLINE(Part,_pan,sleInt8), + MKLINE(Part,_on,sleUint8), + MKLINE(Part,_modwheel,sleUint8), + MKLINE(Part,_pedal,sleUint8), + MKLINE(Part,_program,sleUint8), + MKLINE(Part,_pri,sleUint8), + MKLINE(Part,_chan,sleUint8), + MKLINE(Part,_effect_level,sleUint8), + MKLINE(Part,_chorus,sleUint8), + MKLINE(Part,_gmidi_5,sleUint8), + MKLINE(Part,_gmidi_1,sleUint8), + MKEND() + }; + + if (!ser->isSaving()) { + stop_all_sounds(); + } + + ser->_ref_me = this; + ser->_saveload_ref = ser->isSaving() ? ((void*)&saveReference) : ((void*)&loadReference); + + ser->saveLoadEntries(this, mainEntries); + ser->saveLoadArrayOf(_players, ARRAYSIZE(_players), sizeof(_players[0]), playerEntries); + ser->saveLoadArrayOf(_parts, ARRAYSIZE(_parts),sizeof(_parts[0]), partEntries); + ser->saveLoadArrayOf(_volume_fader,ARRAYSIZE(_volume_fader), + sizeof(_volume_fader[0]), volumeFaderEntries); + + if (!ser->isSaving()) { + /* Load all sounds that we need */ + int i; + fix_players_after_load(); + init_sustaining_notes(); + _active_volume_faders = true; + fix_parts_after_load(); + adjust_priorities(); + } + + return 0; +} + +void SoundEngine::fix_parts_after_load() { + Part *part; + int i; + + for (i=ARRAYSIZE(_parts),part=_parts; i!=0; i--, part++) { + if (part->_player) + part->fix_after_load(); + } +} + +void SoundEngine::fix_players_after_load() { + Player *player = _players; + int i; + + for (i=ARRAYSIZE(_players); i!=0; i--, player++) { + if (player->_active) { + player->set_tempo(player->_tempo); + _s->getResourceAddress(rtSound, player->_id); + } + } +} + +void Part::fix_after_load() { + set_transpose(_transpose); + set_vol(_vol); + set_detune(_detune); + set_pri(_pri,false); + set_pan(_pan); +} + +void Part::key_on(byte note, byte velocity) { + if (_mc) { + _mc->_actives[note>>4] |= (1<<(note&0xF)); + _se->midiNoteOn(_mc->_chan, note, velocity); + } else if (_gmidi_5) { + _se->midiVolume(SPECIAL_CHANNEL, _vol_eff); + _se->midiProgram(SPECIAL_CHANNEL, _gmidi_1); + _se->midiNoteOn(SPECIAL_CHANNEL, note, velocity); + } +} + +void Part::key_off(byte note) { + if (_mc) { + _mc->_actives[note>>4] &= ~(1<<(note&0xF)); + _se->midiNoteOff(_mc->_chan, note); + } else if (_gmidi_5) { + _se->midiNoteOff(SPECIAL_CHANNEL, note); + } +} + +void Part::init(SoundEngine *se) { + _se = se; + _player = NULL; + _next = NULL; + _prev = NULL; + _mc = NULL; +} + +void Part::setup(Player *player) { + _player = player; + + /* Insert first into player's list */ + _prev = NULL; + _next = player->_parts; + if (player->_parts) + player->_parts->_prev = this; + player->_parts = this; + + _gmidi_5 = true; + _on = true; + _pri_eff = player->_priority; + _pri = 0; + _vol = 127; + _vol_eff = player->_vol_eff; + _pan = clamp(player->_pan, -64, 63); + _transpose_eff = player->_transpose; + _transpose = 0; + _detune = 0; + _detune_eff = player->_detune; + _pitchbend_factor = 2; + _pitchbend = 0; + _effect_level = 64; + _program = 255; + _chorus = 0; + _modwheel = 0; + _gmidi_1 = 0; + _pedal = false; + _mc = NULL; +} + +void Part::uninit() { + if (!_player) + return; + off(); + + /* unlink */ + if (_next) + _next->_prev = _prev; + if (_prev) + _prev->_next = _next; + else + _player->_parts = _next; + _player = NULL; + _next = NULL; + _prev = NULL; +} + +void Part::off() { + if (_mc) { + silence(); + _mc->_part = NULL; + _mc = NULL; + } +} + +void Part::silence() { + memset(_mc->_actives, 0, sizeof(_mc->_actives)); + _se->midiSilence(_mc->_chan); +} + +void Part::set_detune(int8 detune) { + _detune_eff = clamp((_detune=detune) + _player->_detune, -128, 127); + mod_changed(); +} + +void Part::set_pitchbend(int value) { + _pitchbend = value * _pitchbend_factor >> 6; + mod_changed(); +} + +void Part::set_vol(uint8 vol) { + _vol_eff = ((_vol=vol)+1)*_player->_vol_eff >> 7; + vol_changed(); +} + +void Part::set_pri(int8 pri, bool recalc) { + _pri_eff = clamp((_pri=pri) + _player->_priority, 0, 255); + if (recalc) + _se->adjust_priorities(); +} + +void Part::set_pan(int8 pan) { + _pan_eff = clamp((_pan=pan) + _player->_pan, -64, 63); + pan_changed(); +} + +void Part::set_transpose(int8 transpose) { + _transpose_eff = transpose_clamp((_transpose=transpose) + + _player->_transpose, -12, 12); + mod_changed(); +} + +void Part::set_pedal(bool value) { + _pedal = value; + pedal_changed(); +} + +void Part::set_modwheel(uint value) { + _modwheel = value; + modwheel_changed(); +} + +void Part::set_chorus(uint chorus) { + _chorus = chorus; + chorus_changed(); +} + +void Part::set_effect_level(uint level) { + _effect_level = level; + effect_level_changed(); +} + +void Part::set_program(byte program) { + if (_program!=program || _gmidi_1!=0) { + _program = program; + _gmidi_1 = 0; + program_changed(); + if (_gmidi_5) { + _gmidi_5 = false; + _se->adjust_priorities(); + } + } +} + +void Part::set_instrument(uint b) { + _gmidi_1 = (byte)(b>>8); + _program = (byte)b; + + if (_program < 128) { + program_changed(); + if(_gmidi_5) { + _gmidi_5 = false; + _se->adjust_priorities(); + } + } +} + +void Part::set_pitchbend_factor(uint8 value) { + if (value > 12) + return; + set_pitchbend(0); + _pitchbend_factor = value; +} + +void Part::set_onoff(bool on) { + if (_on != on) { + _on = on; + if (!on) + off(); + if (!_gmidi_5) + _se->adjust_priorities(); + } +} + +void Part::mod_changed() { + if (_mc) { + _se->midiPitchBend(_mc->_chan, clamp(_pitchbend + _detune_eff + (_transpose_eff<<7), -2048, 2047)); + } +} + +void Part::vol_changed() { + if (_mc) { + _se->midiVolume(_mc->_chan, _vol_eff); + } +} + +void Part::pedal_changed() { + if (_mc) { + _se->midiPedal(_mc->_chan, _pedal); + } +} + +void Part::modwheel_changed() { + if (_mc) { + _se->midiModWheel(_mc->_chan, _modwheel); + } +} + +void Part::effect_level_changed() { + if (_mc) { + _se->midiEffectLevel(_mc->_chan, _effect_level); + } +} + +void Part::chorus_changed() { + if (_mc) { + _se->midiChorus(_mc->_chan, _effect_level); + } +} + +void Part::program_changed() { + if (_mc) { + if (_gmidi_1) { + _se->midiControl0(_mc->_chan, _gmidi_1); + _se->midiProgram(_mc->_chan, _program); + _se->midiControl0(_mc->_chan, 0); + } else { + _se->midiProgram(_mc->_chan, _program); + } + } +} + +void Part::pan_changed() { + if (_mc) { + _se->midiPan(_mc->_chan, _pan_eff); + } +} + +int Part::update_actives(uint16 *active) { + int i,j; + uint16 *act,mask,bits; + int count = 0; + + bits = 1<<_chan; + + act = _mc->_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 MidiChannel::init(byte chan) { + int i; + + _part = NULL; + _chan = chan; + + memset(_actives, 0, sizeof(_actives)); +} + +void SoundEngine::lock() { + _locked++; +} + +void SoundEngine::unlock() { + _locked--; +} + + +void SoundEngine::adjust_priorities() { + Part *part,*hipart; + int i; + byte hipri,lopri; + MidiChannel *mc,*lomc; + + while(true) { + hipri = 0; + hipart = NULL; + for(i=32,part=_parts; i; i--,part++) { + if (part->_player && part->_gmidi_5==0 && 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->vol_changed(); + hipart->pan_changed(); + hipart->mod_changed(); + hipart->modwheel_changed(); + hipart->pedal_changed(); + hipart->effect_level_changed(); + hipart->program_changed(); + } +} + +void SoundEngine::midiInit() { +#ifdef WIN32 + if (midiOutOpen((HMIDIOUT*)&_mo, MIDI_MAPPER, NULL, NULL, 0) != MMSYSERR_NOERROR) + error("midiOutOpen failed"); +#endif +} + +#ifdef WIN32 +#define MIDI_OUT(a,b) midiOutShortMsg((HMIDIOUT)(a), (b)) +#else +#define MIDI_OUT(a,b) +#endif + +void SoundEngine::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 SoundEngine::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 SoundEngine::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 SoundEngine::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 SoundEngine::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 SoundEngine::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 SoundEngine::midiControl0(byte chan, byte value) { + MIDI_OUT(_mo, value<<16 | 0<<8 | 0xB0 | chan); +} + +void SoundEngine::midiProgram(byte chan, byte program) { + MIDI_OUT(_mo, program<<8 | 0xC0 | chan); +} + +void SoundEngine::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 SoundEngine::midiNoteOn(byte chan, byte note, byte velocity) { + MIDI_OUT(_mo, velocity<<16 | note<<8 | 0x90 | chan); +} + +void SoundEngine::midiNoteOff(byte chan, byte note) { + MIDI_OUT(_mo, note<<8 | 0x80 | chan); +} + +void SoundEngine::midiSilence(byte chan) { + MIDI_OUT(_mo, (64<<8)|0xB0|chan); + MIDI_OUT(_mo, (123<<8)|0xB0|chan); +}
\ No newline at end of file diff --git a/resource.cpp b/resource.cpp index fd930af9b0..ff784e006e 100644 --- a/resource.cpp +++ b/resource.cpp @@ -551,12 +551,12 @@ byte *Scumm::getResourceAddress(int type, int index) { ensureResourceLoaded(type, index); } - setResourceCounter(type, index, 1); - ptr=(byte*)res.address[type][index]; if (!ptr) return NULL; + setResourceCounter(type, index, 1); + return ptr + sizeof(ResHeader); } diff --git a/saveload.cpp b/saveload.cpp index 49c4cdd1e9..615d89ad78 100644 --- a/saveload.cpp +++ b/saveload.cpp @@ -21,6 +21,7 @@ #include "stdafx.h" #include "scumm.h" +#include "sound.h" struct SaveGameHeader { uint32 type; @@ -29,7 +30,7 @@ struct SaveGameHeader { char name[32]; }; -#define CURRENT_VER 4 +#define CURRENT_VER 5 bool Scumm::saveState(int slot, bool compat) { char filename[256]; @@ -67,6 +68,7 @@ bool Scumm::loadState(int slot, bool compat) { SaveGameHeader hdr; Serializer ser; int sb,sh; + SoundEngine *se; makeSavegameName(filename, slot, compat); out = fopen(filename,"rb"); @@ -88,7 +90,9 @@ bool Scumm::loadState(int slot, bool compat) { } memcpy(_saveLoadName, hdr.name, sizeof(hdr.name)); - + + pauseSounds(true); + CHECK_HEAP openRoom(-1); @@ -134,6 +138,8 @@ bool Scumm::loadState(int slot, bool compat) { debug(1,"State loaded from '%s'", filename); + pauseSounds(false); + return true; } @@ -413,20 +419,22 @@ void Scumm::saveOrLoad(Serializer *s) { }; const SaveLoadEntry stringTabEntries[] = { + MKLINE(StringTab,xpos,sleInt16), MKLINE(StringTab,t_xpos,sleInt16), + MKLINE(StringTab,ypos,sleInt16), MKLINE(StringTab,t_ypos,sleInt16), + MKLINE(StringTab,right,sleInt16), + MKLINE(StringTab,t_right,sleInt16), + MKLINE(StringTab,color,sleInt8), + MKLINE(StringTab,t_color,sleInt8), + MKLINE(StringTab,charset,sleInt8), + MKLINE(StringTab,t_charset,sleInt8), + MKLINE(StringTab,center,sleByte), MKLINE(StringTab,t_center,sleByte), + MKLINE(StringTab,overhead,sleByte), MKLINE(StringTab,t_overhead,sleByte), + MKLINE(StringTab,no_talk_anim,sleByte), MKLINE(StringTab,t_no_talk_anim,sleByte), - MKLINE(StringTab,t_right,sleInt16), - MKLINE(StringTab,t_color,sleInt16), - MKLINE(StringTab,t_charset,sleInt16), - MKLINE(StringTab,xpos,sleInt16), - MKLINE(StringTab,ypos,sleInt16), - MKLINE(StringTab,center,sleInt16), - MKLINE(StringTab,overhead,sleInt16), - MKLINE(StringTab,no_talk_anim,sleInt16), - MKLINE(StringTab,right,sleInt16), MKEND() }; @@ -478,6 +486,9 @@ void Scumm::saveOrLoad(Serializer *s) { res.flags[r][s->loadWord()] |= 0x80; } } + + if (_soundDriver) + ((SoundEngine*)_soundDriver)->save_or_load(s); } void Scumm::saveLoadResource(Serializer *ser, int type, int index) { @@ -630,9 +641,10 @@ void Serializer::saveLoadEntries(void *d, const SaveLoadEntry *sle) { int replen; byte type; byte *at; - int size; int value; + int num; + void *ptr; while(sle->offs != 0xFFFF) { at = (byte*)d + sle->offs; @@ -642,10 +654,12 @@ void Serializer::saveLoadEntries(void *d, const SaveLoadEntry *sle) { if (size==0xFF) { if (_saveOrLoad) { /* save reference */ - saveWord((*_save_ref)(_ref_me, type, *((void**)at))); + ptr = *((void**)at); + saveWord(ptr ? ((*_save_ref)(_ref_me, type, ptr ) + 1) : 0); } else { /* load reference */ - *((void**)at) = (*_load_ref)(_ref_me, type, loadWord()); + num = loadWord(); + *((void**)at) = num ? (*_load_ref)(_ref_me, type, num-1) : NULL; } } else { replen = 1; @@ -655,8 +669,8 @@ void Serializer::saveLoadEntries(void *d, const SaveLoadEntry *sle) { type&=~128; } saveLoadArrayOf(at, replen, size, type); - sle++; } + sle++; } } diff --git a/script_v1.cpp b/script_v1.cpp index 910f10942a..11bcef6e0a 100644 --- a/script_v1.cpp +++ b/script_v1.cpp @@ -1227,7 +1227,7 @@ void Scumm::o5_putActorInRoom() { void Scumm::o5_quitPauseRestart() { switch(fetchScriptByte()) { case 1: - pauseGame(0); + pauseGame(false); break; case 3: shutDown(0); diff --git a/script_v2.cpp b/script_v2.cpp index 0f7403a9c9..e390e5bf5a 100644 --- a/script_v2.cpp +++ b/script_v2.cpp @@ -1794,7 +1794,7 @@ void Scumm::o6_isAnyOf() { void Scumm::o6_quitPauseRestart() { switch(fetchScriptByte()) { case 158: - pauseGame(0); + pauseGame(false); break; case 160: shutDown(0); @@ -28,8 +28,14 @@ struct Actor; typedef void (Scumm::*OpcodeProc)(); -#define NUM_SCRIPT_SLOT 25 -#define NUM_ACTORS 13 +/* System Wide Constants */ +enum { + SAMPLES_PER_SEC = 22050, + BITS_PER_SAMPLE = 16, + NUM_MIXER = 4, + NUM_SCRIPT_SLOT = 25, + NUM_ACTORS = 13 +}; #pragma START_PACK_STRUCTS @@ -596,6 +602,16 @@ struct Gdi { void updateDirtyScreen(VirtScreen *vs); }; +struct MixerChannel { + void *_sfx_sound; + uint32 _sfx_pos; + uint32 _sfx_size; + uint32 _sfx_fp_speed; + uint32 _sfx_fp_pos; + + void mix(int16 *data, uint32 len); + void clear(); +}; enum GameId { GID_TENTACLE = 1, @@ -640,6 +656,8 @@ struct Scumm { bool _dynamicRoomOffsets; byte _resFilePathId; + bool _soundsPaused; + bool _useTalkAnims; char *_resFilePrefix; @@ -918,6 +936,8 @@ struct Scumm { char _saveLoadName[32]; + MixerChannel _mixer_channel[NUM_MIXER]; + OpcodeProc getOpcode(int i) { return _opcodes[i]; } void openRoom(int room); @@ -925,7 +945,6 @@ struct Scumm { void readRoomsOffsets(); void askForDisk(const char *filename); - bool openResourceFile(const char *filename); void fileClose(void *file); @@ -1449,7 +1468,7 @@ struct Scumm { void addObjectToInventory(uint obj, uint room); void removeObjectFromRoom(int obj); void decodeParseString(); - void pauseGame(int i); + void pauseGame(bool user); void shutDown(int i); void lock(int type, int i); void unlock(int type, int i); @@ -1590,6 +1609,7 @@ struct Scumm { void talkSound(uint32 a, uint32 b, int mode); void processSfxQueues(); void startTalkSound(uint32 a, uint32 b, int mode); + void stopTalkSound(); bool isMouthSyncOff(uint pos); void startSfxSound(void *file); void *openSfxFile(); @@ -1631,6 +1651,17 @@ struct Scumm { void drawEnqueuedObject(EnqueuedObject *eo); void removeEnqueuedObjects(); void removeEnqueuedObject(EnqueuedObject *eo); + + void pauseSounds(bool pause); + + MixerChannel *allocateMixer(); + bool isSfxFinished(); + void playSfxSound(void *sound, uint32 size, uint rate); + void stopSfxSound(); + + void mixWaves(int16 *sounds, int len); + + }; struct ScummDebugger { @@ -1671,6 +1702,7 @@ struct Serializer { union { SerializerSaveReference *_save_ref; SerializerLoadReference *_load_ref; + void *_saveload_ref; }; void *_ref_me; @@ -1690,6 +1722,7 @@ struct Serializer { uint32 loadUint32(); bool isSaving() { return _saveOrLoad; } + }; @@ -1714,4 +1747,4 @@ void blit(byte *dst, byte *src, int w, int h); byte *findResource(uint32 id, byte *searchin, int index); void playSfxSound(void *sound, uint32 size, uint rate); bool isSfxFinished(); -void waitForTimer(Scumm *s, int delay);
\ No newline at end of file +void waitForTimer(Scumm *s, int msec_delay);
\ No newline at end of file diff --git a/scummvm.cpp b/scummvm.cpp index cbccfcafc4..7c6af80dd7 100644 --- a/scummvm.cpp +++ b/scummvm.cpp @@ -196,8 +196,8 @@ void Scumm::scummMain(int argc, char **argv) { _debugMode = 1; - _maxHeapThreshold = 500000; - _minHeapThreshold = 450000; + _maxHeapThreshold = 450000; + _minHeapThreshold = 400000; parseCommandLine(argc, argv); @@ -213,6 +213,11 @@ void Scumm::scummMain(int argc, char **argv) { _bootParam = -7873; } + if (_gameId==GID_MONKEY2 && _bootParam==0) { + _bootParam = 10001; + } + + initGraphics(this, _fullScreen); if (_majorScummVersion==6) @@ -752,9 +757,8 @@ void Scumm::unkRoomFunc4(int a, int b, int c, int d, int e) { warning("unkRoomFunc4: not implemented"); } -void Scumm::pauseGame(int i) { - /* TODO: implement this */ - warning("pauseGame: not implemented"); +void Scumm::pauseGame(bool user) { + ((Gui*)_gui)->pause(); } void Scumm::shutDown(int i) { @@ -777,12 +781,12 @@ void Scumm::processKbd() { if (_lastKeyHit==_vars[VAR_RESTART_KEY]) { warning("Restart not implemented"); - pauseGame(1); +// pauseGame(true); return; } if (_lastKeyHit==_vars[VAR_PAUSE_KEY]) { - warning("Pause not implemented"); + pauseGame(true); /* pause */ return; } @@ -793,6 +797,8 @@ void Scumm::processKbd() { ((Gui*)_gui)->saveLoadDialog(); } else if (_lastKeyHit==_vars[VAR_TALKSTOP_KEY]) { _talkDelay = 0; + if (_sfxMode==2) + stopTalk(); return; } diff --git a/scummvm.dsp b/scummvm.dsp index 0ce6da4ec1..8b0963385e 100644 --- a/scummvm.dsp +++ b/scummvm.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
-# ADD CPP /nologo /Zp4 /W3 /GX /O1 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "DUMP_SCRIPTS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /Zp4 /MD /W3 /GX /O1 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
# ADD BASE RSC /l 0x41d /d "NDEBUG"
# ADD RSC /l 0x41d /d "NDEBUG"
BSC32=bscmake.exe
@@ -157,6 +157,10 @@ 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"
@@ -335,6 +339,10 @@ SOURCE=.\scummsys.h # End Source File
# Begin Source File
+SOURCE=.\sound.h
+# End Source File
+# Begin Source File
+
SOURCE=.\StdAfx.h
# End Source File
# End Group
@@ -24,10 +24,9 @@ #include "stdafx.h" #include "scumm.h" #include "gui.h" - -#if defined(USE_IMUSE) #include "sound.h" -#endif + +#include "SDL_thread.h" #define SCALEUP_2x2 @@ -35,9 +34,7 @@ Scumm scumm; ScummDebugger debugger; Gui gui; -#if defined(USE_IMUSE) SoundEngine sound; -#endif static SDL_Surface *screen; @@ -74,78 +71,90 @@ int mapKey(int key, byte mod) { return key; } -void waitForTimer(Scumm *s, int delay) { +void waitForTimer(Scumm *s, int msec_delay) { SDL_Event event; - - while (SDL_PollEvent(&event)) { - switch(event.type) { - case SDL_KEYDOWN: - s->_keyPressed = mapKey(event.key.keysym.sym, event.key.keysym.mod); - if (event.key.keysym.sym >= '0' && event.key.keysym.sym<='9') { - s->_saveLoadSlot = event.key.keysym.sym - '0'; - if (event.key.keysym.mod&KMOD_SHIFT) { - sprintf(s->_saveLoadName, "Quicksave %d", s->_saveLoadSlot); - s->_saveLoadFlag = 1; - } else if (event.key.keysym.mod&KMOD_CTRL) - s->_saveLoadFlag = 2; - s->_saveLoadCompatible = false; - } - if (event.key.keysym.sym=='z' && event.key.keysym.mod&KMOD_CTRL) { - exit(1); - } - if (event.key.keysym.sym=='f' && event.key.keysym.mod&KMOD_CTRL) { - s->_fastMode ^= 1; - } - if (event.key.keysym.sym=='g' && event.key.keysym.mod&KMOD_CTRL) { - s->_fastMode ^= 2; - } + uint32 start_time; - if (event.key.keysym.sym=='d' && event.key.keysym.mod&KMOD_CTRL) { - debugger.attach(s); - } - if (event.key.keysym.sym=='s' && event.key.keysym.mod&KMOD_CTRL) { - s->resourceStats(); - } + if (msec_delay<0) + return; + + if (s->_fastMode&2) + msec_delay = 0; + else if (s->_fastMode&1) + msec_delay = 10; + + start_time = SDL_GetTicks(); + + do { + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_KEYDOWN: + s->_keyPressed = mapKey(event.key.keysym.sym, event.key.keysym.mod); + if (event.key.keysym.sym >= '0' && event.key.keysym.sym<='9') { + s->_saveLoadSlot = event.key.keysym.sym - '0'; + if (event.key.keysym.mod&KMOD_SHIFT) { + sprintf(s->_saveLoadName, "Quicksave %d", s->_saveLoadSlot); + s->_saveLoadFlag = 1; + } else if (event.key.keysym.mod&KMOD_CTRL) + s->_saveLoadFlag = 2; + s->_saveLoadCompatible = false; + } + if (event.key.keysym.sym=='z' && event.key.keysym.mod&KMOD_CTRL) { + exit(1); + } + if (event.key.keysym.sym=='f' && event.key.keysym.mod&KMOD_CTRL) { + s->_fastMode ^= 1; + } + if (event.key.keysym.sym=='g' && event.key.keysym.mod&KMOD_CTRL) { + s->_fastMode ^= 2; + } -#if defined(__APPLE__) - if (event.key.keysym.sym=='q' && event.key.keysym.mod&KMOD_LMETA) { + if (event.key.keysym.sym=='d' && event.key.keysym.mod&KMOD_CTRL) { + debugger.attach(s); + } + if (event.key.keysym.sym=='s' && event.key.keysym.mod&KMOD_CTRL) { + s->resourceStats(); + } + + #if defined(__APPLE__) + if (event.key.keysym.sym=='q' && event.key.keysym.mod&KMOD_LMETA) { + exit(1); + } + #endif + break; + case SDL_MOUSEMOTION: { + int newx,newy; + #if !defined(SCALEUP_2x2) + newx = event.motion.x; + newy = event.motion.y; + #else + newx = event.motion.x>>1; + newy = event.motion.y>>1; + #endif + if (newx != s->mouse.x || newy != s->mouse.y) { + s->mouse.x = newx; + s->mouse.y = newy; + s->drawMouse(); + updateScreen(s); + } + break; + } + case SDL_MOUSEBUTTONDOWN: + if (event.button.button==SDL_BUTTON_LEFT) + s->_leftBtnPressed |= 1; + else if (event.button.button==SDL_BUTTON_RIGHT) + s->_rightBtnPressed |= 1; + break; + case SDL_QUIT: exit(1); - } -#endif - break; - case SDL_MOUSEMOTION: { - int newx,newy; -#if !defined(SCALEUP_2x2) - newx = event.motion.x; - newy = event.motion.y; -#else - newx = event.motion.x>>1; - newy = event.motion.y>>1; -#endif - if (newx != s->mouse.x || newy != s->mouse.y) { - s->mouse.x = newx; - s->mouse.y = newy; - s->drawMouse(); - updateScreen(s); + break; } - break; - } - case SDL_MOUSEBUTTONDOWN: - if (event.button.button==SDL_BUTTON_LEFT) - s->_leftBtnPressed |= 1; - else if (event.button.button==SDL_BUTTON_RIGHT) - s->_rightBtnPressed |= 1; - break; - case SDL_QUIT: - exit(1); - break; } - } - - if (!(s->_fastMode&2)) { - assert(delay<500); - SDL_Delay(delay*10); - } + + if (SDL_GetTicks() >= start_time + msec_delay) + break; + SDL_Delay(10); + } while (1); } #define MAX_DIRTY_RECTS 40 @@ -468,111 +477,31 @@ void drawMouse(Scumm *s, int xdraw, int ydraw, int color, byte *mask, bool visib old_mouse_x = xdraw; old_mouse_y = ydraw; } - -} - -#define SAMPLES_PER_SEC 22050 -#define BUFFER_SIZE (8192) -#define BITS_PER_SAMPLE 16 - -struct MixerChannel { - void *_sfx_sound; - uint32 _sfx_pos; - uint32 _sfx_size; - uint32 _sfx_fp_speed; - uint32 _sfx_fp_pos; - - void mix(int16 *data, uint32 len); - void clear(); -}; - -#define NUM_MIXER 4 - -static MixerChannel mixer_channel[NUM_MIXER]; - -MixerChannel *find_channel() { - int i; - MixerChannel *mc = mixer_channel; - for(i=0; i<NUM_MIXER; i++,mc++) { - if (!mc->_sfx_sound) - return mc; - } - return NULL; -} - - -bool isSfxFinished() { - int i; - for(i=0; i<NUM_MIXER; i++) - if (mixer_channel[i]._sfx_sound) - return false; - return true; } -void playSfxSound(void *sound, uint32 size, uint rate) { - MixerChannel *mc = find_channel(); - - if (!mc) { - warning("No mixer channel available"); - return; - } - - mc->_sfx_sound = sound; - mc->_sfx_pos = 0; - mc->_sfx_fp_speed = (1<<16) * rate / 22050; - mc->_sfx_fp_pos = 0; +static uint32 midi_counter; - while (size&0xFFFF0000) size>>=1, rate>>=1; - mc->_sfx_size = size * 22050 / rate; +void fill_sound(void *userdata, Uint8 *stream, int len) { + memset(stream, 0, len); + scumm.mixWaves((int16*)stream, len>>1); } -void MixerChannel::mix(int16 *data, uint32 len) { - int8 *s; - int i; - uint32 fp_pos, fp_speed; - - if (!_sfx_sound) - return; - if (len > _sfx_size) - len = _sfx_size; - _sfx_size -= len; +int music_thread(Scumm *s) { + int old_time, cur_time; - s = (int8*)_sfx_sound + _sfx_pos; - fp_pos = _sfx_fp_pos; - fp_speed = _sfx_fp_speed; + old_time = SDL_GetTicks(); do { - fp_pos += fp_speed; - *data++ += (*s<<6); - s += fp_pos >> 16; - fp_pos &= 0x0000FFFF; - } while (--len); - - _sfx_pos = s - (int8*)_sfx_sound; - _sfx_fp_speed = fp_speed; - _sfx_fp_pos = fp_pos; - - if (!_sfx_size) - clear(); -} - -void MixerChannel::clear() { - free(_sfx_sound); - _sfx_sound = NULL; -} - -void fill_sound(void *userdata, Uint8 *stream, int len) { - int i; - -#if defined(USE_IMUSE) - sound.generate_samples((int16*)stream, len>>1); -#else - memset(stream, 0, len); -#endif + SDL_Delay(10); + + cur_time = SDL_GetTicks(); + while (old_time < cur_time) { + old_time += 10; + sound.on_timer(); + } + } while (1); - for(i=NUM_MIXER-1; i>=0;i--) { - mixer_channel[i].mix((int16*)stream, len>>1); - } + return 0; } void initGraphics(Scumm *s, bool fullScreen) { @@ -599,10 +528,12 @@ void initGraphics(Scumm *s, bool fullScreen) { SDL_OpenAudio(&desired, NULL); SDL_PauseAudio(0); - SDL_WM_SetCaption(buf,buf); SDL_ShowCursor(SDL_DISABLE); + /* 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); #else @@ -627,33 +558,31 @@ void initGraphics(Scumm *s, bool fullScreen) { int main(int argc, char* argv[]) { int delta,tmp; + int last_time, new_time; -#if defined(USE_IMUSE) - sound.initialize(NULL); + sound.initialize(&scumm); scumm._soundDriver = &sound; -#endif scumm._gui = &gui; scumm.scummMain(argc, argv); gui.init(&scumm); + last_time = SDL_GetTicks(); delta = 0; do { updateScreen(&scumm); + new_time = SDL_GetTicks(); + waitForTimer(&scumm, delta * 15 + last_time - new_time); + last_time = SDL_GetTicks(); + if (gui._active) { gui.loop(); - tmp = 5; + delta = 5; } else { - tmp = delta = scumm.scummLoop(delta); - tmp += tmp>>1; - - if (scumm._fastMode) - tmp=1; + delta = scumm.scummLoop(delta); } - - waitForTimer(&scumm, tmp); } while(1); return 0; @@ -21,20 +21,7 @@ #include "stdafx.h" #include "scumm.h" - -#if defined(USE_IMUSE) #include "sound.h" -#else -struct SoundEngine { - byte **_base_sounds; - int start_sound(int sound) { return -1; } - int stop_sound(int sound) { return -1; } - int stop_all_sounds() { return -1; } - int32 do_command(int a, int b, int c, int d, int e, int f, int g, int h) { return -1; } - int get_sound_status(int sound) { return -1; } - int clear_queue() { return -1; } -}; -#endif void Scumm::addSoundToQueue(int sound) { _vars[VAR_LAST_SOUND] = sound; @@ -170,6 +157,13 @@ void Scumm::startTalkSound(uint32 offset, uint32 b, int mode) { startSfxSound(_sfxFile); } +void Scumm::stopTalkSound() { + if (_sfxMode==2) { + stopSfxSound(); + _sfxMode = 0; + } +} + bool Scumm::isMouthSyncOff(uint pos) { uint j; bool val = true; @@ -203,7 +197,6 @@ int Scumm::isSoundRunning(int sound) { if (!isResourceLoaded(rtSound, sound)) return 0; - se = (SoundEngine*)_soundDriver; if (!se) @@ -251,6 +244,7 @@ void Scumm::stopAllSounds() { se->clear_queue(); } clearSoundQue(); + stopSfxSound(); } void Scumm::clearSoundQue() { @@ -283,27 +277,41 @@ void Scumm::talkSound(uint32 a, uint32 b, int mode) { _talk_sound_mode = mode; } +/* The sound code currently only supports General Midi. + * General Midi is used in Day Of The Tentacle. + * Roland music is also playable, but doesn't sound well. + * A mapping between roland instruments and GM instruments + * is needed. + */ + static const uint32 sound_tags[] = { - MKID('ADL ') + MKID('GMD ') }; void Scumm::setupSound() { SoundEngine *se = (SoundEngine*)_soundDriver; - if (se) { + if (se) se->_base_sounds = res.address[rtSound]; - } + _soundTagTable = (byte*)sound_tags; _numSoundTags = 1; _sfxFile = openSfxFile(); } +void Scumm::pauseSounds(bool pause) { + SoundEngine *se = (SoundEngine*)_soundDriver; + if (se) + se->pause(pause); + _soundsPaused = pause; +} + struct VOCHeader { byte id[19]; byte extra[7]; }; static const char VALID_VOC_ID[] = "Creative Voice File"; -static const char VALID_VOC_VERSION[] = ""; + void Scumm::startSfxSound(void *file) { VOCHeader hdr; int block_type; @@ -354,4 +362,98 @@ void Scumm::startSfxSound(void *file) { void *Scumm::openSfxFile() { return fopen("monster.sou", "rb"); -}
\ No newline at end of file +} + +#define NUM_MIXER 4 + +MixerChannel *Scumm::allocateMixer() { + int i; + MixerChannel *mc = _mixer_channel; + for(i=0; i<NUM_MIXER; i++,mc++) { + if (!mc->_sfx_sound) + return mc; + } + return NULL; +} + +void Scumm::stopSfxSound() { + MixerChannel *mc = _mixer_channel; + int i; + for(i=0; i<NUM_MIXER; i++,mc++) { + if (mc->_sfx_sound) + mc->clear(); + } +} + + +bool Scumm::isSfxFinished() { + int i; + for(i=0; i<NUM_MIXER; i++) + if (_mixer_channel[i]._sfx_sound) + return false; + return true; +} + +void Scumm::playSfxSound(void *sound, uint32 size, uint rate) { + MixerChannel *mc = allocateMixer(); + + if (!mc) { + warning("No mixer channel available"); + return; + } + + mc->_sfx_sound = sound; + mc->_sfx_pos = 0; + mc->_sfx_fp_speed = (1<<16) * rate / 22050; + mc->_sfx_fp_pos = 0; + + while (size&0xFFFF0000) size>>=1, rate>>=1; + mc->_sfx_size = size * 22050 / rate; +} + +void MixerChannel::mix(int16 *data, uint32 len) { + int8 *s; + int i; + uint32 fp_pos, fp_speed; + + if (!_sfx_sound) + return; + if (len > _sfx_size) + len = _sfx_size; + _sfx_size -= len; + + s = (int8*)_sfx_sound + _sfx_pos; + fp_pos = _sfx_fp_pos; + fp_speed = _sfx_fp_speed; + + do { + fp_pos += fp_speed; + *data++ += (*s<<6); + s += fp_pos >> 16; + fp_pos &= 0x0000FFFF; + } while (--len); + + _sfx_pos = s - (int8*)_sfx_sound; + _sfx_fp_speed = fp_speed; + _sfx_fp_pos = fp_pos; + + if (!_sfx_size) + clear(); +} + +void MixerChannel::clear() { + free(_sfx_sound); + _sfx_sound = NULL; +} + +void Scumm::mixWaves(int16 *sounds, int len) { + int i; + + if (_soundsPaused) + return; + + for(i=NUM_MIXER-1; i>=0;i--) { + _mixer_channel[i].mix(sounds, len); + } +} + diff --git a/sound.h b/sound.h new file mode 100644 index 0000000000..26ac251ee9 --- /dev/null +++ b/sound.h @@ -0,0 +1,358 @@ +/* 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. + * + * Change Log: + * $Log$ + * Revision 1.1 2001/11/14 18:37:38 strigeus + * music support, + * fixed timing bugs + * + */ + +struct Part; +struct MidiChannel; +struct VolumeFader; +struct Player; +struct HookDatas; +struct SoundEngine; + +struct Part { + SoundEngine *_se; + Part *_next, *_prev; + MidiChannel *_mc; + Player *_player; + int16 _pitchbend; + byte _pitchbend_factor; + int8 _transpose,_transpose_eff; + byte _vol,_vol_eff; + int8 _detune,_detune_eff; + int8 _pan,_pan_eff; + bool _on; + byte _modwheel; + bool _pedal; + byte _program; + int8 _pri; + byte _pri_eff; + byte _chan; + byte _effect_level; + byte _chorus; + byte _gmidi_5; + byte _gmidi_1; + + void key_on(byte note, byte velocity); + void key_off(byte note); + void set_param(int b, byte c) {} + void init(SoundEngine *se); + void setup(Player *player); + void uninit(); + void off(); + void silence(); + void set_instrument(uint b); + void set_instrument(byte *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_pan(int8 pan); + void set_modwheel(uint value); + void set_pedal(bool value); + void set_pitchbend(int value); + void release_pedal(); + 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(); +}; + +struct MidiChannel { + Part *_part; + byte _chan; + uint16 _actives[8]; + + void init(byte chan); +}; + +struct VolumeFader { + Player *player; + bool active; + byte curvol; + uint16 speed_lo_max,num_steps; + int8 speed_hi; + int8 direction; + int8 speed_lo; + uint16 speed_lo_counter; + + void initialize() { active = false; } + void on_timer(); +}; + +struct HookDatas { + byte _jump,_transpose; + byte _part_onoff[16]; + byte _part_volume[16]; + byte _part_program[16]; + byte _part_transpose[16]; + + int query_param(int param, byte chan); + int set(byte cls, byte value, byte chan); +}; + +struct Player { + SoundEngine *_se; + + Part *_parts; + bool _active; + bool _scanning; + int _id; + byte _priority; + byte _volume; + int8 _pan; + int8 _transpose; + int8 _detune; + uint _vol_chan; + byte _vol_eff; + + uint _song_index; + uint _track_index; + uint _timer_counter; + uint _loop_to_beat; + uint _loop_from_beat; + uint _loop_counter; + uint _loop_to_tick; + uint _loop_from_tick; + uint32 _tempo; + uint32 _tempo_eff; /* NoSave */ + uint32 _cur_pos; + uint32 _next_pos; + uint32 _song_offset; + uint32 _timer_speed; /* NoSave */ + uint _tick_index; + uint _beat_index; + uint _ticks_per_beat; + byte _speed; /* NoSave */ + bool _abort; + + HookDatas _hook; + + /* Player part */ + void hook_clear(); + void clear(); + bool start_sound(int sound); + void uninit_parts(); + byte *parse_midi(byte *s); + void key_off(uint8 chan, byte data); + void key_on(uint8 chan, byte data, byte velocity); + void part_set_transpose(uint8 chan, byte relative, int8 b); + void parse_sysex(byte *p, uint len); + void maybe_jump(byte *data); + void maybe_set_transpose(byte *data); + void maybe_part_onoff(byte *data); + void maybe_set_volume(byte *data); + void maybe_set_program(byte *data); + void maybe_set_transpose_part(byte *data); + uint update_actives(); + Part *get_part(uint8 part); + void turn_off_pedals(); + int set_vol(byte vol); + int get_param(int param, byte chan); + int query_part_param(int param, byte chan); + int set_transpose(byte relative, int b); + void set_priority(int pri); + void set_pan(int pan); + void set_detune(int detune); + void silence_parts(); + void play_active_notes(); + void cancel_volume_fade(); + + static void decode_sysex_bytes(byte *src, byte *dst, int len); + + void clear_active_note(int chan, byte note); + void set_active_note(int chan, byte note); + void clear_active_notes(); + + /* Sequencer part */ + bool set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick); + void clear_loop(); + void set_speed(byte speed); + bool jump(uint track, uint beat, uint tick); + void uninit_seq(); + void set_tempo(uint32 data); + int start_seq_sound(int sound); + void find_sustaining_notes(byte *a, byte *b, uint32 l); + int scan(uint totrack, uint tobeat, uint totick); + int query_param(int param); + + int fade_vol(byte vol, int time); + void sequencer_timer(); +}; + + +struct SustainingNotes { + SustainingNotes *next; + SustainingNotes *prev; + Player *player; + byte note,chan; + uint32 off_pos; + uint32 pos; + uint16 counter; +}; + +struct CommandQueue { + uint16 array[8]; +}; + +struct IsNoteCmdData { + byte chan; + byte note; + byte vel; +}; + +struct SoundEngine { + void *_mo; /* midi out */ + + byte **_base_sounds; + + Scumm *_s; + + byte _locked; + + bool _paused; + bool _active_volume_faders; + bool _initialized; + byte _volume_fader_counter; + + uint _queue_end, _queue_pos, _queue_sound; + byte _queue_adding; + + SustainingNotes *_sustain_notes_used; + SustainingNotes *_sustain_notes_free; + SustainingNotes *_sustain_notes_head; + + uint16 _timer_counter_1; + + byte _queue_marker; + byte _queue_cleared; + byte _master_volume; + + uint16 _trigger_count; + + uint16 _channel_volume[8]; + uint16 _channel_volume_eff[8]; /* NoSave */ + uint16 _volchan_table[8]; + + Player _players[8]; + 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); + int get_channel_volume(uint a); + void init_players(); + void init_parts(); + void init_volume_fader(); + 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); + Player *get_player_byid(int id); + + int get_volchan_entry(uint a); + int set_volchan_entry(uint a, uint b); + int set_channel_volume(uint chan, uint vol); + void update_volumes(); + void reset_tick(); + VolumeFader *allocate_volume_fader(); + + 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(); + + static int saveReference(SoundEngine *me, byte type, void *ref); + static void *loadReference(SoundEngine *me, byte type, int ref); + + void lock(); + void unlock(); + + void pause(bool paused); +}; diff --git a/string.cpp b/string.cpp index 8b472b777d..b93cba2d8d 100644 --- a/string.cpp +++ b/string.cpp @@ -276,6 +276,8 @@ void Scumm::CHARSET_1() { _lastXstart = virtscr[0].xstart; if (charset._center) { charset._xpos2 -= charset.getStringWidth(0, buffer,0) >> 1; + if (charset._xpos2<0) + charset._xpos2 = 0; } charset._disableOffsX = charset._unk12 = !_keepText; diff --git a/windows.cpp b/windows.cpp index 81290c0da6..07330d8916 100644 --- a/windows.cpp +++ b/windows.cpp @@ -31,11 +31,7 @@ #endif #include "scumm.h" - -#if defined(USE_IMUSE) #include "sound.h" -#endif - #include "gui.h" #if !defined(ALLOW_GDI) @@ -135,10 +131,7 @@ int sel; Scumm scumm; ScummDebugger debugger; Gui gui; - -#if defined(USE_IMUSE) SoundEngine sound; -#endif WndMan wm[1]; byte veryFastMode; @@ -819,95 +812,6 @@ void blitToScreen(Scumm *s, byte *src,int x, int y, int w, int h) { } -#define SAMPLES_PER_SEC 22050 -#define BUFFER_SIZE (8192) -#define BITS_PER_SAMPLE 16 - -struct MixerChannel { - void *_sfx_sound; - uint32 _sfx_pos; - uint32 _sfx_size; - uint32 _sfx_fp_speed; - uint32 _sfx_fp_pos; - - void mix(int16 *data, uint32 len); - void clear(); -}; - -#define NUM_MIXER 4 - -static MixerChannel mixer_channel[NUM_MIXER]; - -MixerChannel *find_channel() { - int i; - MixerChannel *mc = mixer_channel; - for(i=0; i<NUM_MIXER; i++,mc++) { - if (!mc->_sfx_sound) - return mc; - } - return NULL; -} - - -bool isSfxFinished() { - int i; - for(i=0; i<NUM_MIXER; i++) - if (mixer_channel[i]._sfx_sound) - return false; - return true; -} - -void playSfxSound(void *sound, uint32 size, uint rate) { - MixerChannel *mc = find_channel(); - - if (!mc) { - warning("No mixer channel available"); - return; - } - - mc->_sfx_sound = sound; - mc->_sfx_pos = 0; - mc->_sfx_fp_speed = (1<<16) * rate / 22050; - mc->_sfx_fp_pos = 0; - - while (size&0xFFFF0000) size>>=1, rate>>=1; - mc->_sfx_size = size * 22050 / rate; -} - -void MixerChannel::mix(int16 *data, uint32 len) { - int8 *s; - int i; - uint32 fp_pos, fp_speed; - - if (!_sfx_sound) - return; - if (len > _sfx_size) - len = _sfx_size; - _sfx_size -= len; - - s = (int8*)_sfx_sound + _sfx_pos; - fp_pos = _sfx_fp_pos; - fp_speed = _sfx_fp_speed; - - do { - fp_pos += fp_speed; - *data++ += (*s<<6); - s += fp_pos >> 16; - fp_pos &= 0x0000FFFF; - } while (--len); - - _sfx_pos = s - (int8*)_sfx_sound; - _sfx_fp_speed = fp_speed; - _sfx_fp_pos = fp_pos; - - if (!_sfx_size) - clear(); -} - -void MixerChannel::clear() { - free(_sfx_sound); - _sfx_sound = NULL; -} int clock; @@ -923,8 +827,8 @@ void updateScreen(Scumm *s) { void waitForTimer(Scumm *s, int delay) { wm->handleMessage(); if (!veryFastMode) { - assert(delay<500); - Sleep(delay*10); + assert(delay<5000); + Sleep(delay); } } @@ -940,16 +844,8 @@ void drawMouse(Scumm *s, int x, int y, int w, int h, byte *buf, bool visible) { } void fill_buffer(int16 *buf, int len) { - int i; -#if defined(USE_IMUSE) - sound.generate_samples(buf,len); -#else memset(buf, 0, len*2); -#endif - for(i=NUM_MIXER-1; i>=0;i--) { - mixer_channel[i].mix((int16*)buf, len); - } - + scumm.mixWaves(buf, len); } void WndMan::prepare_header(WAVEHDR *wh, int i) { @@ -975,6 +871,7 @@ void WndMan::sound_init() { wfx.nBlockAlign = BITS_PER_SAMPLE * 1 / 8; CreateThread(NULL, 0, (unsigned long (__stdcall *)(void *))&sound_thread, this, 0, &_threadId); + SetThreadPriority((void*)_threadId, THREAD_PRIORITY_HIGHEST); _event = CreateEvent(NULL, false, false, NULL); @@ -988,13 +885,25 @@ void WndMan::sound_init() { DWORD _stdcall WndMan::sound_thread(WndMan *wm) { int i; + bool signaled; + int time = GetTickCount(), cur; + while (1) { - WaitForSingleObject(wm->_event, INFINITE); - for(i=0; i<2; i++) { - WAVEHDR *hdr = &wm->_hdr[i]; - if (hdr->dwFlags & WHDR_DONE) { - fill_buffer((int16*)hdr->lpData, hdr->dwBufferLength>>1); - waveOutWrite(wm->_handle, hdr, sizeof(WAVEHDR)); + cur = GetTickCount(); + while (time < cur) { + sound.on_timer(); + time += 10; + } + + signaled = WaitForSingleObject(wm->_event, time - cur) == WAIT_OBJECT_0; + + if (signaled) { + for(i=0; i<2; i++) { + WAVEHDR *hdr = &wm->_hdr[i]; + if (hdr->dwFlags & WHDR_DONE) { + fill_buffer((int16*)hdr->lpData, hdr->dwBufferLength>>1); + waveOutWrite(wm->_handle, hdr, sizeof(WAVEHDR)); + } } } } @@ -1011,10 +920,8 @@ int main(int argc, char* argv[]) { wm->_vgabuf = (byte*)calloc(320,200); wm->_scumm = &scumm; -#if defined(USE_IMUSE) - sound.initialize(NULL); + sound.initialize(&scumm); scumm._soundDriver = &sound; -#endif scumm._gui = &gui; scumm.scummMain(argc, argv); @@ -1024,6 +931,8 @@ int main(int argc, char* argv[]) { do { updateScreen(&scumm); + waitForTimer(&scumm, tmp*10); + if (gui._active) { gui.loop(); tmp = 5; @@ -1035,8 +944,6 @@ int main(int argc, char* argv[]) { if (scumm._fastMode) tmp=1; } - - waitForTimer(&scumm, tmp); } while(1); return 0; |