[Scummvm-git-logs] scummvm master -> 5fe07a8fb1afbf19fce6f94e8c9eaf609cb299db
athrxx
noreply at scummvm.org
Wed Feb 28 20:33:08 UTC 2024
This automated email contains information about 5 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
c774054a4a SCUMM: (V3/Mac) - restructure sound code
95027d64d3 SCUMM: (V3/Mac) - more preparations to extend sound player
aee2bf53bb SCUMM: (Loom/Mac) - add new sound player
c52bfe4f27 SCUMM: (Loom/Mac) - add music quality slider widget
5fe07a8fb1 SCUMM: (Loom/Mac) - work around original tempo glitch
Commit: c774054a4a2cc26d6caaa6b959a8f00ee8ae0bad
https://github.com/scummvm/scummvm/commit/c774054a4a2cc26d6caaa6b959a8f00ee8ae0bad
Author: athrxx (athrxx at scummvm.org)
Date: 2024-02-28T21:01:00+01:00
Commit Message:
SCUMM: (V3/Mac) - restructure sound code
(preliminary reorganization to make support for other new players easier,
e. g. Loom or MI1)
Changed paths:
A engines/scumm/players/player_mac_intern.h
A engines/scumm/players/player_mac_new.cpp
A engines/scumm/players/player_mac_new.h
R engines/scumm/players/player_mac_indy3.h
engines/scumm/module.mk
engines/scumm/players/player_mac_indy3.cpp
engines/scumm/scumm.cpp
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index c10262493eb..abea3f04492 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -54,6 +54,7 @@ MODULE_OBJS := \
players/player_he.o \
players/player_mac.o \
players/player_mac_indy3.o \
+ players/player_mac_new.o \
players/player_mod.o \
players/player_nes.o \
players/player_pce.o \
diff --git a/engines/scumm/players/player_mac_indy3.cpp b/engines/scumm/players/player_mac_indy3.cpp
index f7b0a62b8b5..cca30683ad6 100644
--- a/engines/scumm/players/player_mac_indy3.cpp
+++ b/engines/scumm/players/player_mac_indy3.cpp
@@ -20,7 +20,8 @@
*/
-#include "scumm/players/player_mac_indy3.h"
+#include "scumm/players/player_mac_new.h"
+#include "scumm/players/player_mac_intern.h"
#include "scumm/resource.h"
#include "scumm/scumm.h"
@@ -31,142 +32,14 @@
namespace Scumm {
#define ASC_DEVICE_RATE 0x56EE8BA3
-#define VBL_UPDATE_RATE 0x003C25BD
#define PCM_BUFFER_SIZE 1024
-#define PCM_BUFFER_RESERVE 64
-#define RATECNV_BIT_PRECSN 24
extern const uint8 *g_pv2ModTbl;
extern const uint32 g_pv2ModTblSize;
-class I3MPlayer;
-class AudioStream_I3M : public Audio::AudioStream {
+class LegacyMusicDriver : public MacSoundDriver {
public:
- AudioStream_I3M(I3MPlayer *drv, uint32 scummVMOutputrate, bool stereo, bool interpolate, bool internal16Bit);
- ~AudioStream_I3M() override;
-
- void initBuffers(uint32 feedBufferSize);
- void initDrivers();
- typedef Common::Functor0Mem<void, I3MPlayer> CallbackProc;
- void setVblCallback(const CallbackProc *proc);
- void clearBuffer();
-
- void setMasterVolume(Audio::Mixer::SoundType type, uint16 vol);
-
- // AudioStream interface
- int readBuffer(int16 *buffer, const int numSamples) override;
- bool isStereo() const override { return _isStereo; }
- int getRate() const override { return _outputRate; }
- bool endOfData() const override { return false; }
-
-private:
- void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType, bool expectStereo) const;
- void runVblTask();
-
- I3MPlayer *_drv;
-
- uint32 _vblSmpQty;
- uint32 _vblSmpQtyRem;
- uint32 _vblCountDown;
- uint32 _vblCountDownRem;
- const CallbackProc *_vblCbProc;
-
- struct SmpBuffer {
- SmpBuffer() : start(0), pos(0), end(0), volume(0x10000), lastL(0), lastR(0), size(0), rateConvInt(0), rateConvFrac(0), rateConvAcc(-1), interpolate(false) {}
- int8 *start;
- int8 *pos;
- const int8 *end;
- uint32 volume;
- int32 lastL;
- int32 lastR;
- uint32 size;
- uint32 rateConvInt;
- uint32 rateConvFrac;
- int32 rateConvAcc;
- bool interpolate;
- } _buffers[2];
-
- const uint32 _outputRate;
- const uint8 _frameSize;
- const bool _interp;
- const int _smpInternalSize;
- const int _volDown;
-
- const bool _isStereo;
-};
-
-class I3MSoundDriver {
-public:
- I3MSoundDriver(Common::Mutex &mutex, uint32 deviceRate, bool canInterpolate, bool internal16Bit) : _mutex(mutex), _caps(deviceRate, canInterpolate),
- _smpSize(internal16Bit ? 2 : 1), _smpMin(internal16Bit ? -32768 : -128), _smpMax(internal16Bit ? 32767 : 127), _status(0) {}
- virtual ~I3MSoundDriver() {}
- virtual void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) = 0;
-
- struct Caps {
- Caps(uint32 rate, bool interp) :deviceRate(rate), allowInterPolation(interp) {}
- const uint32 deviceRate;
- const bool allowInterPolation;
- };
- const Caps &getCaps() const { return _caps; }
-
- enum StatusFlag : uint8 {
- kStatusPlaying = 1 << 0,
- kStatusOverflow = 1 << 1,
- kStatusStartup = 1 << 2,
- kStatusDone = 1 << 3
- };
- uint8 getStatus() const { return _status; }
- void clearFlags(uint8 flags) { _status &= ~flags; }
-
-protected:
- void setFlags(uint8 flags) { _status |= flags; }
-
- Common::Mutex &_mutex;
- const int _smpSize;
- const int16 _smpMin;
- const int16 _smpMax;
- const Caps _caps;
- uint8 _status;
-};
-
-class I3MLowLevelPCMDriver final : public I3MSoundDriver {
-public:
- struct PCMSound {
- PCMSound() : len(0), rate(0), loopst(0), loopend(0), baseFreq(0), stereo(false) {}
- Common::SharedPtr<const byte> data;
- uint32 len;
- uint32 rate;
- uint32 loopst;
- uint32 loopend;
- byte baseFreq;
- bool stereo;
- };
-public:
- I3MLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool enableInterpolation, bool internal16Bit);
- void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) override;
- void play(PCMSound *snd);
- void stop();
-private:
- uint32 calcRate(uint32 outRate, uint32 factor, uint32 dataRate);
-
- Common::SharedPtr<const int8> _res;
- const int8 *_data;
- const bool _interp;
- int8 _lastSmp[2];
- uint32 _len;
- uint16 _rmH;
- uint16 _rmL;
- uint32 _loopSt;
- uint32 _loopEnd;
- byte _baseFreq;
- uint32 _rcPos;
- uint32 _smpWtAcc;
- uint16 _frameSize;
-};
-
-class I3MMusicDriver : public I3MSoundDriver {
-public:
- I3MMusicDriver(uint16 numChannels, Common::Mutex &mutex, bool canInterpolate, bool internal16Bit) : I3MSoundDriver(mutex, ASC_DEVICE_RATE, canInterpolate, internal16Bit), _numChan(numChannels) {}
+ LegacyMusicDriver(uint16 numChannels, Common::Mutex &mutex, bool canInterpolate, bool internal16Bit) : MacSoundDriver(mutex, ASC_DEVICE_RATE, canInterpolate, internal16Bit), _numChan(numChannels) {}
virtual void start() = 0;
virtual void stop() = 0;
@@ -200,10 +73,10 @@ protected:
const uint16 _numChan;
};
-class I3MFourToneSynthDriver final : public I3MMusicDriver {
+class FourToneSynthDriver final : public LegacyMusicDriver {
public:
- I3MFourToneSynthDriver(Common::Mutex &mutex, bool internal16bit);
- ~I3MFourToneSynthDriver() override;
+ FourToneSynthDriver(Common::Mutex &mutex, bool internal16bit);
+ ~FourToneSynthDriver() override;
void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) override;
void start() override;
@@ -228,10 +101,10 @@ private:
Channel *_chan;
};
-class I3MSquareWaveSynthDriver final : public I3MMusicDriver {
+class SquareWaveSynthDriver final : public LegacyMusicDriver {
public:
- I3MSquareWaveSynthDriver(Common::Mutex &mutex, bool internal16Bit);
- ~I3MSquareWaveSynthDriver() override {};
+ SquareWaveSynthDriver(Common::Mutex &mutex, bool internal16Bit);
+ ~SquareWaveSynthDriver() override {};
void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) override;
void start() override;
@@ -250,7 +123,7 @@ private:
uint16 duration;
};
- Common::Array<I3MSquareWaveSynthDriver::Triplet> _tripletsQueue;
+ Common::Array<SquareWaveSynthDriver::Triplet> _tripletsQueue;
Triplet _lastPara;
uint16 _pos;
uint32 _count;
@@ -259,590 +132,18 @@ private:
uint32 _phase;
};
-class I3MPlayer {
-private:
- I3MPlayer(ScummEngine *vm, Audio::Mixer *mixer);
-public:
- ~I3MPlayer();
- static Common::SharedPtr<I3MPlayer> open(ScummEngine *scumm, Audio::Mixer *mixer);
- bool startDevices(uint32 outputRate, uint32 pcmDeviceRate, uint32 feedBufferSize, bool enableInterpolation, bool stereo, bool internal16Bit);
-
- void setMusicVolume(int vol);
- void setSfxVolume(int vol);
- void startSound(int id);
- void stopSound(int id);
- void stopAllSounds();
- int getMusicTimer();
- int getSoundStatus(int id) const;
- void setQuality(int qual);
- void saveLoadWithSerializer(Common::Serializer &ser);
- void restoreAfterLoad();
-
- void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) const;
- void nextTick();
-
- const I3MSoundDriver::Caps &getDriverCaps(uint8 drvID) const;
-
-private:
- void startSong(int id);
- void startSoundEffect(int id);
- void stopSong();
- void stopSoundEffect();
- void stopActiveSound();
- void finishSong();
- void updateSong();
- void updateSoundEffect();
-
- void checkRestartSoundEffects();
- void endOfTrack();
-
- bool isSong(int id) const;
- bool isHiQuality() const;
-
- int _curSound;
- int _curSong;
- int _lastSoundEffectPrio;
- int _soundEffectNumLoops;
- int _songTimer;
- bool _songUnfinished;
- uint _activeChanCount;
- byte _songTimerInternal;
- byte *_soundUsage;
-
- bool _soundEffectPlaying;
- int _qmode;
- bool _16bit;
- bool _qualHi;
- bool _mixerThread;
-
- I3MLowLevelPCMDriver::PCMSound _pcmSnd;
-
- AudioStream_I3M *_macstr;
- Audio::SoundHandle _soundHandle;
- AudioStream_I3M::CallbackProc _nextTickProc;
-
- ScummEngine *_vm;
- Audio::Mixer *_mixer;
- static Common::WeakPtr<I3MPlayer> *_inst;
-
- const byte *_musicIDTable;
- int _musicIDTableLen;
- const int _idRangeMax;
-
- I3MMusicDriver *_mdrv;
- I3MLowLevelPCMDriver *_sdrv;
- Common::Array<I3MSoundDriver*> _drivers;
-
-private:
- class MusicChannel {
- public:
- MusicChannel(I3MPlayer *pl);
- ~MusicChannel();
- void clear();
-
- void start(Common::SharedPtr<const byte> &songRes, uint16 offset, bool hq);
- void nextTick();
- void parseNextEvents();
- void noteOn(uint16 duration, uint8 note);
- uint16 checkPeriod() const;
-
- uint16 _frameLen;
- uint16 _curPos;
- uint16 _freqCur;
- uint16 _freqIncr;
- uint16 _freqEff;
- uint16 _envPhase;
- uint16 _envRate;
- uint16 _tempo;
- uint16 _envSust;
- int16 _transpose;
- uint16 _envAtt;
- uint16 _envShape;
- uint16 _envStep;
- uint16 _envStepLen;
- uint16 _modType;
- uint16 _modState;
- uint16 _modStep;
- uint16 _modSensitivity;
- uint16 _modRange;
- uint16 _localVars[5];
- Common::SharedPtr<const byte> _resource;
- bool _hq;
-
- private:
- typedef bool (I3MPlayer::MusicChannel::*CtrlProc)(const byte *&);
-
- bool ctrl_setShape(const byte *&pos);
- bool ctrl_modPara(const byte *&pos);
- bool ctrl_init(const byte *&pos);
- bool ctrl_returnFromSubroutine(const byte *&pos);
- bool ctrl_jumpToSubroutine(const byte *&pos);
- bool ctrl_initOther(const byte *&pos);
- bool ctrl_decrJumpIf(const byte *&pos);
- bool ctrl_writeVar(const byte *&pos);
-
- const CtrlProc *_ctrlProc;
-
- void limitedClear();
- uint16 &getMemberRef(int pos);
-
- uint16 **_vars;
- int _numVars;
- uint16 &_savedOffset;
-
- uint16 _resSize;
-
- I3MPlayer *_player;
- static MusicChannel *_ctrlChan;
-
- static const uint32 _envShapes[98];
- const uint8 *&_modShapes;
- const uint32 &_modShapesTableSize;
-
- bool ctrlProc(int procId, const byte *&arg);
- void setFrameLen(uint8 len);
- };
-
- MusicChannel **_musicChannels;
- const int _numMusicChannels;
- const int _numMusicTracks;
-
- static const uint8 _fourToneSynthWaveForm[256];
-
-public:
- MusicChannel *getMusicChannel(uint8 id) const;
-};
-
-AudioStream_I3M::AudioStream_I3M(I3MPlayer *drv, uint32 scummVMOutputrate, bool stereo, bool interpolate, bool internal16Bit) : Audio::AudioStream(), _drv(drv),
- _vblSmpQty(0), _vblSmpQtyRem(0), _frameSize((stereo ? 2 : 1) * (internal16Bit ? 2 : 1)), _vblCountDown(0), _vblCountDownRem(0), _outputRate(scummVMOutputrate),
- _vblCbProc(nullptr), _isStereo(stereo), _interp(interpolate), _smpInternalSize(internal16Bit ? 2 : 1), _volDown(internal16Bit ? 2 : 0) {
- assert(_drv);
- _vblSmpQty = (_outputRate << 16) / VBL_UPDATE_RATE;
- _vblSmpQtyRem = (_outputRate << 16) % VBL_UPDATE_RATE;
- _vblCountDown = _vblSmpQty;
- _vblCountDownRem = 0;
-}
-
-AudioStream_I3M::~AudioStream_I3M() {
- for (int i = 0; i < 2; ++i)
- delete[] _buffers[i].start;
-}
-
-void AudioStream_I3M::initBuffers(uint32 feedBufferSize) {
- for (int i = 0; i < 2; ++i)
- delete[] _buffers[i].start;
-
- for (int i = 0; i < 2; ++i) {
- _buffers[i].size = feedBufferSize / _frameSize;
- _buffers[i].start = new int8[_buffers[i].size + PCM_BUFFER_RESERVE];
- _buffers[i].end = &_buffers[i].start[_buffers[i].size];
- }
- clearBuffer();
-}
-
-void AudioStream_I3M::initDrivers() {
- for (int i = 0; i < 2; ++i) {
- if (!_drv)
- error("AudioStream_I3M::initDrivers(): Failed to query device rate for device %d", i);
- uint64 irt = (uint64)_drv->getDriverCaps(i).deviceRate * (1 << RATECNV_BIT_PRECSN) / _outputRate;
- _buffers[i].rateConvInt = irt >> (RATECNV_BIT_PRECSN + 16);
- _buffers[i].rateConvFrac = (irt >> 16) & ((1 << RATECNV_BIT_PRECSN) - 1);
- _buffers[i].rateConvAcc = 0;
- _buffers[i].interpolate = _interp && _drv->getDriverCaps(i).allowInterPolation;
- }
-}
-
-void AudioStream_I3M::setVblCallback(const CallbackProc *proc) {
- _vblCbProc = proc;
-}
-
-void AudioStream_I3M::clearBuffer() {
- for (int i = 0; i < 2; ++i) {
- memset(_buffers[i].start, 0, _buffers[i].size + PCM_BUFFER_RESERVE);
- _buffers[i].pos = _buffers[i].start;
- }
-}
-
-void AudioStream_I3M::setMasterVolume(Audio::Mixer::SoundType type, uint16 vol) {
- if (type == Audio::Mixer::kMusicSoundType || type == Audio::Mixer::kPlainSoundType)
- _buffers[0].volume = vol * vol;
- if (type == Audio::Mixer::kSFXSoundType || type == Audio::Mixer::kPlainSoundType)
- _buffers[1].volume = vol * vol;
-}
-
-int AudioStream_I3M::readBuffer(int16 *buffer, const int numSamples) {
- static const Audio::Mixer::SoundType stype[2] = {
- Audio::Mixer::kMusicSoundType,
- Audio::Mixer::kSFXSoundType
- };
-
- static const char errFnNames[2][8] = {"Buffers", "Drivers"};
- int errNo = (!_buffers[0].size || !_buffers[1].size) ? 0 : ((_buffers[0].rateConvAcc == -1 || _buffers[1].rateConvAcc == -1) ? 1 : -1);
- if (errNo != -1)
- error("AudioStream_I3M::readBuffer(): init%s() must be called before playback", errFnNames[errNo]);
-
- for (int i = _isStereo ? numSamples >> 1 : numSamples; i; --i) {
- if (!--_vblCountDown) {
- _vblCountDown = _vblSmpQty;
- _vblCountDownRem += _vblSmpQtyRem;
- while (_vblCountDownRem >= (_vblSmpQty << 16)) {
- _vblCountDownRem -= (_vblSmpQty << 16);
- ++_vblCountDown;
- }
- runVblTask();
- }
-
- int32 smpL = 0;
- int32 smpR = 0;
- for (int ii = 0; ii < 2; ++ii) {
- int smpN = _smpInternalSize == 2 ? *reinterpret_cast<int16*>(_buffers[ii].pos) : _buffers[ii].pos[0];
- int diff = smpN - _buffers[ii].lastL;
- if (diff && _buffers[ii].rateConvAcc && _buffers[ii].interpolate)
- diff = (diff * _buffers[ii].rateConvAcc) >> RATECNV_BIT_PRECSN;
- smpL += (int32)((_buffers[ii].lastL + diff) * (_buffers[ii].volume >> _volDown));
- }
- if (_isStereo) {
- for (int ii = 0; ii < 2; ++ii) {
- int smpN = _smpInternalSize == 2 ? *reinterpret_cast<int16*>(&_buffers[ii].pos[2]) : _buffers[ii].pos[1];
- int diff = smpN - _buffers[ii].lastR;
- if (diff && _buffers[ii].rateConvAcc && _buffers[ii].interpolate)
- diff = (diff * _buffers[ii].rateConvAcc) >> RATECNV_BIT_PRECSN;
- smpR += (int32)((_buffers[ii].lastR + diff) * (_buffers[ii].volume >> _volDown));
- }
- }
-
- for (int ii = 0; ii < 2; ++ii) {
- uint32 incr = (_buffers[ii].rateConvInt * _frameSize);
- _buffers[ii].rateConvAcc += _buffers[ii].rateConvFrac;
- if (_buffers[ii].rateConvAcc & ~((1 << RATECNV_BIT_PRECSN) - 1)) {
- incr += _frameSize;
- _buffers[ii].rateConvAcc &= ((1 << RATECNV_BIT_PRECSN) - 1);
- }
-
- if (incr) {
- _buffers[ii].pos += incr;
- const int8 *lpos = _buffers[ii].pos;
- if (lpos >= _buffers[ii].start + _frameSize)
- lpos -= _frameSize;
-
- if (_smpInternalSize == 2) {
- _buffers[ii].lastL = *reinterpret_cast<const int16*>(lpos);
- if (_isStereo)
- _buffers[ii].lastR = *reinterpret_cast<const int16*>(&lpos[2]);
- } else {
- _buffers[ii].lastL = lpos[0];
- if (_isStereo)
- _buffers[ii].lastR = lpos[1];
- }
-
- if (_buffers[ii].pos >= _buffers[ii].end) {
- int refreshSize = MIN<int>(_vblCountDown * _frameSize, _buffers[ii].size);
- _buffers[ii].pos -= refreshSize;
- assert(_buffers[ii].pos + refreshSize < _buffers[ii].end + PCM_BUFFER_RESERVE);
- generateData(_buffers[ii].pos, refreshSize, stype[ii], _isStereo);
- }
- }
- }
-
- *buffer++ = CLIP<int16>(smpL >> 8, -32768, 32767);
- if (_isStereo)
- *buffer++ = CLIP<int16>(smpR >> 8, -32768, 32767);
- }
- return numSamples;
-}
-
-void AudioStream_I3M::generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) const {
- if (_drv)
- _drv->generateData(dst, byteSize, type, expectStereo);
-}
-
-void AudioStream_I3M::runVblTask() {
- if (_vblCbProc && _vblCbProc->isValid())
- (*_vblCbProc)();
-}
-
-I3MLowLevelPCMDriver::I3MLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool enableInterpolation, bool internal16Bit) :
- I3MSoundDriver(mutex, deviceRate, true, internal16Bit), _interp(enableInterpolation), _frameSize(1), _len(0), _rmH(0), _rmL(0), _smpWtAcc(0), _loopSt(0),
- _loopEnd(0), _baseFreq(0), _rcPos(0), _data(nullptr) {
- _lastSmp[0] = _lastSmp[1] = 0;
-}
-
-void I3MLowLevelPCMDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) {
- if (dst == nullptr || type != Audio::Mixer::kSFXSoundType)
- return;
-
- memset(dst, 0, byteSize);
-
- if (_data == nullptr)
- return;
-
- int32 diff = 0;
- uint16 destFrameSize = expectStereo ? 2 : 1;
- bool interp = (_interp && _rmL);
-
- for (const int8 *end = &dst[byteSize]; dst < end; ) {
- int8 in = 0;
- for (int i = 0; i < destFrameSize; ++i) {
- if (i < _frameSize) {
- in = _data[_rcPos + i];
- if (interp && in != _lastSmp[i]) {
- diff = in - _lastSmp[i];
- diff = (diff * (_smpWtAcc >> 1)) >> 15;
- in = (_lastSmp[i] + diff) & 0xff;
- }
- }
- if (_smpSize == 2)
- *reinterpret_cast<int16*>(dst) = in << 2;
- else
- *dst = in;
- dst += _smpSize;
- }
- uint32 lpos = _rcPos;
- _rcPos += (_rmH * _frameSize);
- _smpWtAcc += _rmL;
- if (_smpWtAcc > 0xffff) {
- _smpWtAcc &= 0xffff;
- _rcPos += _frameSize;
- }
-
- if (interp && _rcPos >= lpos + _frameSize) {
- for (int i = 0; i < _frameSize; ++i)
- _lastSmp[i] = _data[_rcPos - _frameSize + i];
- }
-
- if (_rcPos >= _len) {
- if (_loopSt && _loopEnd) {
- _rcPos = _loopSt + (_rcPos - _len);
- _len = _loopEnd;
- _lastSmp[0] = _data[_rcPos];
- if ((_len - _rcPos) > 1)
- _lastSmp[1] = _data[_rcPos + 1];
- _smpWtAcc = 0;
- } else {
- _data = nullptr;
- _res.reset();
- end = dst;
- }
- setFlags(kStatusDone);
- }
- }
-}
-
-void I3MLowLevelPCMDriver::play(PCMSound *snd) {
- if (!snd || !snd->data)
- return;
-
- Common::StackLock lock(_mutex);
-
- _res = snd->data.reinterpretCast<const int8>();
- _data = _res.get();
- _len = snd->len;
- uint32 rmul = calcRate(_caps.deviceRate, 0x10000, snd->rate);
-
- if (rmul >= 0x7FFD && rmul <= 0x8003)
- rmul = 0x8000;
- else if (ABS((int16)(rmul & 0xffff)) <= 7)
- rmul = (rmul + 7) & ~0xffff;
-
- if (rmul > (uint32)-64)
- rmul = (uint32)-64;
-
- assert(rmul);
-
- _rmL = rmul & 0xffff;
- _rmH = rmul >> 16;
-
- _frameSize = snd->stereo ? 2 : 1;
-
- if (snd->loopend - snd->loopst < 2 || snd->loopend < snd->loopst) {
- _loopSt = 0;
- _loopEnd = 0;
- } else {
- _loopSt = snd->loopst - (snd->loopst % _frameSize);
- _loopEnd = snd->loopend - (snd->loopend % _frameSize);
- }
-
- _baseFreq = snd->baseFreq;
- _rcPos = 0;
- _smpWtAcc = 0;
- _lastSmp[0] = _data[0];
- if (_len >= _frameSize)
- _lastSmp[1] = _data[1];
- clearFlags(kStatusDone);
-}
-
-void I3MLowLevelPCMDriver::stop() {
- Common::StackLock lock(_mutex);
- _data = nullptr;
- _res.reset();
- setFlags(kStatusDone);
-}
-
-uint32 I3MLowLevelPCMDriver::calcRate(uint32 outRate, uint32 factor, uint32 dataRate) {
- uint32 result = outRate;
- uint64 t = 0;
- uint32 c = 0;
-
- if (!factor || !dataRate)
- return (uint32)-1;
-
- if (factor > 0x10000 && dataRate > 0x10000) {
- bool altpth = true;
-
- if (!(dataRate & 0xffff)) {
- SWAP(factor, dataRate);
- if (!(dataRate & 0xffff)) {
- dataRate = (dataRate >> 16) * (factor >> 16);
- factor = 0;
- altpth = false;
- }
- } else if (factor & 0xffff) {
- t = (dataRate & 0xffff) * (factor >> 16) + (dataRate >> 16) * (factor & 0xffff);
- c = (factor & 0xffff) * (dataRate & 0xffff);
- dataRate = (factor >> 16) * (dataRate >> 16) + (t >> 16);
- t = c + ((t & 0xffff) << 16);
- factor = t & (uint32)-1;
- dataRate += (t >> 32);
- altpth = false;
- }
-
- if (altpth) {
- c = dataRate;
- dataRate = (factor >> 16) * (dataRate >> 16);
- factor = (factor >> 16) * (c & 0xffff);
- dataRate += (factor >> 16);
- factor <<= 16;
- }
-
- } else if (factor < 0x10000 && dataRate < 0x10000) {
- factor = factor * dataRate;
- dataRate = 0;
- } else if (factor == 0x10000 || dataRate == 0x10000) {
- if (dataRate == 0x10000)
- SWAP(dataRate, factor);
- factor = dataRate << 16;
- dataRate = (factor | (dataRate >> 16)) ^ factor;
- } else {
- if (factor > 0x10000 && dataRate <= 0x10000)
- SWAP(dataRate, factor);
-
- c = (dataRate >> 16) * (factor & 0xffff);
- factor = (factor & 0xffff) * (dataRate & 0xffff);
- uint32 x = ((factor >> 16) + (c & 0xffff)) & ~0xffff;
- factor += (c << 16);
- result = (c + x) >> 16;
- dataRate = result;
- }
-
- t = factor + (outRate >> 1);
- factor = t & (uint32)-1;
- dataRate += (t >> 32);
-
- if (dataRate >= outRate)
- return (uint32)-1;
-
- dataRate ^= factor;
-
- if (outRate < 0x10000) {
- factor <<= 16;
- dataRate = (dataRate >> 16) | (dataRate << 16);
- outRate = (outRate >> 16) | (outRate << 16);
- }
-
- int32 sh = -1;
-
- if (outRate < 0x1000000) {
- outRate <<= 8;
- sh = -9;
- }
-
- for (t = (int32)outRate; !(t >> 32); t = (int32)outRate) {
- --sh;
- outRate += outRate;
- }
-
- sh = ~sh;
- if (sh) {
- factor <<= sh;
- dataRate = ((dataRate >> (32 - sh)) | (dataRate << sh));
- }
-
- dataRate ^= factor;
-
- if (outRate & 0xffff) {
- bool altpth = false;
-
- if (dataRate / (outRate >> 16) > 0xffff) {
- dataRate = ((dataRate - outRate) << 16) | (factor >> 16);
- factor &= ~0xffff;
- altpth = true;
- } else {
- c = dataRate % (outRate >> 16);
- dataRate /= (outRate >> 16);
- t = ((c << 16) | (factor >> 16)) - ((dataRate & 0xffff) * (outRate & 0xffff));
- factor = (factor << 16) | dataRate;
- dataRate = t & (uint32)-1;
- altpth = (int64)t < 0;
- }
-
- if (altpth) {
- for (t = dataRate; !(t >> 32); ) {
- --factor;
- t += outRate;
- }
- dataRate = t & (uint32)-1;
- }
-
- if (dataRate / (outRate >> 16) > 0xffff) {
- dataRate = ((dataRate - outRate) << 16) | (factor >> 16);
- factor <<= 16;
- altpth = true;
- } else {
- c = dataRate % (outRate >> 16);
- dataRate /= (outRate >> 16);
- t = ((c << 16) | (factor >> 16)) - (dataRate * (outRate & 0xffff));
- factor = (factor << 16) | dataRate;
- dataRate = t & (uint32)-1;;
- altpth = (int64)t < 0;
- }
-
- if (altpth) {
- t = dataRate;
- do {
- factor = (factor & ~0xffff) | (((factor & 0xffff) - 1) & 0xffff);
- t += outRate;
- } while (!(t >> 32));
- dataRate = t & (uint32)-1;
- }
-
- result = factor;
- } else {
- outRate >>= 16;
- if (outRate == 0x8000) {
- t = factor << 1;
- t = (t >> 32) + (dataRate << 1);
- } else {
- c = dataRate % outRate;
- t = ((dataRate / outRate) << 16) | (((c << 16) | (factor >> 16)) / outRate);
- }
- result = t & (uint32)-1;
- }
-
- return result;
-}
-
-I3MFourToneSynthDriver::I3MFourToneSynthDriver(Common::Mutex &mutex, bool internal16Bit) : I3MMusicDriver(4, mutex, false, internal16Bit), _duration(0), _pos(0), _chan(nullptr) {
+FourToneSynthDriver::FourToneSynthDriver(Common::Mutex &mutex, bool internal16Bit) : LegacyMusicDriver(4, mutex, false, internal16Bit), _duration(0), _pos(0), _chan(nullptr) {
_chan = new Channel[_numChan];
}
-I3MFourToneSynthDriver::~I3MFourToneSynthDriver() {
+FourToneSynthDriver::~FourToneSynthDriver() {
Common::StackLock lock(_mutex);
for (int i = 0; i < _numChan; ++i)
setWaveForm(i, 0, 0);
delete[] _chan;
}
-void I3MFourToneSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) {
+void FourToneSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) {
if (dst == nullptr || type != Audio::Mixer::kMusicSoundType)
return;
@@ -872,13 +173,13 @@ void I3MFourToneSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::Soun
memset(dst, 0, end - dst);
}
-void I3MFourToneSynthDriver::start() {
+void FourToneSynthDriver::start() {
Common::StackLock lock(_mutex);
stop();
setDuration(50);
}
-void I3MFourToneSynthDriver::stop() {
+void FourToneSynthDriver::stop() {
Common::StackLock lock(_mutex);
for (int i = 0; i < _numChan; ++i) {
_chan[i].phase = 0;
@@ -887,7 +188,7 @@ void I3MFourToneSynthDriver::stop() {
setDuration(0);
}
-void I3MFourToneSynthDriver::send(int dataType, ...) {
+void FourToneSynthDriver::send(int dataType, ...) {
Common::StackLock lock(_mutex);
va_list arg;
va_start(arg, dataType);
@@ -913,7 +214,7 @@ void I3MFourToneSynthDriver::send(int dataType, ...) {
va_end(arg);
}
-void I3MFourToneSynthDriver::setWaveForm(uint8 chan, const uint8 *data, uint32 dataSize) {
+void FourToneSynthDriver::setWaveForm(uint8 chan, const uint8 *data, uint32 dataSize) {
assert(chan < _numChan);
delete[] _chan[chan].waveform;
_chan[chan].waveform = nullptr;
@@ -927,23 +228,23 @@ void I3MFourToneSynthDriver::setWaveForm(uint8 chan, const uint8 *data, uint32 d
_chan[chan].waveform = wf;
}
-void I3MFourToneSynthDriver::setDuration(uint16 duration) {
+void FourToneSynthDriver::setDuration(uint16 duration) {
_duration = duration;
_pos = 0;
clearFlags(kStatusDone);
}
-void I3MFourToneSynthDriver::setRate(uint8 chan, uint16 rate) {
+void FourToneSynthDriver::setRate(uint8 chan, uint16 rate) {
assert(chan < _numChan);
_chan[chan].rate = rate ? (0x5060000 / (rate >> ((rate < 1600) ? 4 : 6))) : 0;
}
-I3MSquareWaveSynthDriver::I3MSquareWaveSynthDriver(Common::Mutex &mutex, bool internal16Bit) :
- I3MMusicDriver(1, mutex, false, internal16Bit), _count(0xffff), _duration(0), _amplitude(0), _phase(0), _pos(0) {
+SquareWaveSynthDriver::SquareWaveSynthDriver(Common::Mutex &mutex, bool internal16Bit) :
+ LegacyMusicDriver(1, mutex, false, internal16Bit), _count(0xffff), _duration(0), _amplitude(0), _phase(0), _pos(0) {
}
-void I3MSquareWaveSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) {
+void SquareWaveSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) {
if (dst == nullptr || type != Audio::Mixer::kMusicSoundType)
return;
@@ -975,13 +276,13 @@ void I3MSquareWaveSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::So
memset(dst, 0, end - dst);
}
-void I3MSquareWaveSynthDriver::start() {
+void SquareWaveSynthDriver::start() {
Common::StackLock lock(_mutex);
stop();
setFlags(kStatusPlaying | kStatusStartup);
}
-void I3MSquareWaveSynthDriver::stop() {
+void SquareWaveSynthDriver::stop() {
Common::StackLock lock(_mutex);
_lastPara = Triplet();
_count = 0xffff;
@@ -991,7 +292,7 @@ void I3MSquareWaveSynthDriver::stop() {
setFlags(kStatusDone);
}
-void I3MSquareWaveSynthDriver::send(int dataType, ...) {
+void SquareWaveSynthDriver::send(int dataType, ...) {
Common::StackLock lock(_mutex);
va_list arg;
va_start(arg, dataType);
@@ -1008,7 +309,7 @@ void I3MSquareWaveSynthDriver::send(int dataType, ...) {
va_end(arg);
}
-void I3MSquareWaveSynthDriver::addTriplet(uint16 frequency, uint16 amplitude) {
+void SquareWaveSynthDriver::addTriplet(uint16 frequency, uint16 amplitude) {
if ((_status & kStatusStartup) && frequency < 3)
return;
@@ -1032,11 +333,11 @@ void I3MSquareWaveSynthDriver::addTriplet(uint16 frequency, uint16 amplitude) {
setFlags(kStatusOverflow);
}
-Common::WeakPtr<I3MPlayer> *I3MPlayer::_inst = nullptr;
+Common::WeakPtr<Indy3MacSnd> *Indy3MacSnd::_inst = nullptr;
-I3MPlayer::I3MPlayer(ScummEngine *vm, Audio::Mixer *mixer) :
+Indy3MacSnd::Indy3MacSnd(ScummEngine *vm, Audio::Mixer *mixer) : VblTaskClientDriver(),
_vm(vm), _mixer(mixer), _musicChannels(nullptr), _curSound(0), _curSong(0), _lastSoundEffectPrio(0), _idRangeMax(86), _soundEffectNumLoops(-1),
- _musicIDTable(nullptr), _macstr(nullptr), _musicIDTableLen(0), _soundUsage(0), _mdrv(nullptr), _sdrv(nullptr), _nextTickProc(this, &I3MPlayer::nextTick),
+ _musicIDTable(nullptr), _macstr(nullptr), _musicIDTableLen(0), _soundUsage(0), _mdrv(nullptr), _sdrv(nullptr), _nextTickProc(this, &VblTaskClientDriver::callback),
_soundEffectPlaying(false), _songTimer(0), _songTimerInternal(0), _qmode(0), _16bit(false), _qualHi(false), _mixerThread(false), _activeChanCount(0),
_songUnfinished(false), _numMusicChannels(8), _numMusicTracks(4) {
assert(_vm);
@@ -1056,12 +357,12 @@ I3MPlayer::I3MPlayer(ScummEngine *vm, Audio::Mixer *mixer) :
_musicChannels[i] = new MusicChannel(this);
}
-I3MPlayer::~I3MPlayer() {
+Indy3MacSnd::~Indy3MacSnd() {
_mixer->stopHandle(_soundHandle);
delete _macstr;
delete[] _soundUsage;
- for (Common::Array<I3MSoundDriver*>::const_iterator i = _drivers.begin(); i != _drivers.end(); ++i)
+ for (Common::Array<MacSoundDriver*>::const_iterator i = _drivers.begin(); i != _drivers.end(); ++i)
delete *i;
_drivers.clear();
@@ -1075,33 +376,33 @@ I3MPlayer::~I3MPlayer() {
_inst = nullptr;
}
-Common::SharedPtr<I3MPlayer> I3MPlayer::open(ScummEngine *vm, Audio::Mixer *mixer) {
- Common::SharedPtr<I3MPlayer> scp = nullptr;
+Common::SharedPtr<Indy3MacSnd> Indy3MacSnd::open(ScummEngine *vm, Audio::Mixer *mixer) {
+ Common::SharedPtr<Indy3MacSnd> scp = nullptr;
if (_inst == nullptr) {
- scp = Common::SharedPtr<I3MPlayer>(new I3MPlayer(vm, mixer));
- _inst = new Common::WeakPtr<I3MPlayer>(scp);
+ scp = Common::SharedPtr<Indy3MacSnd>(new Indy3MacSnd(vm, mixer));
+ _inst = new Common::WeakPtr<Indy3MacSnd>(scp);
// We can start this with the ScummVM mixer output rate instead of the ASC rate. The Mac sample rate converter can handle it (at
// least for up to 48 KHz, I haven't tried higher settings) and we don't have to do another rate conversion in the ScummVM mixer.
if ((_inst == nullptr) || (mixer == nullptr) || !(scp->startDevices(mixer->getOutputRate(), mixer->getOutputRate() << 16/*ASC_DEVICE_RATE*/, PCM_BUFFER_SIZE, true, false, true)))
- error("I3MPlayer::open(): Failed to start player");
+ error("Indy3MacSnd::open(): Failed to start player");
}
return _inst->lock();
}
-bool I3MPlayer::startDevices(uint32 outputRate, uint32 pcmDeviceRate, uint32 feedBufferSize, bool enableInterpolation, bool stereo, bool internal16Bit) {
- _macstr = new AudioStream_I3M(this, outputRate, stereo, enableInterpolation, internal16Bit);
+bool Indy3MacSnd::startDevices(uint32 outputRate, uint32 pcmDeviceRate, uint32 feedBufferSize, bool enableInterpolation, bool stereo, bool internal16Bit) {
+ _macstr = new MacPlayerAudioStream(this, outputRate, stereo, enableInterpolation, internal16Bit);
if (!_macstr || !_mixer)
return false;
- _sdrv = new I3MLowLevelPCMDriver(_mixer->mutex(), pcmDeviceRate, enableInterpolation, internal16Bit);
- I3MFourToneSynthDriver *mdrv = new I3MFourToneSynthDriver(_mixer->mutex(), internal16Bit);
+ _sdrv = new MacLowLevelPCMDriver(_mixer->mutex(), pcmDeviceRate, enableInterpolation, internal16Bit);
+ FourToneSynthDriver *mdrv = new FourToneSynthDriver(_mixer->mutex(), internal16Bit);
if (!mdrv || !_sdrv)
return false;
for (int i = 0; i < mdrv->numChannels(); ++i)
- mdrv->send(I3MMusicDriver::kChanWaveform, i, _fourToneSynthWaveForm, sizeof(_fourToneSynthWaveForm));
+ mdrv->send(LegacyMusicDriver::kChanWaveform, i, _fourToneSynthWaveForm, sizeof(_fourToneSynthWaveForm));
_qualHi = true;
_16bit = internal16Bit;
_mdrv = mdrv;
@@ -1118,19 +419,19 @@ bool I3MPlayer::startDevices(uint32 outputRate, uint32 pcmDeviceRate, uint32 fee
return true;
}
-void I3MPlayer::setMusicVolume(int vol) {
+void Indy3MacSnd::setMusicVolume(int vol) {
Common::StackLock lock(_mixer->mutex());
if (_macstr)
_macstr->setMasterVolume(Audio::Mixer::kMusicSoundType, vol);
}
-void I3MPlayer::setSfxVolume(int vol) {
+void Indy3MacSnd::setSfxVolume(int vol) {
Common::StackLock lock(_mixer->mutex());
if (_macstr)
_macstr->setMasterVolume(Audio::Mixer::kSFXSoundType, vol);
}
-void I3MPlayer::startSound(int id) {
+void Indy3MacSnd::startSound(int id) {
if (id < 0 || id >= _idRangeMax)
return;
@@ -1140,9 +441,9 @@ void I3MPlayer::startSound(int id) {
startSoundEffect(id);
}
-void I3MPlayer::stopSound(int id) {
+void Indy3MacSnd::stopSound(int id) {
if (id < 0 || id >= _idRangeMax) {
- warning("I3MPlayer::stopSound(): sound id '%d' out of range (0 - 85)", id);
+ warning("Indy3MacSnd::stopSound(): sound id '%d' out of range (0 - 85)", id);
return;
}
@@ -1153,28 +454,28 @@ void I3MPlayer::stopSound(int id) {
stopActiveSound();
}
-void I3MPlayer::stopAllSounds() {
+void Indy3MacSnd::stopAllSounds() {
Common::StackLock lock(_mixer->mutex());
memset(_soundUsage, 0, _idRangeMax);
stopActiveSound();
}
-int I3MPlayer::getMusicTimer() {
+int Indy3MacSnd::getMusicTimer() {
Common::StackLock lock(_mixer->mutex());
return _songTimer;
}
-int I3MPlayer::getSoundStatus(int id) const {
+int Indy3MacSnd::getSoundStatus(int id) const {
if (id < 0 || id >= _idRangeMax) {
- warning("I3MPlayer::getSoundStatus(): sound id '%d' out of range (0 - 85)", id);
+ warning("Indy3MacSnd::getSoundStatus(): sound id '%d' out of range (0 - 85)", id);
return 0;
}
Common::StackLock lock(_mixer->mutex());
return _soundUsage[id];
}
-void I3MPlayer::setQuality(int qual) {
- assert(qual >= Player_Mac_Indy3::kQualAuto && qual <= Player_Mac_Indy3::kQualLo);
+void Indy3MacSnd::setQuality(int qual) {
+ assert(qual >= MacSound::kQualityAuto && qual <= MacSound::kQualityLow);
while (_qualHi == isHiQuality()) {
if (_qmode == qual)
return;
@@ -1182,20 +483,20 @@ void I3MPlayer::setQuality(int qual) {
}
Common::StackLock lock(_mixer->mutex());
- Common::Array<I3MSoundDriver*>::iterator dr = Common::find(_drivers.begin(), _drivers.end(), _mdrv);
+ Common::Array<MacSoundDriver*>::iterator dr = Common::find(_drivers.begin(), _drivers.end(), _mdrv);
delete _mdrv;
_mdrv = nullptr;
_qmode = qual;
if (isHiQuality()) {
- I3MFourToneSynthDriver *mdrv = new I3MFourToneSynthDriver(_mixer->mutex(), _16bit);
+ FourToneSynthDriver *mdrv = new FourToneSynthDriver(_mixer->mutex(), _16bit);
assert(mdrv);
for (int i = 0; i < mdrv->numChannels(); ++i)
- mdrv->send(I3MMusicDriver::kChanWaveform, i, _fourToneSynthWaveForm, sizeof(_fourToneSynthWaveForm));
+ mdrv->send(LegacyMusicDriver::kChanWaveform, i, _fourToneSynthWaveForm, sizeof(_fourToneSynthWaveForm));
_mdrv = mdrv;
_qualHi = true;
} else {
- _mdrv = new I3MSquareWaveSynthDriver(_mixer->mutex(), _16bit);
+ _mdrv = new SquareWaveSynthDriver(_mixer->mutex(), _16bit);
_qualHi = false;
assert(_mdrv);
}
@@ -1205,17 +506,17 @@ void I3MPlayer::setQuality(int qual) {
else if (_drivers.empty())
_drivers.push_back(_mdrv);
else
- error("I3MPlayer::setQuality(): Invalid usage");
+ error("Indy3MacSnd::setQuality(): Invalid usage");
assert(_macstr);
_macstr->initDrivers();
}
-void I3MPlayer::saveLoadWithSerializer(Common::Serializer &ser) {
+void Indy3MacSnd::saveLoadWithSerializer(Common::Serializer &ser) {
ser.syncBytes(_soundUsage, _idRangeMax, VER(113));
}
-void I3MPlayer::restoreAfterLoad() {
+void Indy3MacSnd::restoreAfterLoad() {
stopActiveSound();
for (int i = 0; i < _idRangeMax; ++i) {
if (_soundUsage[i] && isSong(i)) {
@@ -1225,12 +526,7 @@ void I3MPlayer::restoreAfterLoad() {
}
}
-void I3MPlayer::generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const {
- for (Common::Array<I3MSoundDriver*>::const_iterator i = _drivers.begin(); i != _drivers.end(); ++i)
- (*i)->feed(dst, len, type, expectStereo);
-}
-
-void I3MPlayer::nextTick() {
+void Indy3MacSnd::nextTick() {
if (_songTimerInternal++ == 29) {
_songTimerInternal = 0;
++_songTimer;
@@ -1238,25 +534,30 @@ void I3MPlayer::nextTick() {
_mixerThread = true;
- if (!_curSong && (_sdrv->getStatus() & I3MSoundDriver::kStatusDone))
+ if (!_curSong && (_sdrv->getStatus() & MacSoundDriver::kStatusDone))
updateSoundEffect();
else if (_curSong)
updateSong();
- else if (_songUnfinished && (_mdrv->getStatus() & I3MSoundDriver::kStatusDone))
+ else if (_songUnfinished && (_mdrv->getStatus() & MacSoundDriver::kStatusDone))
stopSong();
_mixerThread = false;
}
-const I3MSoundDriver::Caps &I3MPlayer::getDriverCaps(uint8 drvID) const {
+void Indy3MacSnd::generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const {
+ for (Common::Array<MacSoundDriver*>::const_iterator i = _drivers.begin(); i != _drivers.end(); ++i)
+ (*i)->feed(dst, len, type, expectStereo);
+}
+
+const MacSoundDriver::Caps &Indy3MacSnd::getDriverCaps(uint8 drvID) const {
if (drvID >= _drivers.size())
- error(" I3MPlayer::getDriverCaps(): Invalid driver id %d", drvID);
+ error(" Indy3MacSnd::getDriverCaps(): Invalid driver id %d", drvID);
return _drivers[drvID]->getCaps();
}
-void I3MPlayer::startSong(int id) {
+void Indy3MacSnd::startSong(int id) {
if (_mdrv == nullptr || id < 0 || id >= _idRangeMax) {
- warning("I3MPlayer::startSong(): sound id '%d' out of range (0 - 85)", id);
+ warning("Indy3MacSnd::startSong(): sound id '%d' out of range (0 - 85)", id);
return;
}
@@ -1302,9 +603,9 @@ void I3MPlayer::startSong(int id) {
}
}
-void I3MPlayer::startSoundEffect(int id) {
+void Indy3MacSnd::startSoundEffect(int id) {
if (_sdrv == nullptr || id < 0 || id >= _idRangeMax) {
- warning("I3MPlayer::startSoundEffect(): sound id '%d' out of range (0 - 85)", id);
+ warning("Indy3MacSnd::startSoundEffect(): sound id '%d' out of range (0 - 85)", id);
return;
}
@@ -1314,7 +615,7 @@ void I3MPlayer::startSoundEffect(int id) {
assert(ptr);
if (READ_LE_UINT16(ptr) < 28) {
- warning("I3MPlayer::startSoundEffect(%d): invalid resource", id);
+ warning("Indy3MacSnd::startSoundEffect(%d): invalid resource", id);
return;
}
@@ -1361,14 +662,14 @@ void I3MPlayer::startSoundEffect(int id) {
_soundUsage[id]++;
}
-void I3MPlayer::stopSong() {
+void Indy3MacSnd::stopSong() {
Common::StackLock lock(_mixer->mutex());
_mdrv->stop();
finishSong();
_curSound = 0;
}
-void I3MPlayer::stopSoundEffect() {
+void Indy3MacSnd::stopSoundEffect() {
Common::StackLock lock(_mixer->mutex());
_sdrv->stop();
_soundEffectPlaying = false;
@@ -1376,7 +677,7 @@ void I3MPlayer::stopSoundEffect() {
_curSound = 0;
}
-void I3MPlayer::stopActiveSound() {
+void Indy3MacSnd::stopActiveSound() {
if (_soundEffectPlaying)
stopSoundEffect();
else if (_curSong || _songUnfinished)
@@ -1384,17 +685,17 @@ void I3MPlayer::stopActiveSound() {
_songUnfinished = false;
}
-void I3MPlayer::finishSong() {
+void Indy3MacSnd::finishSong() {
if (_soundUsage[_curSong])
--_soundUsage[_curSong];
_curSong = 0;
- _songUnfinished = !(_mdrv->getStatus() & I3MSoundDriver::kStatusDone);
+ _songUnfinished = !(_mdrv->getStatus() & MacSoundDriver::kStatusDone);
}
-void I3MPlayer::updateSong() {
- if (_curSong && (_qualHi || (_mdrv->getStatus() & I3MSoundDriver::kStatusDone))) {
- _mdrv->clearFlags(I3MSoundDriver::kStatusOverflow);
- while (_curSong && !(_mdrv->getStatus() & I3MSoundDriver::kStatusOverflow)) {
+void Indy3MacSnd::updateSong() {
+ if (_curSong && (_qualHi || (_mdrv->getStatus() & MacSoundDriver::kStatusDone))) {
+ _mdrv->clearFlags(MacSoundDriver::kStatusOverflow);
+ while (_curSong && !(_mdrv->getStatus() & MacSoundDriver::kStatusOverflow)) {
for (int i = 4; i; --i) {
for (int ii = 0; ii < _numMusicTracks && _curSong; ++ii)
_musicChannels[ii]->nextTick();
@@ -1402,23 +703,23 @@ void I3MPlayer::updateSong() {
if (_qualHi) {
for (int i = 0; i < _mdrv->numChannels(); ++i)
- _mdrv->send(I3MMusicDriver::kChanRate, i, _curSong ? _musicChannels[i]->checkPeriod() : 0);
+ _mdrv->send(LegacyMusicDriver::kChanRate, i, _curSong ? _musicChannels[i]->checkPeriod() : 0);
if (_curSong)
- _mdrv->send(I3MMusicDriver::kDuration, 10);
+ _mdrv->send(LegacyMusicDriver::kDuration, 10);
} else {
MusicChannel *ch = nullptr;
for (int i = 0; i < _numMusicTracks && ch == nullptr && _curSong; ++i) {
if (_musicChannels[i]->checkPeriod())
ch = _musicChannels[i];
}
- _mdrv->send(I3MMusicDriver::kSquareWaveTriplet, ch ? ch->checkPeriod() : 0, 0xff);
+ _mdrv->send(LegacyMusicDriver::kSquareWaveTriplet, ch ? ch->checkPeriod() : 0, 0xff);
}
}
}
}
-void I3MPlayer::updateSoundEffect() {
- _sdrv->clearFlags(I3MSoundDriver::kStatusDone);
+void Indy3MacSnd::updateSoundEffect() {
+ _sdrv->clearFlags(MacSoundDriver::kStatusDone);
bool chkRestart = false;
if (!_soundEffectPlaying || !_curSound) {
@@ -1440,7 +741,7 @@ void I3MPlayer::updateSoundEffect() {
}
}
-void I3MPlayer::checkRestartSoundEffects() {
+void Indy3MacSnd::checkRestartSoundEffects() {
for (int i = 1; i < _idRangeMax; ++i) {
if (!_soundUsage[i] || isSong(i))
continue;
@@ -1455,7 +756,7 @@ void I3MPlayer::checkRestartSoundEffects() {
}
}
-const uint8 I3MPlayer::_fourToneSynthWaveForm[256] = {
+const uint8 Indy3MacSnd::_fourToneSynthWaveForm[256] = {
0x80, 0x7a, 0x74, 0x6e, 0x69, 0x63, 0x5d, 0x57, 0x52, 0x4c, 0x47, 0x42, 0x3e, 0x3b, 0x38, 0x35,
0x34, 0x33, 0x34, 0x35, 0x37, 0x3a, 0x3e, 0x43, 0x49, 0x4e, 0x54, 0x5b, 0x61, 0x67, 0x6c, 0x71,
0x75, 0x78, 0x7a, 0x7c, 0x7c, 0x7b, 0x79, 0x76, 0x73, 0x6f, 0x6b, 0x66, 0x62, 0x5e, 0x5b, 0x58,
@@ -1474,37 +775,37 @@ const uint8 I3MPlayer::_fourToneSynthWaveForm[256] = {
0xcc, 0xcb, 0xc8, 0xc5, 0xc2, 0xbe, 0xb9, 0xb4, 0xae, 0xa9, 0xa3, 0x9d, 0x97, 0x92, 0x8c, 0x86
};
-void I3MPlayer::endOfTrack() {
+void Indy3MacSnd::endOfTrack() {
if (!_activeChanCount || !--_activeChanCount)
finishSong();
}
-bool I3MPlayer::isSong(int id) const {
+bool Indy3MacSnd::isSong(int id) const {
return (Common::find(_musicIDTable, &_musicIDTable[_musicIDTableLen], id) != &_musicIDTable[_musicIDTableLen]);
}
-bool I3MPlayer::isHiQuality() const {
- return _mixerThread ? _qualHi : (_qmode == Player_Mac_Indy3::kQualAuto && (_vm->VAR_SOUNDCARD == 0xff || _vm->VAR(_vm->VAR_SOUNDCARD) == 11)) || (_qmode == Player_Mac_Indy3::kQualHi);
+bool Indy3MacSnd::isHiQuality() const {
+ return _mixerThread ? _qualHi : (_qmode == MacSound::kQualityAuto && (_vm->VAR_SOUNDCARD == 0xff || _vm->VAR(_vm->VAR_SOUNDCARD) == 11)) || (_qmode == MacSound::kQualityHigh);
}
-I3MPlayer::MusicChannel *I3MPlayer::getMusicChannel(uint8 id) const {
+Indy3MacSnd::MusicChannel *Indy3MacSnd::getMusicChannel(uint8 id) const {
return (id < _numMusicChannels) ? _musicChannels[id] : 0;
}
uint16 savedOffset = 0;
-I3MPlayer::MusicChannel *I3MPlayer::MusicChannel::_ctrlChan = nullptr;
+Indy3MacSnd::MusicChannel *Indy3MacSnd::MusicChannel::_ctrlChan = nullptr;
-I3MPlayer::MusicChannel::MusicChannel(I3MPlayer *pl) : _player(pl), _vars(nullptr), _numVars(0), _ctrlProc(nullptr),
+Indy3MacSnd::MusicChannel::MusicChannel(Indy3MacSnd *pl) : _player(pl), _vars(nullptr), _numVars(0), _ctrlProc(nullptr),
_resSize(0), _savedOffset(savedOffset), _modShapes(g_pv2ModTbl), _modShapesTableSize(g_pv2ModTblSize) {
static const CtrlProc ctrl[8] {
- &I3MPlayer::MusicChannel::ctrl_setShape,
- &I3MPlayer::MusicChannel::ctrl_modPara,
- &I3MPlayer::MusicChannel::ctrl_init,
- &I3MPlayer::MusicChannel::ctrl_returnFromSubroutine,
- &I3MPlayer::MusicChannel::ctrl_jumpToSubroutine,
- &I3MPlayer::MusicChannel::ctrl_initOther,
- &I3MPlayer::MusicChannel::ctrl_decrJumpIf,
- &I3MPlayer::MusicChannel::ctrl_writeVar
+ &Indy3MacSnd::MusicChannel::ctrl_setShape,
+ &Indy3MacSnd::MusicChannel::ctrl_modPara,
+ &Indy3MacSnd::MusicChannel::ctrl_init,
+ &Indy3MacSnd::MusicChannel::ctrl_returnFromSubroutine,
+ &Indy3MacSnd::MusicChannel::ctrl_jumpToSubroutine,
+ &Indy3MacSnd::MusicChannel::ctrl_initOther,
+ &Indy3MacSnd::MusicChannel::ctrl_decrJumpIf,
+ &Indy3MacSnd::MusicChannel::ctrl_writeVar
};
const uint16 *mVars[] = {
@@ -1525,14 +826,14 @@ _resSize(0), _savedOffset(savedOffset), _modShapes(g_pv2ModTbl), _modShapesTable
clear();
}
-I3MPlayer::MusicChannel::~MusicChannel() {
+Indy3MacSnd::MusicChannel::~MusicChannel() {
clear();
delete[] _vars;
_vars = nullptr;
_numVars = 0;
}
-void I3MPlayer::MusicChannel::clear() {
+void Indy3MacSnd::MusicChannel::clear() {
for (int i = 0; i < _numVars; ++i)
getMemberRef(i) = 0;
_resource.reset();
@@ -1540,7 +841,7 @@ void I3MPlayer::MusicChannel::clear() {
_hq = false;
}
-void I3MPlayer::MusicChannel::start(Common::SharedPtr<const byte> &songRes, uint16 offset, bool hq) {
+void Indy3MacSnd::MusicChannel::start(Common::SharedPtr<const byte> &songRes, uint16 offset, bool hq) {
clear();
_resource = songRes;
_resSize = READ_LE_UINT16(_resource.get());
@@ -1549,7 +850,7 @@ void I3MPlayer::MusicChannel::start(Common::SharedPtr<const byte> &songRes, uint
_hq = hq;
}
-void I3MPlayer::MusicChannel::nextTick() {
+void Indy3MacSnd::MusicChannel::nextTick() {
if (!_frameLen)
return;
@@ -1597,9 +898,9 @@ void I3MPlayer::MusicChannel::nextTick() {
_envRate = *in >> 16;
}
-void I3MPlayer::MusicChannel::parseNextEvents() {
+void Indy3MacSnd::MusicChannel::parseNextEvents() {
if (_resSize && _curPos >= _resSize) {
- warning("I3MPlayer::MusicChannel::parseNext(): playback error");
+ warning("Indy3MacSnd::MusicChannel::parseNext(): playback error");
_frameLen = 0;
_curPos = 0;
_player->stopSong();
@@ -1642,7 +943,7 @@ void I3MPlayer::MusicChannel::parseNextEvents() {
int cp = in - _resource.get();
if ((cp >= _resSize && _frameLen) || cp & ~0xffff) {
- warning("I3MPlayer::MusicChannel::parseNext(): playback error");
+ warning("Indy3MacSnd::MusicChannel::parseNext(): playback error");
_frameLen = 0;
_player->stopSong();
}
@@ -1652,7 +953,7 @@ void I3MPlayer::MusicChannel::parseNextEvents() {
_player->endOfTrack();
}
-void I3MPlayer::MusicChannel::noteOn(uint16 duration, uint8 note) {
+void Indy3MacSnd::MusicChannel::noteOn(uint16 duration, uint8 note) {
static const uint16 noteFreqTable[2][12] = {
{ 0xFFC0, 0xF140, 0xE3C0, 0xD700, 0xCB40, 0xBF80, 0xB4C0, 0xAA80, 0xA100, 0x9800, 0x8F80, 0x8740 },
{ 0x8E84, 0x8684, 0x7EF7, 0x77D7, 0x714F, 0x6AC4, 0x64C6, 0x5F1E, 0x59C7, 0x54BD, 0x4FFC, 0x4B7E }
@@ -1668,11 +969,11 @@ void I3MPlayer::MusicChannel::noteOn(uint16 duration, uint8 note) {
_freqEff = _freqCur = noteFreqTable[_hq ? 0 : 1][n % 12] >> (n / 12);
}
-uint16 I3MPlayer::MusicChannel::checkPeriod() const {
+uint16 Indy3MacSnd::MusicChannel::checkPeriod() const {
return (_frameLen && _envPhase) ? _freqEff : 0;
}
-bool I3MPlayer::MusicChannel::ctrl_setShape(const byte *&pos) {
+bool Indy3MacSnd::MusicChannel::ctrl_setShape(const byte *&pos) {
static const uint16 offsets[15] = { 0, 6, 12, 18, 24, 30, 36, 44, 52, 60, 68, 82, 76, 82, 90 };
uint8 i = (*pos++) >> 1;
assert(i < ARRAYSIZE(offsets));
@@ -1680,64 +981,64 @@ bool I3MPlayer::MusicChannel::ctrl_setShape(const byte *&pos) {
return true;
}
-bool I3MPlayer::MusicChannel::ctrl_modPara(const byte *&pos) {
+bool Indy3MacSnd::MusicChannel::ctrl_modPara(const byte *&pos) {
static const uint16 table[10] = { 0x0000, 0x1000, 0x1000, 0x1000, 0x2000, 0x0020, 0x3020, 0x2000, 0x2020, 0x1000 };
int ix = (*pos++);
if ((ix & 1) || ((ix >> 1) + 1 >= ARRAYSIZE(table)))
- error("I3MPlayer::MusicChannel::ctrl_modPara(): data error");
+ error("Indy3MacSnd::MusicChannel::ctrl_modPara(): data error");
ix >>= 1;
_modType = table[ix];
_modRange = table[ix + 1];
return true;
}
-bool I3MPlayer::MusicChannel::ctrl_init(const byte *&pos) {
+bool Indy3MacSnd::MusicChannel::ctrl_init(const byte *&pos) {
limitedClear();
return true;
}
-bool I3MPlayer::MusicChannel::ctrl_returnFromSubroutine(const byte *&pos) {
+bool Indy3MacSnd::MusicChannel::ctrl_returnFromSubroutine(const byte *&pos) {
pos = _resource.get() + _savedOffset;
if (pos >= _resource.get() + _resSize)
- error("I3MPlayer::MusicChannel::ctrl_returnFromSubroutine(): invalid address");
+ error("Indy3MacSnd::MusicChannel::ctrl_returnFromSubroutine(): invalid address");
return true;
}
-bool I3MPlayer::MusicChannel::ctrl_jumpToSubroutine(const byte *&pos) {
+bool Indy3MacSnd::MusicChannel::ctrl_jumpToSubroutine(const byte *&pos) {
uint16 offs = READ_LE_UINT16(pos);
_savedOffset = pos + 2 - _resource.get();
if (offs >= _resSize)
- error("I3MPlayer::MusicChannel::ctrl_jumpToSubroutine(): invalid address");
+ error("Indy3MacSnd::MusicChannel::ctrl_jumpToSubroutine(): invalid address");
pos = _resource.get() + offs;
return true;
}
-bool I3MPlayer::MusicChannel::ctrl_initOther(const byte *&pos) {
+bool Indy3MacSnd::MusicChannel::ctrl_initOther(const byte *&pos) {
uint16 val = READ_LE_UINT16(pos);
pos += 2;
if (val % 50)
- error("I3MPlayer::MusicChannel::ctrl_initOther(): data error");
+ error("Indy3MacSnd::MusicChannel::ctrl_initOther(): data error");
_ctrlChan = _player->getMusicChannel(val / 50);
assert(_ctrlChan);
_ctrlChan->limitedClear();
return true;
}
-bool I3MPlayer::MusicChannel::ctrl_decrJumpIf(const byte *&pos) {
+bool Indy3MacSnd::MusicChannel::ctrl_decrJumpIf(const byte *&pos) {
uint16 &var = getMemberRef(*pos++ >> 1);
int16 offs = READ_LE_INT16(pos);
pos += 2;
if (var == 0) {
pos += offs;
if (pos < _resource.get() || pos >= _resource.get() + _resSize)
- error("I3MPlayer::MusicChannel::ctrl_jumpToSubroutine(): invalid address");
+ error("Indy3MacSnd::MusicChannel::ctrl_jumpToSubroutine(): invalid address");
} else {
--var;
}
return true;
}
-bool I3MPlayer::MusicChannel::ctrl_writeVar(const byte *&pos) {
+bool Indy3MacSnd::MusicChannel::ctrl_writeVar(const byte *&pos) {
byte ix = *pos++;
uint16 val = READ_LE_UINT16(pos);
pos += 2;
@@ -1745,11 +1046,11 @@ bool I3MPlayer::MusicChannel::ctrl_writeVar(const byte *&pos) {
return (bool)ix;
}
-bool I3MPlayer::MusicChannel::ctrlProc(int procId, const byte *&arg) {
+bool Indy3MacSnd::MusicChannel::ctrlProc(int procId, const byte *&arg) {
return (_ctrlChan && _ctrlProc && procId >= 0 && procId <= 7) ? (_ctrlChan->*_ctrlProc[procId])(arg) : false;
}
-void I3MPlayer::MusicChannel::setFrameLen(uint8 len) {
+void Indy3MacSnd::MusicChannel::setFrameLen(uint8 len) {
static const uint8 durationTicks[22] = {
0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x04, 0x05, 0x06, 0x08, 0x09,
0x0C, 0x10, 0x12, 0x18, 0x20, 0x26, 0x30, 0x40, 0x48, 0x60, 0x00
@@ -1758,11 +1059,11 @@ void I3MPlayer::MusicChannel::setFrameLen(uint8 len) {
assert(_ctrlChan);
len &= 0x1f;
if (len >= ARRAYSIZE(durationTicks))
- error("I3MPlayer::MusicChannel::setFrameLen(): Out of range (val %d, range 0 - %d)", len, ARRAYSIZE(durationTicks) - 1);
+ error("Indy3MacSnd::MusicChannel::setFrameLen(): Out of range (val %d, range 0 - %d)", len, ARRAYSIZE(durationTicks) - 1);
_ctrlChan->_frameLen = MAX<uint16>(_ctrlChan->_tempo, 1) * durationTicks[len];
}
-void I3MPlayer::MusicChannel::limitedClear() {
+void Indy3MacSnd::MusicChannel::limitedClear() {
for (int i = 1; i < 7; ++i)
getMemberRef(i) = 0;
for (int i = 8; i < 10; ++i)
@@ -1773,14 +1074,14 @@ void I3MPlayer::MusicChannel::limitedClear() {
getMemberRef(i) = 0;
}
-uint16 &I3MPlayer::MusicChannel::getMemberRef(int pos) {
+uint16 &Indy3MacSnd::MusicChannel::getMemberRef(int pos) {
assert(_vars);
if (pos < 0 || pos >= _numVars)
- error("I3MPlayer::MusicChannel::getMemberRef(): attempting invalid access (var: %d, valid range: %d - %d)", pos, 0, _numVars - 1);
+ error("Indy3MacSnd::MusicChannel::getMemberRef(): attempting invalid access (var: %d, valid range: %d - %d)", pos, 0, _numVars - 1);
return *_vars[pos];
}
-const uint32 I3MPlayer::MusicChannel::_envShapes[98] = {
+const uint32 Indy3MacSnd::MusicChannel::_envShapes[98] = {
0x0003ffff, 0x00000000, 0x00000000, 0x00000000, 0x0000ffff, 0x00000000,
0x0003ffff, 0x00000020, 0x0000ffff, 0x00000000, 0x0000ffff, 0x00000000,
0x0003ffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -1797,66 +1098,7 @@ const uint32 I3MPlayer::MusicChannel::_envShapes[98] = {
0x88b8ffff, 0x01f40014, 0x00000000, 0x00000000, 0xafc8ffff, 0xfe0c003c, 0x0000ffff, 0x00000000
};
-Player_Mac_Indy3::Player_Mac_Indy3(ScummEngine *vm, Audio::Mixer *mixer) : _player(nullptr) {
- _player = I3MPlayer::open(vm, mixer);
-}
-
-Player_Mac_Indy3::~Player_Mac_Indy3() {
- _player = nullptr;
-}
-
-void Player_Mac_Indy3::setMusicVolume(int vol) {
- if (_player != nullptr)
- _player->setMusicVolume(vol);
-}
-
-void Player_Mac_Indy3::setSfxVolume(int vol) {
- if (_player != nullptr)
- _player->setSfxVolume(vol);
-}
-
-void Player_Mac_Indy3::startSound(int id) {
- if (_player != nullptr)
- _player->startSound(id);
-}
-
-void Player_Mac_Indy3::stopSound(int id) {
- if (_player != nullptr)
- _player->stopSound(id);
-}
-
-void Player_Mac_Indy3::stopAllSounds() {
- if (_player != nullptr)
- _player->stopAllSounds();
-}
-
-int Player_Mac_Indy3::getMusicTimer() {
- return (_player != nullptr) ? _player->getMusicTimer() : 0;
-}
-
-int Player_Mac_Indy3::getSoundStatus(int id) const {
- return (_player != nullptr) ? _player->getSoundStatus(id) : 0;
-}
-
-void Player_Mac_Indy3::setQuality(int qual) {
- if (_player != nullptr)
- _player->setQuality(qual);
-}
-
-void Player_Mac_Indy3::saveLoadWithSerializer(Common::Serializer &ser) {
- if (_player != nullptr)
- _player->saveLoadWithSerializer(ser);
-}
-
-void Player_Mac_Indy3::restoreAfterLoad() {
- if (_player != nullptr)
- _player->restoreAfterLoad();
-}
-
#undef ASC_DEVICE_RATE
-#undef VBL_UPDATE_RATE
#undef PCM_BUFFER_SIZE
-#undef PCM_BUFFER_RESERVE
-#undef RATECNV_BIT_PRECSN
} // End of namespace Scumm
diff --git a/engines/scumm/players/player_mac_intern.h b/engines/scumm/players/player_mac_intern.h
new file mode 100644
index 00000000000..8169e445e6a
--- /dev/null
+++ b/engines/scumm/players/player_mac_intern.h
@@ -0,0 +1,318 @@
+/* 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/>.
+ *
+ */
+
+#ifndef SCUMM_PLAYERS_PLAYER_MAC_INTERN_H
+#define SCUMM_PLAYERS_PLAYER_MAC_INTERN_H
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+
+namespace Scumm {
+
+class MacSoundDriver {
+public:
+ MacSoundDriver(Common::Mutex &mutex, uint32 deviceRate, bool canInterpolate, bool internal16Bit) : _mutex(mutex), _caps(deviceRate, canInterpolate),
+ _smpSize(internal16Bit ? 2 : 1), _smpMin(internal16Bit ? -32768 : -128), _smpMax(internal16Bit ? 32767 : 127), _status(0) {}
+ virtual ~MacSoundDriver() {}
+ virtual void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) = 0;
+
+ struct Caps {
+ Caps(uint32 rate, bool interp) :deviceRate(rate), allowInterPolation(interp) {}
+ const uint32 deviceRate;
+ const bool allowInterPolation;
+ };
+ const Caps &getCaps() const { return _caps; }
+
+ enum StatusFlag : uint8 {
+ kStatusPlaying = 1 << 0,
+ kStatusOverflow = 1 << 1,
+ kStatusStartup = 1 << 2,
+ kStatusDone = 1 << 3
+ };
+ uint8 getStatus() const { return _status; }
+ void clearFlags(uint8 flags) { _status &= ~flags; }
+
+protected:
+ void setFlags(uint8 flags) { _status |= flags; }
+
+ Common::Mutex &_mutex;
+ const int _smpSize;
+ const int16 _smpMin;
+ const int16 _smpMax;
+ const Caps _caps;
+ uint8 _status;
+};
+
+class MacLowLevelPCMDriver final : public MacSoundDriver {
+public:
+ struct PCMSound {
+ PCMSound() : len(0), rate(0), loopst(0), loopend(0), baseFreq(0), stereo(false) {}
+ Common::SharedPtr<const byte> data;
+ uint32 len;
+ uint32 rate;
+ uint32 loopst;
+ uint32 loopend;
+ byte baseFreq;
+ bool stereo;
+ };
+public:
+ MacLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool enableInterpolation, bool internal16Bit);
+ void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) override;
+ void play(PCMSound *snd);
+ void stop();
+private:
+ uint32 calcRate(uint32 outRate, uint32 factor, uint32 dataRate);
+
+ Common::SharedPtr<const int8> _res;
+ const int8 *_data;
+ const bool _interp;
+ int8 _lastSmp[2];
+ uint32 _len;
+ uint16 _rmH;
+ uint16 _rmL;
+ uint32 _loopSt;
+ uint32 _loopEnd;
+ byte _baseFreq;
+ uint32 _rcPos;
+ uint32 _smpWtAcc;
+ uint16 _frameSize;
+};
+
+class VblTaskClientDriver {
+public:
+ virtual void callback() = 0;
+ virtual void generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const = 0;
+ virtual const MacSoundDriver::Caps &getDriverCaps(uint8 drvID) const = 0;
+};
+
+class MacPlayerAudioStream : public Audio::AudioStream {
+public:
+ MacPlayerAudioStream(VblTaskClientDriver *drv, uint32 scummVMOutputrate, bool stereo, bool interpolate, bool internal16Bit);
+ ~MacPlayerAudioStream() override;
+
+ void initBuffers(uint32 feedBufferSize);
+ void initDrivers();
+ typedef Common::Functor0Mem<void, VblTaskClientDriver> CallbackProc;
+ void setVblCallback(const CallbackProc *proc);
+ void clearBuffer();
+
+ void setMasterVolume(Audio::Mixer::SoundType type, uint16 vol);
+
+ // AudioStream interface
+ int readBuffer(int16 *buffer, const int numSamples) override;
+ bool isStereo() const override { return _isStereo; }
+ int getRate() const override { return _outputRate; }
+ bool endOfData() const override { return false; }
+
+private:
+ void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType, bool expectStereo) const;
+ void runVblTask();
+
+ VblTaskClientDriver *_drv;
+
+ uint32 _vblSmpQty;
+ uint32 _vblSmpQtyRem;
+ uint32 _vblCountDown;
+ uint32 _vblCountDownRem;
+ const CallbackProc *_vblCbProc;
+
+ struct SmpBuffer {
+ SmpBuffer() : start(0), pos(0), end(0), volume(0x10000), lastL(0), lastR(0), size(0), rateConvInt(0), rateConvFrac(0), rateConvAcc(-1), interpolate(false) {}
+ int8 *start;
+ int8 *pos;
+ const int8 *end;
+ uint32 volume;
+ int32 lastL;
+ int32 lastR;
+ uint32 size;
+ uint32 rateConvInt;
+ uint32 rateConvFrac;
+ int32 rateConvAcc;
+ bool interpolate;
+ } _buffers[2];
+
+ const uint32 _outputRate;
+ const uint8 _frameSize;
+ const bool _interp;
+ const int _smpInternalSize;
+ const int _volDown;
+
+ const bool _isStereo;
+};
+
+class LegacyMusicDriver;
+class Indy3MacSnd final : public VblTaskClientDriver {
+private:
+ Indy3MacSnd(ScummEngine *vm, Audio::Mixer *mixer);
+public:
+ ~Indy3MacSnd();
+ static Common::SharedPtr<Indy3MacSnd> open(ScummEngine *scumm, Audio::Mixer *mixer);
+ bool startDevices(uint32 outputRate, uint32 pcmDeviceRate, uint32 feedBufferSize, bool enableInterpolation, bool stereo, bool internal16Bit);
+
+ void setMusicVolume(int vol);
+ void setSfxVolume(int vol);
+ void startSound(int id);
+ void stopSound(int id);
+ void stopAllSounds();
+ int getMusicTimer();
+ int getSoundStatus(int id) const;
+ void setQuality(int qual);
+ void saveLoadWithSerializer(Common::Serializer &ser);
+ void restoreAfterLoad();
+
+ void nextTick();
+ void callback() override { nextTick(); }
+ void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) const override;
+ const MacSoundDriver::Caps &getDriverCaps(uint8 drvID) const override;
+
+private:
+ void startSong(int id);
+ void startSoundEffect(int id);
+ void stopSong();
+ void stopSoundEffect();
+ void stopActiveSound();
+ void finishSong();
+ void updateSong();
+ void updateSoundEffect();
+
+ void checkRestartSoundEffects();
+ void endOfTrack();
+
+ bool isSong(int id) const;
+ bool isHiQuality() const;
+
+ int _curSound;
+ int _curSong;
+ int _lastSoundEffectPrio;
+ int _soundEffectNumLoops;
+ int _songTimer;
+ bool _songUnfinished;
+ uint _activeChanCount;
+ byte _songTimerInternal;
+ byte *_soundUsage;
+
+ bool _soundEffectPlaying;
+ int _qmode;
+ bool _16bit;
+ bool _qualHi;
+ bool _mixerThread;
+
+ MacLowLevelPCMDriver::PCMSound _pcmSnd;
+
+ MacPlayerAudioStream *_macstr;
+ Audio::SoundHandle _soundHandle;
+ MacPlayerAudioStream::CallbackProc _nextTickProc;
+
+ ScummEngine *_vm;
+ Audio::Mixer *_mixer;
+ static Common::WeakPtr<Indy3MacSnd> *_inst;
+
+ const byte *_musicIDTable;
+ int _musicIDTableLen;
+ const int _idRangeMax;
+
+ LegacyMusicDriver *_mdrv;
+ MacLowLevelPCMDriver *_sdrv;
+ Common::Array<MacSoundDriver*> _drivers;
+
+private:
+ class MusicChannel {
+ public:
+ MusicChannel(Indy3MacSnd *pl);
+ ~MusicChannel();
+ void clear();
+
+ void start(Common::SharedPtr<const byte> &songRes, uint16 offset, bool hq);
+ void nextTick();
+ void parseNextEvents();
+ void noteOn(uint16 duration, uint8 note);
+ uint16 checkPeriod() const;
+
+ uint16 _frameLen;
+ uint16 _curPos;
+ uint16 _freqCur;
+ uint16 _freqIncr;
+ uint16 _freqEff;
+ uint16 _envPhase;
+ uint16 _envRate;
+ uint16 _tempo;
+ uint16 _envSust;
+ int16 _transpose;
+ uint16 _envAtt;
+ uint16 _envShape;
+ uint16 _envStep;
+ uint16 _envStepLen;
+ uint16 _modType;
+ uint16 _modState;
+ uint16 _modStep;
+ uint16 _modSensitivity;
+ uint16 _modRange;
+ uint16 _localVars[5];
+ Common::SharedPtr<const byte> _resource;
+ bool _hq;
+
+ private:
+ typedef bool (Indy3MacSnd::MusicChannel::*CtrlProc)(const byte *&);
+
+ bool ctrl_setShape(const byte *&pos);
+ bool ctrl_modPara(const byte *&pos);
+ bool ctrl_init(const byte *&pos);
+ bool ctrl_returnFromSubroutine(const byte *&pos);
+ bool ctrl_jumpToSubroutine(const byte *&pos);
+ bool ctrl_initOther(const byte *&pos);
+ bool ctrl_decrJumpIf(const byte *&pos);
+ bool ctrl_writeVar(const byte *&pos);
+
+ const CtrlProc *_ctrlProc;
+
+ void limitedClear();
+ uint16 &getMemberRef(int pos);
+
+ uint16 **_vars;
+ int _numVars;
+ uint16 &_savedOffset;
+
+ uint16 _resSize;
+
+ Indy3MacSnd *_player;
+ static MusicChannel *_ctrlChan;
+
+ static const uint32 _envShapes[98];
+ const uint8 *&_modShapes;
+ const uint32 &_modShapesTableSize;
+
+ bool ctrlProc(int procId, const byte *&arg);
+ void setFrameLen(uint8 len);
+ };
+
+ MusicChannel **_musicChannels;
+ const int _numMusicChannels;
+ const int _numMusicTracks;
+
+ static const uint8 _fourToneSynthWaveForm[256];
+
+public:
+ MusicChannel *getMusicChannel(uint8 id) const;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/players/player_mac_new.cpp b/engines/scumm/players/player_mac_new.cpp
new file mode 100644
index 00000000000..0c414e50500
--- /dev/null
+++ b/engines/scumm/players/player_mac_new.cpp
@@ -0,0 +1,551 @@
+/* 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 "scumm/players/player_mac_new.h"
+#include "scumm/players/player_mac_intern.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "common/endian.h"
+
+namespace Scumm {
+
+#define ASC_DEVICE_RATE 0x56EE8BA3
+#define VBL_UPDATE_RATE 0x003C25BD
+#define PCM_BUFFER_RESERVE 64
+#define RATECNV_BIT_PRECSN 24
+
+MacPlayerAudioStream::MacPlayerAudioStream(VblTaskClientDriver *drv, uint32 scummVMOutputrate, bool stereo, bool interpolate, bool internal16Bit) : Audio::AudioStream(), _drv(drv),
+ _vblSmpQty(0), _vblSmpQtyRem(0), _frameSize((stereo ? 2 : 1) * (internal16Bit ? 2 : 1)), _vblCountDown(0), _vblCountDownRem(0), _outputRate(scummVMOutputrate),
+ _vblCbProc(nullptr), _isStereo(stereo), _interp(interpolate), _smpInternalSize(internal16Bit ? 2 : 1), _volDown(internal16Bit ? 2 : 0) {
+ assert(_drv);
+ _vblSmpQty = (_outputRate << 16) / VBL_UPDATE_RATE;
+ _vblSmpQtyRem = (_outputRate << 16) % VBL_UPDATE_RATE;
+ _vblCountDown = _vblSmpQty;
+ _vblCountDownRem = 0;
+}
+
+MacPlayerAudioStream::~MacPlayerAudioStream() {
+ for (int i = 0; i < 2; ++i)
+ delete[] _buffers[i].start;
+}
+
+void MacPlayerAudioStream::initBuffers(uint32 feedBufferSize) {
+ for (int i = 0; i < 2; ++i)
+ delete[] _buffers[i].start;
+
+ for (int i = 0; i < 2; ++i) {
+ _buffers[i].size = feedBufferSize / _frameSize;
+ _buffers[i].start = new int8[_buffers[i].size + PCM_BUFFER_RESERVE];
+ _buffers[i].end = &_buffers[i].start[_buffers[i].size];
+ }
+ clearBuffer();
+}
+
+void MacPlayerAudioStream::initDrivers() {
+ for (int i = 0; i < 2; ++i) {
+ if (!_drv)
+ error("MacPlayerAudioStream::initDrivers(): Failed to query device rate for device %d", i);
+ uint64 irt = (uint64)_drv->getDriverCaps(i).deviceRate * (1 << RATECNV_BIT_PRECSN) / _outputRate;
+ _buffers[i].rateConvInt = irt >> (RATECNV_BIT_PRECSN + 16);
+ _buffers[i].rateConvFrac = (irt >> 16) & ((1 << RATECNV_BIT_PRECSN) - 1);
+ _buffers[i].rateConvAcc = 0;
+ _buffers[i].interpolate = _interp && _drv->getDriverCaps(i).allowInterPolation;
+ }
+}
+
+void MacPlayerAudioStream::setVblCallback(const CallbackProc *proc) {
+ _vblCbProc = proc;
+}
+
+void MacPlayerAudioStream::clearBuffer() {
+ for (int i = 0; i < 2; ++i) {
+ memset(_buffers[i].start, 0, _buffers[i].size + PCM_BUFFER_RESERVE);
+ _buffers[i].pos = _buffers[i].start;
+ }
+}
+
+void MacPlayerAudioStream::setMasterVolume(Audio::Mixer::SoundType type, uint16 vol) {
+ if (type == Audio::Mixer::kMusicSoundType || type == Audio::Mixer::kPlainSoundType)
+ _buffers[0].volume = vol * vol;
+ if (type == Audio::Mixer::kSFXSoundType || type == Audio::Mixer::kPlainSoundType)
+ _buffers[1].volume = vol * vol;
+}
+
+int MacPlayerAudioStream::readBuffer(int16 *buffer, const int numSamples) {
+ static const Audio::Mixer::SoundType stype[2] = {
+ Audio::Mixer::kMusicSoundType,
+ Audio::Mixer::kSFXSoundType
+ };
+
+ static const char errFnNames[2][8] = {"Buffers", "Drivers"};
+ int errNo = (!_buffers[0].size || !_buffers[1].size) ? 0 : ((_buffers[0].rateConvAcc == -1 || _buffers[1].rateConvAcc == -1) ? 1 : -1);
+ if (errNo != -1)
+ error("MacPlayerAudioStream::readBuffer(): init%s() must be called before playback", errFnNames[errNo]);
+
+ for (int i = _isStereo ? numSamples >> 1 : numSamples; i; --i) {
+ if (!--_vblCountDown) {
+ _vblCountDown = _vblSmpQty;
+ _vblCountDownRem += _vblSmpQtyRem;
+ while (_vblCountDownRem >= (_vblSmpQty << 16)) {
+ _vblCountDownRem -= (_vblSmpQty << 16);
+ ++_vblCountDown;
+ }
+ runVblTask();
+ }
+
+ int32 smpL = 0;
+ int32 smpR = 0;
+ for (int ii = 0; ii < 2; ++ii) {
+ int smpN = _smpInternalSize == 2 ? *reinterpret_cast<int16*>(_buffers[ii].pos) : _buffers[ii].pos[0];
+ int diff = smpN - _buffers[ii].lastL;
+ if (diff && _buffers[ii].rateConvAcc && _buffers[ii].interpolate)
+ diff = (diff * _buffers[ii].rateConvAcc) >> RATECNV_BIT_PRECSN;
+ smpL += (int32)((_buffers[ii].lastL + diff) * (_buffers[ii].volume >> _volDown));
+ }
+ if (_isStereo) {
+ for (int ii = 0; ii < 2; ++ii) {
+ int smpN = _smpInternalSize == 2 ? *reinterpret_cast<int16*>(&_buffers[ii].pos[2]) : _buffers[ii].pos[1];
+ int diff = smpN - _buffers[ii].lastR;
+ if (diff && _buffers[ii].rateConvAcc && _buffers[ii].interpolate)
+ diff = (diff * _buffers[ii].rateConvAcc) >> RATECNV_BIT_PRECSN;
+ smpR += (int32)((_buffers[ii].lastR + diff) * (_buffers[ii].volume >> _volDown));
+ }
+ }
+
+ for (int ii = 0; ii < 2; ++ii) {
+ uint32 incr = (_buffers[ii].rateConvInt * _frameSize);
+ _buffers[ii].rateConvAcc += _buffers[ii].rateConvFrac;
+ if (_buffers[ii].rateConvAcc & ~((1 << RATECNV_BIT_PRECSN) - 1)) {
+ incr += _frameSize;
+ _buffers[ii].rateConvAcc &= ((1 << RATECNV_BIT_PRECSN) - 1);
+ }
+
+ if (incr) {
+ _buffers[ii].pos += incr;
+ const int8 *lpos = _buffers[ii].pos;
+ if (lpos >= _buffers[ii].start + _frameSize)
+ lpos -= _frameSize;
+
+ if (_smpInternalSize == 2) {
+ _buffers[ii].lastL = *reinterpret_cast<const int16*>(lpos);
+ if (_isStereo)
+ _buffers[ii].lastR = *reinterpret_cast<const int16*>(&lpos[2]);
+ } else {
+ _buffers[ii].lastL = lpos[0];
+ if (_isStereo)
+ _buffers[ii].lastR = lpos[1];
+ }
+
+ if (_buffers[ii].pos >= _buffers[ii].end) {
+ int refreshSize = MIN<int>(_vblCountDown * _frameSize, _buffers[ii].size);
+ _buffers[ii].pos -= refreshSize;
+ assert(_buffers[ii].pos + refreshSize < _buffers[ii].end + PCM_BUFFER_RESERVE);
+ generateData(_buffers[ii].pos, refreshSize, stype[ii], _isStereo);
+ }
+ }
+ }
+
+ *buffer++ = CLIP<int16>(smpL >> 8, -32768, 32767);
+ if (_isStereo)
+ *buffer++ = CLIP<int16>(smpR >> 8, -32768, 32767);
+ }
+ return numSamples;
+}
+
+void MacPlayerAudioStream::generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) const {
+ if (_drv)
+ _drv->generateData(dst, byteSize, type, expectStereo);
+}
+
+void MacPlayerAudioStream::runVblTask() {
+ if (_vblCbProc && _vblCbProc->isValid())
+ (*_vblCbProc)();
+}
+
+MacLowLevelPCMDriver::MacLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool enableInterpolation, bool internal16Bit) :
+ MacSoundDriver(mutex, deviceRate, true, internal16Bit), _interp(enableInterpolation), _frameSize(1), _len(0), _rmH(0), _rmL(0), _smpWtAcc(0), _loopSt(0),
+ _loopEnd(0), _baseFreq(0), _rcPos(0), _data(nullptr) {
+ _lastSmp[0] = _lastSmp[1] = 0;
+}
+
+void MacLowLevelPCMDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) {
+ if (dst == nullptr || type != Audio::Mixer::kSFXSoundType)
+ return;
+
+ memset(dst, 0, byteSize);
+
+ if (_data == nullptr)
+ return;
+
+ int32 diff = 0;
+ uint16 destFrameSize = expectStereo ? 2 : 1;
+ bool interp = (_interp && _rmL);
+
+ for (const int8 *end = &dst[byteSize]; dst < end; ) {
+ int8 in = 0;
+ for (int i = 0; i < destFrameSize; ++i) {
+ if (i < _frameSize) {
+ in = _data[_rcPos + i];
+ if (interp && in != _lastSmp[i]) {
+ diff = in - _lastSmp[i];
+ diff = (diff * (_smpWtAcc >> 1)) >> 15;
+ in = (_lastSmp[i] + diff) & 0xff;
+ }
+ }
+ if (_smpSize == 2)
+ *reinterpret_cast<int16*>(dst) = in << 2;
+ else
+ *dst = in;
+ dst += _smpSize;
+ }
+ uint32 lpos = _rcPos;
+ _rcPos += (_rmH * _frameSize);
+ _smpWtAcc += _rmL;
+ if (_smpWtAcc > 0xffff) {
+ _smpWtAcc &= 0xffff;
+ _rcPos += _frameSize;
+ }
+
+ if (interp && _rcPos >= lpos + _frameSize) {
+ for (int i = 0; i < _frameSize; ++i)
+ _lastSmp[i] = _data[_rcPos - _frameSize + i];
+ }
+
+ if (_rcPos >= _len) {
+ if (_loopSt && _loopEnd) {
+ _rcPos = _loopSt + (_rcPos - _len);
+ _len = _loopEnd;
+ _lastSmp[0] = _data[_rcPos];
+ if ((_len - _rcPos) > 1)
+ _lastSmp[1] = _data[_rcPos + 1];
+ _smpWtAcc = 0;
+ } else {
+ _data = nullptr;
+ _res.reset();
+ end = dst;
+ }
+ setFlags(kStatusDone);
+ }
+ }
+}
+
+void MacLowLevelPCMDriver::play(PCMSound *snd) {
+ if (!snd || !snd->data)
+ return;
+
+ Common::StackLock lock(_mutex);
+
+ _res = snd->data.reinterpretCast<const int8>();
+ _data = _res.get();
+ _len = snd->len;
+ uint32 rmul = calcRate(_caps.deviceRate, 0x10000, snd->rate);
+
+ if (rmul >= 0x7FFD && rmul <= 0x8003)
+ rmul = 0x8000;
+ else if (ABS((int16)(rmul & 0xffff)) <= 7)
+ rmul = (rmul + 7) & ~0xffff;
+
+ if (rmul > (uint32)-64)
+ rmul = (uint32)-64;
+
+ assert(rmul);
+
+ _rmL = rmul & 0xffff;
+ _rmH = rmul >> 16;
+
+ _frameSize = snd->stereo ? 2 : 1;
+
+ if (snd->loopend - snd->loopst < 2 || snd->loopend < snd->loopst) {
+ _loopSt = 0;
+ _loopEnd = 0;
+ } else {
+ _loopSt = snd->loopst - (snd->loopst % _frameSize);
+ _loopEnd = snd->loopend - (snd->loopend % _frameSize);
+ }
+
+ _baseFreq = snd->baseFreq;
+ _rcPos = 0;
+ _smpWtAcc = 0;
+ _lastSmp[0] = _data[0];
+ if (_len >= _frameSize)
+ _lastSmp[1] = _data[1];
+ clearFlags(kStatusDone);
+}
+
+void MacLowLevelPCMDriver::stop() {
+ Common::StackLock lock(_mutex);
+ _data = nullptr;
+ _res.reset();
+ setFlags(kStatusDone);
+}
+
+uint32 MacLowLevelPCMDriver::calcRate(uint32 outRate, uint32 factor, uint32 dataRate) {
+ uint32 result = outRate;
+ uint64 t = 0;
+ uint32 c = 0;
+
+ if (!factor || !dataRate)
+ return (uint32)-1;
+
+ if (factor > 0x10000 && dataRate > 0x10000) {
+ bool altpth = true;
+
+ if (!(dataRate & 0xffff)) {
+ SWAP(factor, dataRate);
+ if (!(dataRate & 0xffff)) {
+ dataRate = (dataRate >> 16) * (factor >> 16);
+ factor = 0;
+ altpth = false;
+ }
+ } else if (factor & 0xffff) {
+ t = (dataRate & 0xffff) * (factor >> 16) + (dataRate >> 16) * (factor & 0xffff);
+ c = (factor & 0xffff) * (dataRate & 0xffff);
+ dataRate = (factor >> 16) * (dataRate >> 16) + (t >> 16);
+ t = c + ((t & 0xffff) << 16);
+ factor = t & (uint32)-1;
+ dataRate += (t >> 32);
+ altpth = false;
+ }
+
+ if (altpth) {
+ c = dataRate;
+ dataRate = (factor >> 16) * (dataRate >> 16);
+ factor = (factor >> 16) * (c & 0xffff);
+ dataRate += (factor >> 16);
+ factor <<= 16;
+ }
+
+ } else if (factor < 0x10000 && dataRate < 0x10000) {
+ factor = factor * dataRate;
+ dataRate = 0;
+ } else if (factor == 0x10000 || dataRate == 0x10000) {
+ if (dataRate == 0x10000)
+ SWAP(dataRate, factor);
+ factor = dataRate << 16;
+ dataRate = (factor | (dataRate >> 16)) ^ factor;
+ } else {
+ if (factor > 0x10000 && dataRate <= 0x10000)
+ SWAP(dataRate, factor);
+
+ c = (dataRate >> 16) * (factor & 0xffff);
+ factor = (factor & 0xffff) * (dataRate & 0xffff);
+ uint32 x = ((factor >> 16) + (c & 0xffff)) & ~0xffff;
+ factor += (c << 16);
+ result = (c + x) >> 16;
+ dataRate = result;
+ }
+
+ t = factor + (outRate >> 1);
+ factor = t & (uint32)-1;
+ dataRate += (t >> 32);
+
+ if (dataRate >= outRate)
+ return (uint32)-1;
+
+ dataRate ^= factor;
+
+ if (outRate < 0x10000) {
+ factor <<= 16;
+ dataRate = (dataRate >> 16) | (dataRate << 16);
+ outRate = (outRate >> 16) | (outRate << 16);
+ }
+
+ int32 sh = -1;
+
+ if (outRate < 0x1000000) {
+ outRate <<= 8;
+ sh = -9;
+ }
+
+ for (t = (int32)outRate; !(t >> 32); t = (int32)outRate) {
+ --sh;
+ outRate += outRate;
+ }
+
+ sh = ~sh;
+ if (sh) {
+ factor <<= sh;
+ dataRate = ((dataRate >> (32 - sh)) | (dataRate << sh));
+ }
+
+ dataRate ^= factor;
+
+ if (outRate & 0xffff) {
+ bool altpth = false;
+
+ if (dataRate / (outRate >> 16) > 0xffff) {
+ dataRate = ((dataRate - outRate) << 16) | (factor >> 16);
+ factor &= ~0xffff;
+ altpth = true;
+ } else {
+ c = dataRate % (outRate >> 16);
+ dataRate /= (outRate >> 16);
+ t = ((c << 16) | (factor >> 16)) - ((dataRate & 0xffff) * (outRate & 0xffff));
+ factor = (factor << 16) | dataRate;
+ dataRate = t & (uint32)-1;
+ altpth = (int64)t < 0;
+ }
+
+ if (altpth) {
+ for (t = dataRate; !(t >> 32); ) {
+ --factor;
+ t += outRate;
+ }
+ dataRate = t & (uint32)-1;
+ }
+
+ if (dataRate / (outRate >> 16) > 0xffff) {
+ dataRate = ((dataRate - outRate) << 16) | (factor >> 16);
+ factor <<= 16;
+ altpth = true;
+ } else {
+ c = dataRate % (outRate >> 16);
+ dataRate /= (outRate >> 16);
+ t = ((c << 16) | (factor >> 16)) - (dataRate * (outRate & 0xffff));
+ factor = (factor << 16) | dataRate;
+ dataRate = t & (uint32)-1;;
+ altpth = (int64)t < 0;
+ }
+
+ if (altpth) {
+ t = dataRate;
+ do {
+ factor = (factor & ~0xffff) | (((factor & 0xffff) - 1) & 0xffff);
+ t += outRate;
+ } while (!(t >> 32));
+ dataRate = t & (uint32)-1;
+ }
+
+ result = factor;
+ } else {
+ outRate >>= 16;
+ if (outRate == 0x8000) {
+ t = factor << 1;
+ t = (t >> 32) + (dataRate << 1);
+ } else {
+ c = dataRate % outRate;
+ t = ((dataRate / outRate) << 16) | (((c << 16) | (factor >> 16)) / outRate);
+ }
+ result = t & (uint32)-1;
+ }
+
+ return result;
+}
+
+class Indy3MacSnd;
+
+template<typename T> class Player_Mac_New : public MusicEngine {
+public:
+ Player_Mac_New(ScummEngine *vm, Audio::Mixer *mixer);
+ ~Player_Mac_New() override;
+ void setMusicVolume(int vol) override;
+ void setSfxVolume(int vol) override;
+ void startSound(int id) override;
+ void stopSound(int id) override;
+ void stopAllSounds() override;
+ int getMusicTimer() override;
+ int getSoundStatus(int id) const override;
+ void setQuality(int qual) override;
+ void saveLoadWithSerializer(Common::Serializer &ser) override;
+ void restoreAfterLoad() override;
+
+private:
+ Common::SharedPtr<T> _player;
+};
+
+template <typename T> Player_Mac_New<T>::Player_Mac_New(ScummEngine *vm, Audio::Mixer *mixer) : _player(nullptr) {
+ _player = T::open(vm, mixer);
+}
+
+template <typename T> Player_Mac_New<T>::~Player_Mac_New() {
+ _player = nullptr;
+}
+
+template <typename T> void Player_Mac_New<T>::setMusicVolume(int vol) {
+ if (_player != nullptr)
+ _player->setMusicVolume(vol);
+}
+
+template <typename T> void Player_Mac_New<T>::setSfxVolume(int vol) {
+ if (_player != nullptr)
+ _player->setSfxVolume(vol);
+}
+
+template <typename T> void Player_Mac_New<T>::startSound(int id) {
+ if (_player != nullptr)
+ _player->startSound(id);
+}
+
+template <typename T> void Player_Mac_New<T>::stopSound(int id) {
+ if (_player != nullptr)
+ _player->stopSound(id);
+}
+
+template <typename T> void Player_Mac_New<T>::stopAllSounds() {
+ if (_player != nullptr)
+ _player->stopAllSounds();
+}
+
+template <typename T> int Player_Mac_New<T>::getMusicTimer() {
+ return (_player != nullptr) ? _player->getMusicTimer() : 0;
+}
+
+template <typename T> int Player_Mac_New<T>::getSoundStatus(int id) const {
+ return (_player != nullptr) ? _player->getSoundStatus(id) : 0;
+}
+
+template <typename T> void Player_Mac_New<T>::setQuality(int qual) {
+ if (_player != nullptr)
+ _player->setQuality(qual);
+}
+
+template <typename T> void Player_Mac_New<T>::saveLoadWithSerializer(Common::Serializer &ser) {
+ if (_player != nullptr)
+ _player->saveLoadWithSerializer(ser);
+}
+
+template <typename T> void Player_Mac_New<T>::restoreAfterLoad() {
+ if (_player != nullptr)
+ _player->restoreAfterLoad();
+}
+
+namespace MacSound {
+MusicEngine *createPlayer(ScummEngine *vm) {
+ assert(vm);
+ assert(vm->_mixer);
+ if (vm->_game.id == GID_INDY3)
+ return new Player_Mac_New<Indy3MacSnd>(vm, vm->_mixer);
+
+ return nullptr;
+}
+} // end of namespace MacSound
+
+#undef ASC_DEVICE_RATE
+#undef VBL_UPDATE_RATE
+#undef PCM_BUFFER_SIZE
+#undef PCM_BUFFER_RESERVE
+#undef RATECNV_BIT_PRECSN
+
+} // End of namespace Scumm
diff --git a/engines/scumm/players/player_mac_indy3.h b/engines/scumm/players/player_mac_new.h
similarity index 60%
rename from engines/scumm/players/player_mac_indy3.h
rename to engines/scumm/players/player_mac_new.h
index 864ab50fa77..b7c32c19b5d 100644
--- a/engines/scumm/players/player_mac_indy3.h
+++ b/engines/scumm/players/player_mac_new.h
@@ -24,39 +24,19 @@
#include "scumm/music.h"
-namespace Audio {
-class Mixer;
-}
-
namespace Scumm {
class ScummEngine;
-class I3MPlayer;
-
-class Player_Mac_Indy3 : public MusicEngine {
-public:
- enum Quality {
- kQualAuto = 0,
- kQualHi,
- kQualLo
- };
-
- Player_Mac_Indy3(ScummEngine *vm, Audio::Mixer *mixer);
- ~Player_Mac_Indy3() override;
- void setMusicVolume(int vol) override;
- void setSfxVolume(int vol) override;
- void startSound(int id) override;
- void stopSound(int id) override;
- void stopAllSounds() override;
- int getMusicTimer() override;
- int getSoundStatus(int id) const override;
- void setQuality(int qual) override;
- void saveLoadWithSerializer(Common::Serializer &ser) override;
- void restoreAfterLoad() override;
-private:
- Common::SharedPtr<I3MPlayer> _player;
+namespace MacSound {
+enum {
+ kQualityAuto = 0,
+ kQualityHigh,
+ kQualityLow,
+ kQualityMedium
};
+MusicEngine *createPlayer(ScummEngine *vm);
+} // end of namespace MacSound
} // End of namespace Scumm
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 52927303543..c0340ba282b 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -62,7 +62,7 @@
#include "scumm/players/player_sid.h"
#include "scumm/players/player_pce.h"
#include "scumm/players/player_apple2.h"
-#include "scumm/players/player_mac_indy3.h"
+#include "scumm/players/player_mac_new.h"
#include "scumm/players/player_v1.h"
#include "scumm/players/player_v2.h"
#include "scumm/players/player_v2cms.h"
@@ -2169,9 +2169,9 @@ void ScummEngine::setupMusic(int midi, const Common::Path &macInstrumentFile) {
} else if (_game.platform == Common::kPlatformAmiga && _game.version <= 4) {
_musicEngine = new Player_V4A(this, _mixer);
} else if (_game.platform == Common::kPlatformMacintosh && _game.id == GID_INDY3) {
- _musicEngine = new Player_Mac_Indy3(this, _mixer);
+ _musicEngine = MacSound::createPlayer(this);
if (ConfMan.hasKey("mac_v3_low_quality_music") && ConfMan.getBool("mac_v3_low_quality_music"))
- _musicEngine->setQuality(Player_Mac_Indy3::kQualLo);
+ _musicEngine->setQuality(MacSound::kQualityLow);
_sound->_musicType = MDT_MACINTOSH;
} else if (_game.platform == Common::kPlatformMacintosh && _game.id == GID_LOOM) {
_musicEngine = new Player_V3M(this, _mixer, ConfMan.getBool("mac_v3_low_quality_music"));
Commit: 95027d64d391af38b18fa807eb075fcd8622418a
https://github.com/scummvm/scummvm/commit/95027d64d391af38b18fa807eb075fcd8622418a
Author: athrxx (athrxx at scummvm.org)
Date: 2024-02-28T21:01:35+01:00
Commit Message:
SCUMM: (V3/Mac) - more preparations to extend sound player
Changed paths:
engines/scumm/players/player_mac_indy3.cpp
engines/scumm/players/player_mac_intern.h
engines/scumm/players/player_mac_new.cpp
diff --git a/engines/scumm/players/player_mac_indy3.cpp b/engines/scumm/players/player_mac_indy3.cpp
index cca30683ad6..2e6e6126337 100644
--- a/engines/scumm/players/player_mac_indy3.cpp
+++ b/engines/scumm/players/player_mac_indy3.cpp
@@ -39,7 +39,7 @@ extern const uint32 g_pv2ModTblSize;
class LegacyMusicDriver : public MacSoundDriver {
public:
- LegacyMusicDriver(uint16 numChannels, Common::Mutex &mutex, bool canInterpolate, bool internal16Bit) : MacSoundDriver(mutex, ASC_DEVICE_RATE, canInterpolate, internal16Bit), _numChan(numChannels) {}
+ LegacyMusicDriver(uint16 numChannels, Common::Mutex &mutex, bool canInterpolate, bool internal16Bit) : MacSoundDriver(mutex, ASC_DEVICE_RATE, internal16Bit ? numChannels : 1, canInterpolate, internal16Bit), _numChan(numChannels) {}
virtual void start() = 0;
virtual void stop() = 0;
@@ -57,17 +57,17 @@ protected:
void putSample(int8 *&dst, int16 smp, bool expectStereo) {
if (_smpSize == 2) {
smp = CLIP<int16>(smp, _smpMin, _smpMax);
- *reinterpret_cast<int16*>(dst) = smp;
+ *reinterpret_cast<int16*>(dst) += smp;
dst += _smpSize;
if (expectStereo) {
- *reinterpret_cast<int16*>(dst) = smp;
+ *reinterpret_cast<int16*>(dst) += smp;
dst += _smpSize;
}
} else {
- smp = CLIP<int8>(smp >> 2, _smpMin, _smpMax);
- *dst++ = smp;
+ smp = CLIP<int16>(smp / _numChan, _smpMin, _smpMax);
+ *dst++ += smp;
if (expectStereo)
- *dst++ = smp;
+ *dst++ += smp;
}
}
const uint16 _numChan;
@@ -168,9 +168,6 @@ void FourToneSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundTy
setFlags(kStatusDone);
}
}
-
- if (end > dst)
- memset(dst, 0, end - dst);
}
void FourToneSynthDriver::start() {
@@ -209,7 +206,7 @@ void FourToneSynthDriver::send(int dataType, ...) {
break;
}
- _status |= kStatusOverflow;
+ _status.flags |= kStatusOverflow;
va_end(arg);
}
@@ -241,7 +238,6 @@ void FourToneSynthDriver::setRate(uint8 chan, uint16 rate) {
SquareWaveSynthDriver::SquareWaveSynthDriver(Common::Mutex &mutex, bool internal16Bit) :
LegacyMusicDriver(1, mutex, false, internal16Bit), _count(0xffff), _duration(0), _amplitude(0), _phase(0), _pos(0) {
-
}
void SquareWaveSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) {
@@ -271,9 +267,6 @@ void SquareWaveSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::Sound
if (++_pos == 370)
_pos = 0;
}
-
- if (dst < end)
- memset(dst, 0, end - dst);
}
void SquareWaveSynthDriver::start() {
@@ -310,7 +303,7 @@ void SquareWaveSynthDriver::send(int dataType, ...) {
}
void SquareWaveSynthDriver::addTriplet(uint16 frequency, uint16 amplitude) {
- if ((_status & kStatusStartup) && frequency < 3)
+ if ((_status.flags & kStatusStartup) && frequency < 3)
return;
clearFlags(kStatusStartup);
@@ -320,7 +313,7 @@ void SquareWaveSynthDriver::addTriplet(uint16 frequency, uint16 amplitude) {
if (_lastPara.amplitude == 0xffff)
_lastPara.amplitude = amplitude;
- if ((_status & kStatusPlaying) && _tripletsQueue.size() < 176) {
+ if ((_status.flags & kStatusPlaying) && _tripletsQueue.size() < 176) {
if (frequency >> 3 != _lastPara.count >> 3 || amplitude != _lastPara.amplitude) {
_tripletsQueue.push_back(_lastPara.fromScumm());
_lastPara = Triplet(frequency, amplitude, 0);
@@ -329,7 +322,7 @@ void SquareWaveSynthDriver::addTriplet(uint16 frequency, uint16 amplitude) {
_lastPara.duration++;
}
- if (!(_status & kStatusPlaying) || _tripletsQueue.size() >= 176)
+ if (!(_status.flags & kStatusPlaying) || _tripletsQueue.size() >= 176)
setFlags(kStatusOverflow);
}
@@ -339,7 +332,7 @@ Indy3MacSnd::Indy3MacSnd(ScummEngine *vm, Audio::Mixer *mixer) : VblTaskClientDr
_vm(vm), _mixer(mixer), _musicChannels(nullptr), _curSound(0), _curSong(0), _lastSoundEffectPrio(0), _idRangeMax(86), _soundEffectNumLoops(-1),
_musicIDTable(nullptr), _macstr(nullptr), _musicIDTableLen(0), _soundUsage(0), _mdrv(nullptr), _sdrv(nullptr), _nextTickProc(this, &VblTaskClientDriver::callback),
_soundEffectPlaying(false), _songTimer(0), _songTimerInternal(0), _qmode(0), _16bit(false), _qualHi(false), _mixerThread(false), _activeChanCount(0),
- _songUnfinished(false), _numMusicChannels(8), _numMusicTracks(4) {
+ _songUnfinished(false), _numMusicChannels(8), _numMusicTracks(4), _handle(0) {
assert(_vm);
assert(_mixer);
@@ -362,6 +355,8 @@ Indy3MacSnd::~Indy3MacSnd() {
delete _macstr;
delete[] _soundUsage;
+ _sdrv->disposeChannel(_handle);
+
for (Common::Array<MacSoundDriver*>::const_iterator i = _drivers.begin(); i != _drivers.end(); ++i)
delete *i;
_drivers.clear();
@@ -396,7 +391,7 @@ bool Indy3MacSnd::startDevices(uint32 outputRate, uint32 pcmDeviceRate, uint32 f
if (!_macstr || !_mixer)
return false;
- _sdrv = new MacLowLevelPCMDriver(_mixer->mutex(), pcmDeviceRate, enableInterpolation, internal16Bit);
+ _sdrv = new MacLowLevelPCMDriver(_mixer->mutex(), pcmDeviceRate, internal16Bit);
FourToneSynthDriver *mdrv = new FourToneSynthDriver(_mixer->mutex(), internal16Bit);
if (!mdrv || !_sdrv)
return false;
@@ -407,6 +402,8 @@ bool Indy3MacSnd::startDevices(uint32 outputRate, uint32 pcmDeviceRate, uint32 f
_16bit = internal16Bit;
_mdrv = mdrv;
+ _handle = _sdrv->createChannel(Audio::Mixer::kSFXSoundType, MacLowLevelPCMDriver::kSampledSynth, enableInterpolation);
+
_drivers.push_back(_mdrv);
_drivers.push_back(_sdrv);
@@ -534,25 +531,27 @@ void Indy3MacSnd::nextTick() {
_mixerThread = true;
- if (!_curSong && (_sdrv->getStatus() & MacSoundDriver::kStatusDone))
+ if (!_curSong && (_sdrv->getChannelStatus(_handle) & MacSoundDriver::kStatusDone))
updateSoundEffect();
else if (_curSong)
updateSong();
- else if (_songUnfinished && (_mdrv->getStatus() & MacSoundDriver::kStatusDone))
+ else if (_songUnfinished && (_mdrv->getStatus().flags & MacSoundDriver::kStatusDone))
stopSong();
_mixerThread = false;
}
void Indy3MacSnd::generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const {
+ assert(dst);
+ memset(dst, 0, len);
for (Common::Array<MacSoundDriver*>::const_iterator i = _drivers.begin(); i != _drivers.end(); ++i)
(*i)->feed(dst, len, type, expectStereo);
}
-const MacSoundDriver::Caps &Indy3MacSnd::getDriverCaps(uint8 drvID) const {
+const MacSoundDriver::Status &Indy3MacSnd::getDriverStatus(uint8 drvID) const {
if (drvID >= _drivers.size())
error(" Indy3MacSnd::getDriverCaps(): Invalid driver id %d", drvID);
- return _drivers[drvID]->getCaps();
+ return _drivers[drvID]->getStatus();
}
void Indy3MacSnd::startSong(int id) {
@@ -656,7 +655,7 @@ void Indy3MacSnd::startSoundEffect(int id) {
_pcmSnd.loopend = numSamples - 1;
_pcmSnd.baseFreq = 60;
- _sdrv->play(&_pcmSnd);
+ _sdrv->playSamples(_handle, &_pcmSnd);
_curSound = id;
_soundUsage[id]++;
@@ -671,7 +670,7 @@ void Indy3MacSnd::stopSong() {
void Indy3MacSnd::stopSoundEffect() {
Common::StackLock lock(_mixer->mutex());
- _sdrv->stop();
+ _sdrv->stop(_handle);
_soundEffectPlaying = false;
_lastSoundEffectPrio = 0;
_curSound = 0;
@@ -689,13 +688,13 @@ void Indy3MacSnd::finishSong() {
if (_soundUsage[_curSong])
--_soundUsage[_curSong];
_curSong = 0;
- _songUnfinished = !(_mdrv->getStatus() & MacSoundDriver::kStatusDone);
+ _songUnfinished = !(_mdrv->getStatus().flags & MacSoundDriver::kStatusDone);
}
void Indy3MacSnd::updateSong() {
- if (_curSong && (_qualHi || (_mdrv->getStatus() & MacSoundDriver::kStatusDone))) {
+ if (_curSong && (_qualHi || (_mdrv->getStatus().flags & MacSoundDriver::kStatusDone))) {
_mdrv->clearFlags(MacSoundDriver::kStatusOverflow);
- while (_curSong && !(_mdrv->getStatus() & MacSoundDriver::kStatusOverflow)) {
+ while (_curSong && !(_mdrv->getStatus().flags & MacSoundDriver::kStatusOverflow)) {
for (int i = 4; i; --i) {
for (int ii = 0; ii < _numMusicTracks && _curSong; ++ii)
_musicChannels[ii]->nextTick();
@@ -728,7 +727,7 @@ void Indy3MacSnd::updateSoundEffect() {
if (_soundEffectNumLoops > 0)
--_soundEffectNumLoops;
if (_soundEffectNumLoops)
- _sdrv->play(&_pcmSnd);
+ _sdrv->playSamples(_handle, &_pcmSnd);
else
--_soundUsage[_curSound];
chkRestart = (_soundEffectNumLoops == 0);
diff --git a/engines/scumm/players/player_mac_intern.h b/engines/scumm/players/player_mac_intern.h
index 8169e445e6a..324008acc03 100644
--- a/engines/scumm/players/player_mac_intern.h
+++ b/engines/scumm/players/player_mac_intern.h
@@ -29,17 +29,19 @@ namespace Scumm {
class MacSoundDriver {
public:
- MacSoundDriver(Common::Mutex &mutex, uint32 deviceRate, bool canInterpolate, bool internal16Bit) : _mutex(mutex), _caps(deviceRate, canInterpolate),
- _smpSize(internal16Bit ? 2 : 1), _smpMin(internal16Bit ? -32768 : -128), _smpMax(internal16Bit ? 32767 : 127), _status(0) {}
+ MacSoundDriver(Common::Mutex &mutex, uint32 deviceRate, int activeChannels, bool canInterpolate, bool internal16Bit) : _mutex(mutex), _status(deviceRate, canInterpolate, activeChannels),
+ _smpSize(internal16Bit ? 2 : 1), _smpMin(internal16Bit ? -32768 : -128), _smpMax(internal16Bit ? 32767 : 127) {}
virtual ~MacSoundDriver() {}
virtual void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) = 0;
- struct Caps {
- Caps(uint32 rate, bool interp) :deviceRate(rate), allowInterPolation(interp) {}
- const uint32 deviceRate;
- const bool allowInterPolation;
+ struct Status {
+ Status(uint32 rate, bool interp, int actChan) :deviceRate(rate), allowInterPolation(interp), numExternalMixChannels(actChan), flags(0) {}
+ uint32 deviceRate;
+ int numExternalMixChannels;
+ bool allowInterPolation;
+ uint8 flags;
};
- const Caps &getCaps() const { return _caps; }
+ const Status &getStatus() const { return _status; }
enum StatusFlag : uint8 {
kStatusPlaying = 1 << 0,
@@ -47,18 +49,17 @@ public:
kStatusStartup = 1 << 2,
kStatusDone = 1 << 3
};
- uint8 getStatus() const { return _status; }
- void clearFlags(uint8 flags) { _status &= ~flags; }
+
+ void clearFlags(uint8 flags) { _status.flags &= ~flags; }
protected:
- void setFlags(uint8 flags) { _status |= flags; }
+ void setFlags(uint8 flags) { _status.flags |= flags; }
Common::Mutex &_mutex;
const int _smpSize;
const int16 _smpMin;
const int16 _smpMax;
- const Caps _caps;
- uint8 _status;
+ Status _status;
};
class MacLowLevelPCMDriver final : public MacSoundDriver {
@@ -73,34 +74,71 @@ public:
byte baseFreq;
bool stereo;
};
+
+ enum SynthType {
+ kSquareWaveSynth = 1,
+ kWaveTableSynth = 3,
+ kSampledSynth = 5
+ };
+
+ typedef int ChanHandle;
public:
- MacLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool enableInterpolation, bool internal16Bit);
+ MacLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool internal16Bit);
+ ~MacLowLevelPCMDriver() override;
+
void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) override;
- void play(PCMSound *snd);
- void stop();
+
+ ChanHandle createChannel(Audio::Mixer::SoundType sndType, SynthType synthType, bool interpolate);
+ void disposeChannel(ChanHandle handle);
+
+ void playSamples(ChanHandle handle, PCMSound *snd);
+ void stop(ChanHandle handle);
+
+ uint8 getChannelStatus(ChanHandle handle) const { Channel *ch = findChannel(handle); return ch ? ch->_flags : 0; }
private:
uint32 calcRate(uint32 outRate, uint32 factor, uint32 dataRate);
+ void updateStatus();
- Common::SharedPtr<const int8> _res;
- const int8 *_data;
- const bool _interp;
- int8 _lastSmp[2];
- uint32 _len;
- uint16 _rmH;
- uint16 _rmL;
- uint32 _loopSt;
- uint32 _loopEnd;
- byte _baseFreq;
- uint32 _rcPos;
- uint32 _smpWtAcc;
- uint16 _frameSize;
+private:
+ class Channel {
+ public:
+ Channel(Audio::Mixer::SoundType sndtp, int synth, bool interp);
+ ChanHandle getHandle() const;
+ void stop();
+
+ const Audio::Mixer::SoundType _sndType;
+ const int _synth;
+ const bool _interpolate;
+
+ Common::SharedPtr<const int8> _res;
+ const int8 *_data;
+ int8 _lastSmp[2];
+ uint32 _len;
+ uint16 _rmH;
+ uint16 _rmL;
+ uint32 _loopSt;
+ uint32 _loopEnd;
+ byte _baseFreq;
+ uint32 _rcPos;
+ uint32 _smpWtAcc;
+ uint16 _frameSize;
+ uint8 _flags;
+ };
+
+ void setChanFlags(Channel *ch, uint8 flags) { if (ch) ch->_flags |= flags; }
+ void clearChanFlags(Channel *ch, uint8 flags) { if (ch) ch->_flags &= ~flags; }
+ Channel *findChannel(ChanHandle h) const;
+ Common::Array<Channel*> _channels;
+ int _numInternalMixChannels;
+ int32 *_mixBuffer = 0;
+ uint32 _mixBufferSize;
};
class VblTaskClientDriver {
public:
virtual void callback() = 0;
virtual void generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const = 0;
- virtual const MacSoundDriver::Caps &getDriverCaps(uint8 drvID) const = 0;
+ virtual const MacSoundDriver::Status &getDriverStatus(uint8 drvID) const = 0;
};
class MacPlayerAudioStream : public Audio::AudioStream {
@@ -135,7 +173,7 @@ private:
const CallbackProc *_vblCbProc;
struct SmpBuffer {
- SmpBuffer() : start(0), pos(0), end(0), volume(0x10000), lastL(0), lastR(0), size(0), rateConvInt(0), rateConvFrac(0), rateConvAcc(-1), interpolate(false) {}
+ SmpBuffer() : start(0), pos(0), end(0), volume(0x10000), lastL(0), lastR(0), size(0), rateConvInt(0), rateConvFrac(0), rateConvAcc(-1) {}
int8 *start;
int8 *pos;
const int8 *end;
@@ -146,14 +184,12 @@ private:
uint32 rateConvInt;
uint32 rateConvFrac;
int32 rateConvAcc;
- bool interpolate;
} _buffers[2];
const uint32 _outputRate;
const uint8 _frameSize;
const bool _interp;
const int _smpInternalSize;
- const int _volDown;
const bool _isStereo;
};
@@ -181,7 +217,7 @@ public:
void nextTick();
void callback() override { nextTick(); }
void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) const override;
- const MacSoundDriver::Caps &getDriverCaps(uint8 drvID) const override;
+ const MacSoundDriver::Status &getDriverStatus(uint8 drvID) const override;
private:
void startSong(int id);
@@ -216,6 +252,7 @@ private:
bool _mixerThread;
MacLowLevelPCMDriver::PCMSound _pcmSnd;
+ MacLowLevelPCMDriver::ChanHandle _handle;
MacPlayerAudioStream *_macstr;
Audio::SoundHandle _soundHandle;
diff --git a/engines/scumm/players/player_mac_new.cpp b/engines/scumm/players/player_mac_new.cpp
index 0c414e50500..4ed4af4f1a3 100644
--- a/engines/scumm/players/player_mac_new.cpp
+++ b/engines/scumm/players/player_mac_new.cpp
@@ -38,7 +38,7 @@ namespace Scumm {
MacPlayerAudioStream::MacPlayerAudioStream(VblTaskClientDriver *drv, uint32 scummVMOutputrate, bool stereo, bool interpolate, bool internal16Bit) : Audio::AudioStream(), _drv(drv),
_vblSmpQty(0), _vblSmpQtyRem(0), _frameSize((stereo ? 2 : 1) * (internal16Bit ? 2 : 1)), _vblCountDown(0), _vblCountDownRem(0), _outputRate(scummVMOutputrate),
- _vblCbProc(nullptr), _isStereo(stereo), _interp(interpolate), _smpInternalSize(internal16Bit ? 2 : 1), _volDown(internal16Bit ? 2 : 0) {
+ _vblCbProc(nullptr), _isStereo(stereo), _interp(interpolate), _smpInternalSize(internal16Bit ? 2 : 1) {
assert(_drv);
_vblSmpQty = (_outputRate << 16) / VBL_UPDATE_RATE;
_vblSmpQtyRem = (_outputRate << 16) % VBL_UPDATE_RATE;
@@ -67,11 +67,10 @@ void MacPlayerAudioStream::initDrivers() {
for (int i = 0; i < 2; ++i) {
if (!_drv)
error("MacPlayerAudioStream::initDrivers(): Failed to query device rate for device %d", i);
- uint64 irt = (uint64)_drv->getDriverCaps(i).deviceRate * (1 << RATECNV_BIT_PRECSN) / _outputRate;
+ uint64 irt = (uint64)_drv->getDriverStatus(i).deviceRate * (1 << RATECNV_BIT_PRECSN) / _outputRate;
_buffers[i].rateConvInt = irt >> (RATECNV_BIT_PRECSN + 16);
_buffers[i].rateConvFrac = (irt >> 16) & ((1 << RATECNV_BIT_PRECSN) - 1);
_buffers[i].rateConvAcc = 0;
- _buffers[i].interpolate = _interp && _drv->getDriverCaps(i).allowInterPolation;
}
}
@@ -118,19 +117,23 @@ int MacPlayerAudioStream::readBuffer(int16 *buffer, const int numSamples) {
int32 smpL = 0;
int32 smpR = 0;
for (int ii = 0; ii < 2; ++ii) {
+ int numch = _drv->getDriverStatus(ii).numExternalMixChannels;
+ bool interp = _interp && _drv->getDriverStatus(ii).allowInterPolation;
+ if (!numch)
+ continue;
+
int smpN = _smpInternalSize == 2 ? *reinterpret_cast<int16*>(_buffers[ii].pos) : _buffers[ii].pos[0];
int diff = smpN - _buffers[ii].lastL;
- if (diff && _buffers[ii].rateConvAcc && _buffers[ii].interpolate)
+ if (diff && _buffers[ii].rateConvAcc && interp)
diff = (diff * _buffers[ii].rateConvAcc) >> RATECNV_BIT_PRECSN;
- smpL += (int32)((_buffers[ii].lastL + diff) * (_buffers[ii].volume >> _volDown));
- }
- if (_isStereo) {
- for (int ii = 0; ii < 2; ++ii) {
- int smpN = _smpInternalSize == 2 ? *reinterpret_cast<int16*>(&_buffers[ii].pos[2]) : _buffers[ii].pos[1];
- int diff = smpN - _buffers[ii].lastR;
- if (diff && _buffers[ii].rateConvAcc && _buffers[ii].interpolate)
+ smpL += (int32)((_buffers[ii].lastL + diff) * (_buffers[ii].volume / numch));
+
+ if (_isStereo) {
+ smpN = _smpInternalSize == 2 ? *reinterpret_cast<int16*>(&_buffers[ii].pos[2]) : _buffers[ii].pos[1];
+ diff = smpN - _buffers[ii].lastR;
+ if (diff && _buffers[ii].rateConvAcc && interp)
diff = (diff * _buffers[ii].rateConvAcc) >> RATECNV_BIT_PRECSN;
- smpR += (int32)((_buffers[ii].lastR + diff) * (_buffers[ii].volume >> _volDown));
+ smpR += (int32)((_buffers[ii].lastR + diff) * (_buffers[ii].volume / numch));
}
}
@@ -167,9 +170,9 @@ int MacPlayerAudioStream::readBuffer(int16 *buffer, const int numSamples) {
}
}
- *buffer++ = CLIP<int16>(smpL >> 8, -32768, 32767);
+ *buffer++ = CLIP<int32>(smpL >> 8, -32768, 32767);
if (_isStereo)
- *buffer++ = CLIP<int16>(smpR >> 8, -32768, 32767);
+ *buffer++ = CLIP<int32>(smpR >> 8, -32768, 32767);
}
return numSamples;
}
@@ -184,83 +187,153 @@ void MacPlayerAudioStream::runVblTask() {
(*_vblCbProc)();
}
-MacLowLevelPCMDriver::MacLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool enableInterpolation, bool internal16Bit) :
- MacSoundDriver(mutex, deviceRate, true, internal16Bit), _interp(enableInterpolation), _frameSize(1), _len(0), _rmH(0), _rmL(0), _smpWtAcc(0), _loopSt(0),
- _loopEnd(0), _baseFreq(0), _rcPos(0), _data(nullptr) {
- _lastSmp[0] = _lastSmp[1] = 0;
+MacLowLevelPCMDriver::MacLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool internal16Bit) :
+ MacSoundDriver(mutex, deviceRate, 0, true, internal16Bit), _numInternalMixChannels(1), _mixBufferSize(0), _mixBuffer(nullptr) {
+}
+
+MacLowLevelPCMDriver::~MacLowLevelPCMDriver() {
+ for (Common::Array<Channel*>::const_iterator i = _channels.begin(); i != _channels.end(); ++i) {
+ (*i)->stop();
+ delete *i;
+ }
+ delete[] _mixBuffer;
}
void MacLowLevelPCMDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) {
- if (dst == nullptr || type != Audio::Mixer::kSFXSoundType)
+ if (dst == nullptr)
return;
- memset(dst, 0, byteSize);
-
- if (_data == nullptr)
- return;
+ uint32 mixBufferReqSize = byteSize / _smpSize;
+ assert(!(byteSize & (_smpSize - 1)));
- int32 diff = 0;
+ if (mixBufferReqSize > _mixBufferSize) {
+ delete[] _mixBuffer;
+ _mixBufferSize = mixBufferReqSize;
+ _mixBuffer = new int32[mixBufferReqSize];
+ }
+ memset(_mixBuffer, 0, sizeof(int32) * mixBufferReqSize);
uint16 destFrameSize = expectStereo ? 2 : 1;
- bool interp = (_interp && _rmL);
-
- for (const int8 *end = &dst[byteSize]; dst < end; ) {
- int8 in = 0;
- for (int i = 0; i < destFrameSize; ++i) {
- if (i < _frameSize) {
- in = _data[_rcPos + i];
- if (interp && in != _lastSmp[i]) {
- diff = in - _lastSmp[i];
- diff = (diff * (_smpWtAcc >> 1)) >> 15;
- in = (_lastSmp[i] + diff) & 0xff;
+ bool bufferChanged = false;
+
+ for (Common::Array<Channel*>::const_iterator itr = _channels.begin(); itr != _channels.end(); ++itr) {
+ Channel *ch = *itr;
+ if (ch->_data == nullptr || ch->_sndType != type)
+ continue;
+
+ bufferChanged = true;
+ int32 diff = 0;
+ bool interp = (ch->_interpolate && ch->_rmL);
+ int32 *tmp = _mixBuffer;
+
+ for (const int32 *end = &tmp[mixBufferReqSize]; tmp < end; ) {
+ int8 in = 0;
+ for (int i = 0; i < destFrameSize; ++i) {
+ if (i < ch->_frameSize) {
+ in = ch->_data[ch->_rcPos + i];
+ if (interp && in != ch->_lastSmp[i]) {
+ diff = in - ch->_lastSmp[i];
+ diff = (diff * (ch->_smpWtAcc >> 1)) >> 15;
+ in = (ch->_lastSmp[i] + diff) & 0xff;
+ }
}
+ *tmp++ += in;
}
- if (_smpSize == 2)
- *reinterpret_cast<int16*>(dst) = in << 2;
- else
- *dst = in;
- dst += _smpSize;
- }
- uint32 lpos = _rcPos;
- _rcPos += (_rmH * _frameSize);
- _smpWtAcc += _rmL;
- if (_smpWtAcc > 0xffff) {
- _smpWtAcc &= 0xffff;
- _rcPos += _frameSize;
- }
- if (interp && _rcPos >= lpos + _frameSize) {
- for (int i = 0; i < _frameSize; ++i)
- _lastSmp[i] = _data[_rcPos - _frameSize + i];
- }
+ uint32 lpos = ch->_rcPos;
+ ch->_rcPos += (ch->_rmH * ch->_frameSize);
+ ch->_smpWtAcc += ch->_rmL;
+ if (ch->_smpWtAcc > 0xffff) {
+ ch->_smpWtAcc &= 0xffff;
+ ch->_rcPos += ch->_frameSize;
+ }
+
+ if (interp && ch->_rcPos >= lpos + ch->_frameSize) {
+ for (int i = 0; i < ch->_frameSize; ++i)
+ ch->_lastSmp[i] = ch->_data[ch->_rcPos - ch->_frameSize + i];
+ }
- if (_rcPos >= _len) {
- if (_loopSt && _loopEnd) {
- _rcPos = _loopSt + (_rcPos - _len);
- _len = _loopEnd;
- _lastSmp[0] = _data[_rcPos];
- if ((_len - _rcPos) > 1)
- _lastSmp[1] = _data[_rcPos + 1];
- _smpWtAcc = 0;
- } else {
- _data = nullptr;
- _res.reset();
- end = dst;
+ if (ch->_rcPos >= ch->_len) {
+ if (ch->_loopSt && ch->_loopEnd) {
+ lpos = ch->_rcPos;
+ ch->_rcPos = ch->_loopSt + (ch->_rcPos - ch->_len);
+ ch->_len = ch->_loopEnd;
+ for (int i = 0; i < ch->_frameSize; ++i)
+ ch->_lastSmp[i] = ch->_data[(ch->_rcPos >= ch->_loopSt + ch->_frameSize - i ? ch->_rcPos : ch->_len) - ch->_frameSize + i];
+ /*ch->_lastSmp[0] = ch->_data[ch->_rcPos];
+ if ((ch->_len - ch->_rcPos) > 1)
+ ch->_lastSmp[1] = ch->_data[ch->_rcPos + 1];
+ ch->_smpWtAcc = 0;*/
+ } else {
+ ch->_data = nullptr;
+ ch->_res.reset();
+ end = tmp;
+ }
+ setChanFlags(ch, kStatusDone);
}
- setFlags(kStatusDone);
}
}
+
+ if (!bufferChanged)
+ return;
+
+ const int32 *src = _mixBuffer;
+ for (const int8 *end = &dst[byteSize]; dst < end; ++src) {
+ if (_smpSize == 2)
+ *reinterpret_cast<int16*>(dst) += CLIP<int32>(*src, _smpMin, _smpMax);
+ else
+ *dst += CLIP<int32>(*src / _numInternalMixChannels, _smpMin, _smpMax);
+ dst += _smpSize;
+ }
}
-void MacLowLevelPCMDriver::play(PCMSound *snd) {
+MacLowLevelPCMDriver::ChanHandle MacLowLevelPCMDriver::createChannel(Audio::Mixer::SoundType sndType, SynthType synthType, bool interpolate) {
+ Common::StackLock lock(_mutex);
+ Channel *ch = new Channel(sndType, synthType, synthType == kSampledSynth && interpolate);
+ assert(ch);
+ _channels.push_back(ch);
+ updateStatus();
+ return ch->getHandle();
+}
+
+void MacLowLevelPCMDriver::disposeChannel(ChanHandle handle) {
+ Common::StackLock lock(_mutex);
+ Channel *ch = findChannel(handle);
+ if (!ch) {
+ warning("MacLowLevelPCMDriver::disposeChannel(): Channel not found");
+ return;
+ }
+
+ ch->stop();
+
+ for (Common::Array<Channel*>::iterator i = _channels.begin(); i != _channels.end(); ++i) {
+ if (*i == ch) {
+ delete *i;
+ _channels.erase(i--);
+ }
+ }
+ updateStatus();
+}
+
+void MacLowLevelPCMDriver::playSamples(ChanHandle handle, PCMSound *snd) {
if (!snd || !snd->data)
return;
Common::StackLock lock(_mutex);
+ Channel *ch = findChannel(handle);
+ if (!ch) {
+ warning("MacLowLevelPCMDriver::playSamples(): Channel not found");
+ return;
+ }
+
+ if (ch->_synth != kSampledSynth) {
+ warning("MacLowLevelPCMDriver::playSamples(): Wrong channel type");
+ return;
+ }
- _res = snd->data.reinterpretCast<const int8>();
- _data = _res.get();
- _len = snd->len;
- uint32 rmul = calcRate(_caps.deviceRate, 0x10000, snd->rate);
+ ch->_res = snd->data.reinterpretCast<const int8>();
+ ch->_data = ch->_res.get();
+ ch->_len = snd->len;
+ uint32 rmul = calcRate(_status.deviceRate, 0x10000, snd->rate);
if (rmul >= 0x7FFD && rmul <= 0x8003)
rmul = 0x8000;
@@ -272,33 +345,39 @@ void MacLowLevelPCMDriver::play(PCMSound *snd) {
assert(rmul);
- _rmL = rmul & 0xffff;
- _rmH = rmul >> 16;
+ ch->_rmL = rmul & 0xffff;
+ ch->_rmH = rmul >> 16;
- _frameSize = snd->stereo ? 2 : 1;
+ ch->_frameSize = snd->stereo ? 2 : 1;
if (snd->loopend - snd->loopst < 2 || snd->loopend < snd->loopst) {
- _loopSt = 0;
- _loopEnd = 0;
+ ch->_loopSt = 0;
+ ch->_loopEnd = 0;
} else {
- _loopSt = snd->loopst - (snd->loopst % _frameSize);
- _loopEnd = snd->loopend - (snd->loopend % _frameSize);
+ ch->_loopSt = snd->loopst - (snd->loopst % ch->_frameSize);
+ ch->_loopEnd = snd->loopend - (snd->loopend % ch->_frameSize);
}
- _baseFreq = snd->baseFreq;
- _rcPos = 0;
- _smpWtAcc = 0;
- _lastSmp[0] = _data[0];
- if (_len >= _frameSize)
- _lastSmp[1] = _data[1];
+ ch->_baseFreq = snd->baseFreq;
+ ch->_rcPos = 0;
+ ch->_smpWtAcc = 0;
+ ch->_lastSmp[0] = ch->_data[0];
+ if (ch->_len >= ch->_frameSize)
+ ch->_lastSmp[1] = ch->_data[1];
clearFlags(kStatusDone);
+ clearChanFlags(ch, kStatusDone);
+
}
-void MacLowLevelPCMDriver::stop() {
+void MacLowLevelPCMDriver::stop(ChanHandle handle) {
Common::StackLock lock(_mutex);
- _data = nullptr;
- _res.reset();
- setFlags(kStatusDone);
+ Channel *ch = findChannel(handle);
+ if (!ch) {
+ warning("MacLowLevelPCMDriver::stop(): Channel not found");
+ return;
+ }
+ ch->stop();
+ setChanFlags(ch, kStatusDone);
}
uint32 MacLowLevelPCMDriver::calcRate(uint32 outRate, uint32 factor, uint32 dataRate) {
@@ -454,12 +533,46 @@ uint32 MacLowLevelPCMDriver::calcRate(uint32 outRate, uint32 factor, uint32 data
return result;
}
+void MacLowLevelPCMDriver::updateStatus() {
+ _numInternalMixChannels = _smpSize > 1 ? 1 : _channels.size();
+ _status.numExternalMixChannels = _smpSize > 1 ? _channels.size() : 1;
+ _status.allowInterPolation = true;
+ for (Common::Array<Channel*>::const_iterator ch = _channels.begin(); ch != _channels.end(); ++ch) {
+ if (!(*ch)->_interpolate)
+ _status.allowInterPolation = false;
+ }
+}
+
+MacLowLevelPCMDriver::Channel *MacLowLevelPCMDriver::findChannel(ChanHandle handle) const {
+ for (Common::Array<Channel*>::const_iterator ch = _channels.begin(); ch != _channels.end(); ++ch) {
+ if ((*ch)->getHandle() == handle)
+ return *ch;
+ }
+ return nullptr;
+}
+
+MacLowLevelPCMDriver::Channel::Channel(Audio::Mixer::SoundType sndtp, int synth, bool interp) : _sndType(sndtp), _synth(synth),
+ _interpolate(interp), _frameSize(1), _len(0), _rmH(0), _rmL(0), _smpWtAcc(0), _loopSt(0), _loopEnd(0), _baseFreq(0), _rcPos(0),
+ _flags(0), _data(nullptr) {
+ _lastSmp[0] = _lastSmp[1] = 0;
+}
+
+MacLowLevelPCMDriver::ChanHandle MacLowLevelPCMDriver::Channel::getHandle() const {
+ const void *ptr = this;
+ return *reinterpret_cast<const int*>(&ptr);
+}
+
+void MacLowLevelPCMDriver::Channel::stop() {
+ _data = nullptr;
+ _res.reset();
+}
+
class Indy3MacSnd;
-template<typename T> class Player_Mac_New : public MusicEngine {
+template<typename T> class MusicEngineImpl : public MusicEngine {
public:
- Player_Mac_New(ScummEngine *vm, Audio::Mixer *mixer);
- ~Player_Mac_New() override;
+ MusicEngineImpl(ScummEngine *vm, Audio::Mixer *mixer);
+ ~MusicEngineImpl() override;
void setMusicVolume(int vol) override;
void setSfxVolume(int vol) override;
void startSound(int id) override;
@@ -470,63 +583,62 @@ public:
void setQuality(int qual) override;
void saveLoadWithSerializer(Common::Serializer &ser) override;
void restoreAfterLoad() override;
-
private:
Common::SharedPtr<T> _player;
};
-template <typename T> Player_Mac_New<T>::Player_Mac_New(ScummEngine *vm, Audio::Mixer *mixer) : _player(nullptr) {
+template <typename T> MusicEngineImpl<T>::MusicEngineImpl(ScummEngine *vm, Audio::Mixer *mixer) : _player(nullptr) {
_player = T::open(vm, mixer);
}
-template <typename T> Player_Mac_New<T>::~Player_Mac_New() {
+template <typename T> MusicEngineImpl<T>::~MusicEngineImpl() {
_player = nullptr;
}
-template <typename T> void Player_Mac_New<T>::setMusicVolume(int vol) {
+template <typename T> void MusicEngineImpl<T>::setMusicVolume(int vol) {
if (_player != nullptr)
_player->setMusicVolume(vol);
}
-template <typename T> void Player_Mac_New<T>::setSfxVolume(int vol) {
+template <typename T> void MusicEngineImpl<T>::setSfxVolume(int vol) {
if (_player != nullptr)
_player->setSfxVolume(vol);
}
-template <typename T> void Player_Mac_New<T>::startSound(int id) {
+template <typename T> void MusicEngineImpl<T>::startSound(int id) {
if (_player != nullptr)
_player->startSound(id);
}
-template <typename T> void Player_Mac_New<T>::stopSound(int id) {
+template <typename T> void MusicEngineImpl<T>::stopSound(int id) {
if (_player != nullptr)
_player->stopSound(id);
}
-template <typename T> void Player_Mac_New<T>::stopAllSounds() {
+template <typename T> void MusicEngineImpl<T>::stopAllSounds() {
if (_player != nullptr)
_player->stopAllSounds();
}
-template <typename T> int Player_Mac_New<T>::getMusicTimer() {
+template <typename T> int MusicEngineImpl<T>::getMusicTimer() {
return (_player != nullptr) ? _player->getMusicTimer() : 0;
}
-template <typename T> int Player_Mac_New<T>::getSoundStatus(int id) const {
+template <typename T> int MusicEngineImpl<T>::getSoundStatus(int id) const {
return (_player != nullptr) ? _player->getSoundStatus(id) : 0;
}
-template <typename T> void Player_Mac_New<T>::setQuality(int qual) {
+template <typename T> void MusicEngineImpl<T>::setQuality(int qual) {
if (_player != nullptr)
_player->setQuality(qual);
}
-template <typename T> void Player_Mac_New<T>::saveLoadWithSerializer(Common::Serializer &ser) {
+template <typename T> void MusicEngineImpl<T>::saveLoadWithSerializer(Common::Serializer &ser) {
if (_player != nullptr)
_player->saveLoadWithSerializer(ser);
}
-template <typename T> void Player_Mac_New<T>::restoreAfterLoad() {
+template <typename T> void MusicEngineImpl<T>::restoreAfterLoad() {
if (_player != nullptr)
_player->restoreAfterLoad();
}
@@ -536,7 +648,7 @@ MusicEngine *createPlayer(ScummEngine *vm) {
assert(vm);
assert(vm->_mixer);
if (vm->_game.id == GID_INDY3)
- return new Player_Mac_New<Indy3MacSnd>(vm, vm->_mixer);
+ return new MusicEngineImpl<Indy3MacSnd>(vm, vm->_mixer);
return nullptr;
}
@@ -544,7 +656,6 @@ MusicEngine *createPlayer(ScummEngine *vm) {
#undef ASC_DEVICE_RATE
#undef VBL_UPDATE_RATE
-#undef PCM_BUFFER_SIZE
#undef PCM_BUFFER_RESERVE
#undef RATECNV_BIT_PRECSN
Commit: aee2bf53bbe450c0aca8b57221459e1312d556df
https://github.com/scummvm/scummvm/commit/aee2bf53bbe450c0aca8b57221459e1312d556df
Author: athrxx (athrxx at scummvm.org)
Date: 2024-02-28T21:01:41+01:00
Commit Message:
SCUMM: (Loom/Mac) - add new sound player
This should provide more accurate and feature complete
sound than the existing player (which is still there, just
disabled). I haven't implemented a proper quality selection
method yet.
Changed paths:
A engines/scumm/players/player_mac_loom.cpp
engines/scumm/macgui/macgui_loom.cpp
engines/scumm/module.mk
engines/scumm/players/player_mac.cpp
engines/scumm/players/player_mac_indy3.cpp
engines/scumm/players/player_mac_intern.h
engines/scumm/players/player_mac_new.cpp
engines/scumm/players/player_mac_new.h
engines/scumm/saveload.cpp
engines/scumm/scumm.cpp
diff --git a/engines/scumm/macgui/macgui_loom.cpp b/engines/scumm/macgui/macgui_loom.cpp
index 08cba46e696..693a601e08b 100644
--- a/engines/scumm/macgui/macgui_loom.cpp
+++ b/engines/scumm/macgui/macgui_loom.cpp
@@ -636,7 +636,6 @@ bool MacLoomGui::runOptionsDialog() {
int scrolling = _vm->_snapScroll == 0;
int fullAnimation = _vm->VAR(_vm->VAR_MACHINE_SPEED) == 1 ? 0 : 1;
int textSpeed = _vm->_defaultTextSpeed;
- int musicQuality = (ConfMan.hasKey("mac_v3_low_quality_music") && ConfMan.getBool("mac_v3_low_quality_music")) ? 0 : (_vm->VAR(_vm->VAR_SOUNDCARD) == 10 ? 0 : 2);
MacDialogWindow *window = createDialog(1000);
@@ -652,7 +651,7 @@ bool MacLoomGui::runOptionsDialog() {
window->setWidgetValue(11, textSpeed);
window->addPictureSlider(8, 9, true, 5, 69, 0, 2, 6, 4);
- window->setWidgetValue(12, musicQuality);
+ window->setWidgetValue(12, 2/* TODO: save var*/);
// Machine rating
window->addSubstitution(Common::String::format("%d", _vm->VAR(53)));
@@ -721,11 +720,12 @@ bool MacLoomGui::runOptionsDialog() {
// selection 0 activates the low quality channel in
// the sequence files and mutes everything else)
//
- // This is currently incomplete. Let's just set the proper
- // value for VAR_SOUNDCARD...
- _vm->VAR(_vm->VAR_SOUNDCARD) = window->getWidgetValue(12) == 0 ? 10 : 11;
- ((Player_V3M *)_vm->_musicEngine)->overrideQuality(_vm->VAR(_vm->VAR_SOUNDCARD) == 10);
- ConfMan.setBool("mac_v3_low_quality_music", _vm->VAR(_vm->VAR_SOUNDCARD) == 10);
+
+ //_vm->VAR(_vm->VAR_SOUNDCARD) = window->getWidgetValue(12) == 0 ? 10 : 11;
+ //((Player_V3M *)_vm->_musicEngine)->overrideQuality(_vm->VAR(_vm->VAR_SOUNDCARD) == 10);
+ int musicQuality = (ConfMan.hasKey("mac_v3_low_quality_music") && ConfMan.getBool("mac_v3_low_quality_music")) ? 0 : (_vm->VAR(_vm->VAR_SOUNDCARD) == 10 ? 0 : 2);
+ _vm->_musicEngine->setQuality(musicQuality * 3 + 1 + window->getWidgetValue(12));
+ //ConfMan.setBool("mac_v3_low_quality_music", _vm->VAR(_vm->VAR_SOUNDCARD) == 10);
debug(6, "MacLoomGui::runOptionsDialog(): music quality: %d - unimplemented!", window->getWidgetValue(12));
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index abea3f04492..4ce393f1ac1 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -54,6 +54,7 @@ MODULE_OBJS := \
players/player_he.o \
players/player_mac.o \
players/player_mac_indy3.o \
+ players/player_mac_loom.o \
players/player_mac_new.o \
players/player_mod.o \
players/player_nes.o \
diff --git a/engines/scumm/players/player_mac.cpp b/engines/scumm/players/player_mac.cpp
index 50f6288df25..bb0aa54e363 100644
--- a/engines/scumm/players/player_mac.cpp
+++ b/engines/scumm/players/player_mac.cpp
@@ -119,8 +119,20 @@ void Player_Mac::saveLoadWithSerializer(Common::Serializer &s) {
uint32 mixerSampleRate = _sampleRate;
int i;
- s.syncAsUint32LE(_sampleRate, VER(94));
- s.syncAsSint16LE(_soundPlaying, VER(94));
+ if (s.getVersion() > VER(113)) {
+ if (s.isLoading())
+ warning ("Player_Mac::saveLoadWithSerializer(): Incompatible savegame version. Sound may glitch");
+ byte tmp[200];
+ s.syncBytes(tmp, 200);
+ _soundPlaying = 0;
+ for (i = 1; !_soundPlaying && i < 200; ++i) {
+ if (tmp[i])
+ _soundPlaying = i;
+ }
+ }
+
+ s.syncAsUint32LE(_sampleRate, VER(94), VER(113));
+ s.syncAsSint16LE(_soundPlaying, VER(94), VER(113));
if (s.isLoading() && _soundPlaying != -1) {
const byte *ptr = _vm->getResourceAddress(rtSound, _soundPlaying);
@@ -128,9 +140,9 @@ void Player_Mac::saveLoadWithSerializer(Common::Serializer &s) {
loadMusic(ptr);
}
- s.syncArray(_channel, _numberOfChannels, syncWithSerializer);
+ s.syncArray(_channel, _numberOfChannels, syncWithSerializer, VER(94), VER(113));
for (i = 0; i < _numberOfChannels; i++) {
- if (s.getVersion() >= 94 && s.getVersion() <= 103) {
+ if (s.getVersion() >= VER(94) && s.getVersion() <= VER(103)) {
// It was always the intention to save the instrument entries
// here. Unfortunately there was a regression in late 2017 that
// caused the channel data to be saved a second time, instead
@@ -139,7 +151,7 @@ void Player_Mac::saveLoadWithSerializer(Common::Serializer &s) {
_channel[i]._instrument._pos = 0;
_channel[i]._instrument._subPos = 0;
- } else {
+ } else if (s.getVersion() < VER(114)) {
syncWithSerializer(s, _channel[i]._instrument);
}
}
diff --git a/engines/scumm/players/player_mac_indy3.cpp b/engines/scumm/players/player_mac_indy3.cpp
index 2e6e6126337..f90407982fb 100644
--- a/engines/scumm/players/player_mac_indy3.cpp
+++ b/engines/scumm/players/player_mac_indy3.cpp
@@ -39,7 +39,9 @@ extern const uint32 g_pv2ModTblSize;
class LegacyMusicDriver : public MacSoundDriver {
public:
- LegacyMusicDriver(uint16 numChannels, Common::Mutex &mutex, bool canInterpolate, bool internal16Bit) : MacSoundDriver(mutex, ASC_DEVICE_RATE, internal16Bit ? numChannels : 1, canInterpolate, internal16Bit), _numChan(numChannels) {}
+ LegacyMusicDriver(uint16 numChannels, Common::Mutex &mutex, bool canInterpolate, bool internal16Bit) :
+ MacSoundDriver(mutex, ASC_DEVICE_RATE, internal16Bit ? numChannels : 1, canInterpolate, internal16Bit),
+ _numChan(numChannels), _sndType(Audio::Mixer::kMusicSoundType) {}
virtual void start() = 0;
virtual void stop() = 0;
@@ -71,6 +73,7 @@ protected:
}
}
const uint16 _numChan;
+ const Audio::Mixer::SoundType _sndType;
};
class FourToneSynthDriver final : public LegacyMusicDriver {
@@ -144,7 +147,7 @@ FourToneSynthDriver::~FourToneSynthDriver() {
}
void FourToneSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) {
- if (dst == nullptr || type != Audio::Mixer::kMusicSoundType)
+ if (dst == nullptr || type != _sndType)
return;
const int8 *end = &dst[byteSize];
@@ -206,7 +209,7 @@ void FourToneSynthDriver::send(int dataType, ...) {
break;
}
- _status.flags |= kStatusOverflow;
+ setFlags(kStatusOverflow);
va_end(arg);
}
@@ -241,7 +244,7 @@ SquareWaveSynthDriver::SquareWaveSynthDriver(Common::Mutex &mutex, bool internal
}
void SquareWaveSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) {
- if (dst == nullptr || type != Audio::Mixer::kMusicSoundType)
+ if (dst == nullptr || type != _sndType)
return;
Common::Array<Triplet>::iterator t = _tripletsQueue.begin();
@@ -303,7 +306,7 @@ void SquareWaveSynthDriver::send(int dataType, ...) {
}
void SquareWaveSynthDriver::addTriplet(uint16 frequency, uint16 amplitude) {
- if ((_status.flags & kStatusStartup) && frequency < 3)
+ if ((getStatus().flags & kStatusStartup) && frequency < 3)
return;
clearFlags(kStatusStartup);
@@ -313,7 +316,7 @@ void SquareWaveSynthDriver::addTriplet(uint16 frequency, uint16 amplitude) {
if (_lastPara.amplitude == 0xffff)
_lastPara.amplitude = amplitude;
- if ((_status.flags & kStatusPlaying) && _tripletsQueue.size() < 176) {
+ if ((getStatus().flags & kStatusPlaying) && _tripletsQueue.size() < 176) {
if (frequency >> 3 != _lastPara.count >> 3 || amplitude != _lastPara.amplitude) {
_tripletsQueue.push_back(_lastPara.fromScumm());
_lastPara = Triplet(frequency, amplitude, 0);
@@ -322,7 +325,7 @@ void SquareWaveSynthDriver::addTriplet(uint16 frequency, uint16 amplitude) {
_lastPara.duration++;
}
- if (!(_status.flags & kStatusPlaying) || _tripletsQueue.size() >= 176)
+ if (!(getStatus().flags & kStatusPlaying) || _tripletsQueue.size() >= 176)
setFlags(kStatusOverflow);
}
@@ -330,9 +333,9 @@ Common::WeakPtr<Indy3MacSnd> *Indy3MacSnd::_inst = nullptr;
Indy3MacSnd::Indy3MacSnd(ScummEngine *vm, Audio::Mixer *mixer) : VblTaskClientDriver(),
_vm(vm), _mixer(mixer), _musicChannels(nullptr), _curSound(0), _curSong(0), _lastSoundEffectPrio(0), _idRangeMax(86), _soundEffectNumLoops(-1),
- _musicIDTable(nullptr), _macstr(nullptr), _musicIDTableLen(0), _soundUsage(0), _mdrv(nullptr), _sdrv(nullptr), _nextTickProc(this, &VblTaskClientDriver::callback),
+ _musicIDTable(nullptr), _macstr(nullptr), _musicIDTableLen(0), _soundUsage(0), _mdrv(nullptr), _sdrv(nullptr), _nextTickProc(this, &VblTaskClientDriver::vblCallback),
_soundEffectPlaying(false), _songTimer(0), _songTimerInternal(0), _qmode(0), _16bit(false), _qualHi(false), _mixerThread(false), _activeChanCount(0),
- _songUnfinished(false), _numMusicChannels(8), _numMusicTracks(4), _handle(0) {
+ _songUnfinished(false), _numMusicChannels(8), _numMusicTracks(4), _sfxChan(0) {
assert(_vm);
assert(_mixer);
@@ -355,7 +358,7 @@ Indy3MacSnd::~Indy3MacSnd() {
delete _macstr;
delete[] _soundUsage;
- _sdrv->disposeChannel(_handle);
+ _sdrv->disposeChannel(_sfxChan);
for (Common::Array<MacSoundDriver*>::const_iterator i = _drivers.begin(); i != _drivers.end(); ++i)
delete *i;
@@ -397,12 +400,12 @@ bool Indy3MacSnd::startDevices(uint32 outputRate, uint32 pcmDeviceRate, uint32 f
return false;
for (int i = 0; i < mdrv->numChannels(); ++i)
- mdrv->send(LegacyMusicDriver::kChanWaveform, i, _fourToneSynthWaveForm, sizeof(_fourToneSynthWaveForm));
+ mdrv->send(LegacyMusicDriver::kChanWaveform, i, _fourToneSynthWaveForm, _fourToneSynthWaveForm);
_qualHi = true;
_16bit = internal16Bit;
_mdrv = mdrv;
- _handle = _sdrv->createChannel(Audio::Mixer::kSFXSoundType, MacLowLevelPCMDriver::kSampledSynth, enableInterpolation);
+ _sfxChan = _sdrv->createChannel(Audio::Mixer::kSFXSoundType, MacLowLevelPCMDriver::kSampledSynth, 0, nullptr);
_drivers.push_back(_mdrv);
_drivers.push_back(_sdrv);
@@ -472,7 +475,12 @@ int Indy3MacSnd::getSoundStatus(int id) const {
}
void Indy3MacSnd::setQuality(int qual) {
- assert(qual >= MacSound::kQualityAuto && qual <= MacSound::kQualityLow);
+ if (qual != MacSound::kQualityAuto && qual != MacSound::kQualityLowest && qual != MacSound::kQualityHighest) {
+ warning ("Indy3MacSnd::setQuality(): Indy 3 supports only the following quality settings:"
+ " kQualityAuto, kQualityLowest and kQualityHighest. Setting is now changed to kQualityAuto");
+ qual = MacSound::kQualityAuto;
+ }
+
while (_qualHi == isHiQuality()) {
if (_qmode == qual)
return;
@@ -489,7 +497,7 @@ void Indy3MacSnd::setQuality(int qual) {
FourToneSynthDriver *mdrv = new FourToneSynthDriver(_mixer->mutex(), _16bit);
assert(mdrv);
for (int i = 0; i < mdrv->numChannels(); ++i)
- mdrv->send(LegacyMusicDriver::kChanWaveform, i, _fourToneSynthWaveForm, sizeof(_fourToneSynthWaveForm));
+ mdrv->send(LegacyMusicDriver::kChanWaveform, i, _fourToneSynthWaveForm, _fourToneSynthWaveForm);
_mdrv = mdrv;
_qualHi = true;
} else {
@@ -523,7 +531,7 @@ void Indy3MacSnd::restoreAfterLoad() {
}
}
-void Indy3MacSnd::nextTick() {
+void Indy3MacSnd::vblCallback() {
if (_songTimerInternal++ == 29) {
_songTimerInternal = 0;
++_songTimer;
@@ -531,7 +539,7 @@ void Indy3MacSnd::nextTick() {
_mixerThread = true;
- if (!_curSong && (_sdrv->getChannelStatus(_handle) & MacSoundDriver::kStatusDone))
+ if (!_curSong && (_sdrv->getChannelStatus(_sfxChan) & MacSoundDriver::kStatusDone))
updateSoundEffect();
else if (_curSong)
updateSong();
@@ -548,10 +556,10 @@ void Indy3MacSnd::generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType ty
(*i)->feed(dst, len, type, expectStereo);
}
-const MacSoundDriver::Status &Indy3MacSnd::getDriverStatus(uint8 drvID) const {
+const MacSoundDriver::Status &Indy3MacSnd::getDriverStatus(uint8 drvID, Audio::Mixer::SoundType type) const {
if (drvID >= _drivers.size())
- error(" Indy3MacSnd::getDriverCaps(): Invalid driver id %d", drvID);
- return _drivers[drvID]->getStatus();
+ error("Indy3MacSnd::getDriverCaps(): Invalid driver id %d", drvID);
+ return _drivers[drvID]->getStatus(type);
}
void Indy3MacSnd::startSong(int id) {
@@ -646,7 +654,8 @@ void Indy3MacSnd::startSoundEffect(int id) {
return;
byte *buff = new byte[numSamples - 22];
- memcpy(buff, ptr + spos + 22, numSamples - 22);
+ for (uint16 i = 0; i < numSamples - 22; ++i)
+ buff[i] = ptr[spos + 22 + i] ^ 0x80;
_pcmSnd.rate = 0x4E200000 / (READ_BE_UINT16(ptr + 20 + offs) >> 7);
_pcmSnd.data = Common::SharedPtr<const byte> (buff, Common::ArrayDeleter<const byte>());
@@ -655,7 +664,13 @@ void Indy3MacSnd::startSoundEffect(int id) {
_pcmSnd.loopend = numSamples - 1;
_pcmSnd.baseFreq = 60;
- _sdrv->playSamples(_handle, &_pcmSnd);
+ _sdrv->playSamples(_sfxChan, MacLowLevelPCMDriver::kImmediate, &_pcmSnd);
+
+ if (READ_BE_UINT16(ptr + 6) || _soundEffectNumLoops == -1) {
+ _sdrv->playSamples(_sfxChan, MacLowLevelPCMDriver::kEnqueue, &_pcmSnd);
+ _sdrv->playSamples(_sfxChan, MacLowLevelPCMDriver::kEnqueue, &_pcmSnd);
+ _sdrv->playSamples(_sfxChan, MacLowLevelPCMDriver::kEnqueue, &_pcmSnd);
+ }
_curSound = id;
_soundUsage[id]++;
@@ -670,7 +685,8 @@ void Indy3MacSnd::stopSong() {
void Indy3MacSnd::stopSoundEffect() {
Common::StackLock lock(_mixer->mutex());
- _sdrv->stop(_handle);
+ _sdrv->quiet(_sfxChan, MacLowLevelPCMDriver::kImmediate);
+ _sdrv->flush(_sfxChan, MacLowLevelPCMDriver::kImmediate);
_soundEffectPlaying = false;
_lastSoundEffectPrio = 0;
_curSound = 0;
@@ -718,7 +734,7 @@ void Indy3MacSnd::updateSong() {
}
void Indy3MacSnd::updateSoundEffect() {
- _sdrv->clearFlags(MacSoundDriver::kStatusDone);
+ _sdrv->clearChannelFlags(_sfxChan, MacSoundDriver::kStatusDone);
bool chkRestart = false;
if (!_soundEffectPlaying || !_curSound) {
@@ -727,7 +743,7 @@ void Indy3MacSnd::updateSoundEffect() {
if (_soundEffectNumLoops > 0)
--_soundEffectNumLoops;
if (_soundEffectNumLoops)
- _sdrv->playSamples(_handle, &_pcmSnd);
+ _sdrv->playSamples(_sfxChan, MacLowLevelPCMDriver::kImmediate, &_pcmSnd);
else
--_soundUsage[_curSound];
chkRestart = (_soundEffectNumLoops == 0);
@@ -755,25 +771,6 @@ void Indy3MacSnd::checkRestartSoundEffects() {
}
}
-const uint8 Indy3MacSnd::_fourToneSynthWaveForm[256] = {
- 0x80, 0x7a, 0x74, 0x6e, 0x69, 0x63, 0x5d, 0x57, 0x52, 0x4c, 0x47, 0x42, 0x3e, 0x3b, 0x38, 0x35,
- 0x34, 0x33, 0x34, 0x35, 0x37, 0x3a, 0x3e, 0x43, 0x49, 0x4e, 0x54, 0x5b, 0x61, 0x67, 0x6c, 0x71,
- 0x75, 0x78, 0x7a, 0x7c, 0x7c, 0x7b, 0x79, 0x76, 0x73, 0x6f, 0x6b, 0x66, 0x62, 0x5e, 0x5b, 0x58,
- 0x56, 0x56, 0x57, 0x59, 0x5c, 0x61, 0x67, 0x6e, 0x77, 0x80, 0x8a, 0x95, 0xa0, 0xac, 0xb7, 0xc2,
- 0xcc, 0xd6, 0xdf, 0xe7, 0xee, 0xf4, 0xf8, 0xfb, 0xfe, 0xff, 0xff, 0xfe, 0xfd, 0xfb, 0xf9, 0xf6,
- 0xf3, 0xf0, 0xec, 0xe9, 0xe6, 0xe3, 0xe0, 0xdd, 0xda, 0xd7, 0xd4, 0xd1, 0xce, 0xca, 0xc6, 0xc2,
- 0xbd, 0xb8, 0xb3, 0xad, 0xa7, 0xa1, 0x9a, 0x93, 0x8d, 0x86, 0x7f, 0x79, 0x73, 0x6d, 0x68, 0x63,
- 0x5f, 0x5c, 0x5a, 0x58, 0x57, 0x57, 0x58, 0x5a, 0x5c, 0x5f, 0x63, 0x67, 0x6b, 0x70, 0x75, 0x7b,
- 0x80, 0x85, 0x8b, 0x90, 0x95, 0x99, 0x9d, 0xa1, 0xa4, 0xa6, 0xa8, 0xa9, 0xa9, 0xa8, 0xa6, 0xa4,
- 0xa1, 0x9d, 0x98, 0x93, 0x8d, 0x87, 0x81, 0x7a, 0x73, 0x6d, 0x66, 0x5f, 0x59, 0x53, 0x4d, 0x48,
- 0x43, 0x3e, 0x3a, 0x36, 0x32, 0x2f, 0x2c, 0x29, 0x26, 0x23, 0x20, 0x1d, 0x1a, 0x17, 0x14, 0x10,
- 0x0d, 0x0a, 0x07, 0x05, 0x03, 0x02, 0x01, 0x01, 0x02, 0x05, 0x08, 0x0c, 0x12, 0x19, 0x21, 0x2a,
- 0x34, 0x3e, 0x49, 0x54, 0x60, 0x6b, 0x76, 0x80, 0x89, 0x92, 0x99, 0x9f, 0xa4, 0xa7, 0xa9, 0xaa,
- 0xaa, 0xa8, 0xa5, 0xa2, 0x9e, 0x9a, 0x95, 0x91, 0x8d, 0x8a, 0x87, 0x85, 0x84, 0x84, 0x86, 0x88,
- 0x8b, 0x8f, 0x94, 0x99, 0x9f, 0xa5, 0xac, 0xb2, 0xb7, 0xbd, 0xc2, 0xc6, 0xc9, 0xcb, 0xcc, 0xcd,
- 0xcc, 0xcb, 0xc8, 0xc5, 0xc2, 0xbe, 0xb9, 0xb4, 0xae, 0xa9, 0xa3, 0x9d, 0x97, 0x92, 0x8c, 0x86
-};
-
void Indy3MacSnd::endOfTrack() {
if (!_activeChanCount || !--_activeChanCount)
finishSong();
@@ -784,7 +781,7 @@ bool Indy3MacSnd::isSong(int id) const {
}
bool Indy3MacSnd::isHiQuality() const {
- return _mixerThread ? _qualHi : (_qmode == MacSound::kQualityAuto && (_vm->VAR_SOUNDCARD == 0xff || _vm->VAR(_vm->VAR_SOUNDCARD) == 11)) || (_qmode == MacSound::kQualityHigh);
+ return _mixerThread ? _qualHi : (_qmode == MacSound::kQualityAuto && (_vm->VAR_SOUNDCARD == 0xff || _vm->VAR(_vm->VAR_SOUNDCARD) == 11)) || (_qmode == MacSound::kQualityHighest);
}
Indy3MacSnd::MusicChannel *Indy3MacSnd::getMusicChannel(uint8 id) const {
diff --git a/engines/scumm/players/player_mac_intern.h b/engines/scumm/players/player_mac_intern.h
index 324008acc03..ee5d04b26a8 100644
--- a/engines/scumm/players/player_mac_intern.h
+++ b/engines/scumm/players/player_mac_intern.h
@@ -24,24 +24,33 @@
#include "audio/audiostream.h"
#include "audio/mixer.h"
+#include "common/array.h"
+#include "common/func.h"
namespace Scumm {
class MacSoundDriver {
public:
- MacSoundDriver(Common::Mutex &mutex, uint32 deviceRate, int activeChannels, bool canInterpolate, bool internal16Bit) : _mutex(mutex), _status(deviceRate, canInterpolate, activeChannels),
- _smpSize(internal16Bit ? 2 : 1), _smpMin(internal16Bit ? -32768 : -128), _smpMax(internal16Bit ? 32767 : 127) {}
+ MacSoundDriver(Common::Mutex &mutex, uint32 deviceRate, int activeChannels, bool canInterpolate, bool internal16Bit) : _mutex(mutex),
+ _smpSize(internal16Bit ? 2 : 1), _smpMin(internal16Bit ? -32768 : -128), _smpMax(internal16Bit ? 32767 : 127) {
+ for (int i = 0; i < 4; ++i) {
+ _status[i].deviceRate = deviceRate;
+ _status[i].numExternalMixChannels = activeChannels;
+ _status[i].allowInterPolation = canInterpolate;
+ _status[i].flags = 0;
+ }
+ }
virtual ~MacSoundDriver() {}
virtual void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) = 0;
struct Status {
- Status(uint32 rate, bool interp, int actChan) :deviceRate(rate), allowInterPolation(interp), numExternalMixChannels(actChan), flags(0) {}
+ Status() : deviceRate(0), numExternalMixChannels(0), allowInterPolation(false), flags(0) {}
uint32 deviceRate;
int numExternalMixChannels;
bool allowInterPolation;
uint8 flags;
};
- const Status &getStatus() const { return _status; }
+ const Status &getStatus(Audio::Mixer::SoundType sndType = Audio::Mixer::kPlainSoundType) const { return _status[sndType]; }
enum StatusFlag : uint8 {
kStatusPlaying = 1 << 0,
@@ -50,85 +59,94 @@ public:
kStatusDone = 1 << 3
};
- void clearFlags(uint8 flags) { _status.flags &= ~flags; }
+ void clearFlags(uint8 flags, Audio::Mixer::SoundType sndType = Audio::Mixer::kPlainSoundType) { _status[sndType].flags &= ~flags; }
protected:
- void setFlags(uint8 flags) { _status.flags |= flags; }
+ void setFlags(uint8 flags, Audio::Mixer::SoundType sndType = Audio::Mixer::kPlainSoundType) { _status[sndType].flags |= flags; }
Common::Mutex &_mutex;
const int _smpSize;
const int16 _smpMin;
const int16 _smpMax;
- Status _status;
+ Status _status[4];
};
+class MacSndChannel;
class MacLowLevelPCMDriver final : public MacSoundDriver {
public:
struct PCMSound {
- PCMSound() : len(0), rate(0), loopst(0), loopend(0), baseFreq(0), stereo(false) {}
+ PCMSound() : len(0), rate(0), loopst(0), loopend(0), baseFreq(0), stereo(false), enc(0) {}
+ PCMSound(Common::SharedPtr<const byte> a, uint32 b, uint32 c, uint32 d, uint32 e, byte f, byte g, bool h) : data(a), len(b), rate(c), loopst(d), loopend(e), baseFreq(f), enc(g), stereo(h) {}
Common::SharedPtr<const byte> data;
uint32 len;
uint32 rate;
uint32 loopst;
uint32 loopend;
byte baseFreq;
+ byte enc;
bool stereo;
};
- enum SynthType {
+ enum SynthType : byte {
kSquareWaveSynth = 1,
kWaveTableSynth = 3,
- kSampledSynth = 5
+ kSampledSynth = 5,
+ kIgnoreSynth = 0xff
+ };
+
+ enum ChanAttrib : byte {
+ kInitChanLeft = 2,
+ kInitChanRight = 3,
+ kWaveInitChannel0 = 4,
+ kWaveInitChannel1 = 5,
+ kWaveInitChannel2 = 6,
+ kWaveInitChannel3 = 7,
+ kNoInterp = 4,
+ kInitNoDrop = 8,
+ kInitMono = 0x80,
+ kInitStereo = 0xC0
+ };
+
+ enum ExecMode : byte {
+ kImmediate,
+ kEnqueue,
};
typedef int ChanHandle;
+
+ class CallbackClient {
+ public:
+ virtual ~CallbackClient() {}
+ virtual void sndChannelCallback(uint16 arg1, const void *arg2) = 0;
+ };
+ typedef Common::Functor2Mem<uint16, const void*, void, CallbackClient> ChanCallback;
+
public:
MacLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool internal16Bit);
~MacLowLevelPCMDriver() override;
void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) override;
- ChanHandle createChannel(Audio::Mixer::SoundType sndType, SynthType synthType, bool interpolate);
+ ChanHandle createChannel(Audio::Mixer::SoundType sndType, SynthType synthType, byte attributes, ChanCallback *callback);
void disposeChannel(ChanHandle handle);
- void playSamples(ChanHandle handle, PCMSound *snd);
- void stop(ChanHandle handle);
+ void playSamples(ChanHandle handle, ExecMode mode, const PCMSound *snd);
+ void playNote(ChanHandle handle, ExecMode mode, uint8 note, uint16 duration);
+ void quiet(ChanHandle handle, ExecMode mode);
+ void flush(ChanHandle handle, ExecMode mode);
+ void loadWaveTable(ChanHandle handle, ExecMode mode, const byte *data, uint16 dataSize);
+ void loadInstrument(ChanHandle handle, ExecMode mode, const PCMSound *snd);
+ void setTimbre(ChanHandle handle, ExecMode mode, uint16 timbre);
+ void callback(ChanHandle handle, ExecMode mode, uint16 arg1, const void *arg2);
- uint8 getChannelStatus(ChanHandle handle) const { Channel *ch = findChannel(handle); return ch ? ch->_flags : 0; }
-private:
- uint32 calcRate(uint32 outRate, uint32 factor, uint32 dataRate);
- void updateStatus();
+ uint8 getChannelStatus(ChanHandle handle) const;
+ void clearChannelFlags(ChanHandle handle, uint8 flags);
private:
- class Channel {
- public:
- Channel(Audio::Mixer::SoundType sndtp, int synth, bool interp);
- ChanHandle getHandle() const;
- void stop();
-
- const Audio::Mixer::SoundType _sndType;
- const int _synth;
- const bool _interpolate;
-
- Common::SharedPtr<const int8> _res;
- const int8 *_data;
- int8 _lastSmp[2];
- uint32 _len;
- uint16 _rmH;
- uint16 _rmL;
- uint32 _loopSt;
- uint32 _loopEnd;
- byte _baseFreq;
- uint32 _rcPos;
- uint32 _smpWtAcc;
- uint16 _frameSize;
- uint8 _flags;
- };
-
- void setChanFlags(Channel *ch, uint8 flags) { if (ch) ch->_flags |= flags; }
- void clearChanFlags(Channel *ch, uint8 flags) { if (ch) ch->_flags &= ~flags; }
- Channel *findChannel(ChanHandle h) const;
- Common::Array<Channel*> _channels;
+ void updateStatus(Audio::Mixer::SoundType sndType);
+ MacSndChannel *findAndCheckChannel(ChanHandle h, const char *caller, byte reqSynthType) const;
+ MacSndChannel *findChannel(ChanHandle h) const;
+ Common::Array<MacSndChannel*> _channels;
int _numInternalMixChannels;
int32 *_mixBuffer = 0;
uint32 _mixBufferSize;
@@ -136,9 +154,10 @@ private:
class VblTaskClientDriver {
public:
- virtual void callback() = 0;
+ virtual ~VblTaskClientDriver() {}
+ virtual void vblCallback() = 0;
virtual void generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const = 0;
- virtual const MacSoundDriver::Status &getDriverStatus(uint8 drvID) const = 0;
+ virtual const MacSoundDriver::Status &getDriverStatus(uint8 drvID, Audio::Mixer::SoundType sndType) const = 0;
};
class MacPlayerAudioStream : public Audio::AudioStream {
@@ -214,10 +233,9 @@ public:
void saveLoadWithSerializer(Common::Serializer &ser);
void restoreAfterLoad();
- void nextTick();
- void callback() override { nextTick(); }
+ void vblCallback() override;
void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) const override;
- const MacSoundDriver::Status &getDriverStatus(uint8 drvID) const override;
+ const MacSoundDriver::Status &getDriverStatus(uint8 drvID, Audio::Mixer::SoundType sndType) const override;
private:
void startSong(int id);
@@ -252,7 +270,7 @@ private:
bool _mixerThread;
MacLowLevelPCMDriver::PCMSound _pcmSnd;
- MacLowLevelPCMDriver::ChanHandle _handle;
+ MacLowLevelPCMDriver::ChanHandle _sfxChan;
MacPlayerAudioStream *_macstr;
Audio::SoundHandle _soundHandle;
@@ -344,12 +362,115 @@ private:
const int _numMusicChannels;
const int _numMusicTracks;
- static const uint8 _fourToneSynthWaveForm[256];
-
public:
MusicChannel *getMusicChannel(uint8 id) const;
};
+class LoomMacSnd final : public VblTaskClientDriver, public MacLowLevelPCMDriver::CallbackClient {
+private:
+ LoomMacSnd(ScummEngine *vm, Audio::Mixer *mixer);
+public:
+ ~LoomMacSnd();
+ static Common::SharedPtr<LoomMacSnd> open(ScummEngine *scumm, Audio::Mixer *mixer);
+ bool startDevice(uint32 outputRate, uint32 pcmDeviceRate, uint32 feedBufferSize, bool enableInterpolation, bool stereo, bool internal16Bit);
+
+ void setMusicVolume(int vol);
+ void setSfxVolume(int vol);
+ void startSound(int id, int jumpToTick = 0);
+ void stopSound(int id);
+ void stopAllSounds();
+ int getMusicTimer();
+ int getSoundStatus(int id) const;
+ void setQuality(int qual);
+ void saveLoadWithSerializer(Common::Serializer &ser);
+ void restoreAfterLoad();
+
+ void vblCallback() override;
+ void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) const override;
+ const MacSoundDriver::Status &getDriverStatus(uint8, Audio::Mixer::SoundType sndType) const override;
+
+ void sndChannelCallback(uint16 arg1, const void *arg2) override;
+
+private:
+ void sendSoundCommands(const byte *data, int timeStamp);
+ void stopActiveSound();
+ void setupChannels();
+ void disposeAllChannels();
+
+ void detectQuality();
+ bool isSoundCardType10() const;
+
+ struct Instrument {
+ public:
+ Instrument(uint16 id, Common::SeekableReadStream *&in, Common::String &&name);
+ ~Instrument() { _res.reset(); }
+ const MacLowLevelPCMDriver::PCMSound *data() const { return &_snd; }
+ uint16 id() const { return _id; }
+ private:
+ uint16 _id;
+ Common::SharedPtr<const byte> _res;
+ Common::String _name;
+ MacLowLevelPCMDriver::PCMSound _snd;
+ };
+
+ bool loadInstruments();
+ const Common::SharedPtr<Instrument> *findInstrument(uint16 id) const;
+ Common::Array<Common::SharedPtr<Instrument> > _instruments;
+
+ int _curSound;
+ int _songTimer;
+ byte _songTimerInternal;
+ byte *_soundUsage;
+ byte *_chanConfigTable;
+ const int _idRangeMax;
+ bool _mixerThread;
+
+ MacLowLevelPCMDriver *_sdrv;
+
+ int _machineRating;
+ int _selectedQuality;
+ int _effectiveChanConfig;
+ int _defaultChanConfig;
+ bool _16bit;
+
+ MacLowLevelPCMDriver::ChanHandle _sndChannel;
+ MacLowLevelPCMDriver::ChanHandle _musChannels[4];
+
+ MacPlayerAudioStream *_macstr;
+ Audio::SoundHandle _soundHandle;
+ MacPlayerAudioStream::CallbackProc _songTimerUpdt;
+ MacLowLevelPCMDriver::ChanCallback _chanCbProc;
+
+ ScummEngine *_vm;
+ Audio::Mixer *_mixer;
+ static Common::WeakPtr<LoomMacSnd> *_inst;
+
+ byte _curChanConfig;
+ byte _chanUse;
+ byte _curSynthType;
+ Audio::Mixer::SoundType _curSndType;
+ Audio::Mixer::SoundType _lastSndType;
+ byte _chanPlaying;
+
+ struct SoundConfig {
+ SoundConfig(LoomMacSnd *pl) : player(pl), sndRes6(0), switchable(0), sndRes10(0), chanSetup(0), timbre(0) {
+ assert(player);
+ memset(instruments, 0, sizeof(instruments));
+ }
+ void load(const byte *data);
+ byte sndRes6;
+ byte switchable;
+ byte sndRes10;
+ uint16 chanSetup;
+ uint16 timbre;
+ const Common::SharedPtr<Instrument> *instruments[5];
+ LoomMacSnd *player;
+ } _soundConfig;
+};
+
+extern const uint8 _fourToneSynthWaveForm[256];
+extern const uint32 _fourToneSynthWaveFormSize;
+
} // End of namespace Scumm
#endif
diff --git a/engines/scumm/players/player_mac_loom.cpp b/engines/scumm/players/player_mac_loom.cpp
new file mode 100644
index 00000000000..08485e04f60
--- /dev/null
+++ b/engines/scumm/players/player_mac_loom.cpp
@@ -0,0 +1,573 @@
+/* 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 "scumm/players/player_mac_new.h"
+#include "scumm/players/player_mac_intern.h"
+#include "scumm/resource.h"
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "common/punycode.h"
+#include "common/macresman.h"
+
+namespace Scumm {
+
+#define ASC_DEVICE_RATE 0x56EE8BA3
+#define PCM_BUFFER_SIZE 1024
+
+Common::WeakPtr<LoomMacSnd> *LoomMacSnd::_inst = nullptr;
+
+LoomMacSnd::LoomMacSnd(ScummEngine *vm, Audio::Mixer *mixer) : VblTaskClientDriver(),
+ _vm(vm), _mixer(mixer), _curSound(0), _macstr(nullptr), _soundUsage(0), _sdrv(nullptr), _songTimerUpdt(this, &VblTaskClientDriver::vblCallback), _soundConfig(this),
+ _songTimer(0), _songTimerInternal(0), _machineRating(0), _selectedQuality(2), _effectiveChanConfig(0), _16bit(false), _idRangeMax(200), _sndChannel(0), _chanUse(0),
+ _defaultChanConfig(0), _chanConfigTable(nullptr), _chanPlaying(0), _curChanConfig(0), _curSynthType(0), _curSndType(Audio::Mixer::kPlainSoundType),
+ _mixerThread(false), _lastSndType(Audio::Mixer::kPlainSoundType), _chanCbProc(this, &MacLowLevelPCMDriver::CallbackClient::sndChannelCallback) {
+ assert(_vm);
+ assert(_mixer);
+
+ static const byte cfgtable[] = { 0, 0, 0, 1, 4, 5, 1, 5, 6, 6, 8, 9 };
+ _chanConfigTable = new byte[sizeof(cfgtable)]();
+ memcpy(_chanConfigTable, cfgtable, sizeof(cfgtable));
+ _soundUsage = new byte[_idRangeMax]();
+ memset(_musChannels, 0, sizeof(_musChannels));
+}
+
+LoomMacSnd::~LoomMacSnd() {
+ _mixer->stopHandle(_soundHandle);
+ delete _macstr;
+ delete[] _soundUsage;
+ delete[] _chanConfigTable;
+
+ disposeAllChannels();
+ delete _sdrv;
+
+ delete _inst;
+ _inst = nullptr;
+}
+
+Common::SharedPtr<LoomMacSnd> LoomMacSnd::open(ScummEngine *vm, Audio::Mixer *mixer) {
+ Common::SharedPtr<LoomMacSnd> scp = nullptr;
+
+ if (_inst == nullptr) {
+ scp = Common::SharedPtr<LoomMacSnd>(new LoomMacSnd(vm, mixer));
+ _inst = new Common::WeakPtr<LoomMacSnd>(scp);
+ // We can start this with the ScummVM mixer output rate instead of the ASC rate. The Mac sample rate converter can handle it (at
+ // least for up to 48 KHz, I haven't tried higher settings) and we don't have to do another rate conversion in the ScummVM mixer.
+ if ((_inst == nullptr) || (mixer == nullptr) || !(scp->startDevice(mixer->getOutputRate(), mixer->getOutputRate() << 16/*ASC_DEVICE_RATE*/, PCM_BUFFER_SIZE, true, false, true)))
+ error("LoomMacSnd::open(): Failed to start player");
+ }
+
+ return _inst->lock();
+}
+
+bool LoomMacSnd::startDevice(uint32 outputRate, uint32 pcmDeviceRate, uint32 feedBufferSize, bool enableInterpolation, bool stereo, bool internal16Bit) {
+ _macstr = new MacPlayerAudioStream(this, outputRate, stereo, enableInterpolation, internal16Bit);
+ if (!_macstr || !_mixer)
+ return false;
+
+ if (!loadInstruments())
+ return false;
+
+ _sdrv = new MacLowLevelPCMDriver(_mixer->mutex(), pcmDeviceRate, internal16Bit);
+ if (!_sdrv)
+ return false;
+
+ _effectiveChanConfig = 9;
+ _16bit = internal16Bit;
+
+ _macstr->initDrivers();
+ _macstr->initBuffers(feedBufferSize);
+ _macstr->setVblCallback(&_songTimerUpdt);
+
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _macstr, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+
+ return true;
+}
+
+void LoomMacSnd::setMusicVolume(int vol) {
+ Common::StackLock lock(_mixer->mutex());
+ if (_macstr)
+ _macstr->setMasterVolume(Audio::Mixer::kMusicSoundType, vol);
+}
+
+void LoomMacSnd::setSfxVolume(int vol) {
+ Common::StackLock lock(_mixer->mutex());
+ if (_macstr)
+ _macstr->setMasterVolume(Audio::Mixer::kSFXSoundType, vol);
+}
+
+void LoomMacSnd::startSound(int id, int jumpToTick) {
+ if (_sdrv == nullptr || id < 0 || id >= _idRangeMax) {
+ warning("LoomMacSnd::startSound(): sound id '%d' out of range (0 - %d)", id, _idRangeMax - 1);
+ return;
+ }
+
+ Common::StackLock lock(_mixer->mutex());
+
+ const byte *ptr = _vm->getResourceAddress(rtSound, id);
+ assert(ptr);
+
+ if (READ_BE_UINT16(ptr + 4) != 'so' || READ_BE_UINT32(ptr + 10)) {
+ warning("LoomMacSnd::startSound(): Sound resource '%d' cannot be played", id);
+ return;
+ }
+
+ stopActiveSound();
+ if (_chanUse <= 1)
+ disposeAllChannels();
+
+ if (!_defaultChanConfig)
+ detectQuality();
+
+ _soundConfig.load(ptr);
+ //if (_sndDisableFlags && _soundConfig.switchable)
+ // return;
+
+ if (_soundConfig.chanSetup) {
+ _effectiveChanConfig = _soundConfig.chanSetup;
+ _curSndType = Audio::Mixer::kSFXSoundType;
+ } else {
+ _effectiveChanConfig = _defaultChanConfig;
+ _curSndType = Audio::Mixer::kMusicSoundType;
+ }
+
+ if (_lastSndType != _curSndType)
+ _curChanConfig = 0;
+
+ _curSound = id;
+ _soundUsage[id] = 1;
+
+ setupChannels();
+ sendSoundCommands(ptr, jumpToTick);
+
+ if (!jumpToTick) {
+ _songTimer = 0;
+ _songTimerInternal = 0;
+ }
+}
+
+void LoomMacSnd::stopSound(int id) {
+ if (id < 0 || id >= _idRangeMax) {
+ warning("LoomMacSnd::stopSound(): sound id '%d' out of range (0 - %d)", id, _idRangeMax - 1);
+ return;
+ }
+
+ Common::StackLock lock(_mixer->mutex());
+ _soundUsage[id] = 0;
+
+ if (id == _curSound)
+ stopActiveSound();
+}
+
+void LoomMacSnd::stopAllSounds() {
+ Common::StackLock lock(_mixer->mutex());
+ memset(_soundUsage, 0, _idRangeMax);
+ stopActiveSound();
+}
+
+int LoomMacSnd::getMusicTimer() {
+ Common::StackLock lock(_mixer->mutex());
+ return _songTimer;
+}
+
+int LoomMacSnd::getSoundStatus(int id) const {
+ if (id < 0 || id >= _idRangeMax) {
+ warning("LoomMacSnd::getSoundStatus(): sound id '%d' out of range (0 - %d)", id, _idRangeMax - 1);
+ return 0;
+ }
+ Common::StackLock lock(_mixer->mutex());
+ return _soundUsage[id];
+}
+
+void LoomMacSnd::setQuality(int qual) {
+ assert(qual >= MacSound::kQualityAuto && qual <= MacSound::kQualityHighest);
+ Common::StackLock lock(_mixer->mutex());
+
+ if (qual > MacSound::kQualityAuto) {
+ qual--;
+ _machineRating = (qual / 3) + 1;
+ _selectedQuality = qual % 3;
+ qual = _chanConfigTable[_machineRating * 3 + _selectedQuality];
+ if (qual && qual == _defaultChanConfig)
+ return;
+ }
+
+ int csnd = _curSound;
+ int32 timeStamp = csnd ? _songTimer * 1000 + ((_songTimerInternal * 1000) / 30) : 0;
+ stopActiveSound();
+
+ detectQuality();
+ if (csnd)
+ startSound(csnd, timeStamp);
+}
+
+void LoomMacSnd::saveLoadWithSerializer(Common::Serializer &ser) {
+ if (ser.isLoading() && ser.getVersion() < VER(114)) {
+ memset(_soundUsage, 0, _idRangeMax);
+ // Skip over old driver savedata, since it is not needed here.
+ ser.skip(4);
+ // We need only this
+ ser.syncAsSint16LE(_curSound);
+ _curSound = CLIP<int>(_curSound, 0, _idRangeMax - 1);
+ if (_curSound > 0)
+ _soundUsage[_curSound] = 1;
+ // Skip the rest
+ ser.skip(ser.getVersion() >= VER(94) && ser.getVersion() <= VER(103) ? 120 : 100);
+ } else {
+ ser.syncBytes(_soundUsage, _idRangeMax, VER(114));
+ }
+}
+
+void LoomMacSnd::restoreAfterLoad() {
+ for (int i = 1; i < _idRangeMax; ++i) {
+ if (_soundUsage[i]) {
+ _soundUsage[i] = 0;
+ startSound(i);
+ }
+ }
+}
+
+void LoomMacSnd::vblCallback() {
+ if (_songTimerInternal++ == 29) {
+ _songTimerInternal = 0;
+ ++_songTimer;
+ }
+}
+
+void LoomMacSnd::generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const {
+ assert(dst);
+ memset(dst, 0, len);
+ _sdrv->feed(dst, len, type, expectStereo);
+}
+
+const MacSoundDriver::Status &LoomMacSnd::getDriverStatus(uint8, Audio::Mixer::SoundType sndType) const {
+ return _sdrv->getStatus(sndType);
+}
+
+void LoomMacSnd::sndChannelCallback(uint16 arg1, const void*) {
+ // We do this a little smarter than the original player which would stop the track immediately when
+ // the first channel invoked its end-of-track callback. This would cut of the playback early, in an
+ // unpleasant way. Instead, we stop the playback not before all channels have finished.
+ _chanPlaying &= ~arg1;
+ if (_chanPlaying)
+ return;
+ stopActiveSound();
+}
+
+void LoomMacSnd::sendSoundCommands(const byte *data, int timeStamp) {
+ if (!_defaultChanConfig || !_curSound)
+ return;
+
+ if (_chanUse == 1 && _sndChannel) {
+ const byte *s = data + READ_BE_UINT16(data + 30) + 6;
+ uint16 len = READ_BE_UINT16(s - 2);
+ while (len--) {
+ uint16 p1 = READ_BE_UINT16(s);
+ s += 2;
+ uint32 p2 = 0x8f00 | *s++;
+ if (timeStamp > 0) {
+ int ts = timeStamp;
+ timeStamp = MAX<int>(0, timeStamp - p1);
+ p1 -= ts;
+ }
+ if (!timeStamp)
+ _sdrv->playNote(_sndChannel, MacLowLevelPCMDriver::kEnqueue, p2 & 0x7f, p1);
+ }
+ _sdrv->quiet(_sndChannel, MacLowLevelPCMDriver::kEnqueue);
+ _sdrv->callback(_sndChannel, MacLowLevelPCMDriver::kEnqueue, 1, nullptr);
+ _chanPlaying |= 1;
+
+ } else if (_chanUse == 4) {
+ const byte *src[4];
+ uint16 len[4];
+ int tmstmp[4];
+
+ for (int i = 0; i < 4; ++i) {
+ src[i] = nullptr;
+ len[i] = 0;
+ tmstmp[i] = timeStamp;
+ if (!_musChannels[i])
+ continue;
+ src[i] = data + READ_BE_UINT16(data + 32 + 2 * i) + 6;
+ len[i] = READ_BE_UINT16(src[i] - 2);
+ }
+
+ for (bool loop = true; loop; ) {
+ loop = false;
+ for (int i = 0; i < 4; ++i) {
+ if (!src[i] || !len[i])
+ continue;
+ uint16 p1 = READ_BE_UINT16(src[i]);
+ src[i] += 2;
+ byte note = *src[i]++;
+ uint32 p2 = (/*_curSynthType == 4 && */note == 0) ? 1 : (0x8f00 | note);
+ if (tmstmp[i] > 0) {
+ int ts = tmstmp[i];
+ tmstmp[i] = MAX<int>(0, tmstmp[i] - p1);
+ p1 -= ts;
+ }
+ loop |= static_cast<bool>(--len[i]);
+ if (!tmstmp[i])
+ _sdrv->playNote(_musChannels[i], MacLowLevelPCMDriver::kEnqueue, p2 & 0x7f, p1);
+ }
+ }
+
+ _chanPlaying = 0;
+ for (int i = 0; i < 4; ++i) {
+ if (src[i]) {
+ _sdrv->quiet(_musChannels[i], MacLowLevelPCMDriver::kEnqueue);
+ _sdrv->callback(_musChannels[i], MacLowLevelPCMDriver::kEnqueue, 1 << i, nullptr);
+ _chanPlaying |= (1 << i);
+ }
+ }
+ }
+}
+
+void LoomMacSnd::stopActiveSound() {
+ if (_sndChannel) {
+ _sdrv->quiet(_sndChannel, MacLowLevelPCMDriver::kImmediate);
+ _sdrv->flush(_sndChannel, MacLowLevelPCMDriver::kImmediate);
+ }
+
+ for (int i = 0; i < ARRAYSIZE(_musChannels); ++i) {
+ if (_musChannels[i]) {
+ _sdrv->quiet(_musChannels[i], MacLowLevelPCMDriver::kImmediate);
+ _sdrv->flush(_musChannels[i], MacLowLevelPCMDriver::kImmediate);
+ }
+ }
+
+ if (_curSound) {
+ _soundUsage[_curSound] = 0;
+ _curSound = 0;
+ }
+ _chanPlaying = 0;
+}
+
+void LoomMacSnd::setupChannels() {
+ static const byte synthType[] = { 0x00, 0x01, 0x02, 0x04, 0x04, 0x04, 0x02, 0x04, 0x04, 0x04 };
+ static const byte numChan[] = { 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x04, 0x04, 0x04 };
+ static const byte attrib[] = { 0x00, 0x00, 0x04, 0xAC, 0xA4, 0xA0, 0x04, 0xAC, 0xA4, 0xA0 };
+
+ if (!_defaultChanConfig)
+ return;
+
+ if (_curChanConfig != _effectiveChanConfig) {
+ disposeAllChannels();
+ _curChanConfig = _effectiveChanConfig;
+ _curSynthType = synthType[_curChanConfig];
+ _chanUse = numChan[_curChanConfig];
+ _lastSndType = _curSndType;
+
+ switch (_curSynthType) {
+ case 1:
+ if (_chanUse == 1 && !_sndChannel) {
+ _sndChannel = _sdrv->createChannel(_curSndType, MacLowLevelPCMDriver::kSquareWaveSynth, attrib[_curChanConfig], &_chanCbProc);
+ }
+ break;
+ case 2:
+ if (_chanUse == 1 && !_sndChannel) {
+ _sndChannel = _sdrv->createChannel(_curSndType, MacLowLevelPCMDriver::kWaveTableSynth, attrib[_curChanConfig], &_chanCbProc);
+ } else if (_chanUse == 4) {
+ for (int i = 0; i < ARRAYSIZE(_musChannels); ++i) {
+ if ((_soundConfig.instruments[i + 1] && _soundConfig.instruments[i + 1]->get()->id() == 0x2D1C) || _musChannels[i])
+ continue;
+ _musChannels[i] = _sdrv->createChannel(_curSndType, MacLowLevelPCMDriver::kWaveTableSynth, attrib[_curChanConfig] + i, &_chanCbProc);
+ }
+ }
+ break;
+ case 4:
+ if (_chanUse == 1 && !_sndChannel) {
+ _sndChannel = _sdrv->createChannel(_curSndType, MacLowLevelPCMDriver::kSampledSynth, attrib[_curChanConfig], &_chanCbProc);
+ } else if (_chanUse == 4) {
+ for (int i = 0; i < ARRAYSIZE(_musChannels); ++i) {
+ if ((_soundConfig.instruments[i + 1] && _soundConfig.instruments[i + 1]->get()->id() == 0x2D1C) || _musChannels[i])
+ continue;
+ _musChannels[i] = _sdrv->createChannel(_curSndType, MacLowLevelPCMDriver::kSampledSynth, attrib[_curChanConfig], &_chanCbProc);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (_curSynthType) {
+ case 1:
+ if (_sndChannel)
+ _sdrv->setTimbre(_sndChannel, MacLowLevelPCMDriver::kImmediate, _soundConfig.timbre);
+ break;
+ case 2:
+ if (_chanUse == 1) {
+ if (_sndChannel)
+ _sdrv->loadWaveTable(_sndChannel, MacLowLevelPCMDriver::kImmediate, _fourToneSynthWaveForm, _fourToneSynthWaveFormSize);
+ } else {
+ for (int i = 0; i < ARRAYSIZE(_musChannels); ++i) {
+ if (_musChannels[i])
+ _sdrv->loadWaveTable(_musChannels[i], MacLowLevelPCMDriver::kImmediate, _fourToneSynthWaveForm, _fourToneSynthWaveFormSize);
+ }
+ }
+ break;
+ case 4:
+ if (_chanUse == 1) {
+ if (_sndChannel && _soundConfig.instruments[0])
+ _sdrv->loadInstrument(_sndChannel, MacLowLevelPCMDriver::kImmediate, _soundConfig.instruments[0]->get()->data());
+ } else {
+ for (int i = 0; i < ARRAYSIZE(_musChannels); ++i) {
+ if (_musChannels[i] && _soundConfig.instruments[i + 1])
+ _sdrv->loadInstrument(_musChannels[i], MacLowLevelPCMDriver::kImmediate, _soundConfig.instruments[i + 1]->get()->data());
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void LoomMacSnd::disposeAllChannels() {
+ if (_sndChannel)
+ _sdrv->disposeChannel(_sndChannel);
+ _sndChannel = 0;
+
+ for (int i = 0; i < ARRAYSIZE(_musChannels); ++i) {
+ if (_musChannels[i])
+ _sdrv->disposeChannel(_musChannels[i]);
+ _musChannels[i] = 0;
+ }
+
+ _curChanConfig = 0;
+}
+
+void LoomMacSnd::detectQuality() {
+ if (_machineRating == 0) {
+ if (isSoundCardType10())
+ _machineRating = 1;
+ /*else if (0)
+ _machineQuality = 2;*/
+ else
+ _machineRating = 3;
+ }
+
+ _defaultChanConfig = _effectiveChanConfig = _chanConfigTable[_machineRating * 3 + _selectedQuality];
+ _curChanConfig = 0;
+ disposeAllChannels();
+ setupChannels();
+ _chanConfigTable[_machineRating * 3 + _selectedQuality] = _defaultChanConfig;
+}
+
+bool LoomMacSnd::isSoundCardType10() const {
+ return _mixerThread ? (_machineRating == 1) : (_vm->VAR_SOUNDCARD != 0xff && _vm->VAR(_vm->VAR_SOUNDCARD) == 10);
+}
+
+LoomMacSnd::Instrument::Instrument(uint16 id, Common::SeekableReadStream *&in, Common::String &&name) : _id(id), _name(name) {
+ in->seek(2);
+ uint16 numTypes = in->readUint16BE();
+ in->seek(numTypes * 6 + 4);
+ in->seek(in->readUint16BE() * 8 + numTypes * 6 + 10);
+
+ _snd.len = in->readUint32BE();
+ _snd.rate = in->readUint32BE();
+ _snd.loopst = in->readUint32BE();
+ _snd.loopend = in->readUint32BE();
+ _snd.enc = in->readByte();
+ _snd.baseFreq = in->readByte();
+
+ byte *buff = new byte[_snd.len];
+ in->read(buff, _snd.len);
+ _snd.data = Common::SharedPtr<const byte> (buff, Common::ArrayDeleter<const byte>());
+}
+
+bool LoomMacSnd::loadInstruments() {
+ static const char *tryNames[] = {
+ "Loom",
+ "Loom\xaa",
+ "Loom PPC",
+ "Loom\xaa PPC"
+ };
+
+ const Common::CodePage tryCodePages[] = {
+ Common::kMacRoman,
+ Common::kISO8859_1
+ };
+
+ Common::MacResManager resMan;
+ Common::Path resFile;
+ for (int i = 0; resFile.empty() && i < ARRAYSIZE(tryNames); ++i) {
+ for (int ii = 0; resFile.empty() && ii < ARRAYSIZE(tryCodePages); ++ii) {
+ Common::U32String fn(tryNames[i], tryCodePages[ii]);
+ resFile = Common::Path(fn.encode(Common::kUtf8));
+ if (!resMan.exists(resFile) || !resMan.open(resFile) || !resMan.hasResFork()) {
+ resMan.close();
+ resFile = Common::Path(Common::punycode_encodefilename(fn));
+ if (!resMan.exists(resFile) || !resMan.open(resFile) || !resMan.hasResFork()) {
+ resMan.close();
+ resFile.clear();
+ }
+ }
+ }
+ }
+
+ if (resFile.empty()) {
+ warning("LoomMacSnd::loadInstruments(): Loom resource fork not found");
+ return false;
+ }
+
+ Common::MacResIDArray ids = resMan.getResIDArray(MKTAG('s', 'n', 'd', ' '));
+ for (uint i = 0; i < ids.size(); ++i) {
+ Common::SeekableReadStream *str = resMan.getResource(MKTAG('s', 'n', 'd', ' '), ids[i]);
+ if (!str || str->readUint16BE() != 1) {
+ static const char *const errStr[2] = { "Failed to load", "Invalid sound resource format for" };
+ warning("LoomMacSnd::loadInstruments(): %s instrument with id 0x%04x", errStr[str ? 1 : 0], ids[i]);
+ delete str;
+ return false;
+ }
+ _instruments.push_back(Common::SharedPtr<Instrument>(new Instrument(ids[i], str, resMan.getResName(MKTAG('s', 'n', 'd', ' '), ids[i]))));
+ delete str;
+ }
+
+ return true;
+}
+
+const Common::SharedPtr<LoomMacSnd::Instrument> *LoomMacSnd::findInstrument(uint16 id) const {
+ Common::Array<Common::SharedPtr<Instrument> >::const_iterator replacement = _instruments.end();
+ for (Common::Array<Common::SharedPtr<Instrument> >::const_iterator i = _instruments.begin(); i != _instruments.end(); ++i) {
+ if ((*i)->id() == id)
+ return i;
+ else if ((*i)->id() == 0x2D1C)
+ replacement = i;
+ }
+ return (replacement != _instruments.end()) ? replacement : nullptr;
+}
+
+void LoomMacSnd::SoundConfig::load(const byte *data) {
+ sndRes6 = READ_BE_UINT16(data + 6) & 0xff;
+ switchable = READ_BE_UINT16(data + 8) >> 8;
+ sndRes10 = READ_BE_UINT16(data + 10) >> 8;
+ chanSetup = READ_BE_UINT16(data + 16);
+ timbre = READ_BE_UINT16(data + 18);
+ for (int i = 0; i < 5; ++i)
+ instruments[i] = player->findInstrument(READ_BE_UINT16(data + 20 + 2 * i));
+}
+
+#undef ASC_DEVICE_RATE
+#undef PCM_BUFFER_SIZE
+
+} // End of namespace Scumm
diff --git a/engines/scumm/players/player_mac_new.cpp b/engines/scumm/players/player_mac_new.cpp
index 4ed4af4f1a3..cbaa4082e3d 100644
--- a/engines/scumm/players/player_mac_new.cpp
+++ b/engines/scumm/players/player_mac_new.cpp
@@ -36,6 +36,110 @@ namespace Scumm {
#define PCM_BUFFER_RESERVE 64
#define RATECNV_BIT_PRECSN 24
+
+class MacSndChannel {
+public:
+ MacSndChannel(MacLowLevelPCMDriver *drv, Audio::Mixer::SoundType sndtp, int synth, bool interp, bool enableL, bool enableR, MacLowLevelPCMDriver::ChanCallback *callback);
+ MacLowLevelPCMDriver::ChanHandle getHandle() const;
+
+ void playSamples(const MacLowLevelPCMDriver::PCMSound *snd);
+ void playNote(uint8 note, uint16 duration);
+ void quiet();
+ void flush();
+ void loadWaveTable(const byte *data, uint16 dataSize);
+ void loadInstrument(const MacLowLevelPCMDriver::PCMSound *snd);
+ void setTimbre(uint16 timbre);
+ void callback(uint16 p1, const void *p2);
+
+ struct SoundCommand {
+ SoundCommand() : cmd(0), arg1(0), arg2(0), ptr(nullptr) {}
+ SoundCommand(uint8 c, uint16 p1, uint32 p2) : cmd(c), arg1(p1), arg2(p2), ptr(nullptr) {}
+ SoundCommand(uint8 c, uint16 p1, void *p2, byte ptrType) : cmd(c), arg1(p1), arg2(ptrType), ptr(p2) {}
+ uint8 cmd;
+ uint16 arg1;
+ uint32 arg2;
+ void *ptr;
+ };
+
+ void enqueueSndCmd(uint8 c, uint16 p1, uint32 p2, byte mode);
+ void enqueueSndCmd(uint8 c, uint16 p1, const void *p2, byte ptrType, byte mode);
+ bool idle() const;
+
+ void setFlags(uint8 flags) { _flags |= flags; }
+ void clearFlags(uint8 flags) { _flags &= ~flags; }
+
+ void feed(int32 *dst, uint32 dstSize, byte dstFrameSize);
+
+ const Audio::Mixer::SoundType _sndType;
+ const int _synth;
+ const bool _interpolate;
+ uint8 _flags;
+
+private:
+ void setupSound(const MacLowLevelPCMDriver::PCMSound *snd);
+ void setupRateConv(uint32 drate, uint32 mod, uint32 srate, bool ppr);
+ void startSound(uint32 tmr);
+ void processSndCmdQueue();
+ uint32 calcRate(uint32 outRate, uint32 factor, uint32 dataRate);
+ uint32 calcNoteRateAdj(int diff);
+ void makeSquareWave(int8 *dstBuff, uint16 dstSize, byte timbre);
+
+ Common::Array<SoundCommand> _sndCmdQueue;
+ MacLowLevelPCMDriver *_drv;
+ MacLowLevelPCMDriver::ChanCallback *_callback;
+ Common::SharedPtr<const int8> _res;
+ const int8 *_data;
+
+ int8 _lastSmp[2];
+ bool _enable[2];
+ uint32 _len;
+ uint16 _rmH;
+ uint16 _rmL;
+ uint32 _loopSt2;
+ uint32 _loopSt;
+ uint32 _loopEnd;
+ uint32 _loopLen;
+ byte _baseFreq;
+ uint32 _rcPos;
+ uint32 _smpWtAcc;
+ uint16 _frameSize;
+ byte _timbre;
+ uint32 _srate;
+ uint32 _phase;
+ uint32 _tmrPos;
+ uint32 _tmrInc;
+ uint32 _tmrRate;
+ uint32 _duration;
+};
+
+uint32 fixDiv2Frac(uint32 fxdvnd, uint32 fxdvs, byte prcbits) {
+ uint32 dv = fxdvnd << (prcbits - 16);
+ uint32 res = 0;
+ for (uint32 ck = fxdvs; ck; ck = ck >> 16) {
+ res = (res << 16) | (dv / fxdvs);
+ dv = (dv % fxdvs);
+ fxdvs >>= 16;
+ }
+ return res;
+}
+
+uint32 fracMul(uint32 frac, uint32 fx) {
+ uint32 a = (frac >> 30) * (fx >> 16);
+ uint32 b = (frac >> 30) * (fx & 0xffff);
+ uint32 c = ((frac >> 14) & 0xffff) * (fx >> 16);
+ uint32 d = ((frac >> 14) & 0xffff) * (fx & 0xffff);
+ uint32 e = ((frac << 2) & 0xffff) * (fx >> 16);
+ return (a << 16) + b + c + (d >> 16) + (e >> 16);
+}
+
+uint32 fixMul(uint32 fx1, uint32 fx2) {
+ uint32 a = (fx1 >> 16) * (fx2 >> 16);
+ uint32 b = (fx1 >> 16) * (fx2 & 0xffff);
+ uint32 c = (fx1 & 0xffff) * (fx2 >> 16);
+ uint32 d = (fx1 & 0xffff) * (fx2 & 0xffff);
+ return (a << 16) + b + c + (d >> 16);
+}
+
MacPlayerAudioStream::MacPlayerAudioStream(VblTaskClientDriver *drv, uint32 scummVMOutputrate, bool stereo, bool interpolate, bool internal16Bit) : Audio::AudioStream(), _drv(drv),
_vblSmpQty(0), _vblSmpQtyRem(0), _frameSize((stereo ? 2 : 1) * (internal16Bit ? 2 : 1)), _vblCountDown(0), _vblCountDownRem(0), _outputRate(scummVMOutputrate),
_vblCbProc(nullptr), _isStereo(stereo), _interp(interpolate), _smpInternalSize(internal16Bit ? 2 : 1) {
@@ -67,7 +171,7 @@ void MacPlayerAudioStream::initDrivers() {
for (int i = 0; i < 2; ++i) {
if (!_drv)
error("MacPlayerAudioStream::initDrivers(): Failed to query device rate for device %d", i);
- uint64 irt = (uint64)_drv->getDriverStatus(i).deviceRate * (1 << RATECNV_BIT_PRECSN) / _outputRate;
+ uint64 irt = (uint64)_drv->getDriverStatus(i, Audio::Mixer::kPlainSoundType).deviceRate * (1 << RATECNV_BIT_PRECSN) / _outputRate;
_buffers[i].rateConvInt = irt >> (RATECNV_BIT_PRECSN + 16);
_buffers[i].rateConvFrac = (irt >> 16) & ((1 << RATECNV_BIT_PRECSN) - 1);
_buffers[i].rateConvAcc = 0;
@@ -117,8 +221,8 @@ int MacPlayerAudioStream::readBuffer(int16 *buffer, const int numSamples) {
int32 smpL = 0;
int32 smpR = 0;
for (int ii = 0; ii < 2; ++ii) {
- int numch = _drv->getDriverStatus(ii).numExternalMixChannels;
- bool interp = _interp && _drv->getDriverStatus(ii).allowInterPolation;
+ int numch = _drv->getDriverStatus(ii, stype[ii]).numExternalMixChannels;
+ bool interp = _interp && _drv->getDriverStatus(ii, stype[ii]).allowInterPolation;
if (!numch)
continue;
@@ -192,10 +296,8 @@ MacLowLevelPCMDriver::MacLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRa
}
MacLowLevelPCMDriver::~MacLowLevelPCMDriver() {
- for (Common::Array<Channel*>::const_iterator i = _channels.begin(); i != _channels.end(); ++i) {
- (*i)->stop();
+ for (Common::Array<MacSndChannel*>::const_iterator i = _channels.begin(); i != _channels.end(); ++i)
delete *i;
- }
delete[] _mixBuffer;
}
@@ -212,65 +314,15 @@ void MacLowLevelPCMDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundT
_mixBuffer = new int32[mixBufferReqSize];
}
memset(_mixBuffer, 0, sizeof(int32) * mixBufferReqSize);
- uint16 destFrameSize = expectStereo ? 2 : 1;
bool bufferChanged = false;
- for (Common::Array<Channel*>::const_iterator itr = _channels.begin(); itr != _channels.end(); ++itr) {
- Channel *ch = *itr;
- if (ch->_data == nullptr || ch->_sndType != type)
+ for (Common::Array<MacSndChannel*>::const_iterator itr = _channels.begin(); itr != _channels.end(); ++itr) {
+ MacSndChannel *ch = *itr;
+ if (ch->_sndType != type || ch->idle())
continue;
bufferChanged = true;
- int32 diff = 0;
- bool interp = (ch->_interpolate && ch->_rmL);
- int32 *tmp = _mixBuffer;
-
- for (const int32 *end = &tmp[mixBufferReqSize]; tmp < end; ) {
- int8 in = 0;
- for (int i = 0; i < destFrameSize; ++i) {
- if (i < ch->_frameSize) {
- in = ch->_data[ch->_rcPos + i];
- if (interp && in != ch->_lastSmp[i]) {
- diff = in - ch->_lastSmp[i];
- diff = (diff * (ch->_smpWtAcc >> 1)) >> 15;
- in = (ch->_lastSmp[i] + diff) & 0xff;
- }
- }
- *tmp++ += in;
- }
-
- uint32 lpos = ch->_rcPos;
- ch->_rcPos += (ch->_rmH * ch->_frameSize);
- ch->_smpWtAcc += ch->_rmL;
- if (ch->_smpWtAcc > 0xffff) {
- ch->_smpWtAcc &= 0xffff;
- ch->_rcPos += ch->_frameSize;
- }
-
- if (interp && ch->_rcPos >= lpos + ch->_frameSize) {
- for (int i = 0; i < ch->_frameSize; ++i)
- ch->_lastSmp[i] = ch->_data[ch->_rcPos - ch->_frameSize + i];
- }
-
- if (ch->_rcPos >= ch->_len) {
- if (ch->_loopSt && ch->_loopEnd) {
- lpos = ch->_rcPos;
- ch->_rcPos = ch->_loopSt + (ch->_rcPos - ch->_len);
- ch->_len = ch->_loopEnd;
- for (int i = 0; i < ch->_frameSize; ++i)
- ch->_lastSmp[i] = ch->_data[(ch->_rcPos >= ch->_loopSt + ch->_frameSize - i ? ch->_rcPos : ch->_len) - ch->_frameSize + i];
- /*ch->_lastSmp[0] = ch->_data[ch->_rcPos];
- if ((ch->_len - ch->_rcPos) > 1)
- ch->_lastSmp[1] = ch->_data[ch->_rcPos + 1];
- ch->_smpWtAcc = 0;*/
- } else {
- ch->_data = nullptr;
- ch->_res.reset();
- end = tmp;
- }
- setChanFlags(ch, kStatusDone);
- }
- }
+ ch->feed(_mixBuffer, mixBufferReqSize, expectStereo ? 2 : 1);
}
if (!bufferChanged)
@@ -286,101 +338,481 @@ void MacLowLevelPCMDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundT
}
}
-MacLowLevelPCMDriver::ChanHandle MacLowLevelPCMDriver::createChannel(Audio::Mixer::SoundType sndType, SynthType synthType, bool interpolate) {
+MacLowLevelPCMDriver::ChanHandle MacLowLevelPCMDriver::createChannel(Audio::Mixer::SoundType sndType, SynthType synthType, byte attributes, ChanCallback *callback) {
Common::StackLock lock(_mutex);
- Channel *ch = new Channel(sndType, synthType, synthType == kSampledSynth && interpolate);
+ MacSndChannel *ch = new MacSndChannel(this, sndType, synthType, synthType == kSampledSynth && !(attributes & kNoInterp), !(synthType == kSampledSynth && ((attributes & 3) == kInitChanRight)), !(synthType == kSampledSynth && ((attributes & 3) == kInitChanLeft)), callback);
assert(ch);
+
_channels.push_back(ch);
- updateStatus();
+ updateStatus(sndType);
return ch->getHandle();
}
void MacLowLevelPCMDriver::disposeChannel(ChanHandle handle) {
Common::StackLock lock(_mutex);
- Channel *ch = findChannel(handle);
- if (!ch) {
- warning("MacLowLevelPCMDriver::disposeChannel(): Channel not found");
+ MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kIgnoreSynth);
+ if (!ch)
return;
- }
- ch->stop();
+ Audio::Mixer::SoundType sndType = ch->_sndType;
- for (Common::Array<Channel*>::iterator i = _channels.begin(); i != _channels.end(); ++i) {
+ for (Common::Array<MacSndChannel*>::iterator i = _channels.begin(); i != _channels.end(); ++i) {
if (*i == ch) {
delete *i;
_channels.erase(i--);
}
}
- updateStatus();
+ updateStatus(sndType);
}
-void MacLowLevelPCMDriver::playSamples(ChanHandle handle, PCMSound *snd) {
+void MacLowLevelPCMDriver::playSamples(ChanHandle handle, ExecMode mode, const PCMSound *snd) {
if (!snd || !snd->data)
return;
Common::StackLock lock(_mutex);
- Channel *ch = findChannel(handle);
+ MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kSampledSynth);
+ if (!ch)
+ return;
+
+ ch->enqueueSndCmd(81, 0, snd, 1, mode);
+}
+
+void MacLowLevelPCMDriver::playNote(ChanHandle handle, ExecMode mode, uint8 note, uint16 duration) {
+ Common::StackLock lock(_mutex);
+ MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kIgnoreSynth);
+ if (!ch)
+ return;
+
+ ch->enqueueSndCmd(40, note, duration, mode);
+}
+
+void MacLowLevelPCMDriver::quiet(ChanHandle handle, ExecMode mode) {
+ Common::StackLock lock(_mutex);
+ MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kIgnoreSynth);
+ if (!ch)
+ return;
+
+ ch->enqueueSndCmd(3, 0, 0, mode);
+}
+
+void MacLowLevelPCMDriver::flush(ChanHandle handle, ExecMode mode) {
+ Common::StackLock lock(_mutex);
+ MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kIgnoreSynth);
+ if (!ch)
+ return;
+ ch->enqueueSndCmd(4, 0, 0, mode);
+}
+
+void MacLowLevelPCMDriver::loadWaveTable(ChanHandle handle, ExecMode mode, const byte *data, uint16 dataSize) {
+ if (!data || !dataSize)
+ return;
+
+ Common::StackLock lock(_mutex);
+ MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kWaveTableSynth);
+ if (!ch)
+ return;
+
+ ch->enqueueSndCmd(60, dataSize, data, 2, mode);
+}
+
+void MacLowLevelPCMDriver::loadInstrument(ChanHandle handle, ExecMode mode, const PCMSound *snd) {
+ if (!snd || !snd->data)
+ return;
+
+ Common::StackLock lock(_mutex);
+ MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kSampledSynth);
+ if (!ch)
+ return;
+
+ ch->enqueueSndCmd(80, 0, snd, 1, mode);
+}
+
+void MacLowLevelPCMDriver::setTimbre(ChanHandle handle, ExecMode mode, uint16 timbre) {
+ Common::StackLock lock(_mutex);
+ MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kSquareWaveSynth);
+ if (!ch)
+ return;
+
+ ch->enqueueSndCmd(44, timbre, 0, mode);
+}
+
+void MacLowLevelPCMDriver::callback(ChanHandle handle, ExecMode mode, uint16 arg1, const void *arg2) {
+ Common::StackLock lock(_mutex);
+ MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kIgnoreSynth);
+ if (!ch)
+ return;
+
+ ch->enqueueSndCmd(13, arg1, arg2, 0, mode);
+}
+
+uint8 MacLowLevelPCMDriver::getChannelStatus(ChanHandle handle) const {
+ MacSndChannel *ch = findChannel(handle); return ch ? ch->_flags : 0;
+}
+
+void MacLowLevelPCMDriver::clearChannelFlags(ChanHandle handle, uint8 flags) {
+ MacSndChannel *ch = findChannel(handle); if (ch) ch->clearFlags(flags);
+}
+
+void MacLowLevelPCMDriver::updateStatus(Audio::Mixer::SoundType sndType) {
+ _numInternalMixChannels = _smpSize > 1 ? 1 : _channels.size();
+ _status[sndType].numExternalMixChannels = _smpSize > 1 ? _channels.size() : 1;
+ _status[sndType].allowInterPolation = true;
+ for (Common::Array<MacSndChannel*>::const_iterator ch = _channels.begin(); ch != _channels.end(); ++ch) {
+ if (!(*ch)->_interpolate)
+ _status[sndType].allowInterPolation = false;
+ }
+}
+
+MacSndChannel *MacLowLevelPCMDriver::findAndCheckChannel(ChanHandle h, const char *callingProcName, byte reqSynthType) const {
+ MacSndChannel *ch = findChannel(h);
if (!ch) {
- warning("MacLowLevelPCMDriver::playSamples(): Channel not found");
+ warning("%s(): Channel not found", callingProcName);
+ return nullptr;
+ }
+
+ if (reqSynthType != kIgnoreSynth && reqSynthType != ch->_synth) {
+ warning("%s(): Wrong channel type", callingProcName);
+ return nullptr;
+ }
+
+ return ch;
+}
+
+MacSndChannel *MacLowLevelPCMDriver::findChannel(ChanHandle handle) const {
+ for (Common::Array<MacSndChannel*>::const_iterator ch = _channels.begin(); ch != _channels.end(); ++ch) {
+ if ((*ch)->getHandle() == handle)
+ return *ch;
+ }
+ return nullptr;
+}
+
+MacSndChannel::MacSndChannel(MacLowLevelPCMDriver *drv, Audio::Mixer::SoundType sndtp, int synth, bool interp, bool enableL, bool enableR, MacLowLevelPCMDriver::ChanCallback *callback) : _drv(drv), _sndType(sndtp),
+ _synth(synth), _interpolate(interp), _frameSize(1), _len(0), _rmH(0), _rmL(0), _smpWtAcc(0), _loopSt2(0), _loopEnd(0), _loopLen(0), _loopSt(0), _baseFreq(0), _rcPos(0),
+ _flags(0), _tmrPos(0), _tmrInc(0), _timbre(0), _data(nullptr), _srate(0), _phase(0), _duration(0), _tmrRate(0), _callback(callback) {
+ _lastSmp[0] = _lastSmp[1] = 0;
+ _enable[0] = enableL;
+ _enable[1] = enableR;
+ _tmrRate = fixDiv2Frac(0x10000, fixDiv2Frac(_drv->getStatus().deviceRate, 0x7D00000, 16), 30);
+
+ if (synth != MacLowLevelPCMDriver::kSampledSynth) {
+ _len = 256;
+ _frameSize = 1;
+ _loopSt2 = 0;
+ _loopEnd = _len;
+ _baseFreq = 69;
+ setupRateConv(_drv->getStatus().deviceRate, 0x10000, ASC_DEVICE_RATE, false);
+ if (_synth == MacLowLevelPCMDriver::kSquareWaveSynth)
+ setTimbre(0x50);
+ }
+}
+
+MacLowLevelPCMDriver::ChanHandle MacSndChannel::getHandle() const {
+ const void *ptr = this;
+ return *reinterpret_cast<const int*>(&ptr);
+}
+
+void MacSndChannel::playSamples(const MacLowLevelPCMDriver::PCMSound *snd) {
+ setupRateConv(_drv->getStatus().deviceRate, calcNoteRateAdj(60 - snd->baseFreq), snd->rate, true);
+ setupSound(snd);
+ startSound(0);
+}
+
+void MacSndChannel::playNote(uint8 note, uint16 duration) {
+ note &= 0x7f;
+ if (!note) {
+ quiet();
return;
}
- if (ch->_synth != kSampledSynth) {
- warning("MacLowLevelPCMDriver::playSamples(): Wrong channel type");
+ uint32 adj = calcNoteRateAdj(note - _baseFreq);
+ if (_synth == MacLowLevelPCMDriver::kSampledSynth)
+ setupRateConv(_drv->getStatus().deviceRate, adj, _srate, true);
+ else
+ _srate = fixMul(0x50FBA, adj);
+
+ startSound(duration);
+}
+
+void MacSndChannel::quiet() {
+ _data = nullptr;
+ _tmrInc = 0;
+}
+
+void MacSndChannel::flush() {
+ _sndCmdQueue.clear();
+}
+
+void MacSndChannel::loadWaveTable(const byte *data, uint16 dataSize) {
+ assert(dataSize == _len);
+ int8 *buff = new int8[dataSize]();
+ const int8 *s = reinterpret_cast<const int8*>(data);
+ for (uint32 i = 0; i < dataSize; ++i)
+ buff[i] = *s++ ^ 0x80;
+ _res = Common::SharedPtr<const int8>(buff, Common::ArrayDeleter<const int8>());
+}
+
+void MacSndChannel::loadInstrument(const MacLowLevelPCMDriver::PCMSound *snd) {
+ setupSound(snd);
+ setupRateConv(_drv->getStatus().deviceRate, 0x10000, snd->rate, false);
+}
+
+void MacSndChannel::setTimbre(uint16 timbre) {
+ if (_timbre == timbre)
+ return;
+
+ int8 *buff = new int8[256]();
+ makeSquareWave(buff, 256, timbre);
+ _res = Common::SharedPtr<const int8>(buff);
+ _timbre = timbre;
+}
+
+void MacSndChannel::callback(uint16 p1, const void *p2) {
+ if (_callback && _callback->isValid())
+ (*_callback)(p1, p2);
+}
+
+void MacSndChannel::enqueueSndCmd(uint8 c, uint16 p1, uint32 p2, byte mode) {
+ if (mode == MacLowLevelPCMDriver::kImmediate) {
+ switch (c) {
+ case 3:
+ quiet();
+ break;
+ case 4:
+ flush();
+ break;
+ case 44:
+ setTimbre(p1);
+ break;
+ default:
+ _sndCmdQueue.insert(_sndCmdQueue.begin(), SoundCommand(c, p1, p2));
+ break;
+ }
+
+ } else {
+ _sndCmdQueue.push_back(SoundCommand(c, p1, p2));
+ }
+}
+
+void MacSndChannel::enqueueSndCmd(uint8 c, uint16 p1, const void *p2, byte ptrType, byte mode) {
+ if (mode == MacLowLevelPCMDriver::kImmediate && (c == 60 || c == 80)) {
+ if (c == 60)
+ loadWaveTable(reinterpret_cast<const byte*>(p2), p1);
+ else if (c == 80)
+ loadInstrument(reinterpret_cast<const MacLowLevelPCMDriver::PCMSound*>(p2));
+ else if (c == 13)
+ callback(p1, p2);
return;
}
- ch->_res = snd->data.reinterpretCast<const int8>();
- ch->_data = ch->_res.get();
- ch->_len = snd->len;
- uint32 rmul = calcRate(_status.deviceRate, 0x10000, snd->rate);
+ void *ptr = nullptr;
+ if (ptrType == 1) {
+ const MacLowLevelPCMDriver::PCMSound *s = reinterpret_cast<const MacLowLevelPCMDriver::PCMSound*>(p2);
+ MacLowLevelPCMDriver::PCMSound *p = new MacLowLevelPCMDriver::PCMSound(s->data, s->len, s->rate, s->loopst, s->loopend, s->baseFreq, s->enc, s->stereo);
+ ptr = p;
+ } else if (ptrType == 2) {
+ byte *d = new byte[p1];
+ memcpy(d, p2, p1);
+ ptr = d;
+ }
- if (rmul >= 0x7FFD && rmul <= 0x8003)
- rmul = 0x8000;
- else if (ABS((int16)(rmul & 0xffff)) <= 7)
- rmul = (rmul + 7) & ~0xffff;
+ if (mode == MacLowLevelPCMDriver::kImmediate)
+ _sndCmdQueue.insert(_sndCmdQueue.begin(), SoundCommand(c, p1, ptr, ptrType));
+ else
+ _sndCmdQueue.push_back(SoundCommand(c, p1, ptr, ptrType));
+}
- if (rmul > (uint32)-64)
- rmul = (uint32)-64;
+bool MacSndChannel::idle() const {
+ return _sndCmdQueue.empty() && !(_flags & MacLowLevelPCMDriver::kStatusPlaying);
+}
- assert(rmul);
+void MacSndChannel::feed(int32 *dst, uint32 dstSize, byte dstFrameSize) {
+ const bool interp = _interpolate && _rmL;
+ int32 diff = 0;
- ch->_rmL = rmul & 0xffff;
- ch->_rmH = rmul >> 16;
+ for (const int32 *end = &dst[dstSize]; dst < end; ) {
+ processSndCmdQueue();
- ch->_frameSize = snd->stereo ? 2 : 1;
+ int8 in = 0;
+ for (int i = 0; i < dstFrameSize; ++i) {
+ if (_data != nullptr && i < _frameSize) {
+ if (_synth != MacLowLevelPCMDriver::kSampledSynth) {
+ in = _data[(_phase >> 16) & 0xff];
+ } else {
+ in = _data[_rcPos + i];
+ if (interp && in != _lastSmp[i]) {
+ diff = in - _lastSmp[i];
+ diff = (diff * (_smpWtAcc >> 1)) >> 15;
+ in = (_lastSmp[i] + diff) & 0xff;
+ }
+ }
+ }
+ if (_enable[i])
+ *dst += in;
+ dst++;
+ }
+
+ uint32 cpos = _rcPos;
+ _rcPos += (_rmH * _frameSize);
+ _phase += (_rmH * _srate);
+ _smpWtAcc += _rmL;
+ if (_smpWtAcc > 0xffff) {
+ _smpWtAcc &= 0xffff;
+ _rcPos += _frameSize;
+ _phase += _srate;
+ }
+
+ _tmrPos += _tmrInc;
+ while (_tmrPos > 0x3fffffff) {
+ _tmrPos -= 0x40000000;
+ if (_duration && !--_duration)
+ _data = nullptr;
+ }
+
+ if (_synth != MacLowLevelPCMDriver::kSampledSynth || cpos == _rcPos || _data == nullptr)
+ continue;
+
+ if (interp) {
+ for (int i = 0; i < _frameSize; ++i)
+ _lastSmp[i] = _data[_loopSt + ((_rcPos - _frameSize + i - _loopSt) % _loopLen)];
+ }
+
+ if (_rcPos >= _loopSt + _loopLen) {
+ if (_loopEnd) {
+ _loopSt = _loopSt2;
+ _loopLen = _loopEnd - _loopSt2;
+ if (interp) {
+ for (int i = 0; i < _frameSize; ++i)
+ _lastSmp[i] = _data[_loopSt + ((_rcPos - _frameSize + i - _loopSt) % _loopLen)];
+ }
+ _rcPos = _loopSt + ((_rcPos - _loopSt) % _loopLen);
+ } else {
+ _data = nullptr;
+ }
+ }
+ }
+}
+
+void MacSndChannel::setupSound(const MacLowLevelPCMDriver::PCMSound *snd) {
+ assert(_synth == MacLowLevelPCMDriver::kSampledSynth);
+
+ _len = snd->len;
+ const byte *in = snd->data.get();
+ assert(in);
+ int8 *buff = new int8[_len];
+ for (uint32 i = 0; i < _len; ++i)
+ buff[i] = *in++ ^ 0x80;
+ _res = Common::SharedPtr<const int8>(buff, Common::ArrayDeleter<const int8>());
+ _frameSize = snd->stereo ? 2 : 1;
+ _loopSt = 0;
if (snd->loopend - snd->loopst < 2 || snd->loopend < snd->loopst) {
- ch->_loopSt = 0;
- ch->_loopEnd = 0;
+ _loopSt2 = 0;
+ _loopEnd = 0;
} else {
- ch->_loopSt = snd->loopst - (snd->loopst % ch->_frameSize);
- ch->_loopEnd = snd->loopend - (snd->loopend % ch->_frameSize);
+ _loopSt2 = snd->loopst - (snd->loopst % _frameSize);
+ _loopEnd = snd->loopend - (snd->loopend % _frameSize);
+ assert(_loopEnd <= _len);
}
- ch->_baseFreq = snd->baseFreq;
- ch->_rcPos = 0;
- ch->_smpWtAcc = 0;
- ch->_lastSmp[0] = ch->_data[0];
- if (ch->_len >= ch->_frameSize)
- ch->_lastSmp[1] = ch->_data[1];
- clearFlags(kStatusDone);
- clearChanFlags(ch, kStatusDone);
+ _baseFreq = snd->baseFreq;
+ _srate = snd->rate;
+}
+
+void MacSndChannel::setupRateConv(uint32 drate, uint32 mod, uint32 srate, bool ppr) {
+ uint32 rmul = calcRate(drate, mod, srate);
+
+ if (ppr) {
+ if (rmul >= 0x7FFD && rmul <= 0x8003)
+ rmul = 0x8000;
+ else if (ABS((int16)(rmul & 0xffff)) <= 7)
+ rmul = (rmul + 7) & ~0xffff;
+
+ if (rmul > (uint32)-64)
+ rmul = (uint32)-64;
+ }
+ assert(rmul);
+ _rmL = rmul & 0xffff;
+ _rmH = rmul >> 16;
}
-void MacLowLevelPCMDriver::stop(ChanHandle handle) {
- Common::StackLock lock(_mutex);
- Channel *ch = findChannel(handle);
- if (!ch) {
- warning("MacLowLevelPCMDriver::stop(): Channel not found");
+void MacSndChannel::startSound(uint32 duration) {
+ _duration = duration;
+ if (duration) {
+ _tmrInc = _tmrRate;
+ _tmrPos = 0;
+ }
+
+ _data = _res.get();
+ _lastSmp[0] = _data[0];
+ if (_len >= _frameSize)
+ _lastSmp[1] = _data[1];
+ _rcPos = 0;
+ _loopSt = 0;
+ _loopLen = _loopEnd ? _loopEnd : _len;
+ _smpWtAcc = 0;
+ _phase = 0;
+}
+
+void MacSndChannel::processSndCmdQueue() {
+ if (_data)
+ return;
+
+ if (_sndCmdQueue.empty()) {
+ setFlags(MacLowLevelPCMDriver::kStatusDone);
+ clearFlags(MacLowLevelPCMDriver::kStatusPlaying);
return;
}
- ch->stop();
- setChanFlags(ch, kStatusDone);
+
+ _drv->clearFlags(MacLowLevelPCMDriver::kStatusDone);
+ clearFlags(MacLowLevelPCMDriver::kStatusDone);
+ setFlags(MacLowLevelPCMDriver::kStatusPlaying);
+
+ SoundCommand &c = _sndCmdQueue.front();
+ MacLowLevelPCMDriver::PCMSound *p = (c.ptr && c.arg2 == 1) ? reinterpret_cast<MacLowLevelPCMDriver::PCMSound*>(c.ptr) : nullptr;
+ const byte *b = (c.ptr && c.arg2 == 2) ? reinterpret_cast<byte*>(c.ptr) : nullptr;
+
+ switch (c.cmd) {
+ case 3:
+ quiet();
+ break;
+ case 4:
+ flush();
+ break;
+ case 13:
+ callback(c.arg1, c.ptr);
+ break;
+ case 40:
+ playNote(c.arg1, c.arg2);
+ break;
+ case 44:
+ setTimbre(c.arg1);
+ break;
+ case 60:
+ loadWaveTable(b, c.arg1);
+ break;
+ case 80:
+ loadInstrument(p);
+ break;
+ case 81:
+ playSamples(p);
+ break;
+ default:
+ break;
+ }
+
+ if (p) {
+ p->data.reset();
+ delete p;
+ } else if (b) {
+ delete[] b;
+ }
+
+ if (!_sndCmdQueue.empty())
+ _sndCmdQueue.erase(_sndCmdQueue.begin());
}
-uint32 MacLowLevelPCMDriver::calcRate(uint32 outRate, uint32 factor, uint32 dataRate) {
+uint32 MacSndChannel::calcRate(uint32 outRate, uint32 factor, uint32 dataRate) {
uint32 result = outRate;
uint64 t = 0;
uint32 c = 0;
@@ -533,41 +965,68 @@ uint32 MacLowLevelPCMDriver::calcRate(uint32 outRate, uint32 factor, uint32 data
return result;
}
-void MacLowLevelPCMDriver::updateStatus() {
- _numInternalMixChannels = _smpSize > 1 ? 1 : _channels.size();
- _status.numExternalMixChannels = _smpSize > 1 ? _channels.size() : 1;
- _status.allowInterPolation = true;
- for (Common::Array<Channel*>::const_iterator ch = _channels.begin(); ch != _channels.end(); ++ch) {
- if (!(*ch)->_interpolate)
- _status.allowInterPolation = false;
- }
-}
+uint32 MacSndChannel::calcNoteRateAdj(int diff) {
+ static const uint32 adjFrac[23] = {
+ 0x21e71f26, 0x23eb3588, 0x260dfc14, 0x285145f3, 0x2ab70212, 0x2d413ccd,
+ 0x2ff221af, 0x32cbfd4a, 0x35d13f33, 0x39047c0f, 0x3c686fce, 0x40000000,
+ 0x43ce3e4b, 0x47d66b0f, 0x4c1bf829, 0x50a28be6, 0x556e0424, 0x5a82799a,
+ 0x5fe4435e, 0x6597fa95, 0x6ba27e65, 0x7208f81d, 0x78d0df9c
+ };
-MacLowLevelPCMDriver::Channel *MacLowLevelPCMDriver::findChannel(ChanHandle handle) const {
- for (Common::Array<Channel*>::const_iterator ch = _channels.begin(); ch != _channels.end(); ++ch) {
- if ((*ch)->getHandle() == handle)
- return *ch;
- }
- return nullptr;
+ diff = CLIP<int>(diff, -127, 127);
+ return fracMul(adjFrac[11 + (diff % 12)], (diff >= 0) ? 1 << ((diff / 12) + 16) : 0x10000 / (1 << (-diff / 12)));
}
-MacLowLevelPCMDriver::Channel::Channel(Audio::Mixer::SoundType sndtp, int synth, bool interp) : _sndType(sndtp), _synth(synth),
- _interpolate(interp), _frameSize(1), _len(0), _rmH(0), _rmL(0), _smpWtAcc(0), _loopSt(0), _loopEnd(0), _baseFreq(0), _rcPos(0),
- _flags(0), _data(nullptr) {
- _lastSmp[0] = _lastSmp[1] = 0;
+void MacSndChannel::makeSquareWave(int8 *dstBuff, uint16 dstSize, byte timbre) {
+ static const byte ampTbl[128] = {
+ 0x80, 0x82, 0x83, 0x85, 0x86, 0x88, 0x89, 0x8b, 0x8d, 0x8e, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97,
+ 0x99, 0x9a, 0x9c, 0x9d, 0x9f, 0xa1, 0xa2, 0xa4, 0xa5, 0xa6, 0xa8, 0xa9, 0xab, 0xac, 0xae, 0xaf,
+ 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb8, 0xb9, 0xbb, 0xbc, 0xbd, 0xbf, 0xc0, 0xc1, 0xc3, 0xc4, 0xc5,
+ 0xc7, 0xc8, 0xc9, 0xca, 0xcc, 0xcd, 0xce, 0xcf, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9,
+ 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+ 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee, 0xef, 0xf0, 0xf0, 0xf1, 0xf2, 0xf2, 0xf3, 0xf4, 0xf4,
+ 0xf5, 0xf5, 0xf6, 0xf7, 0xf7, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff
+ };
+ assert(dstSize == 256);
+ timbre = MAX<byte>(8, 255 - ampTbl[127 - (timbre >> 1)]);
+
+ byte p = 0;
+ int8 *d1 = dstBuff;
+ int8 *d2 = &d1[128];
+ for (int i = 0; i < 65; ++i) {
+ *d1++ = *d2-- = ((ampTbl[p] ^ 0x80) >> 2);
+ p = MIN<int>(127, timbre + p);
+ }
+ d1 = dstBuff;
+ d2 = &d1[128];
+ for (int i = 0; i < 128; ++i)
+ *d2++ = *d1++ ^ 0xff;
}
-MacLowLevelPCMDriver::ChanHandle MacLowLevelPCMDriver::Channel::getHandle() const {
- const void *ptr = this;
- return *reinterpret_cast<const int*>(&ptr);
-}
+const uint8 _fourToneSynthWaveForm[256] = {
+ 0x80, 0x7a, 0x74, 0x6e, 0x69, 0x63, 0x5d, 0x57, 0x52, 0x4c, 0x47, 0x42, 0x3e, 0x3b, 0x38, 0x35,
+ 0x34, 0x33, 0x34, 0x35, 0x37, 0x3a, 0x3e, 0x43, 0x49, 0x4e, 0x54, 0x5b, 0x61, 0x67, 0x6c, 0x71,
+ 0x75, 0x78, 0x7a, 0x7c, 0x7c, 0x7b, 0x79, 0x76, 0x73, 0x6f, 0x6b, 0x66, 0x62, 0x5e, 0x5b, 0x58,
+ 0x56, 0x56, 0x57, 0x59, 0x5c, 0x61, 0x67, 0x6e, 0x77, 0x80, 0x8a, 0x95, 0xa0, 0xac, 0xb7, 0xc2,
+ 0xcc, 0xd6, 0xdf, 0xe7, 0xee, 0xf4, 0xf8, 0xfb, 0xfe, 0xff, 0xff, 0xfe, 0xfd, 0xfb, 0xf9, 0xf6,
+ 0xf3, 0xf0, 0xec, 0xe9, 0xe6, 0xe3, 0xe0, 0xdd, 0xda, 0xd7, 0xd4, 0xd1, 0xce, 0xca, 0xc6, 0xc2,
+ 0xbd, 0xb8, 0xb3, 0xad, 0xa7, 0xa1, 0x9a, 0x93, 0x8d, 0x86, 0x7f, 0x79, 0x73, 0x6d, 0x68, 0x63,
+ 0x5f, 0x5c, 0x5a, 0x58, 0x57, 0x57, 0x58, 0x5a, 0x5c, 0x5f, 0x63, 0x67, 0x6b, 0x70, 0x75, 0x7b,
+ 0x80, 0x85, 0x8b, 0x90, 0x95, 0x99, 0x9d, 0xa1, 0xa4, 0xa6, 0xa8, 0xa9, 0xa9, 0xa8, 0xa6, 0xa4,
+ 0xa1, 0x9d, 0x98, 0x93, 0x8d, 0x87, 0x81, 0x7a, 0x73, 0x6d, 0x66, 0x5f, 0x59, 0x53, 0x4d, 0x48,
+ 0x43, 0x3e, 0x3a, 0x36, 0x32, 0x2f, 0x2c, 0x29, 0x26, 0x23, 0x20, 0x1d, 0x1a, 0x17, 0x14, 0x10,
+ 0x0d, 0x0a, 0x07, 0x05, 0x03, 0x02, 0x01, 0x01, 0x02, 0x05, 0x08, 0x0c, 0x12, 0x19, 0x21, 0x2a,
+ 0x34, 0x3e, 0x49, 0x54, 0x60, 0x6b, 0x76, 0x80, 0x89, 0x92, 0x99, 0x9f, 0xa4, 0xa7, 0xa9, 0xaa,
+ 0xaa, 0xa8, 0xa5, 0xa2, 0x9e, 0x9a, 0x95, 0x91, 0x8d, 0x8a, 0x87, 0x85, 0x84, 0x84, 0x86, 0x88,
+ 0x8b, 0x8f, 0x94, 0x99, 0x9f, 0xa5, 0xac, 0xb2, 0xb7, 0xbd, 0xc2, 0xc6, 0xc9, 0xcb, 0xcc, 0xcd,
+ 0xcc, 0xcb, 0xc8, 0xc5, 0xc2, 0xbe, 0xb9, 0xb4, 0xae, 0xa9, 0xa3, 0x9d, 0x97, 0x92, 0x8c, 0x86
+};
-void MacLowLevelPCMDriver::Channel::stop() {
- _data = nullptr;
- _res.reset();
-}
+const uint32 _fourToneSynthWaveFormSize = sizeof(_fourToneSynthWaveForm);
class Indy3MacSnd;
+class LoomMacSnd;
template<typename T> class MusicEngineImpl : public MusicEngine {
public:
@@ -644,14 +1103,26 @@ template <typename T> void MusicEngineImpl<T>::restoreAfterLoad() {
}
namespace MacSound {
+
MusicEngine *createPlayer(ScummEngine *vm) {
assert(vm);
assert(vm->_mixer);
- if (vm->_game.id == GID_INDY3)
- return new MusicEngineImpl<Indy3MacSnd>(vm, vm->_mixer);
+ MusicEngine *res = nullptr;
+
+ switch (vm->_game.id) {
+ case GID_INDY3:
+ res = new MusicEngineImpl<Indy3MacSnd>(vm, vm->_mixer);
+ break;
+ case GID_LOOM:
+ res = new MusicEngineImpl<LoomMacSnd>(vm, vm->_mixer);
+ break;
+ default:
+ break;
+ }
- return nullptr;
+ return res;
}
+
} // end of namespace MacSound
#undef ASC_DEVICE_RATE
diff --git a/engines/scumm/players/player_mac_new.h b/engines/scumm/players/player_mac_new.h
index b7c32c19b5d..3015880dbc8 100644
--- a/engines/scumm/players/player_mac_new.h
+++ b/engines/scumm/players/player_mac_new.h
@@ -31,10 +31,10 @@ class ScummEngine;
namespace MacSound {
enum {
kQualityAuto = 0,
- kQualityHigh,
- kQualityLow,
- kQualityMedium
+ kQualityLowest = 1,
+ kQualityHighest = 9
};
+
MusicEngine *createPlayer(ScummEngine *vm);
} // end of namespace MacSound
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 8c3a870f5ec..3bdcbed12b1 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -69,7 +69,7 @@ struct SaveInfoSection {
#define SaveInfoSectionSize (4+4+4 + 4+4 + 4+2)
-#define CURRENT_VER 113
+#define CURRENT_VER 114
#define INFOSECTION_VERSION 2
#pragma mark -
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index c0340ba282b..fd0cbb9bab5 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2168,14 +2168,19 @@ void ScummEngine::setupMusic(int midi, const Common::Path &macInstrumentFile) {
#endif
} else if (_game.platform == Common::kPlatformAmiga && _game.version <= 4) {
_musicEngine = new Player_V4A(this, _mixer);
- } else if (_game.platform == Common::kPlatformMacintosh && _game.id == GID_INDY3) {
- _musicEngine = MacSound::createPlayer(this);
- if (ConfMan.hasKey("mac_v3_low_quality_music") && ConfMan.getBool("mac_v3_low_quality_music"))
- _musicEngine->setQuality(MacSound::kQualityLow);
- _sound->_musicType = MDT_MACINTOSH;
- } else if (_game.platform == Common::kPlatformMacintosh && _game.id == GID_LOOM) {
- _musicEngine = new Player_V3M(this, _mixer, ConfMan.getBool("mac_v3_low_quality_music"));
- ((Player_V3M *)_musicEngine)->init(macInstrumentFile);
+ } else if (_game.platform == Common::kPlatformMacintosh && (_game.id == GID_INDY3 || _game.id == GID_LOOM)) {
+#if 0
+ if (_game.id == GID_LOOM) {
+ _musicEngine = new Player_V3M(this, _mixer, ConfMan.getBool("mac_v3_low_quality_music"));
+ ((Player_V3M *)_musicEngine)->init(macInstrumentFile);
+ } else
+#endif
+ {
+ _musicEngine = MacSound::createPlayer(this);
+ if (ConfMan.hasKey("mac_v3_low_quality_music") && ConfMan.getBool("mac_v3_low_quality_music"))
+ _musicEngine->setQuality(MacSound::kQualityLowest);
+ _sound->_musicType = MDT_MACINTOSH;
+ }
} else if (_game.platform == Common::kPlatformMacintosh && _game.id == GID_MONKEY) {
_musicEngine = new Player_V5M(this, _mixer);
((Player_V5M *)_musicEngine)->init(macInstrumentFile);
Commit: c52bfe4f270e4fd9d23ed277e9d1d78136f17e7f
https://github.com/scummvm/scummvm/commit/c52bfe4f270e4fd9d23ed277e9d1d78136f17e7f
Author: athrxx (athrxx at scummvm.org)
Date: 2024-02-28T21:31:50+01:00
Commit Message:
SCUMM: (Loom/Mac) - add music quality slider widget
Changed paths:
engines/scumm/dialogs.cpp
engines/scumm/dialogs.h
engines/scumm/macgui/macgui_loom.cpp
engines/scumm/metaengine.cpp
engines/scumm/scumm.cpp
engines/scumm/vars.cpp
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index f6c4fd5d812..52e3c80f1a3 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -1354,7 +1354,6 @@ void LoomEgaGameOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Commo
}
void LoomEgaGameOptionsWidget::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
-
switch (cmd) {
case kOvertureTicksChanged:
updateOvertureTicksValue();
@@ -1371,6 +1370,86 @@ void LoomEgaGameOptionsWidget::updateOvertureTicksValue() {
_overtureTicksValue->setLabel(Common::String::format("%d:%02d.%d", ticks / 600, (ticks % 600) / 10, ticks % 10));
}
+// Mac Loom options
+LoomMacGameOptionsWidget::LoomMacGameOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) :
+ ScummOptionsContainerWidget(boss, name, "LoomMacGameOptionsWidget", domain), _sndQualitySlider(nullptr), _sndQualityValue(nullptr), _enableOriginalGUICheckbox(nullptr), _quality(0) {
+ GUI::StaticTextWidget *text = new GUI::StaticTextWidget(widgetsBoss(), "LoomMacGameOptionsWidget.SndQualityLabel", _("Music Quality:"));
+ text->setAlign(Graphics::TextAlign::kTextAlignEnd);
+
+ _sndQualitySlider = new GUI::SliderWidget(widgetsBoss(), "LoomMacGameOptionsWidget.SndQuality", _("Select music quality. The original would determine the basic setup by hardware detection and speed tests, but it could also be changed in the game menu to some degree."), kQualitySliderUpdate);
+ _sndQualitySlider->setMinValue(0);
+ _sndQualitySlider->setMaxValue(9);
+ _sndQualityValue = new GUI::StaticTextWidget(widgetsBoss(), "LoomMacGameOptionsWidget.SndQualityValue", Common::U32String());
+ _sndQualityValue->setFlags(GUI::WIDGET_CLEARBG);
+ updateQualitySlider();
+
+ createEnhancementsWidget(widgetsBoss(), "LoomMacGameOptionsWidget");
+ _enableOriginalGUICheckbox = createOriginalGUICheckbox(widgetsBoss(), "LoomMacGameOptionsWidget.EnableOriginalGUI");
+}
+
+void LoomMacGameOptionsWidget::load() {
+ ScummOptionsContainerWidget::load();
+
+ _quality = 0;
+
+ if (ConfMan.hasKey("mac_snd_quality", _domain))
+ _quality = ConfMan.getInt("mac_snd_quality", _domain);
+
+ // Migrate old bool setting...
+ if (_quality == 0 && ConfMan.hasKey("mac_v3_low_quality_music", _domain)) {
+ if (ConfMan.getBool("mac_v3_low_quality_music"))
+ _quality = 1;
+ }
+ ConfMan.removeKey("mac_v3_low_quality_music", _domain);
+
+ _sndQualitySlider->setValue(_quality);
+ updateQualitySlider();
+ _enableOriginalGUICheckbox->setState(ConfMan.getBool("original_gui", _domain));
+}
+
+bool LoomMacGameOptionsWidget::save() {
+ bool res = ScummOptionsContainerWidget::save();
+ ConfMan.setInt("mac_snd_quality", _quality, _domain);
+ ConfMan.setBool("original_gui", _enableOriginalGUICheckbox->getState(), _domain);
+ return res;
+}
+
+void LoomMacGameOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const {
+ layouts.addDialog(layoutName, overlayedLayout)
+ .addLayout(GUI::ThemeLayout::kLayoutVertical, 5)
+ .addPadding(0, 0, 0, 0)
+ .addLayout(GUI::ThemeLayout::kLayoutVertical, 4)
+ .addPadding(0, 0, 10, 0)
+ .addWidget("EnableOriginalGUI", "Checkbox");
+ addEnhancementsLayout(layouts)
+ .closeLayout()
+ .addLayout(GUI::ThemeLayout::kLayoutHorizontal, 12)
+ .addPadding(0, 0, 10, 0)
+ .addWidget("SndQualityLabel", "OptionsLabel")
+ .addWidget("SndQuality", "Slider")
+ .addWidget("SndQualityValue", "ShortOptionsLabel")
+ .closeLayout()
+ .closeLayout()
+ .closeDialog();
+}
+
+void LoomMacGameOptionsWidget::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+ switch (cmd) {
+ case kQualitySliderUpdate:
+ updateQualitySlider();
+ break;
+ default:
+ GUI::OptionsContainerWidget::handleCommand(sender, cmd, data);
+ break;
+ }
+}
+
+void LoomMacGameOptionsWidget::updateQualitySlider() {
+ _quality = _sndQualitySlider->getValue();
+ Common::U32String label(_quality == 0 ? "auto" : Common::String::format("%4d", _quality));
+ _sndQualityValue->setLabel(label);
+}
+
// VGA Loom Playback Adjustment settings
LoomVgaGameOptionsWidget::LoomVgaGameOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) :
diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h
index ea15e8d2449..eeeeb373d84 100644
--- a/engines/scumm/dialogs.h
+++ b/engines/scumm/dialogs.h
@@ -298,6 +298,30 @@ private:
void updateOvertureTicksValue();
};
+/**
+* Options widget for Mac Loom.
+*/
+class LoomMacGameOptionsWidget : public ScummOptionsContainerWidget {
+public:
+ LoomMacGameOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain);
+ ~LoomMacGameOptionsWidget() override {};
+
+ void load() override;
+ bool save() override;
+private:
+ enum {
+ kQualitySliderUpdate = 'QUAL'
+ };
+ void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
+ void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
+ void updateQualitySlider();
+
+ GUI::CheckboxWidget *_enableOriginalGUICheckbox;
+ GUI::SliderWidget *_sndQualitySlider;
+ GUI::StaticTextWidget *_sndQualityValue;
+ int _quality;
+};
+
/**
* Options widget for VGA Loom (DOS CD).
*/
diff --git a/engines/scumm/macgui/macgui_loom.cpp b/engines/scumm/macgui/macgui_loom.cpp
index 693a601e08b..93fce2cb5f9 100644
--- a/engines/scumm/macgui/macgui_loom.cpp
+++ b/engines/scumm/macgui/macgui_loom.cpp
@@ -636,6 +636,9 @@ bool MacLoomGui::runOptionsDialog() {
int scrolling = _vm->_snapScroll == 0;
int fullAnimation = _vm->VAR(_vm->VAR_MACHINE_SPEED) == 1 ? 0 : 1;
int textSpeed = _vm->_defaultTextSpeed;
+ int musicQuality = ConfMan.hasKey("mac_snd_quality") ? ConfMan.getInt("mac_snd_quality") : 0;
+ int musicQualityOption = (musicQuality == 0) ? 1 : (musicQuality - 1) % 3;
+ musicQuality = (musicQuality == 0) ? (_vm->VAR(_vm->VAR_SOUNDCARD) == 10 ? 0 : 2) : (musicQuality - 1) / 3;
MacDialogWindow *window = createDialog(1000);
@@ -651,7 +654,7 @@ bool MacLoomGui::runOptionsDialog() {
window->setWidgetValue(11, textSpeed);
window->addPictureSlider(8, 9, true, 5, 69, 0, 2, 6, 4);
- window->setWidgetValue(12, 2/* TODO: save var*/);
+ window->setWidgetValue(12, musicQualityOption);
// Machine rating
window->addSubstitution(Common::String::format("%d", _vm->VAR(53)));
@@ -721,11 +724,10 @@ bool MacLoomGui::runOptionsDialog() {
// the sequence files and mutes everything else)
//
- //_vm->VAR(_vm->VAR_SOUNDCARD) = window->getWidgetValue(12) == 0 ? 10 : 11;
//((Player_V3M *)_vm->_musicEngine)->overrideQuality(_vm->VAR(_vm->VAR_SOUNDCARD) == 10);
- int musicQuality = (ConfMan.hasKey("mac_v3_low_quality_music") && ConfMan.getBool("mac_v3_low_quality_music")) ? 0 : (_vm->VAR(_vm->VAR_SOUNDCARD) == 10 ? 0 : 2);
- _vm->_musicEngine->setQuality(musicQuality * 3 + 1 + window->getWidgetValue(12));
- //ConfMan.setBool("mac_v3_low_quality_music", _vm->VAR(_vm->VAR_SOUNDCARD) == 10);
+ musicQuality = musicQuality * 3 + 1 + window->getWidgetValue(12);
+ _vm->_musicEngine->setQuality(musicQuality);
+ ConfMan.setInt("mac_snd_quality", musicQuality);
debug(6, "MacLoomGui::runOptionsDialog(): music quality: %d - unimplemented!", window->getWidgetValue(12));
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index 647fa986223..51badcb06aa 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -567,7 +567,7 @@ SaveStateDescriptor ScummMetaEngine::querySaveMetaInfos(const char *target, int
GUI::OptionsContainerWidget *ScummMetaEngine::buildLoomOptionsWidget(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const {
Common::Platform platform = Common::parsePlatform(ConfMan.get("platform", target));
- if (platform != Common::kPlatformUnknown && platform != Common::kPlatformDOS)
+ if (platform != Common::kPlatformUnknown && platform != Common::kPlatformDOS && platform != Common::kPlatformMacintosh)
return nullptr;
Common::String extra = ConfMan.get("extra", target);
@@ -580,10 +580,11 @@ GUI::OptionsContainerWidget *ScummMetaEngine::buildLoomOptionsWidget(GUI::GuiObj
if (extra == "Steam")
return MetaEngine::buildEngineOptionsWidget(boss, name, target);
+ else if (platform == Common::kPlatformMacintosh)
+ return new Scumm::LoomMacGameOptionsWidget(boss, name, target);
// These EGA Loom settings are only relevant for the EGA
// version, since that is the only one that has an overture.
-
return new Scumm::LoomEgaGameOptionsWidget(boss, name, target);
}
@@ -653,7 +654,7 @@ static const ExtraGuiOption fmtownsTrimTo200 = {
static const ExtraGuiOption macV3LowQualityMusic = {
_s("Play simplified music"),
- _s("This music was presumably intended for low-end Macs, and uses only one channel."),
+ _s("This music was intended for low-end Macs, and uses only one channel."),
"mac_v3_low_quality_music",
false,
0,
@@ -773,7 +774,7 @@ const ExtraGuiOptions ScummMetaEngine::getExtraGuiOptions(const Common::String &
// The low quality music in Loom was probably intended for low-end
// Macs. It plays only one channel, instead of three.
- if (target.empty() || ((gameid == "loom" || gameid == "indy3") && platform == Common::kPlatformMacintosh && extra != "Steam")) {
+ if (target.empty() || (gameid == "indy3" && platform == Common::kPlatformMacintosh && extra != "Steam")) {
options.push_back(macV3LowQualityMusic);
}
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index fd0cbb9bab5..3562f8f312c 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2179,6 +2179,8 @@ void ScummEngine::setupMusic(int midi, const Common::Path &macInstrumentFile) {
_musicEngine = MacSound::createPlayer(this);
if (ConfMan.hasKey("mac_v3_low_quality_music") && ConfMan.getBool("mac_v3_low_quality_music"))
_musicEngine->setQuality(MacSound::kQualityLowest);
+ else if (ConfMan.hasKey("mac_snd_quality"))
+ _musicEngine->setQuality(ConfMan.getInt("mac_snd_quality"));
_sound->_musicType = MDT_MACINTOSH;
}
} else if (_game.platform == Common::kPlatformMacintosh && _game.id == GID_MONKEY) {
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index 65bba51185c..ae7c807bf7e 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -858,8 +858,10 @@ void ScummEngine::setSoundCardVarToCurrentConfig() {
// 4 Roland
switch (_sound->_musicType) {
case MDT_MACINTOSH:
- if (_game.id == GID_INDY3 || _game.id == GID_LOOM)
+ if (_game.id == GID_INDY3)
VAR(VAR_SOUNDCARD) = (ConfMan.hasKey("mac_v3_low_quality_music") && ConfMan.getBool("mac_v3_low_quality_music")) ? 10 : 11;
+ else if (_game.id == GID_LOOM)
+ VAR(VAR_SOUNDCARD) = (ConfMan.hasKey("mac_snd_quality") && ConfMan.getInt("mac_snd_quality") > 0 && ConfMan.getInt("mac_snd_quality") < 4) ? 10 : 11;
else
VAR(VAR_SOUNDCARD) = 3;
break;
Commit: 5fe07a8fb1afbf19fce6f94e8c9eaf609cb299db
https://github.com/scummvm/scummvm/commit/5fe07a8fb1afbf19fce6f94e8c9eaf609cb299db
Author: athrxx (athrxx at scummvm.org)
Date: 2024-02-28T21:31:59+01:00
Commit Message:
SCUMM: (Loom/Mac) - work around original tempo glitch
Changed paths:
engines/scumm/players/player_mac_intern.h
engines/scumm/players/player_mac_loom.cpp
engines/scumm/players/player_mac_new.cpp
diff --git a/engines/scumm/players/player_mac_intern.h b/engines/scumm/players/player_mac_intern.h
index ee5d04b26a8..3b48d57d23c 100644
--- a/engines/scumm/players/player_mac_intern.h
+++ b/engines/scumm/players/player_mac_intern.h
@@ -134,6 +134,7 @@ public:
void playNote(ChanHandle handle, ExecMode mode, uint8 note, uint16 duration);
void quiet(ChanHandle handle, ExecMode mode);
void flush(ChanHandle handle, ExecMode mode);
+ void wait(ChanHandle handle, ExecMode mode, uint16 duration);
void loadWaveTable(ChanHandle handle, ExecMode mode, const byte *data, uint16 dataSize);
void loadInstrument(ChanHandle handle, ExecMode mode, const PCMSound *snd);
void setTimbre(ChanHandle handle, ExecMode mode, uint16 timbre);
diff --git a/engines/scumm/players/player_mac_loom.cpp b/engines/scumm/players/player_mac_loom.cpp
index 08485e04f60..0b79ca53dab 100644
--- a/engines/scumm/players/player_mac_loom.cpp
+++ b/engines/scumm/players/player_mac_loom.cpp
@@ -283,14 +283,19 @@ void LoomMacSnd::sendSoundCommands(const byte *data, int timeStamp) {
while (len--) {
uint16 p1 = READ_BE_UINT16(s);
s += 2;
- uint32 p2 = 0x8f00 | *s++;
+ uint8 note = *s++;
+
if (timeStamp > 0) {
int ts = timeStamp;
timeStamp = MAX<int>(0, timeStamp - p1);
p1 -= ts;
}
- if (!timeStamp)
- _sdrv->playNote(_sndChannel, MacLowLevelPCMDriver::kEnqueue, p2 & 0x7f, p1);
+ if (timeStamp)
+ continue;
+
+ _sdrv->playNote(_sndChannel, MacLowLevelPCMDriver::kEnqueue, note & 0x7f, p1);
+ if (note == 0) // Workaround for tempo glitch in original driver
+ _sdrv->wait(_sndChannel, MacLowLevelPCMDriver::kEnqueue, p1);
}
_sdrv->quiet(_sndChannel, MacLowLevelPCMDriver::kEnqueue);
_sdrv->callback(_sndChannel, MacLowLevelPCMDriver::kEnqueue, 1, nullptr);
@@ -319,15 +324,21 @@ void LoomMacSnd::sendSoundCommands(const byte *data, int timeStamp) {
uint16 p1 = READ_BE_UINT16(src[i]);
src[i] += 2;
byte note = *src[i]++;
- uint32 p2 = (/*_curSynthType == 4 && */note == 0) ? 1 : (0x8f00 | note);
if (tmstmp[i] > 0) {
int ts = tmstmp[i];
tmstmp[i] = MAX<int>(0, tmstmp[i] - p1);
p1 -= ts;
}
loop |= static_cast<bool>(--len[i]);
- if (!tmstmp[i])
- _sdrv->playNote(_musChannels[i], MacLowLevelPCMDriver::kEnqueue, p2 & 0x7f, p1);
+
+ if (tmstmp[i])
+ continue;
+
+ _sdrv->playNote(_musChannels[i], MacLowLevelPCMDriver::kEnqueue, (_curSynthType == 4 && note == 0) ? 1 : note & 0x7f, p1);
+ // Workaround for tempo glitch in original driver. For the sampled synth in 4 channel mode, there is
+ // some sort of fix in the original (see above), but that really does not work well for the other cases.
+ if (note == 0 && _curSynthType != 4)
+ _sdrv->wait(_musChannels[i], MacLowLevelPCMDriver::kEnqueue, p1);
}
}
@@ -461,7 +472,7 @@ void LoomMacSnd::detectQuality() {
if (isSoundCardType10())
_machineRating = 1;
/*else if (0)
- _machineQuality = 2;*/
+ _machineRating = 2;*/
else
_machineRating = 3;
}
diff --git a/engines/scumm/players/player_mac_new.cpp b/engines/scumm/players/player_mac_new.cpp
index cbaa4082e3d..938084a1b72 100644
--- a/engines/scumm/players/player_mac_new.cpp
+++ b/engines/scumm/players/player_mac_new.cpp
@@ -46,6 +46,7 @@ public:
void playNote(uint8 note, uint16 duration);
void quiet();
void flush();
+ void wait(uint32 duration);
void loadWaveTable(const byte *data, uint16 dataSize);
void loadInstrument(const MacLowLevelPCMDriver::PCMSound *snd);
void setTimbre(uint16 timbre);
@@ -403,6 +404,14 @@ void MacLowLevelPCMDriver::flush(ChanHandle handle, ExecMode mode) {
ch->enqueueSndCmd(4, 0, 0, mode);
}
+void MacLowLevelPCMDriver::wait(ChanHandle handle, ExecMode mode, uint16 duration) {
+ Common::StackLock lock(_mutex);
+ MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kIgnoreSynth);
+ if (!ch)
+ return;
+ ch->enqueueSndCmd(10, duration, 0, mode);
+}
+
void MacLowLevelPCMDriver::loadWaveTable(ChanHandle handle, ExecMode mode, const byte *data, uint16 dataSize) {
if (!data || !dataSize)
return;
@@ -538,6 +547,14 @@ void MacSndChannel::quiet() {
_tmrInc = 0;
}
+void MacSndChannel::wait(uint32 duration) {
+ _duration = duration;
+ if (duration) {
+ _tmrInc = _tmrRate;
+ _tmrPos = 0;
+ }
+}
+
void MacSndChannel::flush() {
_sndCmdQueue.clear();
}
@@ -580,6 +597,9 @@ void MacSndChannel::enqueueSndCmd(uint8 c, uint16 p1, uint32 p2, byte mode) {
case 4:
flush();
break;
+ case 10:
+ wait(p1);
+ break;
case 44:
setTimbre(p1);
break;
@@ -664,8 +684,8 @@ void MacSndChannel::feed(int32 *dst, uint32 dstSize, byte dstFrameSize) {
_tmrPos += _tmrInc;
while (_tmrPos > 0x3fffffff) {
_tmrPos -= 0x40000000;
- if (_duration && !--_duration)
- _data = nullptr;
+ if (_duration)
+ --_duration;
}
if (_synth != MacLowLevelPCMDriver::kSampledSynth || cpos == _rcPos || _data == nullptr)
@@ -738,10 +758,8 @@ void MacSndChannel::setupRateConv(uint32 drate, uint32 mod, uint32 srate, bool p
void MacSndChannel::startSound(uint32 duration) {
_duration = duration;
- if (duration) {
- _tmrInc = _tmrRate;
- _tmrPos = 0;
- }
+ _tmrInc = duration ? _tmrRate : 0;
+ _tmrPos = 0;
_data = _res.get();
_lastSmp[0] = _data[0];
@@ -755,7 +773,7 @@ void MacSndChannel::startSound(uint32 duration) {
}
void MacSndChannel::processSndCmdQueue() {
- if (_data)
+ if ((_data && _tmrInc == 0) || _duration)
return;
if (_sndCmdQueue.empty()) {
@@ -779,6 +797,9 @@ void MacSndChannel::processSndCmdQueue() {
case 4:
flush();
break;
+ case 10:
+ wait(c.arg1);
+ break;
case 13:
callback(c.arg1, c.ptr);
break;
More information about the Scummvm-git-logs
mailing list