Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

player_v2a.cpp

/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001  Ludvig Strigeus
 * Copyright (C) 2001-2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/scumm/player_v2a.cpp,v 2.34.2.1 2005/10/18 02:11:21 sev Exp $
 *
 */

#include "common/stdafx.h"
#include "base/engine.h"
#include "scumm/player_v2a.h"
#include "scumm/scumm.h"

namespace Scumm {

#define BASE_FREQUENCY 3579545

#ifdef PALMOS_68K
static uint32 *CRCtable = NULL;
#else
static uint32 CRCtable[256];
#endif

static void InitCRC (void)
{
      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(NULL) { }
      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() { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            warning("player_v2a - sound %i not supported yet", id);
      }
      virtual bool update() { return false; }
      virtual void stop() { }
};

// 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(0) { }
      V2A_Sound_Base(uint16 offset, uint16 size) : _offset(offset), _size(size), _data(0) { }
      virtual void stop() {
            assert(_id);
            for (int i = 0; i < numChan; i++)
                  _mod->stopChannel(_id | (i << 8));
            _id = 0;
            free(_data);
            _data = 0;
      }
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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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();
      }
      virtual bool update() {
            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;
      }
      virtual void stop() {
            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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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;
      }
      virtual bool update() {
            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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            V2A_Sound_MultiLooped::start(mod, id, data);
            _ticks = 0;
      }
      virtual bool update() {
            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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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_ManiacNuclear : public V2A_Sound_Base<1> {
public:
      V2A_Sound_Special_ManiacNuclear(uint16 offset, uint16 size, uint16 freq, uint8 vol) :
            V2A_Sound_Base<1>(offset, size), _freq(freq), _vol(vol) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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_ManiacElectric : public V2A_Sound_Base<1> {
public:
      V2A_Sound_Special_ManiacElectric(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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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
// old name: SlowPitchbendDownAndFadeout
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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _mod = mod;
            _id = id;
            _data = (char *)malloc(READ_LE_UINT16(data));
            memcpy(_data, data, READ_LE_UINT16(data));
            soundon();
            _ticks = 0;
            _loop = 0;
      }
      virtual bool update() {
            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_ManiacWrench : public V2A_Sound_Base<1> {
public:
      V2A_Sound_Special_ManiacWrench(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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _mod = mod;
            _id = id;
            _data = (char *)malloc(READ_LE_UINT16(data));
            memcpy(_data, data, READ_LE_UINT16(data));
            soundon();
            _loop = 0;
            _loopctr = 0;
      }
      virtual bool update() {
            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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _mod = mod;
            _id = id;
            _data = (char *)malloc(READ_LE_UINT16(data));
            memcpy(_data, data, READ_LE_UINT16(data));
            soundon();
            _curdur = 0;
            _ticks = _durations[_curdur++];
      }
      virtual bool update() {
            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
// old name: TwinSirenMulti
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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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_ManiacSiren : public V2A_Sound_Base<4> {
public:
      V2A_Sound_Special_ManiacSiren(uint16 offset1, uint16 size1, uint16 offset2, uint16 size2, uint8 vol) :
            _offset1(offset1), _size1(size1), _offset2(offset2), _size2(size2), _vol(vol) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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
// some sort of laserbeam-like sound effect in Zak
// old name: QuadFreqLooped
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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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
// some whooshing-type sound in Zak
// old name: QuadFreqFadeout
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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            V2A_Sound_Special_Zak70::start(mod, id, data);
            _ticks = _dur;
      }
      virtual bool update() {
            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
// another whooshing-type noise in Zak
// old name: SingleFadeout
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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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;
      }
      virtual bool update() {
            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 for some weird sound effect
class V2A_Sound_Special_Zak71 : public V2A_Sound_Base<4> {
public:
      V2A_Sound_Special_Zak71(uint16 offset, uint16 size) :
            _offset(offset), _size(size) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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);
      }
      virtual bool update() {
            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 in Zak for the tram on Mars (?)
class V2A_Sound_Special_ZakTram : public V2A_Sound_Base<1> {
public:
      V2A_Sound_Special_ZakTram(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint8 vol) :
            V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2), _vol(vol) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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;
      }
      virtual bool update() {
            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 for some odd sound effect
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) { }
      virtual void start(Player_MOD *mod, int id, const byte *data) {
            _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;
      }
      virtual bool update() {
            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;
};

#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_ManiacNuclear(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_ManiacElectric(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_ManiacWrench(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_ManiacSiren(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_Unsupported());      // 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_ZakTram(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_Unsupported());      // Zak 61
      CRCToSound(0x348F85CE, V2A_Sound_Unsupported());      // 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_Unsupported());      // Zak 110
      CRCToSound(0x92680D9F, V2A_Sound_Unsupported());      // Zak 32
      CRCToSound(0xABFFDB02, V2A_Sound_Unsupported());      // Zak 86
      CRCToSound(0x41045447, V2A_Sound_Unsupported());      // Zak 98
      CRCToSound(0xC8EEBD34, V2A_Sound_Unsupported());      // 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 NULL;
}

Player_V2A::Player_V2A(ScummEngine *scumm) {
      int i;
      _vm = scumm;

#ifdef PALMOS_68K
      if (!CRCtable) CRCtable = (uint32 *)calloc(256, sizeof(uint32));
#endif
      InitCRC();

      for (i = 0; i < V2A_MAXSLOTS; i++) {
            _slot[i].id = 0;
            _slot[i].sound = NULL;
      }

      _mod = new Player_MOD(scumm);
      _mod->setUpdateProc(update_proc, this, 60);
}

Player_V2A::~Player_V2A() {
      delete _mod;
#ifdef PALMOS_68K
      free(CRCtable);
#endif
}

01330 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;
}

01348 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 = NULL;
            _slot[i].id = 0;
      }
}

01359 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 = NULL;
      _slot[i].id = 0;
}

01372 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 == NULL) {
            warning("player_v2a - sound %i not recognized yet (crc %08X)",nr,crc);
            return;
      }
      stopSound(nr);
      int i = getSoundSlot();
      if (i == -1)
            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 = NULL;
                  _slot[i].id = 0;
            }
      }
}

01407 int Player_V2A::getMusicTimer() const {
      return 0;   // FIXME - need to keep track of playing music resources
}

01411 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

Generated by  Doxygen 1.6.0   Back to index