mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-22 09:49:11 +00:00
90f77843ad
svn-id: r9676
280 lines
6.3 KiB
C++
280 lines
6.3 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2001-2003 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 "common/engine.h"
|
|
#include "player_v1.h"
|
|
#include "scumm.h"
|
|
#include "sound/mixer.h"
|
|
|
|
#define TIMER_BASE_FREQ 1193000
|
|
#define FIXP_SHIFT 16
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// V1 PC-Speaker player
|
|
//
|
|
////////////////////////////////////////
|
|
|
|
|
|
Player_V1::Player_V1(Scumm *scumm) : Player_V2(scumm) {
|
|
/* pcjr not yet supported */
|
|
set_pcjr(false);
|
|
|
|
_freq_current = 0;
|
|
}
|
|
|
|
Player_V1::~Player_V1() {
|
|
}
|
|
|
|
void Player_V1::chainSound(int nr, byte *data) {
|
|
int offset = _pcjr ? READ_LE_UINT16(data+4) : 6;
|
|
|
|
current_nr = nr;
|
|
current_data = data;
|
|
_next_cmd = data + (_pcjr ? offset + 2 : offset + 4);
|
|
_repeat_ptr = _next_cmd;
|
|
_music_timer = 0;
|
|
|
|
debug(4, "Chaining new sound %d", nr);
|
|
parse_chunk();
|
|
}
|
|
|
|
void Player_V1::startSound(int nr, byte *data) {
|
|
mutex_up();
|
|
|
|
int offset = _pcjr ? READ_LE_UINT16(data+4) : 6;
|
|
int cprio = current_data ? *(current_data + offset) & 0x7f : 0;
|
|
int prio = *(data + _header_len) & 0x7f;
|
|
int nprio = next_data ? *(next_data + _header_len) & 0x7f : 0;
|
|
|
|
int restartable = *(data + _header_len) & 0x80;
|
|
|
|
if (!current_nr || cprio <= prio) {
|
|
int tnr = current_nr;
|
|
int tprio = cprio;
|
|
byte *tdata = current_data;
|
|
|
|
chainSound(nr, data);
|
|
nr = tnr;
|
|
prio = tprio;
|
|
data = tdata;
|
|
restartable = data ? *(data + _header_len) & 0x80 : 0;
|
|
}
|
|
|
|
if (!current_nr) {
|
|
nr = 0;
|
|
next_nr = 0;
|
|
next_data = 0;
|
|
}
|
|
|
|
if (nr != current_nr
|
|
&& restartable
|
|
&& (!next_nr
|
|
|| nprio <= prio)) {
|
|
|
|
next_nr = nr;
|
|
next_data = data;
|
|
}
|
|
|
|
mutex_down();
|
|
}
|
|
|
|
void Player_V1::restartSound() {
|
|
if (*(current_data + _header_len) & 0x80) {
|
|
/* current sound is restartable */
|
|
chainSound(current_nr, current_data);
|
|
} else {
|
|
chainNextSound();
|
|
}
|
|
}
|
|
|
|
int Player_V1::getMusicTimer() const {
|
|
return _music_timer;
|
|
}
|
|
|
|
void Player_V1::parse_chunk() {
|
|
set_mplex(3000);
|
|
_forced_level = 0;
|
|
|
|
parse_again:
|
|
_chunk_type = READ_LE_UINT16(_next_cmd);
|
|
debug(4, "parse_chunk: sound %d, offset %4x, chunk %x",
|
|
current_nr, _next_cmd - current_data, _chunk_type);
|
|
|
|
_next_cmd += 2;
|
|
switch (_chunk_type) {
|
|
case 0xffff:
|
|
current_nr = 0;
|
|
current_data = 0;
|
|
_freq_current = 0;
|
|
chainNextSound();
|
|
break;
|
|
case 0xfffe:
|
|
_repeat_ptr = _next_cmd;
|
|
goto parse_again;
|
|
|
|
case 0xfffd:
|
|
_next_cmd = _repeat_ptr;
|
|
goto parse_again;
|
|
|
|
case 0xfffc:
|
|
/* handle reset. We don't need this don't we? */
|
|
goto parse_again;
|
|
|
|
case 0:
|
|
_time_left = 1;
|
|
set_mplex(READ_LE_UINT16(_next_cmd));
|
|
_next_cmd += 2;
|
|
break;
|
|
case 1:
|
|
set_mplex(READ_LE_UINT16(_next_cmd));
|
|
_freq_start = READ_LE_UINT16(_next_cmd + 2);
|
|
_freq_end = READ_LE_UINT16(_next_cmd + 4);
|
|
_freq_delta = READ_LE_UINT16(_next_cmd + 6);
|
|
_repeat_ctr = READ_LE_UINT16(_next_cmd + 8);
|
|
_freq_current = _freq_start;
|
|
_next_cmd += 10;
|
|
debug(4, "chunk 1: mplex %d, freq %d -> %d step %d x %d",
|
|
_mplex, _freq_start, _freq_end, _freq_delta, _repeat_ctr);
|
|
break;
|
|
case 2:
|
|
_freq_start = READ_LE_UINT16(_next_cmd);
|
|
_freq_end = READ_LE_UINT16(_next_cmd + 2);
|
|
_freq_delta = READ_LE_UINT16(_next_cmd + 4);
|
|
_freq_current = 0;
|
|
_next_cmd += 6;
|
|
debug(4, "chunk 2: %d -> %d step %d",
|
|
_freq_start, _freq_end, _freq_delta);
|
|
break;
|
|
case 3:
|
|
_freq_start = READ_LE_UINT16(_next_cmd);
|
|
_freq_end = READ_LE_UINT16(_next_cmd + 2);
|
|
_freq_delta = READ_LE_UINT16(_next_cmd + 4);
|
|
_freq_current = 0;
|
|
_next_cmd += 6;
|
|
debug(4, "chunk 3: %d -> %d step %d",
|
|
_freq_start, _freq_end, _freq_delta);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Player_V1::next_speaker_cmd() {
|
|
uint16 lsr;
|
|
switch (_chunk_type) {
|
|
case 0:
|
|
if (--_time_left)
|
|
return;
|
|
_time_left = READ_LE_UINT16(_next_cmd);
|
|
_next_cmd += 2;
|
|
if (_time_left == 0xfffb) {
|
|
/* handle 0xfffb?? */
|
|
_time_left = READ_LE_UINT16(_next_cmd);
|
|
_next_cmd += 2;
|
|
}
|
|
debug(4, "next_speaker_cmd: chunk %d, offset %4x: notelen %d",
|
|
_chunk_type, _next_cmd - 2 - current_data, _time_left);
|
|
|
|
if (_time_left == 0) {
|
|
parse_chunk();
|
|
} else {
|
|
_freq_current = READ_LE_UINT16(_next_cmd);
|
|
_next_cmd += 2;
|
|
debug(4, "freq_current: %d", _freq_current);
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
_freq_current = (_freq_current + _freq_delta) & 0xffff;
|
|
if (_freq_current == _freq_end) {
|
|
if (!--_repeat_ctr)
|
|
parse_chunk();
|
|
_freq_current = _freq_start;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
_freq_start = (_freq_start + _freq_delta) & 0xffff;
|
|
if (_freq_start == _freq_end) {
|
|
parse_chunk();
|
|
}
|
|
set_mplex(_freq_start);
|
|
_forced_level ^= 1;
|
|
break;
|
|
case 3:
|
|
_freq_start = (_freq_start + _freq_delta) & 0xffff;
|
|
if (_freq_start == _freq_end) {
|
|
parse_chunk();
|
|
}
|
|
lsr = _random_lsr + 0x9248;
|
|
lsr = (lsr >> 3) | (lsr << 13);
|
|
_random_lsr = lsr;
|
|
set_mplex((_freq_start & lsr) | 0x180);
|
|
_forced_level ^= 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Player_V1::set_mplex (uint mplex) {
|
|
if (mplex == 0)
|
|
mplex = 65536;
|
|
_mplex = mplex;
|
|
_tick_len = (_sample_rate << FIXP_SHIFT)
|
|
/ (TIMER_BASE_FREQ / mplex);
|
|
}
|
|
|
|
void Player_V1::do_mix (int16 *data, uint len) {
|
|
mutex_up();
|
|
uint step;
|
|
|
|
do {
|
|
step = len;
|
|
if (step > (_next_tick >> FIXP_SHIFT))
|
|
step = (_next_tick >> FIXP_SHIFT);
|
|
generateSpkSamples(data, step);
|
|
data += step;
|
|
_next_tick -= step << FIXP_SHIFT;
|
|
|
|
if (!(_next_tick >> FIXP_SHIFT)) {
|
|
_next_tick += _tick_len;
|
|
next_speaker_cmd();
|
|
}
|
|
} while (len -= step);
|
|
mutex_down();
|
|
}
|
|
|
|
void Player_V1::generateSpkSamples(int16 *data, uint len) {
|
|
uint i;
|
|
|
|
memset (data, 0, sizeof(int16) * len);
|
|
if (_freq_current == 0) {
|
|
if (_forced_level) {
|
|
for (i = 0; i < len; i++) {
|
|
data[i] = _volumetable[0];
|
|
}
|
|
} else if (!_level) {
|
|
return;
|
|
}
|
|
} else {
|
|
squareGenerator(0, _freq_current, 0, 0, data, len);
|
|
}
|
|
lowPassFilter(data, len);
|
|
}
|