Initial commit
This commit is contained in:
622
engines/scumm/players/player_v1.cpp
Normal file
622
engines/scumm/players/player_v1.cpp
Normal file
@@ -0,0 +1,622 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "engines/engine.h"
|
||||
#include "scumm/players/player_v1.h"
|
||||
#include "scumm/scumm.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
#define FB_WNOISE 0x12000 /* feedback for white noise */
|
||||
#define FB_PNOISE 0x08000 /* feedback for periodic noise */
|
||||
|
||||
#define TIMER_BASE_FREQ 1193000
|
||||
#define FIXP_SHIFT 16
|
||||
|
||||
Player_V1::Player_V1(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr)
|
||||
: Player_V2(scumm, mixer, pcjr) {
|
||||
// Initialize channel code
|
||||
for (int i = 0; i < 4; ++i)
|
||||
clear_channel(i);
|
||||
|
||||
_mplex_step = (_sampleRate << FIXP_SHIFT) / 1193000;
|
||||
_next_chunk = _repeat_chunk = nullptr;
|
||||
_forced_level = 0;
|
||||
_random_lsr = 0;
|
||||
}
|
||||
|
||||
Player_V1::~Player_V1() {
|
||||
}
|
||||
|
||||
void Player_V1::chainSound(int nr, byte *data) {
|
||||
uint i;
|
||||
for (i = 0; i < 4; ++i)
|
||||
clear_channel(i);
|
||||
|
||||
_current_nr = nr;
|
||||
_current_data = data;
|
||||
_repeat_chunk = _next_chunk = data + (_pcjr ? 2 : 4);
|
||||
|
||||
debug(4, "Chaining new sound %d", nr);
|
||||
if (_pcjr)
|
||||
parsePCjrChunk();
|
||||
else
|
||||
parseSpeakerChunk();
|
||||
}
|
||||
|
||||
void Player_V1::startSound(int nr) {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
byte *data = _vm->getResourceAddress(rtSound, nr);
|
||||
assert(data);
|
||||
|
||||
int offset = _pcjr ? READ_LE_UINT16(data+4) : 6;
|
||||
int cprio = _current_data ? *(_current_data) & 0x7f : 0;
|
||||
int prio = *(data + offset) & 0x7f;
|
||||
int restartable = *(data + offset) & 0x80;
|
||||
|
||||
debug(4, "startSound %d: prio %d%s, cprio %d",
|
||||
nr, prio, restartable ? " restartable" : "", cprio);
|
||||
|
||||
if (!_current_nr || cprio <= prio) {
|
||||
if (_current_data && (*(_current_data) & 0x80)) {
|
||||
_next_nr = _current_nr;
|
||||
_next_data = _current_data;
|
||||
}
|
||||
|
||||
chainSound(nr, data + offset);
|
||||
}
|
||||
}
|
||||
|
||||
void Player_V1::stopAllSounds() {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
clear_channel(i);
|
||||
_repeat_chunk = _next_chunk = nullptr;
|
||||
_next_nr = _current_nr = 0;
|
||||
_next_data = _current_data = nullptr;
|
||||
}
|
||||
|
||||
void Player_V1::stopSound(int nr) {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
if (_next_nr == nr) {
|
||||
_next_nr = 0;
|
||||
_next_data = nullptr;
|
||||
}
|
||||
if (_current_nr == nr) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
clear_channel(i);
|
||||
}
|
||||
_repeat_chunk = _next_chunk = nullptr;
|
||||
_current_nr = 0;
|
||||
_current_data = nullptr;
|
||||
chainNextSound();
|
||||
}
|
||||
}
|
||||
|
||||
void Player_V1::clear_channel(int i) {
|
||||
_channels[i].freq = 0;
|
||||
_channels[i].volume = 15;
|
||||
}
|
||||
|
||||
int Player_V1::getMusicTimer() {
|
||||
/* Do V1 games have a music timer? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Player_V1::parseSpeakerChunk() {
|
||||
set_mplex(3000);
|
||||
_forced_level = 0;
|
||||
|
||||
parse_again:
|
||||
_chunk_type = READ_LE_UINT16(_next_chunk);
|
||||
debug(6, "parseSpeakerChunk: sound %d, offset %lx, chunk %x",
|
||||
_current_nr, (long)(_next_chunk - _current_data), _chunk_type);
|
||||
|
||||
_next_chunk += 2;
|
||||
switch (_chunk_type) {
|
||||
case 0xffff:
|
||||
_current_nr = 0;
|
||||
_current_data = nullptr;
|
||||
_channels[0].freq = 0;
|
||||
_next_chunk = nullptr;
|
||||
chainNextSound();
|
||||
break;
|
||||
case 0xfffe:
|
||||
_repeat_chunk = _next_chunk;
|
||||
goto parse_again;
|
||||
|
||||
case 0xfffd:
|
||||
_next_chunk = _repeat_chunk;
|
||||
goto parse_again;
|
||||
|
||||
case 0xfffc:
|
||||
/* handle reset. We don't need this do we? */
|
||||
goto parse_again;
|
||||
|
||||
case 0:
|
||||
_time_left = 1;
|
||||
set_mplex(READ_LE_UINT16(_next_chunk));
|
||||
_next_chunk += 2;
|
||||
break;
|
||||
case 1:
|
||||
set_mplex(READ_LE_UINT16(_next_chunk));
|
||||
_start = READ_LE_UINT16(_next_chunk + 2);
|
||||
_end = READ_LE_UINT16(_next_chunk + 4);
|
||||
_delta = (int16) READ_LE_UINT16(_next_chunk + 6);
|
||||
_repeat_ctr = READ_LE_UINT16(_next_chunk + 8);
|
||||
_channels[0].freq = _start;
|
||||
_next_chunk += 10;
|
||||
debug(6, "chunk 1: mplex %d, freq %d -> %d step %d x %d",
|
||||
_mplex, _start, _end, _delta, _repeat_ctr);
|
||||
break;
|
||||
case 2:
|
||||
_start = READ_LE_UINT16(_next_chunk);
|
||||
_end = READ_LE_UINT16(_next_chunk + 2);
|
||||
_delta = (int16) READ_LE_UINT16(_next_chunk + 4);
|
||||
_channels[0].freq = 0;
|
||||
_next_chunk += 6;
|
||||
_forced_level = -1;
|
||||
debug(6, "chunk 2: %d -> %d step %d",
|
||||
_start, _end, _delta);
|
||||
break;
|
||||
case 3:
|
||||
_start = READ_LE_UINT16(_next_chunk);
|
||||
_end = READ_LE_UINT16(_next_chunk + 2);
|
||||
_delta = (int16) READ_LE_UINT16(_next_chunk + 4);
|
||||
_channels[0].freq = 0;
|
||||
_next_chunk += 6;
|
||||
_forced_level = -1;
|
||||
debug(6, "chunk 3: %d -> %d step %d",
|
||||
_start, _end, _delta);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Player_V1::nextSpeakerCmd() {
|
||||
uint16 lsr;
|
||||
switch (_chunk_type) {
|
||||
case 0:
|
||||
if (--_time_left)
|
||||
return;
|
||||
_time_left = READ_LE_UINT16(_next_chunk);
|
||||
_next_chunk += 2;
|
||||
if (_time_left == 0xfffb) {
|
||||
/* handle 0xfffb?? */
|
||||
_time_left = READ_LE_UINT16(_next_chunk);
|
||||
_next_chunk += 2;
|
||||
}
|
||||
debug(7, "nextSpeakerCmd: chunk %d, offset %4lx: notelen %d",
|
||||
_chunk_type, (long)(_next_chunk - 2 - _current_data), _time_left);
|
||||
if (_time_left == 0) {
|
||||
parseSpeakerChunk();
|
||||
} else {
|
||||
_channels[0].freq = READ_LE_UINT16(_next_chunk);
|
||||
_next_chunk += 2;
|
||||
debug(7, "freq_current: %d", _channels[0].freq);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
_channels[0].freq = (_channels[0].freq + _delta) & 0xffff;
|
||||
if (_channels[0].freq == _end) {
|
||||
if (!--_repeat_ctr) {
|
||||
parseSpeakerChunk();
|
||||
return;
|
||||
}
|
||||
_channels[0].freq = _start;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_start = (_start + _delta) & 0xffff;
|
||||
if (_start == _end) {
|
||||
parseSpeakerChunk();
|
||||
return;
|
||||
}
|
||||
set_mplex(_start);
|
||||
_forced_level = -_forced_level;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_start = (_start + _delta) & 0xffff;
|
||||
if (_start == _end) {
|
||||
parseSpeakerChunk();
|
||||
return;
|
||||
}
|
||||
lsr = _random_lsr + 0x9248;
|
||||
lsr = (lsr >> 3) | (lsr << 13);
|
||||
_random_lsr = lsr;
|
||||
set_mplex((_start & lsr) | 0x180);
|
||||
_forced_level = -_forced_level;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Player_V1::parsePCjrChunk() {
|
||||
uint tmp;
|
||||
uint i;
|
||||
|
||||
set_mplex(3000);
|
||||
_forced_level = 0;
|
||||
|
||||
parse_again:
|
||||
|
||||
_chunk_type = READ_LE_UINT16(_next_chunk);
|
||||
debug(6, "parsePCjrChunk: sound %d, offset %4lx, chunk %x",
|
||||
_current_nr, (long)(_next_chunk - _current_data), _chunk_type);
|
||||
|
||||
_next_chunk += 2;
|
||||
switch (_chunk_type) {
|
||||
case 0xffff:
|
||||
for (i = 0; i < 4; ++i)
|
||||
clear_channel(i);
|
||||
_current_nr = 0;
|
||||
_current_data = nullptr;
|
||||
_repeat_chunk = _next_chunk = nullptr;
|
||||
chainNextSound();
|
||||
break;
|
||||
|
||||
case 0xfffe:
|
||||
_repeat_chunk = _next_chunk;
|
||||
goto parse_again;
|
||||
|
||||
case 0xfffd:
|
||||
_next_chunk = _repeat_chunk;
|
||||
goto parse_again;
|
||||
|
||||
case 0xfffc:
|
||||
/* handle reset. We don't need this do we? */
|
||||
goto parse_again;
|
||||
|
||||
case 0:
|
||||
set_mplex(READ_LE_UINT16(_next_chunk));
|
||||
_next_chunk += 2;
|
||||
for (i = 0; i < 4; i++) {
|
||||
tmp = READ_LE_UINT16(_next_chunk);
|
||||
_next_chunk += 2;
|
||||
if (tmp == 0xffff) {
|
||||
_channels[i].cmd_ptr = nullptr;
|
||||
continue;
|
||||
}
|
||||
_channels[i].attack = READ_LE_UINT16(_current_data + tmp);
|
||||
_channels[i].decay = READ_LE_UINT16(_current_data + tmp + 2);
|
||||
_channels[i].level = READ_LE_UINT16(_current_data + tmp + 4);
|
||||
_channels[i].sustain_1 = READ_LE_UINT16(_current_data + tmp + 6);
|
||||
_channels[i].sustain_2 = READ_LE_UINT16(_current_data + tmp + 8);
|
||||
_channels[i].notelen = 1;
|
||||
_channels[i].volume = 15;
|
||||
_channels[i].cmd_ptr = _current_data + tmp + 10;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
set_mplex(READ_LE_UINT16(_next_chunk));
|
||||
tmp = READ_LE_UINT16(_next_chunk + 2);
|
||||
_channels[0].cmd_ptr = tmp != 0xffff ? _current_data + tmp : nullptr;
|
||||
tmp = READ_LE_UINT16(_next_chunk + 4);
|
||||
_start = READ_LE_UINT16(_next_chunk + 6);
|
||||
_delta = (int16) READ_LE_UINT16(_next_chunk + 8);
|
||||
_time_left = READ_LE_UINT16(_next_chunk + 10);
|
||||
_next_chunk += 12;
|
||||
if (tmp >= 0xe0) {
|
||||
_channels[3].freq = tmp & 0xf;
|
||||
_value_ptr = &_channels[3].volume;
|
||||
} else {
|
||||
assert(!(tmp & 0x10));
|
||||
tmp = (tmp & 0x60) >> 5;
|
||||
_value_ptr = &_channels[tmp].freq;
|
||||
_channels[tmp].volume = 0;
|
||||
}
|
||||
*_value_ptr = _start;
|
||||
if (_channels[0].cmd_ptr) {
|
||||
tmp = READ_LE_UINT16(_channels[0].cmd_ptr);
|
||||
_start_2 = READ_LE_UINT16(_channels[0].cmd_ptr + 2);
|
||||
_delta_2 = (int16) READ_LE_UINT16(_channels[0].cmd_ptr + 4);
|
||||
_time_left_2 = READ_LE_UINT16(_channels[0].cmd_ptr + 6);
|
||||
_channels[0].cmd_ptr += 8;
|
||||
if (_value_ptr == &_channels[3].volume) {
|
||||
tmp = (tmp & 0x70) >> 4;
|
||||
if (tmp & 1)
|
||||
_value_ptr_2 = &_channels[tmp >> 1].volume;
|
||||
else
|
||||
_value_ptr_2 = &_channels[tmp >> 1].freq;
|
||||
} else {
|
||||
assert(!(tmp & 0x10));
|
||||
tmp = (tmp & 0x60) >> 5;
|
||||
_value_ptr_2 = &_channels[tmp].freq;
|
||||
_channels[tmp].volume = 0;
|
||||
}
|
||||
*_value_ptr_2 = _start_2;
|
||||
}
|
||||
debug(6, "chunk 1: %lu: %d step %d for %d, %lu: %d step %d for %d",
|
||||
(long)(_value_ptr - (uint *)_channels), _start, _delta, _time_left,
|
||||
(long)(_value_ptr_2 - (uint *)_channels), _start_2, _delta_2, _time_left_2);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_start = READ_LE_UINT16(_next_chunk);
|
||||
_end = READ_LE_UINT16(_next_chunk + 2);
|
||||
_delta = (int16) READ_LE_UINT16(_next_chunk + 4);
|
||||
_channels[0].freq = 0;
|
||||
_next_chunk += 6;
|
||||
_forced_level = -1;
|
||||
debug(6, "chunk 2: %d -> %d step %d",
|
||||
_start, _end, _delta);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
set_mplex(READ_LE_UINT16(_next_chunk));
|
||||
tmp = READ_LE_UINT16(_next_chunk + 2);
|
||||
assert((tmp & 0xf0) == 0xe0);
|
||||
_channels[3].freq = tmp & 0xf;
|
||||
if ((tmp & 3) == 3) {
|
||||
_next_chunk += 2;
|
||||
_channels[2].freq = READ_LE_UINT16(_next_chunk + 2);
|
||||
}
|
||||
_channels[3].volume = READ_LE_UINT16(_next_chunk + 4);
|
||||
_repeat_ctr = READ_LE_UINT16(_next_chunk + 6);
|
||||
_delta = (int16) READ_LE_UINT16(_next_chunk + 8);
|
||||
_next_chunk += 10;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Player_V1::nextPCjrCmd() {
|
||||
uint i;
|
||||
int dummy;
|
||||
switch (_chunk_type) {
|
||||
case 0:
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (!_channels[i].cmd_ptr)
|
||||
continue;
|
||||
if (!--_channels[i].notelen) {
|
||||
dummy = READ_LE_UINT16(_channels[i].cmd_ptr);
|
||||
if (dummy >= 0xfffe) {
|
||||
if (dummy == 0xfffe)
|
||||
_next_chunk = _current_data + 2;
|
||||
parsePCjrChunk();
|
||||
return;
|
||||
}
|
||||
_channels[i].notelen = 4 * dummy;
|
||||
dummy = READ_LE_UINT16(_channels[i].cmd_ptr + 2);
|
||||
if (dummy == 0) {
|
||||
_channels[i].hull_counter = 4;
|
||||
_channels[i].sustctr = _channels[i].sustain_2;
|
||||
} else {
|
||||
_channels[i].hull_counter = 1;
|
||||
_channels[i].freq = dummy;
|
||||
}
|
||||
debug(7, "chunk 0: channel %d play %d for %d",
|
||||
i, dummy, _channels[i].notelen);
|
||||
_channels[i].cmd_ptr += 4;
|
||||
}
|
||||
|
||||
|
||||
switch (_channels[i].hull_counter) {
|
||||
case 1:
|
||||
_channels[i].volume -= _channels[i].attack;
|
||||
if ((int)_channels[i].volume <= 0) {
|
||||
_channels[i].volume = 0;
|
||||
_channels[i].hull_counter++;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
_channels[i].volume += _channels[i].decay;
|
||||
if (_channels[i].volume >= _channels[i].level) {
|
||||
_channels[i].volume = _channels[i].level;
|
||||
_channels[i].hull_counter++;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (--_channels[i].sustctr < 0) {
|
||||
_channels[i].sustctr = _channels[i].sustain_2;
|
||||
_channels[i].volume += _channels[i].sustain_1;
|
||||
if ((int)_channels[i].volume >= 15) {
|
||||
_channels[i].volume = 15;
|
||||
_channels[i].hull_counter++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
_start += _delta;
|
||||
*_value_ptr = _start;
|
||||
if (!--_time_left) {
|
||||
_start = READ_LE_UINT16(_next_chunk);
|
||||
_next_chunk += 2;
|
||||
if (_start == 0xffff) {
|
||||
parsePCjrChunk();
|
||||
return;
|
||||
}
|
||||
_delta = (int16) READ_LE_UINT16(_next_chunk);
|
||||
_time_left = READ_LE_UINT16(_next_chunk + 2);
|
||||
_next_chunk += 4;
|
||||
*_value_ptr = _start;
|
||||
}
|
||||
|
||||
if (_channels[0].cmd_ptr) {
|
||||
_start_2 += _delta_2;
|
||||
*_value_ptr_2 = _start_2;
|
||||
if (!--_time_left_2) {
|
||||
_start_2 = READ_LE_UINT16(_channels[0].cmd_ptr);
|
||||
if (_start_2 == 0xffff) {
|
||||
_next_chunk = _channels[0].cmd_ptr + 2;
|
||||
parsePCjrChunk();
|
||||
return;
|
||||
}
|
||||
_delta_2 = (int16) READ_LE_UINT16(_channels[0].cmd_ptr + 2);
|
||||
_time_left_2 = READ_LE_UINT16(_channels[0].cmd_ptr + 4);
|
||||
_channels[0].cmd_ptr += 6;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_start += _delta;
|
||||
if (_start == _end) {
|
||||
parsePCjrChunk();
|
||||
return;
|
||||
}
|
||||
set_mplex(_start);
|
||||
debug(7, "chunk 2: mplex %d curve %d", _start, _forced_level);
|
||||
_forced_level = -_forced_level;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
dummy = _channels[3].volume + _delta;
|
||||
if (dummy >= 15) {
|
||||
_channels[3].volume = 15;
|
||||
} else if (dummy <= 0) {
|
||||
_channels[3].volume = 0;
|
||||
} else {
|
||||
_channels[3].volume = dummy;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!--_repeat_ctr) {
|
||||
parsePCjrChunk();
|
||||
return;
|
||||
}
|
||||
_delta = READ_LE_UINT16(_next_chunk);
|
||||
_next_chunk += 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Player_V1::set_mplex(uint mplex) {
|
||||
if (mplex == 0)
|
||||
mplex = 65536;
|
||||
_mplex = mplex;
|
||||
_tick_len = _mplex_step * mplex;
|
||||
}
|
||||
|
||||
void Player_V1::nextTick() {
|
||||
if (_next_chunk) {
|
||||
if (_pcjr)
|
||||
nextPCjrCmd();
|
||||
else
|
||||
nextSpeakerCmd();
|
||||
}
|
||||
}
|
||||
|
||||
void Player_V1::generateSpkSamples(int16 *data, uint len) {
|
||||
uint i;
|
||||
|
||||
memset(data, 0, 2 * sizeof(int16) * len);
|
||||
if (_channels[0].freq == 0) {
|
||||
if (_forced_level) {
|
||||
int sample = _forced_level * _volumetable[0];
|
||||
for (i = 0; i < len; i++)
|
||||
data[2*i] = data[2*i+1] = sample;
|
||||
debug(9, "speaker: %8x: forced one", _tick_len);
|
||||
} else if (!_level) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
squareGenerator(0, _channels[0].freq, 0, 0, data, len);
|
||||
debug(9, "speaker: %8x: freq %d %.1f", _tick_len,
|
||||
_channels[0].freq, 1193000.0 / _channels[0].freq);
|
||||
}
|
||||
lowPassFilter(data, len);
|
||||
}
|
||||
|
||||
void Player_V1::generatePCjrSamples(int16 *data, uint len) {
|
||||
uint i, j;
|
||||
uint freq, vol;
|
||||
bool hasdata = false;
|
||||
|
||||
memset(data, 0, 2 * sizeof(int16) * len);
|
||||
|
||||
if (_forced_level) {
|
||||
int sample = _forced_level * _volumetable[0];
|
||||
for (i = 0; i < len; i++)
|
||||
data[2*i] = data[2*i+1] = sample;
|
||||
hasdata = true;
|
||||
debug(9, "channel[4]: %8x: forced one", _tick_len);
|
||||
}
|
||||
|
||||
for (i = 1; i < 3; i++) {
|
||||
freq = _channels[i].freq;
|
||||
if (freq) {
|
||||
for (j = 0; j < i; j++) {
|
||||
if (freq == _channels[j].freq) {
|
||||
/* HACK: this channel is playing at
|
||||
* the same frequency as another.
|
||||
* Synchronize it to the same phase to
|
||||
* prevent interference.
|
||||
*/
|
||||
_timer_count[i] = _timer_count[j];
|
||||
_timer_output ^= (1 << i) &
|
||||
(_timer_output ^ _timer_output << (i - j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
freq = _channels[i].freq;
|
||||
vol = _channels[i].volume;
|
||||
if (!_volumetable[_channels[i].volume]) {
|
||||
_timer_count[i] -= len << FIXP_SHIFT;
|
||||
if (_timer_count[i] < 0)
|
||||
_timer_count[i] = 0;
|
||||
} else if (i < 3) {
|
||||
hasdata = true;
|
||||
squareGenerator(i, freq, vol, 0, data, len);
|
||||
debug(9, "channel[%d]: %8x: freq %d %.1f ; volume %d",
|
||||
i, _tick_len, freq, 111860.0 / freq, vol);
|
||||
} else {
|
||||
int noiseFB = (freq & 4) ? FB_WNOISE : FB_PNOISE;
|
||||
int n = (freq & 3);
|
||||
|
||||
freq = (n == 3) ? 2 * (_channels[2].freq) : 1 << (5 + n);
|
||||
hasdata = true;
|
||||
squareGenerator(i, freq, vol, noiseFB, data, len);
|
||||
debug(9, "channel[%d]: %x: noise freq %d %.1f ; volume %d",
|
||||
i, _tick_len, freq, 111860.0 / freq, vol);
|
||||
}
|
||||
}
|
||||
|
||||
if (_level || hasdata)
|
||||
lowPassFilter(data, len);
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
Reference in New Issue
Block a user