mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
For now this is implemented via config file only. It will be added to the GUI as soon as the enhancements revamping PR is merged, which includes a generic SCUMM engine widget. The game speed changes in this commit were verified against the latest WinUAE version. MI1 apparently has internal routines which keep the speed of game and music constant between PAL and NTSC; LOOM does that only for the music. Older games have their video and sound speed based on the main VBlank rate. iMUSE games do their own thing as usual and already work fine :-)
1960 lines
63 KiB
C++
1960 lines
63 KiB
C++
/* 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_v2a.h"
|
|
#include "scumm/scumm.h"
|
|
|
|
namespace Scumm {
|
|
|
|
#define BASE_FREQUENCY 3579545
|
|
|
|
static uint32 CRCtable[256];
|
|
|
|
|
|
static void InitCRC() {
|
|
const uint32 poly = 0xEDB88320;
|
|
int i, j;
|
|
uint32 n;
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
n = i;
|
|
for (j = 0; j < 8; j++)
|
|
n = (n & 1) ? ((n >> 1) ^ poly) : (n >> 1);
|
|
CRCtable[i] = n;
|
|
}
|
|
}
|
|
|
|
static uint32 GetCRC(byte *data, int len) {
|
|
uint32 CRC = 0xFFFFFFFF;
|
|
int i;
|
|
for (i = 0; i < len; i++)
|
|
CRC = (CRC >> 8) ^ CRCtable[(CRC ^ data[i]) & 0xFF];
|
|
return CRC ^ 0xFFFFFFFF;
|
|
}
|
|
|
|
class V2A_Sound {
|
|
public:
|
|
V2A_Sound() : _id(0), _mod(nullptr) { }
|
|
virtual ~V2A_Sound() {}
|
|
virtual void start(Player_MOD *mod, int id, const byte *data) = 0;
|
|
virtual bool update() = 0;
|
|
virtual void stop() = 0;
|
|
protected:
|
|
int _id;
|
|
Player_MOD *_mod;
|
|
};
|
|
|
|
// unsupported sound effect, print warning message to console
|
|
class V2A_Sound_Unsupported : public V2A_Sound {
|
|
public:
|
|
V2A_Sound_Unsupported() { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
warning("player_v2a - sound %i not supported", id);
|
|
}
|
|
bool update() override { return false; }
|
|
void stop() override { }
|
|
};
|
|
|
|
// template, automatically stops all channels when a sound is silenced
|
|
template<int numChan>
|
|
class V2A_Sound_Base : public V2A_Sound {
|
|
public:
|
|
V2A_Sound_Base() : _offset(0), _size(0), _data(nullptr) { }
|
|
V2A_Sound_Base(uint16 offset, uint16 size) : _offset(offset), _size(size), _data(nullptr) { }
|
|
void stop() override {
|
|
assert(_id);
|
|
for (int i = 0; i < numChan; i++)
|
|
_mod->stopChannel(_id | (i << 8));
|
|
_id = 0;
|
|
free(_data);
|
|
_data = nullptr;
|
|
}
|
|
protected:
|
|
const uint16 _offset;
|
|
const uint16 _size;
|
|
|
|
char *_data;
|
|
};
|
|
|
|
// plays a music track
|
|
class V2A_Sound_Music : public V2A_Sound {
|
|
public:
|
|
V2A_Sound_Music(uint16 instoff, uint16 voloff, uint16 chan1off, uint16 chan2off, uint16 chan3off, uint16 chan4off, uint16 sampoff, bool looped) :
|
|
_instoff(instoff), _voloff(voloff), _chan1off(chan1off), _chan2off(chan2off), _chan3off(chan3off), _chan4off(chan4off), _sampoff(sampoff), _looped(looped) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
|
|
_chan[0].dataptr_i = _chan1off;
|
|
_chan[1].dataptr_i = _chan2off;
|
|
_chan[2].dataptr_i = _chan3off;
|
|
_chan[3].dataptr_i = _chan4off;
|
|
for (int i = 0; i < 4; i++) {
|
|
_chan[i].dataptr = _chan[i].dataptr_i;
|
|
_chan[i].volbase = 0;
|
|
_chan[i].volptr = 0;
|
|
_chan[i].chan = 0;
|
|
_chan[i].dur = 0;
|
|
_chan[i].ticks = 0;
|
|
}
|
|
update();
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
int i, j = 0;
|
|
for (i = 0; i < 4; i++) {
|
|
if (_chan[i].dur) {
|
|
if (!--_chan[i].dur) {
|
|
_mod->stopChannel(_id | (_chan[i].chan << 8));
|
|
} else {
|
|
_mod->setChannelVol(_id | (_chan[i].chan << 8),
|
|
READ_BE_UINT16(_data + _chan[i].volbase + (_chan[i].volptr++ << 1)));
|
|
if (_chan[i].volptr == 0) {
|
|
_mod->stopChannel(_id | (_chan[i].chan << 8));
|
|
_chan[i].dur = 0;
|
|
}
|
|
}
|
|
}
|
|
if (!_chan[i].dataptr) {
|
|
j++;
|
|
continue;
|
|
}
|
|
if (READ_BE_UINT16(_data + _chan[i].dataptr) <= _chan[i].ticks) {
|
|
if (READ_BE_UINT16(_data + _chan[i].dataptr + 2) == 0xFFFF) {
|
|
if (_looped) {
|
|
_chan[i].dataptr = _chan[i].dataptr_i;
|
|
_chan[i].ticks = 0;
|
|
if (READ_BE_UINT16(_data + _chan[i].dataptr) > 0) {
|
|
_chan[i].ticks++;
|
|
continue;
|
|
}
|
|
} else {
|
|
_chan[i].dataptr = 0;
|
|
j++;
|
|
continue;
|
|
}
|
|
}
|
|
int freq = BASE_FREQUENCY / READ_BE_UINT16(_data + _chan[i].dataptr + 2);
|
|
int inst = READ_BE_UINT16(_data + _chan[i].dataptr + 8);
|
|
_chan[i].volbase = _voloff + (READ_BE_UINT16(_data + _instoff + (inst << 5)) << 9);
|
|
_chan[i].volptr = 0;
|
|
_chan[i].chan = READ_BE_UINT16(_data + _chan[i].dataptr + 6) & 0x3;
|
|
|
|
if (_chan[i].dur) // if there's something playing, stop it
|
|
_mod->stopChannel(_id | (_chan[i].chan << 8));
|
|
|
|
_chan[i].dur = READ_BE_UINT16(_data + _chan[i].dataptr + 4);
|
|
|
|
int vol = READ_BE_UINT16(_data + _chan[i].volbase + (_chan[i].volptr++ << 1));
|
|
|
|
int pan;
|
|
if ((_chan[i].chan == 0) || (_chan[i].chan == 3))
|
|
pan = -127;
|
|
else
|
|
pan = 127;
|
|
int offset = READ_BE_UINT16(_data + _instoff + (inst << 5) + 0x14);
|
|
int len = READ_BE_UINT16(_data + _instoff + (inst << 5) + 0x18);
|
|
int loopoffset = READ_BE_UINT16(_data + _instoff + (inst << 5) + 0x16);
|
|
int looplen = READ_BE_UINT16(_data + _instoff + (inst << 5) + 0x10);
|
|
|
|
int size = len + looplen;
|
|
char *data = (char *)malloc(size);
|
|
memcpy(data, _data + _sampoff + offset, len);
|
|
memcpy(data + len, _data + _sampoff + loopoffset, looplen);
|
|
|
|
_mod->startChannel(_id | (_chan[i].chan << 8), data, size, freq, vol, len, looplen + len, pan);
|
|
_chan[i].dataptr += 16;
|
|
}
|
|
_chan[i].ticks++;
|
|
}
|
|
if (j == 4)
|
|
return false;
|
|
return true;
|
|
}
|
|
void stop() override {
|
|
assert(_id);
|
|
for (int i = 0; i < 4; i++) {
|
|
if (_chan[i].dur)
|
|
_mod->stopChannel(_id | (_chan[i].chan << 8));
|
|
}
|
|
free(_data);
|
|
_id = 0;
|
|
}
|
|
private:
|
|
const uint16 _instoff;
|
|
const uint16 _voloff;
|
|
const uint16 _chan1off;
|
|
const uint16 _chan2off;
|
|
const uint16 _chan3off;
|
|
const uint16 _chan4off;
|
|
const uint16 _sampoff;
|
|
const bool _looped;
|
|
|
|
char *_data;
|
|
struct tchan {
|
|
uint16 dataptr_i;
|
|
uint16 dataptr;
|
|
uint16 volbase;
|
|
uint8 volptr;
|
|
uint16 chan;
|
|
uint16 dur;
|
|
uint16 ticks;
|
|
} _chan[4];
|
|
};
|
|
|
|
// plays a single waveform
|
|
class V2A_Sound_Single : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Single(uint16 offset, uint16 size, uint16 freq, uint8 vol) :
|
|
V2A_Sound_Base<1>(offset, size), _freq(freq), _vol(vol) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, data + _offset, _size);
|
|
int vol = (_vol << 2) | (_vol >> 4);
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, vol, 0, 0);
|
|
_ticks = 1 + (60 * _size * _freq) / BASE_FREQUENCY;
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
_ticks--;
|
|
if (!_ticks) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq;
|
|
const uint8 _vol;
|
|
|
|
int _ticks;
|
|
};
|
|
|
|
// plays a single looped waveform
|
|
class V2A_Sound_SingleLooped : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_SingleLooped(uint16 offset, uint16 size, uint16 freq, uint8 vol, uint16 loopoffset, uint16 loopsize) :
|
|
V2A_Sound_Base<1>(offset, size), _loopoffset(loopoffset), _loopsize(loopsize), _freq(freq), _vol(vol) { }
|
|
V2A_Sound_SingleLooped(uint16 offset, uint16 size, uint16 freq, uint8 vol) :
|
|
V2A_Sound_Base<1>(offset, size), _loopoffset(0), _loopsize(size), _freq(freq), _vol(vol) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, data + _offset, _size);
|
|
int vol = (_vol << 2) | (_vol >> 4);
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, vol, _loopoffset, _loopoffset + _loopsize);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _loopoffset;
|
|
const uint16 _loopsize;
|
|
const uint16 _freq;
|
|
const uint8 _vol;
|
|
};
|
|
|
|
// plays two looped waveforms
|
|
class V2A_Sound_MultiLooped : public V2A_Sound_Base<2> {
|
|
public:
|
|
V2A_Sound_MultiLooped(uint16 offset, uint16 size, uint16 freq1, uint8 vol1, uint16 freq2, uint8 vol2) :
|
|
V2A_Sound_Base<2>(offset, size), _freq1(freq1), _vol1(vol1), _freq2(freq2), _vol2(vol2) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data1 = (char *)malloc(_size);
|
|
char *tmp_data2 = (char *)malloc(_size);
|
|
memcpy(tmp_data1, data + _offset, _size);
|
|
memcpy(tmp_data2, data + _offset, _size);
|
|
int vol1 = (_vol1 << 1) | (_vol1 >> 5);
|
|
int vol2 = (_vol2 << 1) | (_vol2 >> 5);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _freq1, vol1, 0, _size, -127);
|
|
_mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _freq2, vol2, 0, _size, 127);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq1;
|
|
const uint8 _vol1;
|
|
const uint16 _freq2;
|
|
const uint8 _vol2;
|
|
};
|
|
|
|
// plays two looped waveforms for a fixed number of frames
|
|
class V2A_Sound_MultiLoopedDuration : public V2A_Sound_MultiLooped {
|
|
public:
|
|
V2A_Sound_MultiLoopedDuration(uint16 offset, uint16 size, uint16 freq1, uint8 vol1, uint16 freq2, uint8 vol2, uint16 numframes) :
|
|
V2A_Sound_MultiLooped(offset, size, freq1, vol1, freq2, vol2), _duration(numframes) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
V2A_Sound_MultiLooped::start(mod, id, data);
|
|
_ticks = 0;
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
_ticks++;
|
|
if (_ticks >= _duration)
|
|
return false;
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _duration;
|
|
|
|
int _ticks;
|
|
};
|
|
|
|
// plays a single looped waveform which starts at one frequency and bends to another frequency, where it remains until stopped
|
|
class V2A_Sound_SingleLoopedPitchbend : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_SingleLoopedPitchbend(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint8 vol, uint8 step) :
|
|
V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2), _vol(vol), _step(step) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, data + _offset, _size);
|
|
int vol = (_vol << 2) | (_vol >> 4);
|
|
_curfreq = _freq1;
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, vol, 0, _size);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
if (_freq1 < _freq2) {
|
|
_curfreq += _step;
|
|
if (_curfreq > _freq2)
|
|
_curfreq = _freq2;
|
|
else
|
|
_mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
|
|
} else {
|
|
_curfreq -= _step;
|
|
if (_curfreq < _freq2)
|
|
_curfreq = _freq2;
|
|
else
|
|
_mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq1;
|
|
const uint16 _freq2;
|
|
const uint8 _vol;
|
|
const uint16 _step;
|
|
|
|
uint16 _curfreq;
|
|
};
|
|
|
|
// plays a single looped waveform starting at a specific frequency/volume, dropping in frequency and fading volume to zero
|
|
// used when Maniac Mansion explodes
|
|
class V2A_Sound_Special_Maniac69 : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_Maniac69(uint16 offset, uint16 size, uint16 freq, uint8 vol) :
|
|
V2A_Sound_Base<1>(offset, size), _freq(freq), _vol(vol) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, data + _offset, _size);
|
|
_curvol = (_vol << 3) | (_vol >> 3);
|
|
_curfreq = _freq;
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, _curvol >> 1, 0, _size);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
_curfreq += 2;
|
|
_mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
|
|
_curvol--;
|
|
if (_curvol == 0)
|
|
return false;
|
|
_mod->setChannelVol(_id, _curvol >> 1);
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq;
|
|
const uint8 _vol;
|
|
|
|
uint16 _curfreq;
|
|
uint16 _curvol;
|
|
};
|
|
|
|
// plays a single looped waveform, fading the volume from zero to maximum at one rate, then back to zero at another rate
|
|
// used when a microwave oven goes 'Ding'
|
|
class V2A_Sound_Special_ManiacDing : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_ManiacDing(uint16 offset, uint16 size, uint16 freq, uint8 fadeinrate, uint8 fadeoutrate) :
|
|
V2A_Sound_Base<1>(offset, size), _freq(freq), _fade1(fadeinrate), _fade2(fadeoutrate) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, data + _offset, _size);
|
|
_curvol = 1;
|
|
_dir = 0;
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, _curvol, 0, _size);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
if (_dir == 0) {
|
|
_curvol += _fade1;
|
|
if (_curvol > 0x3F) {
|
|
_curvol = 0x3F;
|
|
_dir = 1;
|
|
}
|
|
} else {
|
|
_curvol -= _fade2;
|
|
if (_curvol < 1)
|
|
return false;
|
|
}
|
|
_mod->setChannelVol(_id, (_curvol << 2) | (_curvol >> 4));
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq;
|
|
const uint16 _fade1;
|
|
const uint16 _fade2;
|
|
|
|
int _curvol;
|
|
int _dir;
|
|
};
|
|
|
|
// plays two looped waveforms, fading the volume from zero to maximum at one rate, then back to zero at another rate
|
|
// used in Zak McKracken for several stereo 'Ding' sounds
|
|
class V2A_Sound_Special_ZakStereoDing : public V2A_Sound_Base<2> {
|
|
public:
|
|
V2A_Sound_Special_ZakStereoDing(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint8 fadeinrate, uint8 fadeoutrate) :
|
|
V2A_Sound_Base<2>(offset, size), _freq1(freq1), _freq2(freq2), _fade1(fadeinrate), _fade2(fadeoutrate) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data1 = (char *)malloc(_size);
|
|
char *tmp_data2 = (char *)malloc(_size);
|
|
memcpy(tmp_data1, data + _offset, _size);
|
|
memcpy(tmp_data2, data + _offset, _size);
|
|
_curvol = 1;
|
|
_dir = 0;
|
|
_mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _freq1, 1, 0, _size, -127);
|
|
_mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _freq2, 1, 0, _size, 127);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
if (_dir == 0) {
|
|
_curvol += _fade1;
|
|
if (_curvol > 0x3F) {
|
|
_curvol = 0x3F;
|
|
_dir = 1;
|
|
}
|
|
} else {
|
|
_curvol -= _fade2;
|
|
if (_curvol < 1)
|
|
return false;
|
|
}
|
|
_mod->setChannelVol(_id | 0x000, (_curvol << 1) | (_curvol >> 5));
|
|
_mod->setChannelVol(_id | 0x100, (_curvol << 1) | (_curvol >> 5));
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq1;
|
|
const uint16 _freq2;
|
|
const uint16 _fade1;
|
|
const uint16 _fade2;
|
|
|
|
int _curvol;
|
|
int _dir;
|
|
};
|
|
|
|
// plays a single looped waveform, starting at one frequency and at full volume, bending down to another frequency, and then fading volume to zero
|
|
// used in Maniac Mansion for the tentacle sounds
|
|
class V2A_Sound_Special_ManiacTentacle : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_ManiacTentacle(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint16 step) :
|
|
V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2), _step(step) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, data + _offset, _size);
|
|
_curfreq = _freq1;
|
|
_curvol = 0x3F;
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, (_curvol << 2) | (_curvol >> 4), 0, _size);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
if (_curfreq > _freq2)
|
|
_curvol = 0x3F + _freq2 - _curfreq;
|
|
if (_curvol < 1)
|
|
return false;
|
|
_curfreq += _step;
|
|
_mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
|
|
_mod->setChannelVol(_id, (_curvol << 2) | (_curvol >> 4));
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq1;
|
|
const uint16 _freq2;
|
|
const uint16 _step;
|
|
|
|
uint16 _curfreq;
|
|
int _curvol;
|
|
};
|
|
|
|
// plays a single looped waveform, starting at one frequency, bending down to another frequency, and then back up to the original frequency
|
|
// used for electronic noises
|
|
class V2A_Sound_Special_Maniac59 : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_Maniac59(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint16 step, uint8 vol) :
|
|
V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2), _step(step), _vol(vol) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, data + _offset, _size);
|
|
int vol = (_vol << 2) | (_vol >> 4);
|
|
_curfreq = _freq1;
|
|
_dir = 2;
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, vol, 0, _size);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
if (_dir == 2) {
|
|
_curfreq += _step;
|
|
if (_curfreq > _freq2) {
|
|
_curfreq = _freq2;
|
|
_dir = 1;
|
|
}
|
|
_mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
|
|
} else if (_dir == 1) {
|
|
_curfreq -= _step;
|
|
if (_curfreq < _freq1) {
|
|
_curfreq = _freq1;
|
|
_dir = 0;
|
|
}
|
|
_mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq1;
|
|
const uint16 _freq2;
|
|
const uint16 _step;
|
|
const uint8 _vol;
|
|
|
|
uint16 _curfreq;
|
|
int _dir;
|
|
};
|
|
|
|
// plays a single looped waveform, simultaneously bending the frequency downward and slowly fading volume to zero
|
|
// don't remember where this one is used
|
|
class V2A_Sound_Special_Maniac61 : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_Maniac61(uint16 offset, uint16 size, uint16 freq1, uint16 freq2) :
|
|
V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, data + _offset, _size);
|
|
_curfreq = _freq1;
|
|
_curvol = 0x3F;
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, (_curvol << 2) | (_curvol >> 4), 0, _size);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
_curfreq++;
|
|
if (!(_curfreq & 3))
|
|
_curvol--;
|
|
if ((_curfreq == _freq2) || (_curvol == 0))
|
|
return false;
|
|
_mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
|
|
_mod->setChannelVol(_id, (_curvol << 2) | (_curvol >> 4));
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq1;
|
|
const uint16 _freq2;
|
|
|
|
uint16 _curfreq;
|
|
uint8 _curvol;
|
|
};
|
|
|
|
// intermittently plays two looped waveforms for a specific duration
|
|
// used for ringing telephones
|
|
class V2A_Sound_Special_ManiacPhone : public V2A_Sound_Base<2> {
|
|
public:
|
|
V2A_Sound_Special_ManiacPhone(uint16 offset, uint16 size, uint16 freq1, uint8 vol1, uint16 freq2, uint8 vol2, uint16 numframes, uint8 playwidth, uint8 loopwidth) :
|
|
V2A_Sound_Base<2>(offset, size), _freq1(freq1), _vol1(vol1), _freq2(freq2), _vol2(vol2), _duration(numframes), _playwidth(playwidth), _loopwidth(loopwidth) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
soundon();
|
|
_ticks = 0;
|
|
_loop = 0;
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
if (_loop == _playwidth) {
|
|
_mod->stopChannel(_id | 0x000);
|
|
_mod->stopChannel(_id | 0x100);
|
|
}
|
|
if (_loop == _loopwidth) {
|
|
_loop = 0;
|
|
soundon();
|
|
}
|
|
_loop++;
|
|
_ticks++;
|
|
if (_ticks >= _duration)
|
|
return false;
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq1;
|
|
const uint8 _vol1;
|
|
const uint16 _freq2;
|
|
const uint8 _vol2;
|
|
const uint16 _duration;
|
|
const uint8 _playwidth;
|
|
const uint8 _loopwidth;
|
|
|
|
int _ticks;
|
|
int _loop;
|
|
|
|
void soundon() {
|
|
char *tmp_data1 = (char *)malloc(_size);
|
|
char *tmp_data2 = (char *)malloc(_size);
|
|
memcpy(tmp_data1, _data + _offset, _size);
|
|
memcpy(tmp_data2, _data + _offset, _size);
|
|
int vol1 = (_vol1 << 1) | (_vol1 >> 5);
|
|
int vol2 = (_vol2 << 1) | (_vol2 >> 5);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _freq1, vol1, 0, _size, -127);
|
|
_mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _freq2, vol2, 0, _size, 127);
|
|
}
|
|
};
|
|
|
|
// intermittently plays a single waveform for a specified duration
|
|
// used when applying a wrench to a pipe
|
|
class V2A_Sound_Special_Maniac46 : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_Maniac46(uint16 offset, uint16 size, uint16 freq, uint8 vol, uint8 loopwidth, uint8 numloops) :
|
|
V2A_Sound_Base<1>(offset, size), _freq(freq), _vol(vol), _loopwidth(loopwidth), _numloops(numloops) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
soundon();
|
|
_loop = 0;
|
|
_loopctr = 0;
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
_loop++;
|
|
if (_loop == _loopwidth) {
|
|
_loop = 0;
|
|
_loopctr++;
|
|
if (_loopctr == _numloops)
|
|
return false;
|
|
_mod->stopChannel(_id);
|
|
soundon();
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq;
|
|
const uint8 _vol;
|
|
const uint8 _loopwidth;
|
|
const uint8 _numloops;
|
|
|
|
int _loop;
|
|
int _loopctr;
|
|
|
|
void soundon() {
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, _data + _offset, _size);
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, (_vol << 2) | (_vol >> 4), 0, 0);
|
|
}
|
|
};
|
|
|
|
// plays a single waveform at irregular intervals for a specified number of frames, possibly looped
|
|
// used for typewriter noises, as well as tapping on the bus in Zak McKracken
|
|
class V2A_Sound_Special_ManiacTypewriter : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_ManiacTypewriter(uint16 offset, uint16 size, uint16 freq, uint8 vol, uint8 numdurs, const uint8 *durations, bool looped) :
|
|
V2A_Sound_Base<1>(offset, size), _freq(freq), _vol(vol), _numdurs(numdurs), _durations(durations), _looped(looped) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
soundon();
|
|
_curdur = 0;
|
|
_ticks = _durations[_curdur++];
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
_ticks--;
|
|
if (!_ticks) {
|
|
if (_curdur == _numdurs) {
|
|
if (_looped)
|
|
_curdur = 0;
|
|
else
|
|
return false;
|
|
}
|
|
_mod->stopChannel(_id);
|
|
soundon();
|
|
_ticks = _durations[_curdur++];
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq;
|
|
const uint8 _vol;
|
|
const uint8 _numdurs;
|
|
const uint8 *_durations;
|
|
const bool _looped;
|
|
|
|
int _ticks;
|
|
int _curdur;
|
|
|
|
void soundon() {
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, _data + _offset, _size);
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, (_vol << 2) | (_vol >> 4), 0, 0);
|
|
}
|
|
};
|
|
|
|
// plays two looped waveforms pitch bending up at various predefined rates
|
|
// used for some sort of siren-like noise in Maniac Mansion
|
|
class V2A_Sound_Special_Maniac44 : public V2A_Sound_Base<2> {
|
|
public:
|
|
V2A_Sound_Special_Maniac44(uint16 offset1, uint16 size1, uint16 offset2, uint16 size2, uint16 freq1, uint16 freq2, uint8 vol) :
|
|
_offset1(offset1), _size1(size1), _offset2(offset2), _size2(size2), _freq1(freq1), _freq2(freq2), _vol(vol) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
|
|
_loopnum = 1;
|
|
_step = 2;
|
|
_curfreq = _freq1;
|
|
|
|
soundon(_data + _offset1, _size1);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
_mod->setChannelFreq(_id | 0x000, BASE_FREQUENCY / _curfreq);
|
|
_mod->setChannelFreq(_id | 0x100, BASE_FREQUENCY / (_curfreq + 3));
|
|
_curfreq -= _step;
|
|
if (_loopnum == 7) {
|
|
if ((BASE_FREQUENCY / _curfreq) >= 65536)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
if (_curfreq >= _freq2)
|
|
return true;
|
|
const char steps[8] = {0, 2, 2, 3, 4, 8, 15, 2};
|
|
_curfreq = _freq1;
|
|
_step = steps[++_loopnum];
|
|
if (_loopnum == 7) {
|
|
_mod->stopChannel(_id | 0x000);
|
|
_mod->stopChannel(_id | 0x100);
|
|
soundon(_data + _offset2, _size2);
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _offset1;
|
|
const uint16 _size1;
|
|
const uint16 _offset2;
|
|
const uint16 _size2;
|
|
const uint16 _freq1;
|
|
const uint16 _freq2;
|
|
const uint8 _vol;
|
|
|
|
int _curfreq;
|
|
uint16 _loopnum;
|
|
uint16 _step;
|
|
|
|
void soundon(const char *data, int size) {
|
|
char *tmp_data1 = (char *)malloc(size);
|
|
char *tmp_data2 = (char *)malloc(size);
|
|
memcpy(tmp_data1, data, size);
|
|
memcpy(tmp_data2, data, size);
|
|
int vol = (_vol << 1) | (_vol >> 5);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, size, BASE_FREQUENCY / _curfreq, vol, 0, size, -127);
|
|
_mod->startChannel(_id | 0x100, tmp_data2, size, BASE_FREQUENCY / (_curfreq + 3), vol, 0, size, 127);
|
|
}
|
|
};
|
|
|
|
// plays 4 looped waveforms, each at modulating frequencies
|
|
// used for the siren noise in Maniac Mansion
|
|
class V2A_Sound_Special_Maniac32 : public V2A_Sound_Base<4> {
|
|
public:
|
|
V2A_Sound_Special_Maniac32(uint16 offset1, uint16 size1, uint16 offset2, uint16 size2, uint8 vol) :
|
|
_offset1(offset1), _size1(size1), _offset2(offset2), _size2(size2), _vol(vol) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
|
|
_freq1 = 0x02D0;
|
|
_step1 = -0x000A;
|
|
_freq2 = 0x0122;
|
|
_step2 = 0x000A;
|
|
_freq3 = 0x02BC;
|
|
_step3 = -0x0005;
|
|
_freq4 = 0x010E;
|
|
_step4 = 0x0007;
|
|
|
|
char *tmp_data1 = (char *)malloc(_size1);
|
|
char *tmp_data2 = (char *)malloc(_size2);
|
|
char *tmp_data3 = (char *)malloc(_size1);
|
|
char *tmp_data4 = (char *)malloc(_size2);
|
|
memcpy(tmp_data1, data + _offset1, _size1);
|
|
memcpy(tmp_data2, data + _offset2, _size2);
|
|
memcpy(tmp_data3, data + _offset1, _size1);
|
|
memcpy(tmp_data4, data + _offset2, _size2);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, _size1, BASE_FREQUENCY / _freq1, _vol, 0, _size1, -127);
|
|
_mod->startChannel(_id | 0x100, tmp_data2, _size2, BASE_FREQUENCY / _freq2, _vol, 0, _size2, 127);
|
|
_mod->startChannel(_id | 0x200, tmp_data3, _size1, BASE_FREQUENCY / _freq3, _vol, 0, _size1, 127);
|
|
_mod->startChannel(_id | 0x300, tmp_data4, _size2, BASE_FREQUENCY / _freq4, _vol, 0, _size2, -127);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
updatefreq(_freq1, _step1, 0x00AA, 0x00FA);
|
|
updatefreq(_freq2, _step2, 0x019A, 0x03B6);
|
|
updatefreq(_freq3, _step3, 0x00AA, 0x00FA);
|
|
updatefreq(_freq4, _step4, 0x019A, 0x03B6);
|
|
_mod->setChannelFreq(_id | 0x000, BASE_FREQUENCY / _freq1);
|
|
_mod->setChannelFreq(_id | 0x100, BASE_FREQUENCY / _freq2);
|
|
_mod->setChannelFreq(_id | 0x200, BASE_FREQUENCY / _freq3);
|
|
_mod->setChannelFreq(_id | 0x300, BASE_FREQUENCY / _freq4);
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _offset1;
|
|
const uint16 _size1;
|
|
const uint16 _offset2;
|
|
const uint16 _size2;
|
|
const uint8 _vol;
|
|
|
|
uint16 _freq1;
|
|
int16 _step1;
|
|
uint16 _freq2;
|
|
int16 _step2;
|
|
uint16 _freq3;
|
|
int16 _step3;
|
|
uint16 _freq4;
|
|
int16 _step4;
|
|
|
|
void updatefreq(uint16 &freq, int16 &step, uint16 min, uint16 max) {
|
|
freq += step;
|
|
if (freq <= min) {
|
|
freq = min;
|
|
step = -step;
|
|
}
|
|
if (freq >= max) {
|
|
freq = max;
|
|
step = -step;
|
|
}
|
|
}
|
|
};
|
|
|
|
// plays 4 looped waveforms
|
|
// used in the white crystal chamber
|
|
class V2A_Sound_Special_Zak70 : public V2A_Sound_Base<4> {
|
|
public:
|
|
V2A_Sound_Special_Zak70(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint16 freq3, uint16 freq4, uint8 vol) :
|
|
V2A_Sound_Base<4>(offset, size), _freq1(freq1), _freq2(freq2), _freq3(freq3), _freq4(freq4), _vol(vol) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
|
|
char *tmp_data1 = (char *)malloc(_size);
|
|
char *tmp_data2 = (char *)malloc(_size);
|
|
char *tmp_data3 = (char *)malloc(_size);
|
|
char *tmp_data4 = (char *)malloc(_size);
|
|
memcpy(tmp_data1, data + _offset, _size);
|
|
memcpy(tmp_data2, data + _offset, _size);
|
|
memcpy(tmp_data3, data + _offset, _size);
|
|
memcpy(tmp_data4, data + _offset, _size);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _freq1, _vol, 0, _size, -127);
|
|
_mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _freq2, _vol, 0, _size, 127);
|
|
_mod->startChannel(_id | 0x200, tmp_data3, _size, BASE_FREQUENCY / _freq3, _vol, 0, _size, 127);
|
|
_mod->startChannel(_id | 0x300, tmp_data4, _size, BASE_FREQUENCY / _freq4, _vol, 0, _size, -127);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
return true;
|
|
}
|
|
protected:
|
|
const uint16 _freq1;
|
|
const uint16 _freq2;
|
|
const uint16 _freq3;
|
|
const uint16 _freq4;
|
|
const uint8 _vol;
|
|
};
|
|
|
|
// plays 4 looped waveforms and fades volume to zero after a specific delay
|
|
// used when the Mindbender disappears
|
|
class V2A_Sound_Special_Zak101 : public V2A_Sound_Special_Zak70 {
|
|
public:
|
|
V2A_Sound_Special_Zak101(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint16 freq3, uint16 freq4, uint8 vol, uint16 dur) :
|
|
V2A_Sound_Special_Zak70(offset, size, freq1, freq2, freq3, freq4, vol), _dur(dur) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
V2A_Sound_Special_Zak70::start(mod, id, data);
|
|
_ticks = _dur;
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
if (!--_ticks)
|
|
return false;
|
|
if (_ticks < _vol) {
|
|
_mod->setChannelVol(_id | 0x000, _ticks);
|
|
_mod->setChannelVol(_id | 0x100, _ticks);
|
|
_mod->setChannelVol(_id | 0x200, _ticks);
|
|
_mod->setChannelVol(_id | 0x300, _ticks);
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _dur;
|
|
|
|
int _ticks;
|
|
};
|
|
|
|
// plays a single looped waveform and slowly fades volume to zero
|
|
// used when refilling oxygen
|
|
class V2A_Sound_Special_Zak37 : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_Zak37(uint16 offset, uint16 size, uint16 freq, uint8 vol) :
|
|
V2A_Sound_Base<1>(offset, size), _freq(freq), _vol(vol) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, data + _offset, _size);
|
|
_curvol = _vol << 2;
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, _curvol, 0, _size);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
if (!--_curvol)
|
|
return false;
|
|
_mod->setChannelVol(_id, _curvol);
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq;
|
|
const uint8 _vol;
|
|
|
|
int _curvol;
|
|
};
|
|
|
|
// plays a single looped waveform, slowly bending from one frequency to another and then slowly fading volume from max to zero
|
|
// used in Zak for airplane taking off and landing
|
|
class V2A_Sound_Special_ZakAirplane : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_ZakAirplane(uint16 offset, uint16 size, uint16 freq1, uint16 freq2) :
|
|
V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, data + _offset, _size);
|
|
_curfreq = _freq1;
|
|
_curvol = 0x3F;
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, (_curvol << 2) | (_curvol >> 4), 0, _size);
|
|
_ticks = 0;
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
_ticks++;
|
|
if (_ticks < 4)
|
|
return true;
|
|
_ticks = 0;
|
|
if (_curfreq == _freq2) {
|
|
_curvol--;
|
|
if (_curvol == 0)
|
|
return false;
|
|
_mod->setChannelVol(_id, (_curvol << 2) | (_curvol >> 4));
|
|
} else {
|
|
if (_freq1 < _freq2)
|
|
_curfreq++;
|
|
else
|
|
_curfreq--;
|
|
_mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq1;
|
|
const uint16 _freq2;
|
|
|
|
uint16 _curfreq;
|
|
int _curvol;
|
|
int _ticks;
|
|
};
|
|
|
|
// plays 4 looped waveforms, starting at specific frequencies and bending at different rates while fading volume to zero
|
|
// used when the white crystal machine turns off
|
|
class V2A_Sound_Special_Zak71 : public V2A_Sound_Base<4> {
|
|
public:
|
|
V2A_Sound_Special_Zak71(uint16 offset, uint16 size) :
|
|
_offset(offset), _size(size) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
|
|
_freq1 = 0x00C8;
|
|
_freq2 = 0x0190;
|
|
_freq3 = 0x0320;
|
|
_freq4 = 0x0640;
|
|
_vol = 0x78;
|
|
|
|
char *tmp_data1 = (char *)malloc(_size);
|
|
char *tmp_data2 = (char *)malloc(_size);
|
|
char *tmp_data3 = (char *)malloc(_size);
|
|
char *tmp_data4 = (char *)malloc(_size);
|
|
memcpy(tmp_data1, data + _offset, _size);
|
|
memcpy(tmp_data2, data + _offset, _size);
|
|
memcpy(tmp_data3, data + _offset, _size);
|
|
memcpy(tmp_data4, data + _offset, _size);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _freq1, MIN((_vol >> 1) + 3, 0x32), 0, _size, -127);
|
|
_mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _freq2, MIN((_vol >> 1) + 3, 0x32), 0, _size, 127);
|
|
_mod->startChannel(_id | 0x200, tmp_data3, _size, BASE_FREQUENCY / _freq3, MIN((_vol >> 1) + 3, 0x32), 0, _size, 127);
|
|
_mod->startChannel(_id | 0x300, tmp_data4, _size, BASE_FREQUENCY / _freq4, MIN((_vol >> 1) + 3, 0x32), 0, _size, -127);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
_freq1 += 0x14;
|
|
_freq2 += 0x1E;
|
|
_freq3 += 0x32;
|
|
_freq4 += 0x50;
|
|
_mod->setChannelFreq(_id | 0x000, BASE_FREQUENCY / _freq1);
|
|
_mod->setChannelFreq(_id | 0x100, BASE_FREQUENCY / _freq2);
|
|
_mod->setChannelFreq(_id | 0x200, BASE_FREQUENCY / _freq3);
|
|
_mod->setChannelFreq(_id | 0x300, BASE_FREQUENCY / _freq4);
|
|
_vol--;
|
|
if (_vol == 0)
|
|
return false;
|
|
_mod->setChannelVol(_id | 0x000, MIN((_vol >> 1) + 3, 0x32));
|
|
_mod->setChannelVol(_id | 0x100, MIN((_vol >> 1) + 3, 0x32));
|
|
_mod->setChannelVol(_id | 0x200, MIN((_vol >> 1) + 3, 0x32));
|
|
_mod->setChannelVol(_id | 0x300, MIN((_vol >> 1) + 3, 0x32));
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _offset;
|
|
const uint16 _size;
|
|
|
|
uint16 _freq1;
|
|
uint16 _freq2;
|
|
uint16 _freq3;
|
|
uint16 _freq4;
|
|
uint8 _vol;
|
|
};
|
|
|
|
// plays a single looped waveform, bending the frequency upward at a varying rate
|
|
// used when the Skolarian device activates
|
|
class V2A_Sound_Special_Zak99 : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_Zak99(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint8 vol) :
|
|
V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2), _vol(vol) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, data + _offset, _size);
|
|
_curfreq = _freq1;
|
|
_mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, (_vol << 2) | (_vol >> 4), 0, _size);
|
|
_bendrate = 8;
|
|
_bendctr = 100;
|
|
_holdctr = 30;
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
if (_curfreq >= _freq2) {
|
|
_mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
|
|
_curfreq -= _bendrate;
|
|
if (--_bendctr)
|
|
return true;
|
|
_bendrate--;
|
|
if (_bendrate < 2)
|
|
_bendrate = 2;
|
|
} else {
|
|
if (!--_holdctr)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _freq1;
|
|
const uint16 _freq2;
|
|
const uint16 _vol;
|
|
|
|
uint16 _curfreq;
|
|
uint16 _bendrate;
|
|
uint16 _bendctr;
|
|
uint16 _holdctr;
|
|
};
|
|
|
|
// plays one waveform, then switches to a different looped waveform and slowly fades volume to zero
|
|
// used when depressurizing the hostel
|
|
class V2A_Sound_Special_Zak54 : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_Zak54(uint16 offset1, uint16 size1, uint16 offset2, uint16 size2, uint16 freq) :
|
|
_offset1(offset1), _size1(size1), _offset2(offset2), _size2(size2), _freq(freq) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
char *tmp_data = (char *)malloc(_size1);
|
|
memcpy(tmp_data, data + _offset1, _size1);
|
|
_vol = 0xFC;
|
|
_mod->startChannel(_id, tmp_data, _size1, BASE_FREQUENCY / _freq, _vol, 0, _size1);
|
|
_loop = _size1 * _freq * 60 / BASE_FREQUENCY;
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
if (!_loop) {
|
|
_vol--;
|
|
if (_vol)
|
|
_mod->setChannelVol(_id, _vol);
|
|
else
|
|
return false;
|
|
} else if (!--_loop) {
|
|
_mod->stopChannel(_id);
|
|
char *tmp_data = (char *)malloc(_size2);
|
|
memcpy(tmp_data, _data + _offset2, _size2);
|
|
_mod->startChannel(_id, tmp_data, _size2, BASE_FREQUENCY / _freq, _vol, 0, _size2);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const uint16 _offset1;
|
|
const uint16 _offset2;
|
|
const uint16 _size1;
|
|
const uint16 _size2;
|
|
const uint16 _freq;
|
|
|
|
int _vol;
|
|
int _loop;
|
|
};
|
|
|
|
// plays 2 looped waveforms at different frequencies, pulsing at different frequencies and ramping the volume up and down once
|
|
// used when abducted at the Bermuda Triangle
|
|
class V2A_Sound_Special_Zak110 : public V2A_Sound_Base<2> {
|
|
public:
|
|
V2A_Sound_Special_Zak110(uint16 offset1, uint16 size1, uint16 offset2, uint16 size2, uint16 freq1, uint16 freq2) :
|
|
_offset1(offset1), _size1(size1), _offset2(offset2), _size2(size2), _freq1(freq1), _freq2(freq2) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
|
|
_loopnum = 0;
|
|
_vol = 0x1500;
|
|
_beepcount = 0;
|
|
}
|
|
bool update() override {
|
|
char *tmp_data;
|
|
assert(_id);
|
|
|
|
int vol = (((_vol >> 7) & 0x7E) | ((_vol >> 15) & 0x01));
|
|
_beepcount++;
|
|
|
|
switch (_beepcount & 0x3) {
|
|
case 0:
|
|
_mod->stopChannel(_id | 0x000);
|
|
break;
|
|
case 1:
|
|
tmp_data = (char *)malloc(_size1);
|
|
memcpy(tmp_data, _data + _offset1, _size1);
|
|
_mod->startChannel(_id | 0x000, tmp_data, _size1, BASE_FREQUENCY / _freq1, vol, 0, _size1, -127);
|
|
break;
|
|
default:
|
|
_mod->setChannelVol(_id | 0x000, vol);
|
|
break;
|
|
}
|
|
|
|
switch (_beepcount & 0x7) {
|
|
case 0:
|
|
_mod->stopChannel(_id | 0x100);
|
|
break;
|
|
case 1:
|
|
tmp_data = (char *)malloc(_size2);
|
|
memcpy(tmp_data, _data + _offset2, _size2);
|
|
_mod->startChannel(_id | 0x100, tmp_data, _size2, BASE_FREQUENCY / _freq2, vol, 0, _size2, 127);
|
|
break;
|
|
default:
|
|
_mod->setChannelVol(_id | 0x100, vol);
|
|
break;
|
|
}
|
|
|
|
if (_loopnum == 0) {
|
|
_vol += 0x80;
|
|
if (_vol == 0x4000) {
|
|
_vol = 0x3F00;
|
|
_loopnum = 1;
|
|
}
|
|
} else if (_loopnum == 1) {
|
|
_vol -= 0x20;
|
|
if (_vol == 0x2000)
|
|
_loopnum = 2;
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _offset1;
|
|
const uint16 _size1;
|
|
const uint16 _offset2;
|
|
const uint16 _size2;
|
|
const uint16 _freq1;
|
|
const uint16 _freq2;
|
|
|
|
uint16 _loopnum;
|
|
uint16 _vol;
|
|
uint16 _beepcount;
|
|
};
|
|
|
|
// plays a stereo siren, sweeping up and down quickly several times before sweeping up slowly, stopping, and then going silent
|
|
// door orb sound in the Mars Face
|
|
class V2A_Sound_Special_Zak32 : public V2A_Sound_Base<2> {
|
|
public:
|
|
V2A_Sound_Special_Zak32(uint16 offset1, uint16 offset2, uint16 size1, uint16 size2) :
|
|
_offset1(offset1), _offset2(offset2), _size1(size1), _size2(size2) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
|
|
_loopnum = 1;
|
|
_freqmod = -4;
|
|
_freq = 0x00C8;
|
|
|
|
char *tmp_data1 = (char *)malloc(_size1);
|
|
char *tmp_data2 = (char *)malloc(_size1);
|
|
memcpy(tmp_data1, _data + _offset1, _size1);
|
|
memcpy(tmp_data2, _data + _offset1, _size1);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, _size1, BASE_FREQUENCY / _freq, 0x7F, 0, _size1, -127);
|
|
_mod->startChannel(_id | 0x100, tmp_data2, _size1, BASE_FREQUENCY / (_freq + 3), 0x7F, 0, _size1, 127);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
|
|
if (_loopnum < 7) {
|
|
_mod->setChannelFreq(_id | 0x000, BASE_FREQUENCY / _freq);
|
|
_mod->setChannelFreq(_id | 0x100, BASE_FREQUENCY / (_freq + 3));
|
|
_freq += _freqmod;
|
|
if (_freq <= 0x80)
|
|
_freqmod = -_freqmod;
|
|
else if (_freq >= 0xC8) {
|
|
_freqmod = -_freqmod;
|
|
_loopnum++;
|
|
if (_loopnum == 7) {
|
|
_freq = 0x00C8;
|
|
_freqmod = 2;
|
|
}
|
|
}
|
|
return true;
|
|
} else {
|
|
if (_loopnum == 7) {
|
|
_mod->stopChannel(_id | 0x000);
|
|
_mod->stopChannel(_id | 0x100);
|
|
|
|
char *tmp_data1 = (char *)malloc(_size2);
|
|
char *tmp_data2 = (char *)malloc(_size2);
|
|
memcpy(tmp_data1, _data + _offset2, _size2);
|
|
memcpy(tmp_data2, _data + _offset2, _size2);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, _size2, BASE_FREQUENCY / (_freq), 0x7F, 0, _size2, -127);
|
|
_mod->startChannel(_id | 0x100, tmp_data2, _size2, BASE_FREQUENCY / (_freq + 3), 0x7F, 0, _size2, 127);
|
|
_loopnum++;
|
|
} else {
|
|
_mod->setChannelFreq(_id | 0x000, BASE_FREQUENCY / _freq);
|
|
_mod->setChannelFreq(_id | 0x100, BASE_FREQUENCY / (_freq + 3));
|
|
}
|
|
_freq -= _freqmod;
|
|
if (_freq > 0)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
private:
|
|
const uint16 _offset1;
|
|
const uint16 _offset2;
|
|
const uint16 _size1;
|
|
const uint16 _size2;
|
|
|
|
uint16 _loopnum;
|
|
int16 _freqmod;
|
|
uint16 _freq;
|
|
};
|
|
|
|
// plays a looped waveform, increasing frequency and reducing volume once the frequency reaches a certain point
|
|
// probably used for some sort of vehicle sound
|
|
class V2A_Sound_Special_Zak52 : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_Zak52(uint16 offset, uint16 size) :
|
|
_offset(offset), _size(size) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
|
|
_curfreq = 0x0312;
|
|
|
|
char *tmp_data = (char *)malloc(_size);
|
|
memcpy(tmp_data, _data + _offset, _size);
|
|
_mod->startChannel(_id | 0x000, tmp_data, _size, BASE_FREQUENCY / _curfreq, 0xFF, 0, _size, -127);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
int vol = (_curfreq - 0xC8) >> 3;
|
|
if (vol > 0x3F)
|
|
vol = 0x3F;
|
|
vol = (vol << 2) | (vol >> 4);
|
|
_mod->setChannelFreq(_id | 0x000, BASE_FREQUENCY / _curfreq);
|
|
_mod->setChannelVol(_id | 0x000, vol);
|
|
_curfreq--;
|
|
if (_curfreq >= 0x107)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
private:
|
|
const uint16 _offset;
|
|
const uint16 _size;
|
|
|
|
uint16 _curfreq;
|
|
};
|
|
|
|
// plays a looped waveform, sweeping the frequency up while modulating it (alternating which channel updates) and fading volume out
|
|
// used when teleporting out with the yellow crystal
|
|
class V2A_Sound_Special_Zak61 : public V2A_Sound_Base<2> {
|
|
public:
|
|
V2A_Sound_Special_Zak61(uint16 offset, uint16 size) :
|
|
_offset(offset), _size(size) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
|
|
_loop = 1;
|
|
_curfreq = 0x01F4;
|
|
|
|
char *tmp_data1 = (char *)malloc(_size);
|
|
char *tmp_data2 = (char *)malloc(_size);
|
|
memcpy(tmp_data1, _data + _offset, _size);
|
|
memcpy(tmp_data2, _data + _offset, _size);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _curfreq, 0x7F, 0, _size, -127);
|
|
// start 2nd channel silent
|
|
_mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _curfreq, 0, 0, _size, 127);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
int freq = (_loop << 4) + _curfreq;
|
|
int vol = freq - 0x76;
|
|
if (vol > 0x3F)
|
|
vol = 0x3F;
|
|
vol = (vol << 1) | (vol >> 5);
|
|
switch (_loop) {
|
|
case 0:
|
|
_mod->setChannelFreq(_id | 0x000, BASE_FREQUENCY / freq);
|
|
_mod->setChannelVol(_id | 0x000, vol);
|
|
break;
|
|
case 1:
|
|
_mod->setChannelFreq(_id | 0x100, BASE_FREQUENCY / freq);
|
|
_mod->setChannelVol(_id | 0x100, vol);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
_loop = (_loop + 1) & 3;
|
|
if (!_loop) {
|
|
_curfreq -= 4;
|
|
if (_curfreq <= 0x80)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _offset;
|
|
const uint16 _size;
|
|
|
|
uint16 _loop;
|
|
uint16 _curfreq;
|
|
};
|
|
|
|
// just like Zak61, but sweeps frequency in the other direction
|
|
// used when teleporting in with the yellow crystal
|
|
class V2A_Sound_Special_Zak62 : public V2A_Sound_Base<2> {
|
|
public:
|
|
V2A_Sound_Special_Zak62(uint16 offset, uint16 size) :
|
|
_offset(offset), _size(size) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
|
|
_loop = 1;
|
|
_curfreq = 0x0080;
|
|
|
|
char *tmp_data1 = (char *)malloc(_size);
|
|
char *tmp_data2 = (char *)malloc(_size);
|
|
memcpy(tmp_data1, _data + _offset, _size);
|
|
memcpy(tmp_data2, _data + _offset, _size);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _curfreq, 0x7F, 0, _size, -127);
|
|
// start 2nd channel silent
|
|
_mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _curfreq, 0, 0, _size, 127);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
int freq = (_loop << 4) + _curfreq;
|
|
int vol = 0x0200 - freq;
|
|
if (vol > 0x3F)
|
|
vol = 0x3F;
|
|
vol = (vol << 1) | (vol >> 5);
|
|
switch (_loop) {
|
|
case 0:
|
|
_mod->setChannelFreq(_id | 0x000, BASE_FREQUENCY / freq);
|
|
_mod->setChannelVol(_id | 0x000, vol);
|
|
break;
|
|
case 1:
|
|
_mod->setChannelFreq(_id | 0x100, BASE_FREQUENCY / freq);
|
|
_mod->setChannelVol(_id | 0x100, vol);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
_loop = (_loop + 1) & 3;
|
|
if (!_loop) {
|
|
_curfreq += 4;
|
|
if (_curfreq >= 0x01F4)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _offset;
|
|
const uint16 _size;
|
|
|
|
uint16 _loop;
|
|
uint16 _curfreq;
|
|
};
|
|
|
|
// plays a series of double-looped sounds at varying frequencies and delays, very specialized
|
|
// Guardian of the Sphinx, perhaps?
|
|
class V2A_Sound_Special_Zak82 : public V2A_Sound_Base<4> {
|
|
public:
|
|
V2A_Sound_Special_Zak82(uint16 offset, uint16 size) :
|
|
_offset(offset), _size(size) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
|
|
// Wait values were to insure playing an integral number of loops on each sample
|
|
// and have been adjusted to reflect the actual duration spent playing
|
|
_loop = 0;
|
|
_playctr = 240;
|
|
_wait1 = 76; // was 39, extended to loop twice
|
|
_wait2 = 10000;
|
|
_wait3 = 10000;
|
|
_wait4 = 10000;
|
|
|
|
int size = 2000;
|
|
int offset = _offset;
|
|
assert(offset + size <= _offset + _size);
|
|
char *tmp_data = (char *)malloc(size);
|
|
memcpy(tmp_data, _data + offset, size);
|
|
_mod->startChannel(_id | 0x000, tmp_data, size, BASE_FREQUENCY / 0x0479, 0xFF, 0, size);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
char *tmp_data1, *tmp_data2;
|
|
int size, offset = _offset;
|
|
|
|
if (!--_wait1) {
|
|
_wait1 = 10000;
|
|
_mod->stopChannel(_id | 0x000);
|
|
} else if (!--_wait2) {
|
|
_wait2 = 10000;
|
|
_mod->stopChannel(_id | 0x000);
|
|
} else if (!--_wait3) {
|
|
_wait3 = 10000;
|
|
_mod->stopChannel(_id | 0x200);
|
|
} else if (!--_wait4) {
|
|
_wait4 = 10000;
|
|
_mod->stopChannel(_id | 0x100);
|
|
_mod->stopChannel(_id | 0x300);
|
|
}
|
|
if (--_playctr)
|
|
return true;
|
|
|
|
switch (++_loop) {
|
|
case 1:
|
|
size = 6300;
|
|
offset += 0x07D0;
|
|
assert(offset + size <= _offset + _size);
|
|
tmp_data1 = (char *)malloc(size);
|
|
memcpy(tmp_data1, _data + offset, size);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, size, BASE_FREQUENCY / 0x0479, 0x7F, 0, size, -127);
|
|
_wait2 = 241; // was 120, extended to loop twice
|
|
_playctr = 10;
|
|
break;
|
|
case 2:
|
|
size = 6292;
|
|
offset += 0x206C;
|
|
assert(offset + size <= _offset + _size);
|
|
tmp_data1 = (char *)malloc(size);
|
|
memcpy(tmp_data1, _data + offset, size);
|
|
_mod->startChannel(_id | 0x200, tmp_data1, size, BASE_FREQUENCY / 0x0384, 0x7F, 0, size, 127);
|
|
_wait3 = 189; // was 94, extended to loop twice
|
|
_playctr = 20;
|
|
break;
|
|
case 3:
|
|
size = 6300;
|
|
offset += 0x07D0;
|
|
assert(offset + size <= _offset + _size);
|
|
tmp_data1 = (char *)malloc(size);
|
|
tmp_data2 = (char *)malloc(size);
|
|
memcpy(tmp_data1, _data + offset, size);
|
|
memcpy(tmp_data2, _data + offset, size);
|
|
_mod->startChannel(_id | 0x100, tmp_data1, size, BASE_FREQUENCY / 0x01E0, 0x7F, 0, size, 127);
|
|
_mod->startChannel(_id | 0x300, tmp_data2, size, BASE_FREQUENCY / 0x01E0, 0x7F, 0, size, -127);
|
|
_wait4 = 101; // was 50, extended to loop twice
|
|
_playctr = 120;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _offset;
|
|
const uint16 _size;
|
|
|
|
uint16 _loop;
|
|
uint16 _playctr;
|
|
uint16 _wait1;
|
|
uint16 _wait2;
|
|
uint16 _wait3;
|
|
uint16 _wait4;
|
|
};
|
|
|
|
// plays a "ding" (volume 0-max-0) followed by a sound sample, a pause, then loops again
|
|
// Mars Tram about to depart
|
|
class V2A_Sound_Special_Zak86 : public V2A_Sound_Base<1> {
|
|
public:
|
|
V2A_Sound_Special_Zak86(uint16 offset, uint16 size) :
|
|
_offset(offset), _size(size) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
|
|
_mode = 0;
|
|
_vol = 0;
|
|
_volmod = 16;
|
|
|
|
int size = 32;
|
|
int offset = _offset + 0x2B8E;
|
|
assert(offset + size <= _offset + _size);
|
|
char *tmp_data = (char *)malloc(size);
|
|
memcpy(tmp_data, _data + offset, size);
|
|
_mod->startChannel(_id | 0x000, tmp_data, size, BASE_FREQUENCY / 0x0096, 0, 0, size, 0);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
int size, offset;
|
|
char *tmp_data;
|
|
|
|
switch (_mode) {
|
|
case 0:
|
|
_mod->setChannelVol(_id | 0x000, (_vol << 2) | (_vol >> 4));
|
|
if (_vol + _volmod > 0) {
|
|
_vol += _volmod;
|
|
if (_vol > 0x3F) {
|
|
_vol = 0x3F;
|
|
_volmod = -4;
|
|
}
|
|
return true;
|
|
}
|
|
_mod->stopChannel(_id | 0x000);
|
|
_mode = 1;
|
|
|
|
size = 0x2B8E;
|
|
offset = _offset;
|
|
assert(offset + size <= _offset + _size);
|
|
tmp_data = (char *)malloc(size);
|
|
memcpy(tmp_data, _data + offset, size);
|
|
_mod->startChannel(_id | 0x000, tmp_data, size, BASE_FREQUENCY / 0x0152, 0x3F);
|
|
_volmod = 100;
|
|
break;
|
|
case 1:
|
|
if (!--_volmod) {
|
|
size = 32;
|
|
offset = _offset + 0x2B8E;
|
|
assert(offset + size <= _offset + _size);
|
|
tmp_data = (char *)malloc(size);
|
|
memcpy(tmp_data, _data + offset, size);
|
|
_mod->startChannel(_id | 0x000, tmp_data, size, BASE_FREQUENCY / 0x0096, 0, 0, size, 0);
|
|
_mode = 0;
|
|
_vol = 0;
|
|
_volmod = 16;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _offset;
|
|
const uint16 _size;
|
|
|
|
uint16 _mode;
|
|
uint16 _vol;
|
|
int16 _volmod;
|
|
};
|
|
|
|
// modulates volume on 4 samples, frequency on only 2 of them
|
|
// Skolarian device pedestal activated without any parts
|
|
class V2A_Sound_Special_Zak98 : public V2A_Sound_Base<4> {
|
|
public:
|
|
V2A_Sound_Special_Zak98(uint16 offset, uint16 size) :
|
|
_offset(offset), _size(size) { }
|
|
void start(Player_MOD *mod, int id, const byte *data) override {
|
|
_mod = mod;
|
|
_id = id;
|
|
_data = (char *)malloc(READ_LE_UINT16(data));
|
|
memcpy(_data, data, READ_LE_UINT16(data));
|
|
|
|
_freq[0] = 0x1E0;
|
|
_freq[1] = 0x3E8;
|
|
_freq[2] = 0x200;
|
|
_freq[3] = 0x408;
|
|
_vol[0] = 0x3F;
|
|
_vol[1] = 0x3F;
|
|
_vol[2] = 0x3F;
|
|
_vol[3] = 0x3F;
|
|
_freqmod = 4;
|
|
_volmod[0] = -2;
|
|
_volmod[1] = -1;
|
|
|
|
char *tmp_data1 = (char *)malloc(_size);
|
|
char *tmp_data2 = (char *)malloc(_size);
|
|
char *tmp_data3 = (char *)malloc(_size);
|
|
char *tmp_data4 = (char *)malloc(_size);
|
|
memcpy(tmp_data1, _data + _offset, _size);
|
|
memcpy(tmp_data2, _data + _offset, _size);
|
|
memcpy(tmp_data3, _data + _offset, _size);
|
|
memcpy(tmp_data4, _data + _offset, _size);
|
|
_mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _freq[0], _vol[0], 0, _size, -127);
|
|
_mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _freq[1], _vol[1], 0, _size, 127);
|
|
_mod->startChannel(_id | 0x200, tmp_data3, _size, BASE_FREQUENCY / _freq[2], _vol[2], 0, _size, 127);
|
|
_mod->startChannel(_id | 0x300, tmp_data4, _size, BASE_FREQUENCY / _freq[3], _vol[3], 0, _size, -127);
|
|
}
|
|
bool update() override {
|
|
assert(_id);
|
|
const uint16 _minvol[2] = {0x2E, 0x32};
|
|
int i;
|
|
for (i = 0; i < 4; i++) {
|
|
_mod->setChannelFreq(_id | (i << 8), BASE_FREQUENCY / _freq[i]);
|
|
_mod->setChannelVol(_id | (i << 8), _vol[i]);
|
|
}
|
|
for (i = 0; i < 2; i++) {
|
|
_vol[i] += _volmod[i];
|
|
if (_vol[i] > 0x3F) {
|
|
_vol[i] = 0x3F;
|
|
_volmod[i] = -_volmod[i];
|
|
} else if (_vol[i] < _minvol[i]) {
|
|
_vol[i] = _minvol[i];
|
|
_volmod[i] = -_volmod[i];
|
|
}
|
|
_vol[i + 2] = _vol[i];
|
|
}
|
|
_freq[0] += _freqmod;
|
|
if (_freq[0] > 0x2BC) {
|
|
_freq[0] = 0x2BC;
|
|
_freqmod = -_freqmod;
|
|
} else if (_freq[0] < 0x1E0) {
|
|
_freq[0] = 0x1E0;
|
|
_freqmod = -_freqmod;
|
|
}
|
|
_freq[2] = _freq[0] + 0x20;
|
|
return true;
|
|
}
|
|
private:
|
|
const uint16 _offset;
|
|
const uint16 _size;
|
|
|
|
uint16 _freq[4];
|
|
uint16 _vol[4];
|
|
int16 _freqmod;
|
|
int16 _volmod[2];
|
|
};
|
|
|
|
#define CRCToSound(CRC, SOUND) \
|
|
if (crc == CRC) \
|
|
return new SOUND
|
|
|
|
static V2A_Sound *findSound(unsigned long crc) {
|
|
CRCToSound(0x8FAB08C4, V2A_Sound_SingleLooped(0x006C, 0x2B58, 0x016E, 0x3F)); // Maniac 17
|
|
CRCToSound(0xB673160A, V2A_Sound_SingleLooped(0x006C, 0x1E78, 0x01C2, 0x1E)); // Maniac 38
|
|
CRCToSound(0x4DB1D0B2, V2A_Sound_MultiLooped(0x0072, 0x1BC8, 0x023D, 0x3F, 0x0224, 0x3F)); // Maniac 20
|
|
CRCToSound(0x754D75EF, V2A_Sound_Single(0x0076, 0x0738, 0x01FC, 0x3F)); // Maniac 10
|
|
CRCToSound(0x6E3454AF, V2A_Sound_Single(0x0076, 0x050A, 0x017C, 0x3F)); // Maniac 12
|
|
CRCToSound(0x92F0BBB6, V2A_Sound_Single(0x0076, 0x3288, 0x012E, 0x3F)); // Maniac 41
|
|
CRCToSound(0xE1B13982, V2A_Sound_MultiLoopedDuration(0x0078, 0x0040, 0x007C, 0x3F, 0x007B, 0x3F, 0x001E)); // Maniac 21
|
|
CRCToSound(0x288B16CF, V2A_Sound_MultiLoopedDuration(0x007A, 0x0040, 0x007C, 0x3F, 0x007B, 0x3F, 0x000A)); // Maniac 11
|
|
CRCToSound(0xA7565268, V2A_Sound_MultiLoopedDuration(0x007A, 0x0040, 0x00F8, 0x3F, 0x00F7, 0x3F, 0x000A)); // Maniac 19
|
|
CRCToSound(0x7D419BFC, V2A_Sound_MultiLoopedDuration(0x007E, 0x0040, 0x012C, 0x3F, 0x0149, 0x3F, 0x001E)); // Maniac 22
|
|
CRCToSound(0x1B52280C, V2A_Sound_Single(0x0098, 0x0A58, 0x007F, 0x32)); // Maniac 6
|
|
CRCToSound(0x38D4A810, V2A_Sound_Single(0x0098, 0x2F3C, 0x0258, 0x32)); // Maniac 7
|
|
CRCToSound(0x09F98FC2, V2A_Sound_Single(0x0098, 0x0A56, 0x012C, 0x32)); // Maniac 16
|
|
CRCToSound(0x90440A65, V2A_Sound_Single(0x0098, 0x0208, 0x0078, 0x28)); // Maniac 28
|
|
CRCToSound(0x985C76EF, V2A_Sound_Single(0x0098, 0x0D6E, 0x00C8, 0x32)); // Maniac 30
|
|
CRCToSound(0x76156137, V2A_Sound_Single(0x0098, 0x2610, 0x017C, 0x39)); // Maniac 39
|
|
CRCToSound(0x5D95F88C, V2A_Sound_Single(0x0098, 0x0A58, 0x007F, 0x1E)); // Maniac 65
|
|
CRCToSound(0x92D704EA, V2A_Sound_SingleLooped(0x009C, 0x29BC, 0x012C, 0x3F, 0x1BD4, 0x0DE8)); // Maniac 15
|
|
CRCToSound(0x92F5513C, V2A_Sound_Single(0x009E, 0x0DD4, 0x01F4, 0x3F)); // Maniac 13
|
|
CRCToSound(0xCC2F3B5A, V2A_Sound_Single(0x009E, 0x00DE, 0x01AC, 0x3F)); // Maniac 43
|
|
CRCToSound(0x153207D3, V2A_Sound_Single(0x009E, 0x0E06, 0x02A8, 0x3F)); // Maniac 67
|
|
CRCToSound(0xC4F370CE, V2A_Sound_Single(0x00AE, 0x0330, 0x01AC, 0x3F)); // Maniac 8
|
|
CRCToSound(0x928C4BAC, V2A_Sound_Single(0x00AE, 0x08D6, 0x01AC, 0x3F)); // Maniac 9
|
|
CRCToSound(0x62D5B11F, V2A_Sound_Single(0x00AE, 0x165C, 0x01CB, 0x3F)); // Maniac 27
|
|
CRCToSound(0x3AB22CB5, V2A_Sound_Single(0x00AE, 0x294E, 0x012A, 0x3F)); // Maniac 62
|
|
CRCToSound(0x2D70BBE9, V2A_Sound_SingleLoopedPitchbend(0x00B4, 0x1702, 0x03E8, 0x0190, 0x3F, 5)); // Maniac 64
|
|
CRCToSound(0xFA4C1B1C, V2A_Sound_Special_Maniac69(0x00B2, 0x1702, 0x0190, 0x3F)); // Maniac 69
|
|
CRCToSound(0x19D50D67, V2A_Sound_Special_ManiacDing(0x00B6, 0x0020, 0x00C8, 16, 2)); // Maniac 14
|
|
CRCToSound(0x3E6FBE15, V2A_Sound_Special_ManiacTentacle(0x00B2, 0x0010, 0x007C, 0x016D, 1)); // Maniac 25
|
|
CRCToSound(0x5305753C, V2A_Sound_Special_ManiacTentacle(0x00B2, 0x0010, 0x007C, 0x016D, 7)); // Maniac 36
|
|
CRCToSound(0x28895106, V2A_Sound_Special_Maniac59(0x00C0, 0x00FE, 0x00E9, 0x0111, 4, 0x0A)); // Maniac 59
|
|
CRCToSound(0xB641ACF6, V2A_Sound_Special_Maniac61(0x00C8, 0x0100, 0x00C8, 0x01C2)); // Maniac 61
|
|
CRCToSound(0xE1A91583, V2A_Sound_Special_ManiacPhone(0x00D0, 0x0040, 0x007C, 0x3F, 0x007B, 0x3F, 0x3C, 5, 6)); // Maniac 23
|
|
CRCToSound(0x64816ED5, V2A_Sound_Special_ManiacPhone(0x00D0, 0x0040, 0x00BE, 0x37, 0x00BD, 0x37, 0x3C, 5, 6)); // Maniac 24
|
|
CRCToSound(0x639D72C2, V2A_Sound_Special_Maniac46(0x00D0, 0x10A4, 0x0080, 0x3F, 0x28, 3)); // Maniac 46
|
|
CRCToSound(0xE8826D92, V2A_Sound_Special_ManiacTypewriter(0x00EC, 0x025A, 0x023C, 0x3F, 8, (const uint8 *)"\x20\x41\x04\x21\x08\x10\x13\x07", true)); // Maniac 45
|
|
CRCToSound(0xEDFF3D41, V2A_Sound_Single(0x00F8, 0x2ADE, 0x01F8, 0x3F)); // Maniac 42 (this should echo, but it's barely noticeable and I don't feel like doing it)
|
|
CRCToSound(0x15606D06, V2A_Sound_Special_Maniac32(0x0148, 0x0020, 0x0168, 0x0020, 0x3F)); // Maniac 32
|
|
CRCToSound(0x753EAFE3, V2A_Sound_Special_Maniac44(0x017C, 0x0010, 0x018C, 0x0020, 0x00C8, 0x0080, 0x3F)); // Maniac 44
|
|
CRCToSound(0xB1AB065C, V2A_Sound_Music(0x0032, 0x00B2, 0x08B2, 0x1222, 0x1A52, 0x23C2, 0x3074, false)); // Maniac 50
|
|
CRCToSound(0x091F5D9C, V2A_Sound_Music(0x0032, 0x0132, 0x0932, 0x1802, 0x23D2, 0x3EA2, 0x4F04, false)); // Maniac 58
|
|
|
|
CRCToSound(0x8E2C8AB3, V2A_Sound_SingleLooped(0x005C, 0x0F26, 0x0168, 0x3C)); // Zak 41
|
|
CRCToSound(0x3792071F, V2A_Sound_SingleLooped(0x0060, 0x1A18, 0x06A4, 0x3F)); // Zak 88
|
|
CRCToSound(0xF192EDE9, V2A_Sound_SingleLooped(0x0062, 0x0054, 0x01FC, 0x1E)); // Zak 68
|
|
CRCToSound(0xC43B0245, V2A_Sound_Special_Zak70(0x006C, 0x166E, 0x00C8, 0x0190, 0x0320, 0x0640, 0x32)); // Zak 70
|
|
CRCToSound(0xCEB51670, V2A_Sound_SingleLooped(0x00AC, 0x26DC, 0x012C, 0x3F)); // Zak 42
|
|
CRCToSound(0x10347B51, V2A_Sound_SingleLooped(0x006C, 0x00E0, 0x0594, 0x3F)); // Zak 18
|
|
CRCToSound(0x9D2FADC0, V2A_Sound_MultiLooped(0x0072, 0x1FC8, 0x016A, 0x3F, 0x01CE, 0x3F)); // Zak 80
|
|
CRCToSound(0xFAD2C676, V2A_Sound_MultiLooped(0x0076, 0x0010, 0x0080, 0x3F, 0x0090, 0x3B)); // Zak 40
|
|
CRCToSound(0x01508B48, V2A_Sound_Single(0x0076, 0x0D8C, 0x017C, 0x3F)); // Zak 90
|
|
CRCToSound(0x9C18DC46, V2A_Sound_Single(0x0076, 0x0D8C, 0x015E, 0x3F)); // Zak 91
|
|
CRCToSound(0xF98F7EAC, V2A_Sound_Single(0x0076, 0x0D8C, 0x0140, 0x3F)); // Zak 92
|
|
CRCToSound(0xC925FBEF, V2A_Sound_MultiLoopedDuration(0x0080, 0x0010, 0x0080, 0x3F, 0x0090, 0x3B, 0x0168)); // Zak 53
|
|
CRCToSound(0xCAB35257, V2A_Sound_Special_Zak101(0x00DA, 0x425C, 0x023C, 0x08F0, 0x0640, 0x0478, 0x3F, 0x012C)); // Zak 101
|
|
CRCToSound(0xA31FE4FD, V2A_Sound_Single(0x0094, 0x036A, 0x00E1, 0x3F)); // Zak 97
|
|
CRCToSound(0x0A1AE0F5, V2A_Sound_Single(0x009E, 0x0876, 0x0168, 0x3F)); // Zak 5
|
|
CRCToSound(0xD01A66CB, V2A_Sound_Single(0x009E, 0x04A8, 0x0168, 0x3F)); // Zak 47
|
|
CRCToSound(0x5497B912, V2A_Sound_Single(0x009E, 0x0198, 0x01F4, 0x3F)); // Zak 39
|
|
CRCToSound(0x2B50362F, V2A_Sound_Single(0x009E, 0x09B6, 0x023D, 0x3F)); // Zak 67
|
|
CRCToSound(0x7BFB6E72, V2A_Sound_Single(0x009E, 0x0D14, 0x0078, 0x3F)); // Zak 69
|
|
CRCToSound(0xB803A792, V2A_Sound_Single(0x009E, 0x2302, 0x02BC, 0x3F)); // Zak 78
|
|
CRCToSound(0x7AB82E39, V2A_Sound_SingleLooped(0x00A0, 0x2A3C, 0x016E, 0x3F, 0x1018, 0x1A24)); // Zak 100
|
|
CRCToSound(0x28057CEC, V2A_Sound_Single(0x0098, 0x0FEC, 0x0140, 0x32)); // Zak 63
|
|
CRCToSound(0x1180A2FC, V2A_Sound_Single(0x0098, 0x0F06, 0x0190, 0x32)); // Zak 64
|
|
CRCToSound(0x12616755, V2A_Sound_Single(0x0098, 0x14C8, 0x023C, 0x14)); // Zak 9
|
|
CRCToSound(0x642723AA, V2A_Sound_Special_Zak37(0x00A2, 0x1702, 0x01F4, 0x3F)); // Zak 37
|
|
CRCToSound(0xDEE56848, V2A_Sound_Single(0x009A, 0x0F86, 0x0100, 0x3F)); // Zak 93
|
|
CRCToSound(0xF9BE27B8, V2A_Sound_Special_Zak37(0x011C, 0x1704, 0x0228, 0x3F)); // Zak 113
|
|
CRCToSound(0xC73487B2, V2A_Sound_Single(0x00B0, 0x18BA, 0x0478, 0x3F)); // Zak 81
|
|
CRCToSound(0x32D8F925, V2A_Sound_Single(0x00B0, 0x2E46, 0x00F0, 0x3F)); // Zak 94
|
|
CRCToSound(0x988C83A5, V2A_Sound_Single(0x00B0, 0x0DE0, 0x025B, 0x3F)); // Zak 106
|
|
CRCToSound(0x8F1E3B3D, V2A_Sound_Single(0x00B0, 0x05FE, 0x04E2, 0x3F)); // Zak 107
|
|
CRCToSound(0x0A2A7646, V2A_Sound_Single(0x00B0, 0x36FE, 0x016E, 0x3F)); // Zak 43
|
|
CRCToSound(0x6F1FC435, V2A_Sound_Single(0x00B0, 0x2808, 0x044C, 0x3F)); // Zak 108
|
|
CRCToSound(0x870EFC29, V2A_Sound_SingleLoopedPitchbend(0x00BA, 0x0100, 0x03E8, 0x00C8, 0x3F, 3)); // Zak 55
|
|
CRCToSound(0xED773699, V2A_Sound_Special_ManiacDing(0x00B4, 0x0020, 0x012C, 8, 4)); // Zak 3
|
|
CRCToSound(0x0BF59774, V2A_Sound_Special_ZakStereoDing(0x00BE, 0x0020, 0x00F8, 0x00F7, 8, 1)); // Zak 72
|
|
CRCToSound(0x656FFEDE, V2A_Sound_Special_ZakStereoDing(0x00BE, 0x0020, 0x00C4, 0x00C3, 8, 1)); // Zak 73
|
|
CRCToSound(0xFC4D41E5, V2A_Sound_Special_ZakStereoDing(0x00BE, 0x0020, 0x00A5, 0x00A4, 8, 1)); // Zak 74
|
|
CRCToSound(0xC0DD2089, V2A_Sound_Special_ZakStereoDing(0x00BE, 0x0020, 0x009C, 0x009B, 8, 1)); // Zak 75
|
|
CRCToSound(0x627DFD92, V2A_Sound_Special_ZakStereoDing(0x00BE, 0x0020, 0x008B, 0x008A, 8, 1)); // Zak 76
|
|
CRCToSound(0x703E05C1, V2A_Sound_Special_ZakStereoDing(0x00BE, 0x0020, 0x007C, 0x007B, 8, 1)); // Zak 77
|
|
CRCToSound(0xB0F77006, V2A_Sound_Special_Zak52(0x00B0, 0x01BC)); // Zak 52
|
|
CRCToSound(0x5AE9D6A7, V2A_Sound_Special_ZakAirplane(0x00CA, 0x22A4, 0x0113, 0x0227)); // Zak 109
|
|
CRCToSound(0xABE0D3B0, V2A_Sound_Special_ZakAirplane(0x00CE, 0x22A4, 0x0227, 0x0113)); // Zak 105
|
|
CRCToSound(0x788CC749, V2A_Sound_Special_Zak71(0x00C8, 0x0B37)); // Zak 71
|
|
CRCToSound(0x2E2AB1FA, V2A_Sound_Special_Zak99(0x00D4, 0x04F0, 0x0FE3, 0x0080, 0x3F)); // Zak 99
|
|
CRCToSound(0x1304CF20, V2A_Sound_Special_ManiacTypewriter(0x00DC, 0x0624, 0x023C, 0x3C, 2, (const uint8 *)"\x14\x11", false)); // Zak 79
|
|
CRCToSound(0xAE68ED91, V2A_Sound_Special_Zak54(0x00D4, 0x1A25, 0x1E1E, 0x0B80, 0x01F4)); // Zak 54
|
|
CRCToSound(0xA4F40F97, V2A_Sound_Special_Zak61(0x00E4, 0x0020)); // Zak 61
|
|
CRCToSound(0x348F85CE, V2A_Sound_Special_Zak62(0x00E4, 0x0020)); // Zak 62
|
|
CRCToSound(0xD473AB86, V2A_Sound_Special_ManiacTypewriter(0x0122, 0x03E8, 0x00BE, 0x3F, 7, (const uint8 *)"\x0F\x0B\x04\x0F\x1E\x0F\x66", false)); // Zak 46
|
|
CRCToSound(0x84A0BA90, V2A_Sound_Special_Zak110(0x0126, 0x0040, 0x0136, 0x0080, 0x007C, 0x0087)); // Zak 110
|
|
CRCToSound(0x92680D9F, V2A_Sound_Special_Zak32(0x0140, 0x0150, 0x0010, 0x0010)); // Zak 32
|
|
CRCToSound(0xABFFDB02, V2A_Sound_Special_Zak86(0x01A2, 0x2BAE)); // Zak 86
|
|
CRCToSound(0x41045447, V2A_Sound_Special_Zak98(0x017A, 0x0020)); // Zak 98
|
|
CRCToSound(0xC8EEBD34, V2A_Sound_Special_Zak82(0x01A6, 0x3900)); // Zak 82
|
|
CRCToSound(0x42F9469F, V2A_Sound_Music(0x05F6, 0x0636, 0x0456, 0x0516, 0x05D6, 0x05E6, 0x0A36, true)); // Zak 96
|
|
CRCToSound(0x038BBD78, V2A_Sound_Music(0x054E, 0x05CE, 0x044E, 0x04BE, 0x052E, 0x053E, 0x0BCE, true)); // Zak 85
|
|
CRCToSound(0x06FFADC5, V2A_Sound_Music(0x0626, 0x0686, 0x0446, 0x04F6, 0x0606, 0x0616, 0x0C86, true)); // Zak 87
|
|
CRCToSound(0xCE20ECF0, V2A_Sound_Music(0x0636, 0x0696, 0x0446, 0x0576, 0x0616, 0x0626, 0x0E96, true)); // Zak 114
|
|
CRCToSound(0xBDA01BB6, V2A_Sound_Music(0x0678, 0x06B8, 0x0458, 0x0648, 0x0658, 0x0668, 0x0EB8, false)); // Zak 33
|
|
CRCToSound(0x59976529, V2A_Sound_Music(0x088E, 0x092E, 0x048E, 0x05EE, 0x074E, 0x07EE, 0x112E, true)); // Zak 49
|
|
CRCToSound(0xED1EED02, V2A_Sound_Music(0x08D0, 0x0950, 0x0440, 0x07E0, 0x08B0, 0x08C0, 0x1350, false)); // Zak 112
|
|
CRCToSound(0x5A16C037, V2A_Sound_Music(0x634A, 0x64CA, 0x049A, 0x18FA, 0x398A, 0x511A, 0x6CCA, false)); // Zak 95
|
|
return nullptr;
|
|
}
|
|
|
|
Player_V2A::Player_V2A(ScummEngine *scumm, Audio::Mixer *mixer) {
|
|
int i;
|
|
_vm = scumm;
|
|
|
|
InitCRC();
|
|
|
|
for (i = 0; i < V2A_MAXSLOTS; i++) {
|
|
_slot[i].id = 0;
|
|
_slot[i].sound = nullptr;
|
|
}
|
|
|
|
_mod = new Player_MOD(mixer);
|
|
_mod->setUpdateProc(update_proc, this, _vm->getAmigaMusicTimerFrequency() / 4);
|
|
}
|
|
|
|
Player_V2A::~Player_V2A() {
|
|
delete _mod;
|
|
}
|
|
|
|
void Player_V2A::setMusicVolume(int vol) {
|
|
_mod->setMusicVolume(vol);
|
|
}
|
|
|
|
int Player_V2A::getSoundSlot(int id) const {
|
|
int i;
|
|
for (i = 0; i < V2A_MAXSLOTS; i++) {
|
|
if (_slot[i].id == id)
|
|
break;
|
|
}
|
|
if (i == V2A_MAXSLOTS) {
|
|
if (id == 0)
|
|
warning("player_v2a - out of sound slots");
|
|
return -1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
void Player_V2A::stopAllSounds() {
|
|
for (int i = 0; i < V2A_MAXSLOTS; i++) {
|
|
if (!_slot[i].id)
|
|
continue;
|
|
_slot[i].sound->stop();
|
|
delete _slot[i].sound;
|
|
_slot[i].sound = nullptr;
|
|
_slot[i].id = 0;
|
|
}
|
|
}
|
|
|
|
void Player_V2A::stopSound(int nr) {
|
|
int i;
|
|
if (nr == 0)
|
|
return;
|
|
i = getSoundSlot(nr);
|
|
if (i == -1)
|
|
return;
|
|
_slot[i].sound->stop();
|
|
delete _slot[i].sound;
|
|
_slot[i].sound = nullptr;
|
|
_slot[i].id = 0;
|
|
}
|
|
|
|
void Player_V2A::startSound(int nr) {
|
|
assert(_vm);
|
|
byte *data = _vm->getResourceAddress(rtSound, nr);
|
|
assert(data);
|
|
uint32 crc = GetCRC(data + 0x0A, READ_BE_UINT16(data + 0x08));
|
|
V2A_Sound *snd = findSound(crc);
|
|
if (snd == nullptr) {
|
|
warning("player_v2a - sound %i not recognized yet (crc %08X)", nr, crc);
|
|
return;
|
|
}
|
|
stopSound(nr);
|
|
int i = getSoundSlot();
|
|
if (i == -1) {
|
|
delete snd;
|
|
return;
|
|
}
|
|
_slot[i].id = nr;
|
|
_slot[i].sound = snd;
|
|
_slot[i].sound->start(_mod, nr, data);
|
|
}
|
|
|
|
void Player_V2A::update_proc(void *param) {
|
|
((Player_V2A *)param)->updateSound();
|
|
}
|
|
|
|
void Player_V2A::updateSound() {
|
|
int i;
|
|
for (i = 0; i < V2A_MAXSLOTS; i++) {
|
|
if ((_slot[i].id) && (!_slot[i].sound->update())) {
|
|
_slot[i].sound->stop();
|
|
delete _slot[i].sound;
|
|
_slot[i].sound = nullptr;
|
|
_slot[i].id = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int Player_V2A::getMusicTimer() {
|
|
return 0; // FIXME - need to keep track of playing music resources
|
|
}
|
|
|
|
int Player_V2A::getSoundStatus(int nr) const {
|
|
for (int i = 0; i < V2A_MAXSLOTS; i++) {
|
|
if (_slot[i].id == nr)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
} // End of namespace Scumm
|