diff options
author | Ludvig Strigeus | 2001-12-01 17:23:50 +0000 |
---|---|---|
committer | Ludvig Strigeus | 2001-12-01 17:23:50 +0000 |
commit | 8fa0bb4b29acf5e7d2a5fa8a26a81072d4fa40dd (patch) | |
tree | f24c6a3007aa19e4880f5e43e3f1730b20529a51 /sound | |
parent | 95646feac9f3aa20a32c828d16ebf662fb281883 (diff) | |
download | scummvm-rg350-8fa0bb4b29acf5e7d2a5fa8a26a81072d4fa40dd.tar.gz scummvm-rg350-8fa0bb4b29acf5e7d2a5fa8a26a81072d4fa40dd.tar.bz2 scummvm-rg350-8fa0bb4b29acf5e7d2a5fa8a26a81072d4fa40dd.zip |
adlib sound support, use USE_ADLIB
svn-id: r3511
Diffstat (limited to 'sound')
-rw-r--r-- | sound/imuse.cpp | 2312 |
1 files changed, 2312 insertions, 0 deletions
diff --git a/sound/imuse.cpp b/sound/imuse.cpp new file mode 100644 index 0000000000..6914cbcde3 --- /dev/null +++ b/sound/imuse.cpp @@ -0,0 +1,2312 @@ +/* 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" + +int num_mix; + +#define TICKS_PER_BEAT 480 + +#ifdef USE_ADLIB +#define TEMPO_BASE 0x1924E0 +#define HARDWARE_TYPE 1 +#else +#define TEMPO_BASE 0x400000 +#define HARDWARE_TYPE 5 +#endif +#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 + +int clamp(int val, int min, int max) { + if (val<min) + return min; + if (val>max) + return max; + return val; +} + +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; +} + +/**********************************************************************/ + +void SoundEngine::lock() { + _locked++; +} + +void SoundEngine::unlock() { + _locked--; +} + +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; + + for (i=0,part=_parts; i!=ARRAYSIZE(_parts); i++, part++) { + part->init(_driver); + part->_slot = 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(); + _driver->on_timer(); + + 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, SoundDriver *driver) { + int i; + if (_initialized) + return -1; + + scumm->_soundEngine = this; + _s = scumm; + + _driver = (SOUND_DRIVER_TYPE*)driver; + + _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(); + + _driver->init(this); + + _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(); + +#if 0 + + 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(); + } + } +#endif + + _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); + _se->_driver->update_pris(); + 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; + byte hw; + + /* too big? */ + if (len>=sizeof(buf)*2) + return; + + /* skip sysex manufacturer */ + p++; + len -= 2; + + switch(code=*p++) { + case 16: /* set instrument in part */ + a = *p++ & 0x0F; + if (HARDWARE_TYPE != *p++) + break; + decode_sysex_bytes(p, buf, len - 3); + part = get_part(a); + if(part) + part->set_instrument((Instrument*)buf); + break; + + case 17: /* set global instrument */ + p++; + if (HARDWARE_TYPE != *p++) + break; + a=*p++; + decode_sysex_bytes(p, buf, len - 4); + _se->_driver->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_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); + } + _se->_driver->update_pris(); +} + +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++; + + turn_off_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; + _se->driver()->update_pris(); + 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::turn_off_parts() { + Part *part; + + for(part=_parts; part; part = part->_next) + part->off(); +} + +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,_percussion,sleUint8), + MKLINE(Part,_bank,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(); + _driver->update_pris(); + } + + return 0; +} + +#undef MKLINE +#undef MKEND + +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(); + } +} + +/* Only call this routine from the main thread, + * since it uses getResourceAddress */ +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::set_detune(int8 detune) { + _detune_eff = clamp((_detune=detune) + _player->_detune, -128, 127); + changed(SoundDriver::pcMod); +} + +void Part::set_pitchbend(int value) { + _pitchbend = value * _pitchbend_factor >> 6; + changed(SoundDriver::pcMod); +} + +void Part::set_vol(uint8 vol) { + _vol_eff = ((_vol=vol)+1)*_player->_vol_eff >> 7; + changed(SoundDriver::pcVolume); +} + +void Part::set_pri(int8 pri) { + _pri_eff = clamp((_pri=pri) + _player->_priority, 0, 255); +} + +void Part::set_pan(int8 pan) { + _pan_eff = clamp((_pan=pan) + _player->_pan, -64, 63); + changed(SoundDriver::pcPan); +} + +void Part::set_transpose(int8 transpose) { + _transpose_eff = transpose_clamp((_transpose=transpose) + + _player->_transpose, -12, 12); + changed(SoundDriver::pcMod); +} + +void Part::set_pedal(bool value) { + _pedal = value; + changed(SoundDriver::pcPedal); +} + +void Part::set_modwheel(uint value) { + _modwheel = value; + changed(SoundDriver::pcModwheel); +} + +void Part::set_chorus(uint chorus) { + _chorus = chorus; + changed(SoundDriver::pcChorus); +} + +void Part::set_effect_level(uint level) { + _effect_level = level; + changed(SoundDriver::pcEffectLevel); +} + +void Part::fix_after_load() { + set_transpose(_transpose); + set_vol(_vol); + set_detune(_detune); + set_pri(_pri); + set_pan(_pan); +} + +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 (!_percussion) + update_pris(); + } +} + +void Part::set_instrument(Instrument *data) { + _drv->part_set_instrument(this, data); +} + +void Part::key_on(byte note, byte velocity) { + _drv->part_key_on(this, note, velocity); +} + +void Part::key_off(byte note) { + _drv->part_key_off(this, note); +} + +void Part::init(SoundDriver *driver) { + _drv = (SOUND_DRIVER_TYPE*)driver; + _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; + + _percussion = 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; + _bank = 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() { + _drv->part_off(this); +} + +void Part::changed(byte what) { + _drv->part_changed(this, what); +} + +void Part::set_param(byte param, int value) { + _drv->part_set_param(this,param,value); +} + +void Part::update_pris() { + _drv->update_pris(); +} + +int Part::update_actives(uint16 *active) { + return _drv->part_update_active(this, active); +} + +void Part::set_program(byte program) { + if (_program!=program || _bank!=0) { + _program = program; + _bank = 0; + changed(SoundDriver::pcProgram); + } +} + +void Part::set_instrument(uint b) { + _bank = (byte)(b>>8); + _program = (byte)b; + changed(SoundDriver::pcProgram); +} + |