Initial commit
This commit is contained in:
2344
engines/kyra/sound/drivers/adlib.cpp
Normal file
2344
engines/kyra/sound/drivers/adlib.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1556
engines/kyra/sound/drivers/audiomaster2.cpp
Normal file
1556
engines/kyra/sound/drivers/audiomaster2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
70
engines/kyra/sound/drivers/audiomaster2.h
Normal file
70
engines/kyra/sound/drivers/audiomaster2.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#ifndef KYRA_SOUND_AUDIOMASTER2_H
|
||||
#define KYRA_SOUND_AUDIOMASTER2_H
|
||||
|
||||
namespace Audio {
|
||||
class Mixer;
|
||||
}
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class AudioMaster2Internal;
|
||||
|
||||
class AudioMaster2 {
|
||||
public:
|
||||
AudioMaster2(Audio::Mixer *mixer);
|
||||
~AudioMaster2();
|
||||
|
||||
bool init();
|
||||
bool loadRessourceFile(Common::SeekableReadStream *data);
|
||||
|
||||
bool startSound(const Common::String &name);
|
||||
bool stopSound(const Common::String &name);
|
||||
|
||||
void flushResource(const Common::String &name);
|
||||
void flushAllResources();
|
||||
|
||||
void fadeOut(int delay);
|
||||
bool isFading();
|
||||
|
||||
int getPlayDuration();
|
||||
|
||||
void setMusicVolume(int volume);
|
||||
void setSoundEffectVolume(int volume);
|
||||
|
||||
private:
|
||||
AudioMaster2Internal *_am2i;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
324
engines/kyra/sound/drivers/audstream.cpp
Normal file
324
engines/kyra/sound/drivers/audstream.cpp
Normal file
@@ -0,0 +1,324 @@
|
||||
/* 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 "kyra/resource/resource.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
// Thanks to Torbjorn Andersson (eriktorbjorn) for his aud player on which
|
||||
// this code is based on
|
||||
|
||||
// TODO: cleanup of whole AUDStream
|
||||
|
||||
class AUDStream : public Audio::SeekableAudioStream {
|
||||
public:
|
||||
AUDStream(Common::SeekableReadStream* stream);
|
||||
~AUDStream() override;
|
||||
|
||||
int readBuffer(int16* buffer, const int numSamples) override;
|
||||
|
||||
bool isStereo() const override { return false; }
|
||||
bool endOfData() const override { return _endOfData; }
|
||||
|
||||
int getRate() const override { return _rate; }
|
||||
|
||||
bool seek(const Audio::Timestamp& where) override;
|
||||
Audio::Timestamp getLength() const override { return _length; }
|
||||
private:
|
||||
Common::SeekableReadStream* _stream;
|
||||
uint32 _streamStart;
|
||||
bool _endOfData;
|
||||
int _rate;
|
||||
uint _processedSize;
|
||||
uint _totalSize;
|
||||
Audio::Timestamp _length;
|
||||
|
||||
int _bytesLeft;
|
||||
|
||||
byte* _outBuffer;
|
||||
int _outBufferOffset;
|
||||
uint _outBufferSize;
|
||||
|
||||
byte* _inBuffer;
|
||||
uint _inBufferSize;
|
||||
|
||||
int readChunk(int16* buffer, const int maxSamples);
|
||||
|
||||
static const int8 WSTable2Bit[];
|
||||
static const int8 WSTable4Bit[];
|
||||
};
|
||||
|
||||
const int8 AUDStream::WSTable2Bit[] = { -2, -1, 0, 1 };
|
||||
const int8 AUDStream::WSTable4Bit[] = {
|
||||
-9, -8, -6, -5, -4, -3, -2, -1,
|
||||
0, 1, 2, 3, 4, 5, 6, 8
|
||||
};
|
||||
|
||||
AUDStream::AUDStream(Common::SeekableReadStream *stream) : _stream(stream), _endOfData(true), _rate(0),
|
||||
_processedSize(0), _totalSize(0), _length(0, 1), _bytesLeft(0), _outBuffer(nullptr),
|
||||
_outBufferOffset(0), _outBufferSize(0), _inBuffer(nullptr), _inBufferSize(0), _streamStart(0) {
|
||||
|
||||
if (_stream->size() < 8) {
|
||||
warning("No AUD file: too short");
|
||||
return;
|
||||
}
|
||||
|
||||
_rate = _stream->readUint16LE();
|
||||
_totalSize = _stream->readUint32LE();
|
||||
|
||||
// TODO?: add checks
|
||||
int flags = _stream->readByte(); // flags
|
||||
int type = _stream->readByte(); // type
|
||||
|
||||
_streamStart = stream->pos();
|
||||
|
||||
debugC(5, kDebugLevelSound, "AUD Info: rate: %d, totalSize: %d, flags: %d, type: %d, streamStart: %d", _rate, _totalSize, flags, type, _streamStart);
|
||||
|
||||
_length = Audio::Timestamp(0, _rate);
|
||||
for (uint32 i = 0; i < _totalSize;) {
|
||||
uint16 size = _stream->readUint16LE();
|
||||
uint16 outSize = _stream->readUint16LE();
|
||||
|
||||
_length = _length.addFrames(outSize);
|
||||
stream->seek(size + 4, SEEK_CUR);
|
||||
i += size + 8;
|
||||
}
|
||||
|
||||
stream->seek(_streamStart, SEEK_SET);
|
||||
|
||||
if (type == 1 && !flags)
|
||||
_endOfData = false;
|
||||
else
|
||||
warning("No AUD file (rate: %d, size: %d, flags: 0x%X, type: %d)", _rate, _totalSize, flags, type);
|
||||
}
|
||||
|
||||
AUDStream::~AUDStream() {
|
||||
delete[] _outBuffer;
|
||||
delete[] _inBuffer;
|
||||
delete _stream;
|
||||
}
|
||||
|
||||
int AUDStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samplesRead = 0, samplesLeft = numSamples;
|
||||
|
||||
while (samplesLeft > 0 && !_endOfData) {
|
||||
int samples = readChunk(buffer, samplesLeft);
|
||||
samplesRead += samples;
|
||||
samplesLeft -= samples;
|
||||
buffer += samples;
|
||||
}
|
||||
|
||||
return samplesRead;
|
||||
}
|
||||
|
||||
inline int16 clip8BitSample(int16 sample) {
|
||||
return CLIP<int16>(sample, 0, 255);
|
||||
}
|
||||
|
||||
int AUDStream::readChunk(int16 *buffer, const int maxSamples) {
|
||||
int samplesProcessed = 0;
|
||||
|
||||
// if no bytes of the old chunk are left, read the next one
|
||||
if (_bytesLeft <= 0) {
|
||||
if (_processedSize >= _totalSize) {
|
||||
_endOfData = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 size = _stream->readUint16LE();
|
||||
uint16 outSize = _stream->readUint16LE();
|
||||
uint32 id = _stream->readUint32LE();
|
||||
|
||||
assert(id == 0x0000DEAF);
|
||||
|
||||
_processedSize += 8 + size;
|
||||
|
||||
_outBufferOffset = 0;
|
||||
if (size == outSize) {
|
||||
if (outSize > _outBufferSize) {
|
||||
_outBufferSize = outSize;
|
||||
delete[] _outBuffer;
|
||||
_outBuffer = new uint8[_outBufferSize];
|
||||
assert(_outBuffer);
|
||||
}
|
||||
|
||||
_bytesLeft = size;
|
||||
|
||||
_stream->read(_outBuffer, _bytesLeft);
|
||||
} else {
|
||||
_bytesLeft = outSize;
|
||||
|
||||
if (outSize > _outBufferSize) {
|
||||
_outBufferSize = outSize;
|
||||
delete[] _outBuffer;
|
||||
_outBuffer = new uint8[_outBufferSize];
|
||||
assert(_outBuffer);
|
||||
}
|
||||
|
||||
if (size > _inBufferSize) {
|
||||
_inBufferSize = size;
|
||||
delete[] _inBuffer;
|
||||
_inBuffer = new uint8[_inBufferSize];
|
||||
assert(_inBuffer);
|
||||
}
|
||||
|
||||
if (_stream->read(_inBuffer, size) != size) {
|
||||
_endOfData = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16 curSample = 0x80;
|
||||
byte code = 0;
|
||||
int8 count = 0;
|
||||
uint16 input = 0;
|
||||
int j = 0;
|
||||
int i = 0;
|
||||
|
||||
while (outSize > 0) {
|
||||
input = _inBuffer[i++] << 2;
|
||||
code = (input >> 8) & 0xFF;
|
||||
count = (input & 0xFF) >> 2;
|
||||
|
||||
switch (code) {
|
||||
case 2:
|
||||
if (count & 0x20) {
|
||||
/* NOTE: count is signed! */
|
||||
count <<= 3;
|
||||
curSample += (count >> 3);
|
||||
_outBuffer[j++] = curSample & 0xFF;
|
||||
outSize--;
|
||||
} else {
|
||||
for (; count >= 0; count--) {
|
||||
_outBuffer[j++] = _inBuffer[i++];
|
||||
outSize--;
|
||||
}
|
||||
curSample = _inBuffer[i - 1];
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
for (; count >= 0; count--) {
|
||||
code = _inBuffer[i++];
|
||||
|
||||
curSample += WSTable4Bit[code & 0x0F];
|
||||
curSample = clip8BitSample(curSample);
|
||||
_outBuffer[j++] = curSample;
|
||||
|
||||
curSample += WSTable4Bit[code >> 4];
|
||||
curSample = clip8BitSample(curSample);
|
||||
_outBuffer[j++] = curSample;
|
||||
|
||||
outSize -= 2;
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
for (; count >= 0; count--) {
|
||||
code = (uint8)_inBuffer[i++];
|
||||
|
||||
curSample += WSTable2Bit[code & 0x03];
|
||||
curSample = clip8BitSample(curSample);
|
||||
_outBuffer[j++] = curSample & 0xFF;
|
||||
|
||||
curSample += WSTable2Bit[(code >> 2) & 0x03];
|
||||
curSample = clip8BitSample(curSample);
|
||||
_outBuffer[j++] = curSample & 0xFF;
|
||||
|
||||
curSample += WSTable2Bit[(code >> 4) & 0x03];
|
||||
curSample = clip8BitSample(curSample);
|
||||
_outBuffer[j++] = curSample & 0xFF;
|
||||
|
||||
curSample += WSTable2Bit[(code >> 6) & 0x03];
|
||||
curSample = clip8BitSample(curSample);
|
||||
_outBuffer[j++] = curSample & 0xFF;
|
||||
|
||||
outSize -= 4;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
for (; count >= 0; count--) {
|
||||
_outBuffer[j++] = curSample & 0xFF;
|
||||
outSize--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copies the chunk data to the output buffer
|
||||
if (_bytesLeft > 0) {
|
||||
int samples = MIN(_bytesLeft, maxSamples);
|
||||
samplesProcessed += samples;
|
||||
_bytesLeft -= samples;
|
||||
|
||||
while (samples--) {
|
||||
int16 sample = (_outBuffer[_outBufferOffset++] << 8) ^ 0x8000;
|
||||
|
||||
*buffer++ = sample;
|
||||
}
|
||||
}
|
||||
|
||||
return samplesProcessed;
|
||||
}
|
||||
|
||||
bool AUDStream::seek(const Audio::Timestamp &where) {
|
||||
const uint32 seekSample = Audio::convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
|
||||
|
||||
_stream->seek(_streamStart);
|
||||
_processedSize = 0;
|
||||
_bytesLeft = 0;
|
||||
_endOfData = false;
|
||||
|
||||
uint32 curSample = 0;
|
||||
|
||||
while (!endOfData()) {
|
||||
uint16 size = _stream->readUint16LE();
|
||||
uint16 outSize = _stream->readUint16LE();
|
||||
|
||||
if (curSample + outSize > seekSample) {
|
||||
_stream->seek(-4, SEEK_CUR);
|
||||
|
||||
uint32 samples = seekSample - curSample;
|
||||
int16 *temp = new int16[samples];
|
||||
assert(temp);
|
||||
|
||||
readChunk(temp, samples);
|
||||
delete[] temp;
|
||||
curSample += samples;
|
||||
break;
|
||||
} else {
|
||||
curSample += outSize;
|
||||
_processedSize += 8 + size;
|
||||
_stream->seek(size + 4, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
_endOfData = (_processedSize >= _totalSize);
|
||||
|
||||
return (curSample == seekSample);
|
||||
}
|
||||
|
||||
Audio::SeekableAudioStream* makeAUDStream(Common::SeekableReadStream* stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
return new AUDStream(stream);
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
1549
engines/kyra/sound/drivers/capcom98.cpp
Normal file
1549
engines/kyra/sound/drivers/capcom98.cpp
Normal file
File diff suppressed because it is too large
Load Diff
71
engines/kyra/sound/drivers/capcom98.h
Normal file
71
engines/kyra/sound/drivers/capcom98.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#ifndef KYRA_SOUND_CAPCOM98_H
|
||||
#define KYRA_SOUND_CAPCOM98_H
|
||||
|
||||
#include "audio/mididrv.h"
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Audio {
|
||||
class Mixer;
|
||||
}
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class CapcomPC98AudioDriverInternal;
|
||||
|
||||
class CapcomPC98AudioDriver {
|
||||
public:
|
||||
CapcomPC98AudioDriver(Audio::Mixer *mixer, MidiDriver::DeviceHandle dev);
|
||||
~CapcomPC98AudioDriver();
|
||||
|
||||
bool isUsable() const;
|
||||
|
||||
// All data passed to the following functions has to be maintained by the caller.
|
||||
void reset();
|
||||
void loadFMInstruments(const uint8 *data);
|
||||
void startSong(const uint8 *data, uint8 volume, bool loop);
|
||||
void stopSong();
|
||||
void startSoundEffect(const uint8 *data, uint8 volume);
|
||||
void stopSoundEffect();
|
||||
|
||||
int checkSoundMarker() const;
|
||||
bool songIsPlaying() const;
|
||||
bool soundEffectIsPlaying() const;
|
||||
|
||||
void fadeOut();
|
||||
void allNotesOff();
|
||||
|
||||
void setMusicVolume(int volume);
|
||||
void setSoundEffectVolume(int volume);
|
||||
|
||||
private:
|
||||
CapcomPC98AudioDriverInternal *_drv;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
2829
engines/kyra/sound/drivers/halestorm.cpp
Normal file
2829
engines/kyra/sound/drivers/halestorm.cpp
Normal file
File diff suppressed because it is too large
Load Diff
98
engines/kyra/sound/drivers/halestorm.h
Normal file
98
engines/kyra/sound/drivers/halestorm.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* 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 KYRA_SOUND_HALESTORM_H
|
||||
#define KYRA_SOUND_HALESTORM_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Audio {
|
||||
class Mixer;
|
||||
}
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class HSSoundSystem;
|
||||
|
||||
class HalestormLoader {
|
||||
public:
|
||||
virtual ~HalestormLoader() {}
|
||||
|
||||
virtual Common::SeekableReadStream *getResource(uint16 id, uint32 type) = 0;
|
||||
};
|
||||
|
||||
class HalestormDriver {
|
||||
public:
|
||||
HalestormDriver(HalestormLoader *res, Audio::Mixer *mixer);
|
||||
~HalestormDriver();
|
||||
|
||||
enum InterpolationMode {
|
||||
kNone = 0,
|
||||
kSimple = 1,
|
||||
kTable = 2
|
||||
};
|
||||
|
||||
// The original driver would generate 8-bit sound, which makes
|
||||
// sense, since most of the hardware had support for only that.
|
||||
// However, the internal quality of the intermediate pcm stream
|
||||
// (before the downsampling to 8-bit in the final pass) is a bit
|
||||
// higher (up to 12 bits, depending on the channel use). I have
|
||||
// added an "output16bit" option which will output the unmodified
|
||||
// intermediate data (but converting it from unsigned to signed).
|
||||
bool init(bool hiQuality, InterpolationMode imode, int numChanSfx, bool output16bit);
|
||||
|
||||
void registerSamples(const uint16 *resList, bool registerOnly);
|
||||
void releaseSamples();
|
||||
int changeSystemVoices(int numChanMusicTotal, int numChanMusicPoly, int numChanSfx);
|
||||
|
||||
void startSoundEffect(int id, int rate = 0);
|
||||
void enqueueSoundEffect(int id, int rate, int note);
|
||||
void stopSoundEffect(int id);
|
||||
void stopAllSoundEffects();
|
||||
|
||||
enum HSCommands {
|
||||
kSongPlayOnce = 0,
|
||||
kSongPlayLoop = 1,
|
||||
kSongAbort = 2,
|
||||
kSongIsPlaying = 3,
|
||||
kSongFadeOut = 10,
|
||||
kSongFadeIn = 11,
|
||||
kSongFadeGetState = 12,
|
||||
kSongFadeReset = 13,
|
||||
kSetRateAndIntrplMode = 14
|
||||
};
|
||||
|
||||
int doCommand(int cmd, ...);
|
||||
|
||||
void setMusicVolume(int volume);
|
||||
void setSoundEffectVolume(int volume);
|
||||
|
||||
private:
|
||||
HSSoundSystem *_hs;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
1946
engines/kyra/sound/drivers/mlalf98.cpp
Normal file
1946
engines/kyra/sound/drivers/mlalf98.cpp
Normal file
File diff suppressed because it is too large
Load Diff
89
engines/kyra/sound/drivers/mlalf98.h
Normal file
89
engines/kyra/sound/drivers/mlalf98.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#ifndef KYRA_SOUND_MLALF98_H
|
||||
#define KYRA_SOUND_MLALF98_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
class Mixer;
|
||||
}
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class MLALF98Internal;
|
||||
|
||||
class MLALF98 {
|
||||
public:
|
||||
enum EmuType {
|
||||
kType9801_26 = 1,
|
||||
kType9801_86 = 2
|
||||
};
|
||||
|
||||
struct ADPCMData {
|
||||
ADPCMData() : smpStart(0), smpEnd(0), unk4(0), unk5(0), volume(0), unk7(0) {}
|
||||
uint16 smpStart;
|
||||
uint16 smpEnd;
|
||||
uint8 unk4;
|
||||
uint8 unk5;
|
||||
uint8 volume;
|
||||
uint8 unk7;
|
||||
};
|
||||
|
||||
typedef Common::Array<ADPCMData> ADPCMDataArray;
|
||||
|
||||
public:
|
||||
MLALF98(Audio::Mixer *mixer, EmuType emuType);
|
||||
~MLALF98();
|
||||
|
||||
// The caller has to dispose of the stream. The stream can be discarded
|
||||
// immediately after calling the respective loader function.
|
||||
void loadMusicData(Common::SeekableReadStream *data);
|
||||
void loadSoundEffectData(Common::SeekableReadStream *data);
|
||||
void loadExtData(ADPCMDataArray &data);
|
||||
|
||||
void startMusic(int track);
|
||||
void fadeOutMusic();
|
||||
void startSoundEffect(int track);
|
||||
|
||||
void allChannelsOff();
|
||||
void resetExtUnit();
|
||||
|
||||
void setMusicVolume(int volume);
|
||||
void setSoundEffectVolume(int volume);
|
||||
|
||||
private:
|
||||
MLALF98Internal *_drv;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
89
engines/kyra/sound/drivers/pc_base.h
Normal file
89
engines/kyra/sound/drivers/pc_base.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* 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 KYRA_SOUND_PCDRIVER_H
|
||||
#define KYRA_SOUND_PCDRIVER_H
|
||||
|
||||
#include "kyra/resource/resource.h"
|
||||
|
||||
namespace Audio {
|
||||
class Mixer;
|
||||
}
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class PCSoundDriver {
|
||||
public:
|
||||
PCSoundDriver() : _soundData(0), _soundDataSize(0) {}
|
||||
virtual ~PCSoundDriver() {}
|
||||
|
||||
virtual void initDriver() = 0;
|
||||
virtual void setSoundData(uint8 *data, uint32 size) = 0;
|
||||
virtual void startSound(int track, int volume) = 0;
|
||||
virtual bool isChannelPlaying(int channel) const = 0;
|
||||
virtual void stopAllChannels() = 0;
|
||||
|
||||
virtual int getSoundTrigger() const { return 0; }
|
||||
virtual void resetSoundTrigger() {}
|
||||
|
||||
virtual void setMusicVolume(uint8 volume) = 0;
|
||||
virtual void setSfxVolume(uint8 volume) = 0;
|
||||
|
||||
// AdLiB (Kyra 1) specific
|
||||
virtual void setSyncJumpMask(uint16) {}
|
||||
|
||||
protected:
|
||||
uint8 *getProgram(int progId) {
|
||||
// Safety check: invalid progId would crash.
|
||||
if (progId < 0 || progId >= (int32)_soundDataSize / 2)
|
||||
return nullptr;
|
||||
|
||||
const uint16 offset = READ_LE_UINT16(_soundData + 2 * progId);
|
||||
|
||||
// In case an invalid offset is specified we return nullptr to
|
||||
// indicate an error. 0xFFFF seems to indicate "this is not a valid
|
||||
// program/instrument". However, 0 is also invalid because it points
|
||||
// inside the offset table itself. We also ignore any offsets outside
|
||||
// of the actual data size.
|
||||
// The original does not contain any safety checks and will simply
|
||||
// read outside of the valid sound data in case an invalid offset is
|
||||
// encountered.
|
||||
if (offset == 0 || offset >= _soundDataSize) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return _soundData + offset;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 *_soundData;
|
||||
uint32 _soundDataSize;
|
||||
|
||||
public:
|
||||
static PCSoundDriver *createAdLib(Audio::Mixer *mixer, int version);
|
||||
#ifdef ENABLE_EOB
|
||||
static PCSoundDriver *createPCSpk(Audio::Mixer *mixer, bool pcJRMode);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
389
engines/kyra/sound/drivers/pcspeaker_v1.cpp
Normal file
389
engines/kyra/sound/drivers/pcspeaker_v1.cpp
Normal file
@@ -0,0 +1,389 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#include "kyra/sound/drivers/pc_base.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "common/mutex.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class PCSpeakerDriver : public PCSoundDriver, public Audio::AudioStream {
|
||||
public:
|
||||
PCSpeakerDriver(Audio::Mixer *mixer, bool pcJRMode);
|
||||
~PCSpeakerDriver() override;
|
||||
|
||||
void initDriver() override;
|
||||
void setSoundData(uint8 *data, uint32 size) override;
|
||||
void startSound(int id, int) override;
|
||||
bool isChannelPlaying(int channel) const override;
|
||||
void stopAllChannels() override;
|
||||
|
||||
void setMusicVolume(uint8 volume) override;
|
||||
void setSfxVolume(uint8) override {}
|
||||
|
||||
void update();
|
||||
|
||||
// AudioStream interface
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
bool isStereo() const override { return false; }
|
||||
int getRate() const override { return _outputRate; }
|
||||
bool endOfData() const override { return false; }
|
||||
|
||||
private:
|
||||
void noteOn(int chan, uint16 period);
|
||||
void chanOff(int chan);
|
||||
void generateSamples(int16 *buffer, int numSamples);
|
||||
|
||||
struct Channel {
|
||||
Channel(uint8 attnDB) : curSample(32767.0 / pow(2.0, (double)attnDB / 6.0)),
|
||||
dataPtr(0), timer(0), timerScale(0), repeatCounter1(0), repeatCounter2(0), period(-1), samplesLeft(0) {}
|
||||
const uint8 *dataPtr;
|
||||
int16 timer;
|
||||
uint8 timerScale;
|
||||
uint8 repeatCounter1;
|
||||
uint8 repeatCounter2;
|
||||
int32 period;
|
||||
int32 curSample;
|
||||
uint32 samplesLeft;
|
||||
};
|
||||
|
||||
Channel **_channels;
|
||||
int _numChannels;
|
||||
|
||||
const uint8 *_newTrackData;
|
||||
const uint8 *_trackData;
|
||||
|
||||
Common::Mutex _mutex;
|
||||
Audio::Mixer *_mixer;
|
||||
Audio::SoundHandle _handle;
|
||||
|
||||
uint _outputRate;
|
||||
int _samplesUpdateIntv;
|
||||
int _samplesUpdateIntvRem;
|
||||
int _samplesUpdateTmr;
|
||||
int _samplesUpdateTmrRem;
|
||||
|
||||
int _masterVolume;
|
||||
bool _ready;
|
||||
|
||||
const int _clock;
|
||||
const int _updateRate;
|
||||
const bool _pcJR;
|
||||
const int _periodDiv;
|
||||
const int _levelAdjust;
|
||||
const uint16 * const _periodsTable;
|
||||
|
||||
static const uint16 _periodsPCSpk[96];
|
||||
static const uint16 _periodsPCjr[96];
|
||||
};
|
||||
|
||||
PCSpeakerDriver::PCSpeakerDriver(Audio::Mixer *mixer, bool pcJRMode) : PCSoundDriver(), _mixer(mixer), _samplesUpdateIntv(0), _samplesUpdateIntvRem(0),
|
||||
_outputRate(0), _samplesUpdateTmr(0), _samplesUpdateTmrRem(0), _newTrackData(0), _trackData(0), _pcJR(pcJRMode), _numChannels(pcJRMode ? 3 : 1), _channels(0),
|
||||
_clock(pcJRMode ? 111860 : 1193180), _updateRate(292), _masterVolume(63), _periodsTable(pcJRMode ? _periodsPCjr : _periodsPCSpk), _periodDiv(2),
|
||||
_levelAdjust(pcJRMode ? 1 : 0), _ready(false) {
|
||||
_outputRate = _mixer->getOutputRate();
|
||||
_samplesUpdateIntv = _outputRate / _updateRate;
|
||||
_samplesUpdateIntvRem = _outputRate % _updateRate;
|
||||
|
||||
_channels = new Channel*[_numChannels];
|
||||
assert(_channels);
|
||||
for (int i = 0; i < _numChannels; ++i) {
|
||||
_channels[i] = new Channel(i * 10);
|
||||
assert(_channels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
PCSpeakerDriver::~PCSpeakerDriver() {
|
||||
_ready = false;
|
||||
_mixer->stopHandle(_handle);
|
||||
|
||||
if (_channels) {
|
||||
for (int i = 0; i < _numChannels; ++i)
|
||||
delete _channels[i];
|
||||
delete[] _channels;
|
||||
}
|
||||
}
|
||||
|
||||
void PCSpeakerDriver::initDriver() {
|
||||
if (_ready)
|
||||
return;
|
||||
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
|
||||
_ready = true;
|
||||
}
|
||||
|
||||
void PCSpeakerDriver::setSoundData(uint8 *data, uint32 size) {
|
||||
Common::StackLock lock(_mutex);
|
||||
if (!_ready)
|
||||
return;
|
||||
|
||||
_soundData = data;
|
||||
_soundDataSize = size;
|
||||
}
|
||||
|
||||
void PCSpeakerDriver::startSound(int id, int) {
|
||||
Common::StackLock lock(_mutex);
|
||||
if (!_ready)
|
||||
return;
|
||||
_newTrackData = getProgram(id & 0x7F);
|
||||
}
|
||||
|
||||
bool PCSpeakerDriver::isChannelPlaying(int channel) const {
|
||||
Common::StackLock lock(_mutex);
|
||||
if (!_ready)
|
||||
return false;
|
||||
return _trackData;
|
||||
}
|
||||
|
||||
void PCSpeakerDriver::stopAllChannels() {
|
||||
Common::StackLock lock(_mutex);
|
||||
if (!_ready)
|
||||
return;
|
||||
for (int i = 0; i < _numChannels; ++i)
|
||||
chanOff(i);
|
||||
_trackData = 0;
|
||||
}
|
||||
|
||||
void PCSpeakerDriver::setMusicVolume(uint8 volume) {
|
||||
Common::StackLock lock(_mutex);
|
||||
_masterVolume = volume >> 2;
|
||||
}
|
||||
|
||||
void PCSpeakerDriver::update() {
|
||||
Common::StackLock lock(_mutex);
|
||||
if (!_ready)
|
||||
return;
|
||||
|
||||
if (_newTrackData) {
|
||||
_trackData = _newTrackData;
|
||||
_newTrackData = 0;
|
||||
|
||||
for (int i = _numChannels - 1; i >= 0; --i) {
|
||||
_channels[i]->dataPtr = _trackData;
|
||||
_channels[i]->timer = i * 35;
|
||||
_channels[i]->timerScale = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = _numChannels - 1; i >= 0; --i) {
|
||||
const uint8 *pos = _channels[i]->dataPtr;
|
||||
if (!pos)
|
||||
continue;
|
||||
|
||||
for (bool runloop = true; runloop; ) {
|
||||
if (--_channels[i]->timer > -1)
|
||||
break;
|
||||
_channels[i]->timer = 0;
|
||||
|
||||
int8 cmd = (int8)*pos++;
|
||||
if (cmd >= 0) {
|
||||
if (cmd > 95)
|
||||
cmd = 0;
|
||||
|
||||
noteOn(i, _periodsTable[cmd]);
|
||||
uint8 nextTimer = 1 + *pos++;
|
||||
_channels[i]->timer = _channels[i]->timerScale * nextTimer;
|
||||
|
||||
} else {
|
||||
switch (cmd) {
|
||||
case -23: {
|
||||
uint16 ts = _channels[i]->timerScale + *pos++;
|
||||
_channels[i]->timerScale = (uint8)MIN<uint16>(ts, 0xFF);
|
||||
} break;
|
||||
|
||||
case -24: {
|
||||
int16 ts = _channels[i]->timerScale - *pos++;
|
||||
_channels[i]->timerScale = (uint8)MAX<int16>(ts, 1);
|
||||
} break;
|
||||
|
||||
case -26: {
|
||||
uint16 prd = _clock / READ_LE_UINT16(pos);
|
||||
if (_pcJR && prd >= 0x400)
|
||||
prd = 0x3FF;
|
||||
pos += 2;
|
||||
noteOn(i, prd);
|
||||
uint8 nextTimer = 1 + *pos++;
|
||||
_channels[i]->timer = _channels[i]->timerScale * nextTimer;
|
||||
} break;
|
||||
|
||||
case -30: {
|
||||
_channels[i]->timerScale = *pos++;
|
||||
if (!_channels[i]->timerScale)
|
||||
_channels[i]->timerScale = 1;
|
||||
} break;
|
||||
|
||||
case -46: {
|
||||
if (--_channels[i]->repeatCounter2)
|
||||
pos -= *pos;
|
||||
else
|
||||
pos += 2;
|
||||
} break;
|
||||
|
||||
case -47: {
|
||||
_channels[i]->repeatCounter2 = *pos++;
|
||||
if (!_channels[i]->repeatCounter2)
|
||||
_channels[i]->repeatCounter2 = 1;
|
||||
} break;
|
||||
|
||||
case -50: {
|
||||
if (--_channels[i]->repeatCounter1)
|
||||
pos -= *pos;
|
||||
else
|
||||
pos += 2;
|
||||
} break;
|
||||
|
||||
case -51: {
|
||||
_channels[i]->repeatCounter1 = *pos++;
|
||||
if (!_channels[i]->repeatCounter1)
|
||||
_channels[i]->repeatCounter1 = 1;
|
||||
} break;
|
||||
|
||||
default:
|
||||
chanOff(i);
|
||||
pos = 0;
|
||||
runloop = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_channels[i]->dataPtr = pos;
|
||||
}
|
||||
}
|
||||
|
||||
int PCSpeakerDriver::readBuffer(int16 *buffer, const int numSamples) {
|
||||
Common::StackLock lock(_mutex);
|
||||
if (!_ready)
|
||||
return 0;
|
||||
|
||||
int render = 0;
|
||||
|
||||
for (int samplesLeft = numSamples; samplesLeft; samplesLeft -= render) {
|
||||
if (_samplesUpdateTmr <= 0) {
|
||||
_samplesUpdateTmr += _samplesUpdateIntv;
|
||||
update();
|
||||
}
|
||||
|
||||
_samplesUpdateTmrRem += _samplesUpdateIntvRem;
|
||||
while (_samplesUpdateTmrRem >= _updateRate) {
|
||||
_samplesUpdateTmr++;
|
||||
_samplesUpdateTmrRem -= _updateRate;
|
||||
}
|
||||
|
||||
render = MIN<int>(_samplesUpdateTmr, samplesLeft);
|
||||
_samplesUpdateTmr -= render;
|
||||
|
||||
generateSamples(buffer, render);
|
||||
buffer += render;
|
||||
}
|
||||
|
||||
return numSamples;
|
||||
}
|
||||
|
||||
void PCSpeakerDriver::noteOn(int chan, uint16 period) {
|
||||
if (chan >= _numChannels)
|
||||
return;
|
||||
|
||||
if (period == 0) {
|
||||
chanOff(chan);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 p = (_outputRate << 10) / ((_clock << 10) / period);
|
||||
if (_channels[chan]->period == -1 || _channels[chan]->samplesLeft == 0)
|
||||
_channels[chan]->samplesLeft = p / _periodDiv;
|
||||
_channels[chan]->period = p & 0xFFFF;
|
||||
}
|
||||
|
||||
void PCSpeakerDriver::chanOff(int chan) {
|
||||
if (chan >= _numChannels)
|
||||
return;
|
||||
_channels[chan]->period = -1;
|
||||
}
|
||||
|
||||
void PCSpeakerDriver::generateSamples(int16 *buffer, int numSamples) {
|
||||
int render = 0;
|
||||
for (int samplesLeft = numSamples; samplesLeft; samplesLeft -= render) {
|
||||
render = samplesLeft;
|
||||
|
||||
for (int i = _numChannels - 1; i >= 0; --i)
|
||||
if (_channels[i]->period != -1)
|
||||
render = MIN<int>(render, _channels[i]->samplesLeft);
|
||||
|
||||
int32 smp = 0;
|
||||
for (int i = _numChannels - 1; i >= 0; --i)
|
||||
if (_channels[i]->period != -1)
|
||||
smp += _channels[i]->curSample;
|
||||
smp = (smp * _masterVolume) >> (8 + _levelAdjust);
|
||||
|
||||
Common::fill<int16*, int16>(buffer, &buffer[render], smp);
|
||||
buffer += render;
|
||||
|
||||
for (int i = _numChannels - 1; i >= 0; --i) {
|
||||
if (_channels[i]->period == -1)
|
||||
continue;
|
||||
|
||||
_channels[i]->samplesLeft -= render;
|
||||
if (_channels[i]->samplesLeft == 0) {
|
||||
_channels[i]->samplesLeft = _channels[i]->period / _periodDiv;
|
||||
_channels[i]->curSample = ~_channels[i]->curSample;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uint16 PCSpeakerDriver::_periodsPCSpk[96] = {
|
||||
0x0000, 0xfdff, 0xefa2, 0xe241, 0xd582, 0xc998, 0xbe3d, 0xb38a,
|
||||
0xa97c, 0x9ff2, 0x96fc, 0x8e89, 0x8683, 0x7ef7, 0x77d9, 0x7121,
|
||||
0x6ac7, 0x64c6, 0x5f1f, 0x59ca, 0x54be, 0x4ffd, 0x4b7e, 0x4742,
|
||||
0x4342, 0x3f7b, 0x3bdb, 0x388f, 0x3562, 0x3263, 0x2f8f, 0x2ce4,
|
||||
0x2a5f, 0x27fe, 0x25c0, 0x23a1, 0x21a1, 0x1fbe, 0x1df6, 0x1c48,
|
||||
0x1ab1, 0x1932, 0x17c8, 0x1672, 0x1530, 0x13ff, 0x12e0, 0x11d1,
|
||||
0x10d1, 0x0fdf, 0x0efb, 0x0e24, 0x0d59, 0x0c99, 0x0be4, 0x0b39,
|
||||
0x0a98, 0x0a00, 0x0970, 0x08e8, 0x0868, 0x07f0, 0x077e, 0x0712,
|
||||
0x06ac, 0x064c, 0x05f2, 0x059d, 0x054c, 0x0500, 0x04b8, 0x0474,
|
||||
0x0434, 0x03f8, 0x03bf, 0x0382, 0x0356, 0x0326, 0x02f9, 0x02ce,
|
||||
0x02a6, 0x0280, 0x025c, 0x023a, 0x021a, 0x01fc, 0x01df, 0x01c4,
|
||||
0x01ab, 0x0193, 0x017c, 0x0167, 0x0153, 0x0140, 0x012e, 0x011d
|
||||
};
|
||||
|
||||
const uint16 PCSpeakerDriver::_periodsPCjr[96] = {
|
||||
0x0000, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff,
|
||||
0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff,
|
||||
0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff,
|
||||
0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff,
|
||||
0x03f9, 0x03c0, 0x038a, 0x0357, 0x0327, 0x02fa, 0x02cf, 0x02a7,
|
||||
0x0281, 0x025d, 0x023b, 0x021b, 0x01fc, 0x01e0, 0x01c5, 0x01ac,
|
||||
0x0194, 0x017d, 0x0168, 0x0153, 0x0140, 0x012e, 0x011d, 0x010d,
|
||||
0x00fe, 0x00f0, 0x00e2, 0x00d6, 0x00ca, 0x00be, 0x00b4, 0x00aa,
|
||||
0x00a0, 0x0097, 0x008f, 0x0087, 0x007f, 0x0078, 0x0071, 0x006b,
|
||||
0x0065, 0x005f, 0x005a, 0x0054, 0x0050, 0x004c, 0x0047, 0x0043,
|
||||
0x0040, 0x003c, 0x0039, 0x0035, 0x0032, 0x0030, 0x002d, 0x002a,
|
||||
0x0028, 0x0026, 0x0024, 0x0022, 0x0020, 0x001e, 0x001c, 0x001b
|
||||
};
|
||||
|
||||
PCSoundDriver *PCSoundDriver::createPCSpk(Audio::Mixer *mixer, bool pcJRMode) {
|
||||
return new PCSpeakerDriver(mixer, pcJRMode);
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
362
engines/kyra/sound/drivers/pcspeaker_v2.cpp
Normal file
362
engines/kyra/sound/drivers/pcspeaker_v2.cpp
Normal file
@@ -0,0 +1,362 @@
|
||||
/* 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 "kyra/sound/sound_intern.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/softsynth/pcspk.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
MidiDriver_PCSpeaker::MidiDriver_PCSpeaker(Audio::Mixer *mixer)
|
||||
: MidiDriver_Emulated(mixer), _rate(mixer->getOutputRate()) {
|
||||
_timerValue = 0;
|
||||
memset(_channel, 0, sizeof(_channel));
|
||||
memset(_note, 0, sizeof(_note));
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
_note[i].hardwareChannel = 0xFF;
|
||||
|
||||
_speaker = new Audio::PCSpeakerStream(_rate);
|
||||
assert(_speaker);
|
||||
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
|
||||
|
||||
_countdown = 0xFFFF;
|
||||
_hardwareChannel[0] = 0xFF;
|
||||
_modulationFlag = false;
|
||||
}
|
||||
|
||||
MidiDriver_PCSpeaker::~MidiDriver_PCSpeaker() {
|
||||
_mixer->stopHandle(_mixerSoundHandle);
|
||||
delete _speaker;
|
||||
_speaker = nullptr;
|
||||
}
|
||||
|
||||
void MidiDriver_PCSpeaker::send(uint32 data) {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
uint8 channel = data & 0x0F;
|
||||
uint8 param1 = (data >> 8) & 0xFF;
|
||||
uint8 param2 = (data >> 16) & 0xFF;
|
||||
|
||||
uint8 flags = 0x00;
|
||||
|
||||
if (channel > 1)
|
||||
return;
|
||||
|
||||
switch (data & 0xF0) {
|
||||
case 0x80: // note off
|
||||
noteOff(channel, param1);
|
||||
return;
|
||||
|
||||
case 0x90: // note on
|
||||
if (param2)
|
||||
noteOn(channel, param1);
|
||||
else
|
||||
noteOff(channel, param1);
|
||||
return;
|
||||
|
||||
case 0xB0: // controller
|
||||
switch (param1) {
|
||||
case 0x01: // modulation
|
||||
_channel[channel].modulation = param2;
|
||||
break;
|
||||
|
||||
case 0x40: // hold
|
||||
_channel[channel].hold = param2;
|
||||
if (param2 < 0x40)
|
||||
resetController(channel);
|
||||
return;
|
||||
|
||||
case 0x70: // voice protect
|
||||
_channel[channel].voiceProtect = param2;
|
||||
return;
|
||||
|
||||
case 0x79: // all notes off
|
||||
_channel[channel].hold = 0;
|
||||
resetController(channel);
|
||||
_channel[channel].modulation = 0;
|
||||
_channel[channel].pitchBendLow = 0;
|
||||
_channel[channel].pitchBendHigh = 0x40;
|
||||
flags = 0x01;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE0: // pitch bend
|
||||
flags = 0x01;
|
||||
_channel[channel].pitchBendLow = param1;
|
||||
_channel[channel].pitchBendHigh = param2;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (_note[i].enabled && _note[i].midiChannel == channel) {
|
||||
_note[i].flags |= flags;
|
||||
setupTone(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_PCSpeaker::resetController(int channel) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (_note[i].enabled && _note[i].midiChannel == channel && _note[i].processHold)
|
||||
noteOff(channel, _note[i].note);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_PCSpeaker::noteOn(int channel, int note) {
|
||||
int n = 0;
|
||||
|
||||
while (n < 2 && _note[n].enabled)
|
||||
++n;
|
||||
|
||||
if (n >= 2)
|
||||
return;
|
||||
|
||||
_note[n].midiChannel = channel;
|
||||
_note[n].note = note;
|
||||
_note[n].enabled = true;
|
||||
_note[n].processHold = false;
|
||||
_note[n].hardwareFlags = 0x20;
|
||||
_note[n].priority = 0x7FFF;
|
||||
_note[n].flags = 0x01;
|
||||
|
||||
turnNoteOn(n);
|
||||
}
|
||||
|
||||
void MidiDriver_PCSpeaker::turnNoteOn(int note) {
|
||||
if (_hardwareChannel[0] == 0xFF) {
|
||||
_note[note].hardwareChannel = 0;
|
||||
++_channel[_note[note].midiChannel].noteCount;
|
||||
_hardwareChannel[0] = _note[note].midiChannel;
|
||||
_note[note].flags = 0x01;
|
||||
|
||||
setupTone(note);
|
||||
} else {
|
||||
overwriteNote(note);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_PCSpeaker::overwriteNote(int note) {
|
||||
int totalNotes = 0;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (_note[i].enabled) {
|
||||
++totalNotes;
|
||||
const int channel = _note[i].midiChannel;
|
||||
|
||||
uint16 priority = 0xFFFF;
|
||||
if (_channel[channel].voiceProtect < 0x40)
|
||||
priority = _note[i].priority;
|
||||
|
||||
if (_channel[channel].noteCount > priority)
|
||||
priority = 0;
|
||||
else
|
||||
priority -= _channel[channel].noteCount;
|
||||
|
||||
_note[i].precedence = priority;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalNotes <= 1)
|
||||
return;
|
||||
|
||||
do {
|
||||
uint16 maxValue = 0;
|
||||
uint16 minValue = 0xFFFF;
|
||||
int newNote = 0;
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (_note[i].enabled) {
|
||||
if (_note[i].hardwareChannel == 0xFF) {
|
||||
if (_note[i].precedence >= maxValue) {
|
||||
maxValue = _note[i].precedence;
|
||||
newNote = i;
|
||||
}
|
||||
} else {
|
||||
if (_note[i].precedence <= minValue) {
|
||||
minValue = _note[i].precedence;
|
||||
note = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxValue < minValue)
|
||||
return;
|
||||
|
||||
turnNoteOff(_note[note].hardwareChannel);
|
||||
_note[note].enabled = false;
|
||||
|
||||
_note[newNote].hardwareChannel = _note[note].hardwareChannel;
|
||||
++_channel[_note[newNote].midiChannel].noteCount;
|
||||
_hardwareChannel[_note[note].hardwareChannel] = _note[newNote].midiChannel;
|
||||
_note[newNote].flags = 0x01;
|
||||
|
||||
setupTone(newNote);
|
||||
} while (--totalNotes);
|
||||
}
|
||||
|
||||
void MidiDriver_PCSpeaker::noteOff(int channel, int note) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (_note[i].enabled && _note[i].note == note && _note[i].midiChannel == channel) {
|
||||
if (_channel[i].hold < 0x40) {
|
||||
turnNoteOff(i);
|
||||
_note[i].enabled = false;
|
||||
} else {
|
||||
_note[i].processHold = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_PCSpeaker::turnNoteOff(int note) {
|
||||
if (_note[note].hardwareChannel != 0xFF) {
|
||||
_note[note].hardwareFlags &= 0xDF;
|
||||
_note[note].flags |= 1;
|
||||
|
||||
setupTone(note);
|
||||
|
||||
--_channel[_note[note].midiChannel].noteCount;
|
||||
|
||||
_hardwareChannel[_note[note].hardwareChannel] = 0xFF;
|
||||
_note[note].hardwareChannel = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_PCSpeaker::setupTone(int note) {
|
||||
if (_note[note].hardwareChannel == 0xFF)
|
||||
return;
|
||||
|
||||
if (!(_note[note].flags & 0x01))
|
||||
return;
|
||||
|
||||
if (!(_note[note].hardwareFlags & 0x20)) {
|
||||
_speaker->stop();
|
||||
} else {
|
||||
const int midiChannel = _note[note].midiChannel;
|
||||
uint16 pitchBend = (_channel[midiChannel].pitchBendHigh << 7) | _channel[midiChannel].pitchBendLow;
|
||||
|
||||
int noteValue = _note[note].note;
|
||||
|
||||
noteValue -= 24;
|
||||
do {
|
||||
noteValue += 12;
|
||||
} while (noteValue < 0);
|
||||
|
||||
noteValue += 12;
|
||||
do {
|
||||
noteValue -= 12;
|
||||
} while (noteValue > 95);
|
||||
|
||||
int16 modulation = _note[note].modulation;
|
||||
|
||||
int tableIndex = MAX(noteValue - 12, 0);
|
||||
uint16 note1 = (_noteTable2[tableIndex] << 8) | _noteTable1[tableIndex];
|
||||
tableIndex = MIN(noteValue + 12, 95);
|
||||
uint16 note2 = (_noteTable2[tableIndex] << 8) | _noteTable1[tableIndex];
|
||||
uint16 note3 = (_noteTable2[noteValue] << 8) | _noteTable1[noteValue];
|
||||
|
||||
int32 countdown = pitchBend - 0x2000;
|
||||
countdown += modulation;
|
||||
|
||||
if (countdown >= 0)
|
||||
countdown *= (note2 - note3);
|
||||
else
|
||||
countdown *= (note3 - note1);
|
||||
|
||||
countdown /= 0x2000;
|
||||
countdown += note3;
|
||||
|
||||
countdown = uint16(countdown & 0xFFFF);
|
||||
if (countdown != _countdown)
|
||||
_countdown = countdown;
|
||||
|
||||
_speaker->play(Audio::PCSpeaker::kWaveFormSquare, 1193180 / _countdown, -1);
|
||||
}
|
||||
|
||||
_note[note].flags &= 0xFE;
|
||||
}
|
||||
|
||||
void MidiDriver_PCSpeaker::generateSamples(int16 *buffer, int numSamples) {
|
||||
Common::StackLock lock(_mutex);
|
||||
_speaker->readBuffer(buffer, numSamples);
|
||||
}
|
||||
|
||||
void MidiDriver_PCSpeaker::onTimer() {
|
||||
/*Common::StackLock lock(_mutex);
|
||||
|
||||
_timerValue += 20;
|
||||
if (_timerValue < 120)
|
||||
return;
|
||||
_timerValue -= 120;
|
||||
|
||||
_modulationFlag = !_modulationFlag;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (_note[i].enabled) {
|
||||
uint16 modValue = 5 * _channel[_note[i].midiChannel].modulation;
|
||||
if (_modulationFlag)
|
||||
modValue = -modValue;
|
||||
_note[i].modulation = modValue;
|
||||
_note[i].flags |= 1;
|
||||
|
||||
setupTone(i);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
const uint8 MidiDriver_PCSpeaker::_noteTable1[] = {
|
||||
0x88, 0xB5, 0x4E, 0x40, 0x41, 0xCD, 0xC4, 0x3D,
|
||||
0x43, 0x7C, 0x2A, 0xD6, 0x88, 0xB5, 0xFF, 0xD1,
|
||||
0x20, 0xA7, 0xE2, 0x1E, 0xCE, 0xBE, 0xF2, 0x8A,
|
||||
0x44, 0x41, 0x7F, 0xE8, 0x90, 0x63, 0x63, 0x8F,
|
||||
0xE7, 0x5F, 0x01, 0xBD, 0xA2, 0xA0, 0xBF, 0xF4,
|
||||
0x48, 0xB1, 0x31, 0xC7, 0x70, 0x2F, 0xFE, 0xE0,
|
||||
0xD1, 0xD0, 0xDE, 0xFB, 0x24, 0x58, 0x98, 0xE3,
|
||||
0x39, 0x97, 0xFF, 0x6F, 0xE8, 0x68, 0xEF, 0x7D,
|
||||
0x11, 0xAC, 0x4C, 0xF1, 0x9C, 0x4B, 0xFF, 0xB7,
|
||||
0x74, 0x34, 0xF7, 0xBE, 0x88, 0x56, 0x26, 0xF8,
|
||||
0xCE, 0xA5, 0x7F, 0x5B, 0x3A, 0x1A, 0xFB, 0xDF,
|
||||
0xC4, 0xAB, 0x93, 0x7C, 0x67, 0x52, 0x3F, 0x2D
|
||||
};
|
||||
|
||||
const uint8 MidiDriver_PCSpeaker::_noteTable2[] = {
|
||||
0x8E, 0x86, 0xFD, 0xF0, 0xE2, 0xD5, 0xC9, 0xBE,
|
||||
0xB3, 0xA9, 0xA0, 0x96, 0x8E, 0x86, 0x7E, 0x77,
|
||||
0x71, 0x6A, 0x64, 0x5F, 0x59, 0x54, 0x4F, 0x4B,
|
||||
0x47, 0x43, 0x3F, 0x3B, 0x38, 0x35, 0x32, 0x2F,
|
||||
0x2C, 0x2A, 0x28, 0x25, 0x23, 0x21, 0x1F, 0x1D,
|
||||
0x1C, 0x1A, 0x19, 0x17, 0x16, 0x15, 0x13, 0x12,
|
||||
0x11, 0x10, 0x0F, 0x0E, 0x0E, 0x0D, 0x0C, 0x0B,
|
||||
0x0B, 0x0A, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07,
|
||||
0x07, 0x06, 0x06, 0x05, 0x05, 0x05, 0x04, 0x04,
|
||||
0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
1076
engines/kyra/sound/drivers/segacd.cpp
Normal file
1076
engines/kyra/sound/drivers/segacd.cpp
Normal file
File diff suppressed because it is too large
Load Diff
64
engines/kyra/sound/drivers/segacd.h
Normal file
64
engines/kyra/sound/drivers/segacd.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#ifndef KYRA_SOUND_SEGACD_H
|
||||
#define KYRA_SOUND_SEGACD_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Audio {
|
||||
class Mixer;
|
||||
}
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class SegaAudioDriverInternal;
|
||||
|
||||
class SegaAudioDriver {
|
||||
public:
|
||||
SegaAudioDriver(Audio::Mixer *mixer);
|
||||
~SegaAudioDriver();
|
||||
|
||||
enum PrioFlags {
|
||||
kPrioHigh = 0x10,
|
||||
kPrioLow = 0x20
|
||||
};
|
||||
|
||||
void startFMSound(const uint8 *trackData, uint8 volume, PrioFlags prioFlags);
|
||||
|
||||
void loadPCMData(uint16 address, const uint8 *data, uint16 dataLen);
|
||||
void startPCMSound(uint8 channel, uint8 dataStart, uint16 loopStart = 0xFF00, uint16 rate = 0x300, uint8 pan = 0xFF, uint8 vol = 0xFF);
|
||||
|
||||
void setMusicVolume(int volume);
|
||||
void setSoundEffectVolume(int volume);
|
||||
|
||||
private:
|
||||
SegaAudioDriverInternal *_drv;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
367
engines/kyra/sound/sound.cpp
Normal file
367
engines/kyra/sound/sound.cpp
Normal file
@@ -0,0 +1,367 @@
|
||||
/* 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 "kyra/sound/sound.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#include "audio/decoders/flac.h"
|
||||
#include "audio/decoders/mp3.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "audio/decoders/voc.h"
|
||||
#include "audio/decoders/vorbis.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
Sound::Sound(KyraEngine_v1 *vm, Audio::Mixer *mixer)
|
||||
: _vm(vm), _mixer(mixer), _soundChannels(), _musicEnabled(1),
|
||||
_sfxEnabled(true) {
|
||||
}
|
||||
|
||||
Sound::~Sound() {
|
||||
}
|
||||
|
||||
Sound::kType Sound::getSfxType() const {
|
||||
return getMusicType();
|
||||
}
|
||||
|
||||
bool Sound::isPlaying() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sound::isVoicePresent(const char *file) const {
|
||||
Common::String filename;
|
||||
|
||||
for (int i = 0; _supportedCodecs[i].fileext; ++i) {
|
||||
filename = file;
|
||||
filename += _supportedCodecs[i].fileext;
|
||||
|
||||
if (_vm->resource()->exists(filename.c_str()))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 Sound::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) {
|
||||
Audio::SeekableAudioStream *audioStream = getVoiceStream(file);
|
||||
|
||||
if (!audioStream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int playTime = audioStream->getLength().msecs();
|
||||
playVoiceStream(audioStream, handle, volume, priority, isSfx);
|
||||
return playTime;
|
||||
}
|
||||
|
||||
Audio::SeekableAudioStream *Sound::getVoiceStream(const char *file) const {
|
||||
Common::Path filename;
|
||||
|
||||
Audio::SeekableAudioStream *audioStream = nullptr;
|
||||
for (int i = 0; _supportedCodecs[i].fileext; ++i) {
|
||||
filename = file;
|
||||
filename.appendInPlace(_supportedCodecs[i].fileext);
|
||||
|
||||
Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
|
||||
if (!stream)
|
||||
continue;
|
||||
|
||||
audioStream = _supportedCodecs[i].streamFunc(stream, DisposeAfterUse::YES);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!audioStream) {
|
||||
warning("Couldn't load sound file '%s'", file);
|
||||
return nullptr;
|
||||
} else {
|
||||
return audioStream;
|
||||
}
|
||||
}
|
||||
|
||||
bool Sound::playVoiceStream(Audio::AudioStream *stream, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) {
|
||||
int h = 0;
|
||||
while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h].handle))
|
||||
++h;
|
||||
|
||||
if (h >= kNumChannelHandles) {
|
||||
h = 0;
|
||||
while (h < kNumChannelHandles && _soundChannels[h].priority > priority)
|
||||
++h;
|
||||
if (h < kNumChannelHandles)
|
||||
voiceStop(&_soundChannels[h].handle);
|
||||
}
|
||||
|
||||
if (h >= kNumChannelHandles) {
|
||||
// When we run out of handles we need to destroy the stream object,
|
||||
// this is to avoid memory leaks in some scenes where too many sfx
|
||||
// are started.
|
||||
// See bug #5886 "LOL-CD: Memory leak in caves level 3".
|
||||
delete stream;
|
||||
return false;
|
||||
}
|
||||
|
||||
_mixer->playStream(isSfx ? Audio::Mixer::kSFXSoundType : Audio::Mixer::kSpeechSoundType, &_soundChannels[h].handle, stream, -1, volume);
|
||||
_soundChannels[h].priority = priority;
|
||||
if (handle)
|
||||
*handle = _soundChannels[h].handle;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sound::voiceStop(const Audio::SoundHandle *handle) {
|
||||
if (!handle) {
|
||||
for (int h = 0; h < kNumChannelHandles; ++h) {
|
||||
if (_mixer->isSoundHandleActive(_soundChannels[h].handle))
|
||||
_mixer->stopHandle(_soundChannels[h].handle);
|
||||
}
|
||||
} else {
|
||||
_mixer->stopHandle(*handle);
|
||||
}
|
||||
}
|
||||
|
||||
bool Sound::voiceIsPlaying(const Audio::SoundHandle *handle) const {
|
||||
if (!handle) {
|
||||
for (int h = 0; h < kNumChannelHandles; ++h) {
|
||||
if (_mixer->isSoundHandleActive(_soundChannels[h].handle))
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return _mixer->isSoundHandleActive(*handle);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sound::allVoiceChannelsPlaying() const {
|
||||
for (int i = 0; i < kNumChannelHandles; ++i)
|
||||
if (!_mixer->isSoundHandleActive(_soundChannels[i].handle))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
MixedSoundDriver::MixedSoundDriver(KyraEngine_v1 *vm, Audio::Mixer *mixer, Sound *music, Sound *sfx)
|
||||
: Sound(vm, mixer), _music(music), _sfx(sfx) {
|
||||
}
|
||||
|
||||
MixedSoundDriver::~MixedSoundDriver() {
|
||||
delete _music;
|
||||
delete _sfx;
|
||||
}
|
||||
|
||||
Sound::kType MixedSoundDriver::getMusicType() const {
|
||||
return _music->getMusicType();
|
||||
}
|
||||
|
||||
Sound::kType MixedSoundDriver::getSfxType() const {
|
||||
return _sfx->getSfxType();
|
||||
}
|
||||
|
||||
bool MixedSoundDriver::init() {
|
||||
return (_music->init() && _sfx->init());
|
||||
}
|
||||
|
||||
void MixedSoundDriver::process() {
|
||||
_music->process();
|
||||
_sfx->process();
|
||||
}
|
||||
|
||||
void MixedSoundDriver::updateVolumeSettings() {
|
||||
_music->updateVolumeSettings();
|
||||
_sfx->updateVolumeSettings();
|
||||
}
|
||||
|
||||
void MixedSoundDriver::initAudioResourceInfo(int set, void *info) {
|
||||
_music->initAudioResourceInfo(set, info);
|
||||
_sfx->initAudioResourceInfo(set, info);
|
||||
}
|
||||
|
||||
void MixedSoundDriver::selectAudioResourceSet(int set) {
|
||||
_music->selectAudioResourceSet(set);
|
||||
_sfx->selectAudioResourceSet(set);
|
||||
}
|
||||
|
||||
bool MixedSoundDriver::hasSoundFile(uint file) const {
|
||||
return _music->hasSoundFile(file) && _sfx->hasSoundFile(file);
|
||||
}
|
||||
|
||||
void MixedSoundDriver::loadSoundFile(uint file) {
|
||||
_music->loadSoundFile(file);
|
||||
_sfx->loadSoundFile(file);
|
||||
}
|
||||
|
||||
void MixedSoundDriver::loadSoundFile(const Common::Path &file) {
|
||||
_music->loadSoundFile(file);
|
||||
_sfx->loadSoundFile(file);
|
||||
}
|
||||
|
||||
void MixedSoundDriver::loadSfxFile(const Common::Path &file) {
|
||||
_sfx->loadSoundFile(file);
|
||||
}
|
||||
|
||||
void MixedSoundDriver::playTrack(uint8 track) {
|
||||
_music->playTrack(track);
|
||||
}
|
||||
|
||||
void MixedSoundDriver::haltTrack() {
|
||||
_music->haltTrack();
|
||||
}
|
||||
|
||||
bool MixedSoundDriver::isPlaying() const {
|
||||
return _music->isPlaying() || _sfx->isPlaying();
|
||||
}
|
||||
|
||||
void MixedSoundDriver::playSoundEffect(uint16 track, uint8 volume) {
|
||||
_sfx->playSoundEffect(track, volume);
|
||||
}
|
||||
|
||||
void MixedSoundDriver::stopAllSoundEffects() {
|
||||
_sfx->stopAllSoundEffects();
|
||||
}
|
||||
|
||||
void MixedSoundDriver::beginFadeOut() {
|
||||
_music->beginFadeOut();
|
||||
}
|
||||
|
||||
void MixedSoundDriver::pause(bool paused) {
|
||||
_music->pause(paused);
|
||||
_sfx->pause(paused);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
void KyraEngine_v1::snd_playTheme(int file, int track) {
|
||||
if (_curMusicTheme == file)
|
||||
return;
|
||||
|
||||
_curSfxFile = _curMusicTheme = file;
|
||||
_sound->loadSoundFile(_curMusicTheme);
|
||||
|
||||
// Kyrandia 2 uses a special file for
|
||||
// MIDI sound effects, so we load
|
||||
// this here
|
||||
if (_flags.gameID == GI_KYRA2)
|
||||
_sound->loadSfxFile("K2SFX");
|
||||
|
||||
if (track != -1)
|
||||
_sound->playTrack(track);
|
||||
}
|
||||
|
||||
void KyraEngine_v1::snd_playSoundEffect(int track, int volume) {
|
||||
_sound->playSoundEffect(track, volume);
|
||||
}
|
||||
|
||||
void KyraEngine_v1::snd_playWanderScoreViaMap(int command, int restart) {
|
||||
if (restart)
|
||||
_lastMusicCommand = -1;
|
||||
|
||||
// no track mapping given
|
||||
// so don't do anything here
|
||||
if (!_trackMap || !_trackMapSize)
|
||||
return;
|
||||
|
||||
if (_flags.platform == Common::kPlatformDOS) {
|
||||
assert(command * 2 + 1 < _trackMapSize);
|
||||
if (_curMusicTheme != _trackMap[command * 2]) {
|
||||
if (_trackMap[command * 2] != -1 && _trackMap[command * 2] != -2)
|
||||
snd_playTheme(_trackMap[command * 2], -1);
|
||||
}
|
||||
|
||||
if (command != 1) {
|
||||
if (_lastMusicCommand != command) {
|
||||
_sound->haltTrack();
|
||||
_sound->playTrack(_trackMap[command * 2 + 1]);
|
||||
}
|
||||
} else {
|
||||
_sound->beginFadeOut();
|
||||
}
|
||||
|
||||
} else if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) {
|
||||
if (command == -1) {
|
||||
_sound->haltTrack();
|
||||
} else {
|
||||
assert(command * 2 + 1 < _trackMapSize);
|
||||
if (_trackMap[command * 2] != -2 && command != _lastMusicCommand) {
|
||||
_sound->haltTrack();
|
||||
_sound->playTrack(command);
|
||||
}
|
||||
}
|
||||
} else if (_flags.platform == Common::kPlatformMacintosh || _flags.platform == Common::kPlatformAmiga) {
|
||||
if (_curMusicTheme != 1)
|
||||
snd_playTheme(1, -1);
|
||||
|
||||
assert(command < _trackMapSize);
|
||||
if (_lastMusicCommand == -1 || _trackMap[_lastMusicCommand] != _trackMap[command])
|
||||
_sound->playTrack(_trackMap[command]);
|
||||
}
|
||||
|
||||
_lastMusicCommand = command;
|
||||
}
|
||||
|
||||
void KyraEngine_v1::snd_stopVoice() {
|
||||
_sound->voiceStop(&_speechHandle);
|
||||
}
|
||||
|
||||
bool KyraEngine_v1::snd_voiceIsPlaying() {
|
||||
return _sound->voiceIsPlaying(&_speechHandle);
|
||||
}
|
||||
|
||||
// static res
|
||||
|
||||
namespace {
|
||||
|
||||
// A simple wrapper to create VOC streams the way like creating MP3, OGG/Vorbis and FLAC streams.
|
||||
// Possible TODO: Think of making this complete and moving it to sound/voc.cpp ?
|
||||
Audio::SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
Audio::SeekableAudioStream *as = Audio::makeVOCStream(stream, Audio::FLAG_UNSIGNED, disposeAfterUse);
|
||||
return as;
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
const Sound::SpeechCodecs Sound::_supportedCodecs[] = {
|
||||
{ ".VOC", makeVOCStream },
|
||||
{ "", makeVOCStream },
|
||||
|
||||
#ifdef USE_MAD
|
||||
{ ".VO3", Audio::makeMP3Stream },
|
||||
{ ".MP3", Audio::makeMP3Stream },
|
||||
#endif // USE_MAD
|
||||
|
||||
#ifdef USE_VORBIS
|
||||
{ ".VOG", Audio::makeVorbisStream },
|
||||
{ ".OGG", Audio::makeVorbisStream },
|
||||
#endif // USE_VORBIS
|
||||
|
||||
#ifdef USE_FLAC
|
||||
{ ".VOF", Audio::makeFLACStream },
|
||||
{ ".FLA", Audio::makeFLACStream },
|
||||
#endif // USE_FLAC
|
||||
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
365
engines/kyra/sound/sound.h
Normal file
365
engines/kyra/sound/sound.h
Normal file
@@ -0,0 +1,365 @@
|
||||
/* 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 KYRA_SOUND_H
|
||||
#define KYRA_SOUND_H
|
||||
|
||||
#include "kyra/kyra_v1.h"
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Audio {
|
||||
class AudioStream;
|
||||
class SeekableAudioStream;
|
||||
} // End of namespace Audio
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
// Helper structs to format the data passed to the various initAudioResourceInfo() implementations
|
||||
struct SoundResourceInfo_PC {
|
||||
SoundResourceInfo_PC(const char *const *files, int numFiles) : fileList(files), fileListSize(numFiles) {}
|
||||
const char *const *fileList;
|
||||
uint fileListSize;
|
||||
};
|
||||
|
||||
struct SoundResourceInfo_Towns {
|
||||
SoundResourceInfo_Towns(const char *const *files, int numFiles, const int32 *cdaTbl, int cdaTblSize) : fileList(files), fileListSize(numFiles), cdaTable(cdaTbl), cdaTableSize(cdaTblSize) {}
|
||||
const char *const *fileList;
|
||||
uint fileListSize;
|
||||
const int32 *cdaTable;
|
||||
uint cdaTableSize;
|
||||
};
|
||||
|
||||
struct SoundResourceInfo_PC98 {
|
||||
SoundResourceInfo_PC98(const char *fileNamePattern) : pattern(fileNamePattern) {}
|
||||
const char *pattern;
|
||||
};
|
||||
|
||||
struct SoundResourceInfo_TownsPC98V2 {
|
||||
SoundResourceInfo_TownsPC98V2(const char *const *files, int numFiles, const char *fileNamePattern, const uint16 *cdaTbl, int cdaTblSize) : fileList(files), fileListSize(numFiles), pattern(fileNamePattern), cdaTable(cdaTbl), cdaTableSize(cdaTblSize) {}
|
||||
const char *const *fileList;
|
||||
uint fileListSize;
|
||||
const char *pattern;
|
||||
const uint16 *cdaTable;
|
||||
uint cdaTableSize;
|
||||
};
|
||||
|
||||
struct SoundResourceInfo_TownsEoB {
|
||||
SoundResourceInfo_TownsEoB(const char *const *filelist, uint numfiles, const uint8 *pcmdata, uint pcmdataSize, int pcmvolume) : fileList(filelist), numFiles(numfiles), pcmData(pcmdata), pcmDataSize(pcmdataSize), pcmVolume(pcmvolume) {}
|
||||
const uint8 *pcmData;
|
||||
uint pcmDataSize;
|
||||
int pcmVolume;
|
||||
const char *const *fileList;
|
||||
uint numFiles;
|
||||
};
|
||||
|
||||
|
||||
struct SoundResourceInfo_AmigaEoB {
|
||||
SoundResourceInfo_AmigaEoB(const char *const *files, int numFiles, const char *const *soundmap, int numSounds) : fileList(files), fileListSize(numFiles), soundList(soundmap), soundListSize(numSounds) {}
|
||||
const char *const *fileList;
|
||||
uint fileListSize;
|
||||
const char *const *soundList;
|
||||
uint soundListSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Analog audio output device API for Kyrandia games.
|
||||
* It contains functionality to play music tracks,
|
||||
* sound effects and voices.
|
||||
*/
|
||||
class Sound {
|
||||
public:
|
||||
Sound(KyraEngine_v1 *vm, Audio::Mixer *mixer);
|
||||
virtual ~Sound();
|
||||
|
||||
enum kType {
|
||||
kAdLib,
|
||||
kMidiMT32,
|
||||
kMidiGM,
|
||||
kTowns,
|
||||
kPC98,
|
||||
kPCSpkr,
|
||||
kPCjr,
|
||||
kAmiga,
|
||||
kSegaCD,
|
||||
kMac
|
||||
};
|
||||
|
||||
virtual kType getMusicType() const = 0;
|
||||
virtual kType getSfxType() const;
|
||||
|
||||
// This is obviously not a real implementation. The original allows disabling digital sfx (or simply not having a suitable sound card),
|
||||
// thus falling back to the music driver's sfx. We never supported this, since we don't even have a setting for it in the launcher.
|
||||
// Currently, the only purpose of this function is fixing KyraEngine_HoF::o2_playFireflyScore() (bug #10877: "Sound issues in the Legend of Kyrandia 2").
|
||||
virtual bool useDigitalSfx() const { return true; }
|
||||
|
||||
/**
|
||||
* Initializes the output device.
|
||||
*
|
||||
* @return true on success, else false
|
||||
*/
|
||||
virtual bool init() = 0;
|
||||
|
||||
/**
|
||||
* Updates the device, this is needed for some devices.
|
||||
*/
|
||||
virtual void process() {}
|
||||
|
||||
/**
|
||||
* Updates internal volume settings according to ConfigManager.
|
||||
*/
|
||||
virtual void updateVolumeSettings() {}
|
||||
|
||||
/**
|
||||
* Assigns static resource data with information on how to load
|
||||
* audio resources to
|
||||
*
|
||||
* @param set value defined in AudioResourceSet enum
|
||||
* info various types of resource info data (file list, file name pattern, struct, etc. - depending on the inheriting driver type)
|
||||
*/
|
||||
virtual void initAudioResourceInfo(int set, void *info) = 0;
|
||||
|
||||
/**
|
||||
* Select audio resource set.
|
||||
*
|
||||
* @param set value defined in AudioResourceSet enum
|
||||
*/
|
||||
virtual void selectAudioResourceSet(int set) = 0;
|
||||
|
||||
/**
|
||||
* Checks if a given sound file is present.
|
||||
*
|
||||
* @param track track number
|
||||
* @return true if available, false otherwise
|
||||
*/
|
||||
virtual bool hasSoundFile(uint file) const = 0;
|
||||
|
||||
/**
|
||||
* Load a specifc sound file for use of
|
||||
* playing music and sound effects.
|
||||
*/
|
||||
virtual void loadSoundFile(uint file) = 0;
|
||||
|
||||
/**
|
||||
* Load a sound file for playing music
|
||||
* (and sometimes sound effects) from.
|
||||
*/
|
||||
virtual void loadSoundFile(const Common::Path &file) = 0;
|
||||
|
||||
/**
|
||||
* Unload a specifc sound file that has been loaded before.
|
||||
*/
|
||||
virtual void unloadSoundFile(const Common::String &file) {}
|
||||
|
||||
/**
|
||||
* Load a sound file for playing sound
|
||||
* effects from.
|
||||
*/
|
||||
virtual void loadSfxFile(const Common::Path &file) {}
|
||||
|
||||
/**
|
||||
* Plays the specified track.
|
||||
*
|
||||
* @param track track number
|
||||
*/
|
||||
virtual void playTrack(uint8 track) = 0;
|
||||
|
||||
/**
|
||||
* Stop playback of the current track.
|
||||
*/
|
||||
virtual void haltTrack() = 0;
|
||||
|
||||
/**
|
||||
* Plays the specified sound effect.
|
||||
*
|
||||
* @param track sound effect id
|
||||
*/
|
||||
virtual void playSoundEffect(uint16 track, uint8 volume = 0xFF) = 0;
|
||||
|
||||
/**
|
||||
* Stop playback of all sfx tracks.
|
||||
*/
|
||||
virtual void stopAllSoundEffects() {}
|
||||
|
||||
/**
|
||||
* Checks if the sound driver plays any sound.
|
||||
*
|
||||
* @return true if playing, false otherwise
|
||||
*/
|
||||
virtual bool isPlaying() const;
|
||||
|
||||
/**
|
||||
* Starts fading out the volume.
|
||||
*
|
||||
* This keeps fading out the output until
|
||||
* it is silenced, but does not change
|
||||
* the volume set by setVolume! It will
|
||||
* automatically reset the volume when
|
||||
* playing a new track or sound effect.
|
||||
*/
|
||||
virtual void beginFadeOut() = 0;
|
||||
virtual void beginFadeOut(int) { beginFadeOut(); }
|
||||
|
||||
/**
|
||||
* Stops all audio playback when paused. Continues after end of pause.
|
||||
*/
|
||||
virtual void pause(bool paused) {}
|
||||
|
||||
virtual void enableMusic(int enable) { _musicEnabled = enable; }
|
||||
int musicEnabled() const { return _musicEnabled; }
|
||||
void enableSFX(bool enable) { _sfxEnabled = enable; }
|
||||
bool sfxEnabled() const { return _sfxEnabled; }
|
||||
|
||||
/**
|
||||
* Checks whether a voice file with the given name is present
|
||||
*
|
||||
* @param file file name
|
||||
* @return true if available, false otherwise
|
||||
*/
|
||||
bool isVoicePresent(const char *file) const;
|
||||
|
||||
/**
|
||||
* Plays the specified voice file.
|
||||
*
|
||||
* Also before starting to play the
|
||||
* specified voice file, it stops the
|
||||
* current voice.
|
||||
*
|
||||
* @param file file to be played
|
||||
* @param volume volume of the voice file
|
||||
* @param isSfx marks file as sfx instead of voice
|
||||
* @param handle store a copy of the sound handle
|
||||
* @return playtime of the voice file (-1 marks unknown playtime)
|
||||
*/
|
||||
virtual int32 voicePlay(const char *file, Audio::SoundHandle *handle = 0, uint8 volume = 255, uint8 priority = 255, bool isSfx = false);
|
||||
|
||||
Audio::SeekableAudioStream *getVoiceStream(const char *file) const;
|
||||
|
||||
bool playVoiceStream(Audio::AudioStream *stream, Audio::SoundHandle *handle = 0, uint8 volume = 255, uint8 priority = 255, bool isSfx = false);
|
||||
|
||||
/**
|
||||
* Checks if a voice is being played.
|
||||
*
|
||||
* @return true when playing, else false
|
||||
*/
|
||||
bool voiceIsPlaying(const Audio::SoundHandle *handle = 0) const;
|
||||
|
||||
/**
|
||||
* Checks if all voice handles are used.
|
||||
*
|
||||
* @return false when a handle is free, else true
|
||||
*/
|
||||
bool allVoiceChannelsPlaying() const;
|
||||
|
||||
/**
|
||||
* Checks how long a voice has been playing
|
||||
*
|
||||
* @return time in milliseconds
|
||||
*/
|
||||
uint32 voicePlayedTime(const Audio::SoundHandle &handle) const {
|
||||
return _mixer->getSoundElapsedTime(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops playback of the current voice.
|
||||
*/
|
||||
void voiceStop(const Audio::SoundHandle *handle = 0);
|
||||
|
||||
/**
|
||||
* Receive notifications from a song at certain points.
|
||||
*/
|
||||
virtual int checkTrigger() { return 0; }
|
||||
|
||||
/**
|
||||
* Reset sound trigger.
|
||||
*/
|
||||
virtual void resetTrigger() {}
|
||||
protected:
|
||||
enum {
|
||||
kNumChannelHandles = 4
|
||||
};
|
||||
|
||||
struct SoundChannel {
|
||||
SoundChannel() : handle(), priority(0) {}
|
||||
Audio::SoundHandle handle;
|
||||
int priority;
|
||||
};
|
||||
|
||||
SoundChannel _soundChannels[kNumChannelHandles];
|
||||
|
||||
int _musicEnabled;
|
||||
bool _sfxEnabled;
|
||||
|
||||
KyraEngine_v1 *_vm;
|
||||
Audio::Mixer *_mixer;
|
||||
|
||||
private:
|
||||
struct SpeechCodecs {
|
||||
const char *fileext;
|
||||
Audio::SeekableAudioStream *(*streamFunc)(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
};
|
||||
|
||||
static const SpeechCodecs _supportedCodecs[];
|
||||
};
|
||||
|
||||
class MixedSoundDriver : public Sound {
|
||||
public:
|
||||
MixedSoundDriver(KyraEngine_v1 *vm, Audio::Mixer *mixer, Sound *music, Sound *sfx);
|
||||
~MixedSoundDriver() override;
|
||||
|
||||
kType getMusicType() const override;
|
||||
kType getSfxType() const override;
|
||||
|
||||
bool init() override;
|
||||
void process() override;
|
||||
|
||||
void updateVolumeSettings() override;
|
||||
|
||||
void initAudioResourceInfo(int set, void *info) override;
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint file) const override;
|
||||
void loadSoundFile(uint file) override;
|
||||
void loadSoundFile(const Common::Path &file) override;
|
||||
|
||||
void loadSfxFile(const Common::Path &file) override;
|
||||
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
bool isPlaying() const override;
|
||||
|
||||
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
|
||||
|
||||
void stopAllSoundEffects() override;
|
||||
|
||||
void beginFadeOut() override;
|
||||
void pause(bool paused) override;
|
||||
private:
|
||||
Sound *_music, *_sfx;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
239
engines/kyra/sound/sound_amiga_eob.cpp
Normal file
239
engines/kyra/sound/sound_amiga_eob.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#include "kyra/sound/sound_intern.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
#include "kyra/sound/drivers/audiomaster2.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/memstream.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
SoundAmiga_EoB::SoundAmiga_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer),
|
||||
_vm(vm), _driver(0), _currentResourceSet(-1), _ready(false) {
|
||||
_fileBuffer = new uint8[64000];
|
||||
memset(_resInfo, 0, sizeof(_resInfo));
|
||||
}
|
||||
|
||||
SoundAmiga_EoB::~SoundAmiga_EoB() {
|
||||
delete _driver;
|
||||
delete[] _fileBuffer;
|
||||
for (int i = 0; i < 3; i++)
|
||||
initAudioResourceInfo(i, 0);
|
||||
}
|
||||
|
||||
Sound::kType SoundAmiga_EoB::getMusicType() const {
|
||||
return kAmiga;
|
||||
}
|
||||
|
||||
bool SoundAmiga_EoB::init() {
|
||||
_driver = new AudioMaster2(_mixer);
|
||||
if (!_driver->init())
|
||||
return false;
|
||||
|
||||
_ready = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundAmiga_EoB::initAudioResourceInfo(int set, void *info) {
|
||||
delete _resInfo[set];
|
||||
_resInfo[set] = info ? new SoundResourceInfo_AmigaEoB(*(SoundResourceInfo_AmigaEoB*)info) : 0;
|
||||
}
|
||||
|
||||
void SoundAmiga_EoB::selectAudioResourceSet(int set) {
|
||||
if (set == _currentResourceSet || !_ready)
|
||||
return;
|
||||
|
||||
_driver->flushAllResources();
|
||||
if (!_resInfo[set])
|
||||
return;
|
||||
|
||||
for (uint i = 0; i < _resInfo[set]->fileListSize; ++i)
|
||||
loadSoundFile(_resInfo[set]->fileList[i]);
|
||||
|
||||
_currentResourceSet = set;
|
||||
}
|
||||
|
||||
void SoundAmiga_EoB::loadSoundFile(const Common::Path &file) {
|
||||
if (!_ready)
|
||||
return;
|
||||
|
||||
Common::SeekableReadStream *in = _vm->resource()->createReadStream(file);
|
||||
debugC(6, kDebugLevelSound, "SoundAmiga_EoB::loadSoundFile(): Attempting to load sound file '%s'...%s", file.toString().c_str(), in ? "SUCCESS" : "FILE NOT FOUND");
|
||||
if (!in)
|
||||
return;
|
||||
|
||||
// This value can deviate up to 5 bytes from the real size in EOB II Amiga.
|
||||
// The original simply tries to read 64000 bytes from the file (ignoring this
|
||||
// value). We do the same.
|
||||
// EOB I strangely always seems to have correct values.
|
||||
uint16 readSize = in->readUint16LE() - 10;
|
||||
uint8 cmp = in->readByte();
|
||||
in->seek(1, SEEK_CUR);
|
||||
uint32 outSize = in->readUint32LE();
|
||||
in->seek(2, SEEK_CUR);
|
||||
|
||||
readSize = in->read(_fileBuffer, 64000);
|
||||
delete in;
|
||||
|
||||
if (cmp == 0 && readSize < outSize)
|
||||
outSize = readSize;
|
||||
|
||||
uint8 *buf = new uint8[outSize];
|
||||
|
||||
if (cmp == 0) {
|
||||
memcpy(buf, _fileBuffer, outSize);
|
||||
} else if (cmp == 3) {
|
||||
Screen::decodeFrame3(_fileBuffer, buf, outSize, true);
|
||||
} else if (cmp == 4) {
|
||||
Screen::decodeFrame4(_fileBuffer, buf, outSize);
|
||||
} else {
|
||||
error("SoundAmiga_EoB::loadSoundFile(): Failed to load sound file '%s'", file.toString().c_str());
|
||||
}
|
||||
|
||||
Common::MemoryReadStream soundFile(buf, outSize);
|
||||
if (!_driver->loadRessourceFile(&soundFile))
|
||||
error("SoundAmiga_EoB::loadSoundFile(): Failed to load sound file '%s'", file.toString().c_str());
|
||||
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
void SoundAmiga_EoB::unloadSoundFile(const Common::String &file) {
|
||||
if (!_ready)
|
||||
return;
|
||||
debugC(5, kDebugLevelSound, "SoundAmiga_EoB::unloadSoundFile(): Attempting to free resource '%s'...%s", file.c_str(), _driver->stopSound(file) ? "SUCCESS" : "FAILURE");
|
||||
_driver->flushResource(file);
|
||||
}
|
||||
|
||||
void SoundAmiga_EoB::playTrack(uint8 track) {
|
||||
if (!_musicEnabled || !_ready)
|
||||
return;
|
||||
|
||||
Common::String newSound;
|
||||
if (_vm->game() == GI_EOB1) {
|
||||
if (_currentResourceSet == kMusicIntro) {
|
||||
if (track == 1)
|
||||
newSound = "NEWINTRO1.SMUS";
|
||||
else if (track == 20)
|
||||
newSound = "CHARGEN1.SMUS";
|
||||
} else if (_currentResourceSet == kMusicFinale) {
|
||||
newSound = "FINALE.SMUS";
|
||||
}
|
||||
} else if (_vm->game() == GI_EOB2) {
|
||||
if (_currentResourceSet == kMusicIntro) {
|
||||
if (track > 11 && track < 16) {
|
||||
const char *const songs[] = { "INTRO1A.SMUS", "CHARGEN3.SMUS", "INTRO1B.SMUS", "INTRO1C.SMUS" };
|
||||
newSound = songs[track - 12];
|
||||
}
|
||||
} else if (_currentResourceSet == kMusicFinale) {
|
||||
if (track > 0 && track < 4) {
|
||||
const char *const songs[] = { "FINALE1B.SMUS", "FINALE1C.SMUS", "FINALE1D.SMUS" };
|
||||
newSound = songs[track - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!newSound.empty() && _ready) {
|
||||
_driver->startSound(newSound);
|
||||
_lastSound = Common::move(newSound);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundAmiga_EoB::haltTrack() {
|
||||
if (!_lastSound.empty())
|
||||
_driver->stopSound(_lastSound);
|
||||
_lastSound.clear();
|
||||
}
|
||||
|
||||
void SoundAmiga_EoB::playSoundEffect(uint16 track, uint8 volume) {
|
||||
if (_currentResourceSet == -1 || !_sfxEnabled || !_ready)
|
||||
return;
|
||||
|
||||
if (_vm->game() == GI_EOB2 && _currentResourceSet == kMusicIntro && track == 14) {
|
||||
_driver->startSound("TELEPORT.SAM");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_resInfo[_currentResourceSet]->soundList || track >= 120 || !_sfxEnabled)
|
||||
return;
|
||||
|
||||
if (_vm->game() == GI_EOB2 && track == 2) {
|
||||
beginFadeOut(60);
|
||||
return;
|
||||
}
|
||||
|
||||
Common::String newSound = _resInfo[_currentResourceSet]->soundList[track];
|
||||
const char *suffix = (_vm->game() == GI_EOB1) ? "1.SAM" : ((track > 51 && track < 68) ? ".SMUS" : ".SAM");
|
||||
|
||||
if (!newSound.empty()) {
|
||||
if (volume == 255) {
|
||||
if (_driver->startSound(newSound + suffix)) {
|
||||
_lastSound = newSound + suffix;
|
||||
return;
|
||||
} else {
|
||||
volume = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (volume > 0 && volume < 5)
|
||||
newSound = Common::String::format("%s%d", newSound.c_str(), volume);
|
||||
|
||||
if (!_driver->startSound(newSound)) {
|
||||
// WORKAROUND for wrongly named resources. This applies to at least 'BLADE' in the EOB II dungeons (instead of 'BLADE1').
|
||||
newSound = _resInfo[_currentResourceSet]->soundList[track];
|
||||
if (_driver->startSound(newSound))
|
||||
debugC(5, kDebugLevelSound, "SoundAmiga_EoB::playSoundEffect(): Triggered workaround for wrongly named resource: '%s'", newSound.c_str());
|
||||
}
|
||||
|
||||
_lastSound = Common::move(newSound);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundAmiga_EoB::beginFadeOut(int delay) {
|
||||
_driver->fadeOut(delay);
|
||||
while (_driver->isFading() && !_vm->shouldQuit())
|
||||
_vm->delay(5);
|
||||
haltTrack();
|
||||
}
|
||||
|
||||
void SoundAmiga_EoB::updateVolumeSettings() {
|
||||
if (!_driver || !_ready)
|
||||
return;
|
||||
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
|
||||
_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
|
||||
}
|
||||
|
||||
int SoundAmiga_EoB::checkTrigger() {
|
||||
return _driver->getPlayDuration();
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
231
engines/kyra/sound/sound_amiga_lok.cpp
Normal file
231
engines/kyra/sound/sound_amiga_lok.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
/* 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 "kyra/sound/sound_intern.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/mods/maxtrax.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
SoundAmiga_LoK::SoundAmiga_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer)
|
||||
: Sound(vm, mixer),
|
||||
_driver(0),
|
||||
_musicHandle(),
|
||||
_fileLoaded(kFileNone),
|
||||
_tableSfxIntro(0),
|
||||
_tableSfxGame(0),
|
||||
_tableSfxIntro_Size(0),
|
||||
_tableSfxGame_Size(0) {
|
||||
}
|
||||
|
||||
SoundAmiga_LoK::~SoundAmiga_LoK() {
|
||||
_mixer->stopHandle(_musicHandle);
|
||||
delete _driver;
|
||||
}
|
||||
|
||||
bool SoundAmiga_LoK::init() {
|
||||
_driver = new Audio::MaxTrax(_mixer->getOutputRate(), true);
|
||||
|
||||
_tableSfxIntro = _vm->staticres()->loadAmigaSfxTable(k1AmigaIntroSFXTable, _tableSfxIntro_Size);
|
||||
_tableSfxGame = _vm->staticres()->loadAmigaSfxTable(k1AmigaGameSFXTable, _tableSfxGame_Size);
|
||||
|
||||
return _driver != 0 && _tableSfxIntro && _tableSfxGame;
|
||||
}
|
||||
|
||||
void SoundAmiga_LoK::initAudioResourceInfo(int set, void *info) {
|
||||
// See comment below
|
||||
}
|
||||
|
||||
void SoundAmiga_LoK::selectAudioResourceSet(int set) {
|
||||
// It seems that loadSoundFile() is doing what would normally be done in here.
|
||||
// As long as this driver is only required for one single target (Kyra 1 Amiga)
|
||||
// this doesn't matter much.
|
||||
}
|
||||
|
||||
bool SoundAmiga_LoK::hasSoundFile(uint file) const {
|
||||
if (file < 3)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoundAmiga_LoK::loadSoundFile(uint file) {
|
||||
debugC(5, kDebugLevelSound, "SoundAmiga_LoK::loadSoundFile(%d)", file);
|
||||
|
||||
static const char *const tableFilenames[3][2] = {
|
||||
{ "introscr.mx", "introinst.mx" },
|
||||
{ "kyramusic.mx", 0 },
|
||||
{ "finalescr.mx", "introinst.mx" }
|
||||
};
|
||||
assert(file < ARRAYSIZE(tableFilenames));
|
||||
if (_fileLoaded == (FileType)file)
|
||||
return;
|
||||
const char *scoreName = tableFilenames[file][0];
|
||||
const char *sampleName = tableFilenames[file][1];
|
||||
bool loaded = false;
|
||||
|
||||
Common::SeekableReadStream *scoreIn = _vm->resource()->createReadStream(scoreName);
|
||||
if (sampleName) {
|
||||
Common::SeekableReadStream *sampleIn = _vm->resource()->createReadStream(sampleName);
|
||||
if (scoreIn && sampleIn) {
|
||||
_fileLoaded = kFileNone;
|
||||
loaded = _driver->load(*scoreIn, true, false);
|
||||
loaded = loaded && _driver->load(*sampleIn, false, true);
|
||||
} else
|
||||
warning("SoundAmiga_LoK: missing atleast one of those music files: %s, %s", scoreName, sampleName);
|
||||
delete sampleIn;
|
||||
} else {
|
||||
if (scoreIn) {
|
||||
_fileLoaded = kFileNone;
|
||||
loaded = _driver->load(*scoreIn);
|
||||
} else
|
||||
warning("SoundAmiga_LoK: missing music file: %s", scoreName);
|
||||
}
|
||||
delete scoreIn;
|
||||
|
||||
if (loaded)
|
||||
_fileLoaded = (FileType)file;
|
||||
}
|
||||
|
||||
void SoundAmiga_LoK::playTrack(uint8 track) {
|
||||
debugC(5, kDebugLevelSound, "SoundAmiga_LoK::playTrack(%d)", track);
|
||||
|
||||
static const byte tempoIntro[] = { 0x46, 0x55, 0x3C, 0x41 };
|
||||
static const byte tempoFinal[] = { 0x78, 0x50 };
|
||||
static const byte tempoIngame[] = {
|
||||
0x64, 0x64, 0x64, 0x64, 0x64, 0x73, 0x4B, 0x64,
|
||||
0x64, 0x64, 0x55, 0x9C, 0x6E, 0x91, 0x78, 0x84,
|
||||
0x32, 0x64, 0x64, 0x6E, 0x3C, 0xD8, 0xAF
|
||||
};
|
||||
static const byte loopIngame[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00
|
||||
};
|
||||
|
||||
int score = -1;
|
||||
bool loop = false;
|
||||
byte volume = 0x40;
|
||||
byte tempo = 0;
|
||||
|
||||
|
||||
switch (_fileLoaded) {
|
||||
case kFileIntro:
|
||||
if (track >= 2 && track < ARRAYSIZE(tempoIntro) + 2) {
|
||||
score = track - 2;
|
||||
tempo = tempoIntro[score];
|
||||
}
|
||||
break;
|
||||
|
||||
case kFileGame:
|
||||
if (track >= 11 && track < ARRAYSIZE(tempoIngame) + 11) {
|
||||
score = track - 11;
|
||||
loop = loopIngame[score] != 0;
|
||||
tempo = tempoIngame[score];
|
||||
}
|
||||
break;
|
||||
|
||||
case kFileFinal:
|
||||
// score 0 gets started immediately after loading the music-files with different tempo.
|
||||
// we need to define a track-value for the fake call of this function
|
||||
if (track >= 2 && track < ARRAYSIZE(tempoFinal) + 2) {
|
||||
score = track - 2;
|
||||
loop = true;
|
||||
tempo = tempoFinal[score];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (score >= 0) {
|
||||
if (_musicEnabled && _driver->playSong(score, loop)) {
|
||||
_driver->setVolume(volume);
|
||||
_driver->setTempo(tempo << 4);
|
||||
if (!_mixer->isSoundHandleActive(_musicHandle))
|
||||
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_musicHandle, _driver, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
|
||||
}
|
||||
} else if (track == 0)
|
||||
_driver->stopMusic();
|
||||
else if (track == 1)
|
||||
beginFadeOut();
|
||||
}
|
||||
|
||||
void SoundAmiga_LoK::haltTrack() {
|
||||
debugC(5, kDebugLevelSound, "SoundAmiga_LoK::haltTrack()");
|
||||
_driver->stopMusic();
|
||||
}
|
||||
|
||||
void SoundAmiga_LoK::beginFadeOut() {
|
||||
debugC(5, kDebugLevelSound, "SoundAmiga_LoK::beginFadeOut()");
|
||||
for (int i = 0x3F; i >= 0; --i) {
|
||||
_driver->setVolume((byte)i);
|
||||
_vm->delay(_vm->tickLength());
|
||||
}
|
||||
|
||||
_driver->stopMusic();
|
||||
_vm->delay(_vm->tickLength());
|
||||
_driver->setVolume(0x40);
|
||||
}
|
||||
|
||||
void SoundAmiga_LoK::playSoundEffect(uint16 track, uint8) {
|
||||
debugC(5, kDebugLevelSound, "SoundAmiga_LoK::playSoundEffect(%d)", track);
|
||||
const AmigaSfxTable *sfx = 0;
|
||||
bool pan = false;
|
||||
|
||||
switch (_fileLoaded) {
|
||||
case kFileFinal:
|
||||
case kFileIntro:
|
||||
// We only allow playing of sound effects, which are included in the table.
|
||||
if (track < _tableSfxIntro_Size) {
|
||||
sfx = &_tableSfxIntro[track];
|
||||
pan = (sfx->pan != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case kFileGame:
|
||||
if (0x61 <= track && track <= 0x63)
|
||||
playTrack(track - 0x4F);
|
||||
|
||||
if (track >= _tableSfxGame_Size)
|
||||
return;
|
||||
|
||||
if (_tableSfxGame[track].note) {
|
||||
sfx = &_tableSfxGame[track];
|
||||
pan = (sfx->pan != 0) && (sfx->pan != 2);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (_sfxEnabled && sfx) {
|
||||
const bool success = _driver->playNote(sfx->note, sfx->patch, sfx->duration, sfx->volume, pan);
|
||||
if (success && !_mixer->isSoundHandleActive(_musicHandle))
|
||||
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_musicHandle, _driver, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
247
engines/kyra/sound/sound_digital_mr.cpp
Normal file
247
engines/kyra/sound/sound_digital_mr.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
/* 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 "kyra/sound/sound_digital_mr.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
#include "kyra/engine/kyra_mr.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/mp3.h"
|
||||
#include "audio/decoders/vorbis.h"
|
||||
#include "audio/decoders/flac.h"
|
||||
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class KyraAudioStream : public Audio::SeekableAudioStream {
|
||||
public:
|
||||
KyraAudioStream(Audio::SeekableAudioStream *impl) : _impl(impl), _rate(impl->getRate()), _fadeSamples(0), _fadeCount(0), _fading(0), _endOfData(false) {}
|
||||
~KyraAudioStream() override { delete _impl; _impl = nullptr; }
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
bool isStereo() const override { return _impl->isStereo(); }
|
||||
bool endOfData() const override { return _impl->endOfData() | _endOfData; }
|
||||
int getRate() const override { return _rate; }
|
||||
|
||||
void setRate(int newRate) { _rate = newRate; }
|
||||
void beginFadeOut(uint32 millis);
|
||||
|
||||
bool seek(const Audio::Timestamp &where) override { return _impl->seek(where); }
|
||||
Audio::Timestamp getLength() const override { return _impl->getLength(); }
|
||||
private:
|
||||
Audio::SeekableAudioStream *_impl;
|
||||
|
||||
int _rate;
|
||||
|
||||
int32 _fadeSamples;
|
||||
int32 _fadeCount;
|
||||
int _fading;
|
||||
|
||||
bool _endOfData;
|
||||
};
|
||||
|
||||
void KyraAudioStream::beginFadeOut(uint32 millis) {
|
||||
_fadeSamples = (millis * getRate()) / 1000;
|
||||
if (_fading == 0)
|
||||
_fadeCount = _fadeSamples;
|
||||
_fading = -1;
|
||||
}
|
||||
|
||||
int KyraAudioStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samplesRead = _impl->readBuffer(buffer, numSamples);
|
||||
|
||||
if (_fading) {
|
||||
int samplesProcessed = 0;
|
||||
for (; samplesProcessed < samplesRead; ++samplesProcessed) {
|
||||
// To help avoid overflows for long fade times, we divide both
|
||||
// _fadeSamples and _fadeCount when calculating the new sample.
|
||||
|
||||
int32 div = _fadeSamples / 256;
|
||||
if (_fading) {
|
||||
*buffer = (*buffer * (_fadeCount / 256)) / div;
|
||||
++buffer;
|
||||
|
||||
_fadeCount += _fading;
|
||||
|
||||
if (_fadeCount < 0) {
|
||||
_fadeCount = 0;
|
||||
_endOfData = true;
|
||||
} else if (_fadeCount > _fadeSamples) {
|
||||
_fadeCount = _fadeSamples;
|
||||
_fading = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_endOfData) {
|
||||
memset(buffer, 0, (samplesRead - samplesProcessed) * sizeof(int16));
|
||||
samplesRead = samplesProcessed;
|
||||
}
|
||||
}
|
||||
|
||||
return samplesRead;
|
||||
}
|
||||
|
||||
SoundDigital_MR::SoundDigital_MR(KyraEngine_MR *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
|
||||
for (uint i = 0; i < ARRAYSIZE(_sounds); ++i)
|
||||
_sounds[i].stream = nullptr;
|
||||
}
|
||||
|
||||
SoundDigital_MR::~SoundDigital_MR() {
|
||||
for (int i = 0; i < ARRAYSIZE(_sounds); ++i)
|
||||
stopSound(i);
|
||||
}
|
||||
|
||||
int SoundDigital_MR::playSound(const char *filename, uint8 priority, Audio::Mixer::SoundType type, int volume, bool loop, int channel) {
|
||||
Sound *use = nullptr;
|
||||
if (channel != -1 && channel < ARRAYSIZE(_sounds)) {
|
||||
stopSound(channel);
|
||||
use = &_sounds[channel];
|
||||
} else {
|
||||
for (channel = 0; !use && channel < ARRAYSIZE(_sounds); ++channel) {
|
||||
if (!isPlaying(channel)) {
|
||||
stopSound(channel);
|
||||
use = &_sounds[channel];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (channel = 0; !use && channel < ARRAYSIZE(_sounds); ++channel) {
|
||||
if (strcmp(_sounds[channel].filename, filename) == 0) {
|
||||
stopSound(channel);
|
||||
use = &_sounds[channel];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (channel = 0; !use && channel < ARRAYSIZE(_sounds); ++channel) {
|
||||
if (_sounds[channel].priority <= priority) {
|
||||
stopSound(channel);
|
||||
use = &_sounds[channel];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!use) {
|
||||
warning("no free sound channel");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *stream = nullptr;
|
||||
int usedCodec = -1;
|
||||
for (int i = 0; _supportedCodecs[i].fileext; ++i) {
|
||||
Common::Path file(filename);
|
||||
file.appendInPlace(_supportedCodecs[i].fileext);
|
||||
|
||||
if (!_vm->resource()->exists(file))
|
||||
continue;
|
||||
|
||||
stream = _vm->resource()->createReadStream(file);
|
||||
usedCodec = i;
|
||||
}
|
||||
|
||||
if (!stream) {
|
||||
warning("Couldn't find soundfile '%s'", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Common::strlcpy(use->filename, filename, sizeof(use->filename));
|
||||
use->priority = priority;
|
||||
debugC(5, kDebugLevelSound, "playSound: \"%s\"", use->filename);
|
||||
Audio::SeekableAudioStream *audioStream = _supportedCodecs[usedCodec].streamFunc(stream, DisposeAfterUse::YES);
|
||||
if (!audioStream) {
|
||||
warning("Couldn't create audio stream for file '%s'", filename);
|
||||
return -1;
|
||||
}
|
||||
use->stream = new KyraAudioStream(audioStream);
|
||||
assert(use->stream);
|
||||
if (use->stream->endOfData()) {
|
||||
delete use->stream;
|
||||
use->stream = nullptr;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (volume > 255)
|
||||
volume = 255;
|
||||
volume = (volume * Audio::Mixer::kMaxChannelVolume) / 255;
|
||||
|
||||
if (type == Audio::Mixer::kSpeechSoundType && _vm->heliumMode())
|
||||
use->stream->setRate(32765);
|
||||
|
||||
_mixer->playStream(type, &use->handle, makeLoopingAudioStream(use->stream, loop ? 0 : 1), -1, volume);
|
||||
return use - _sounds;
|
||||
}
|
||||
|
||||
bool SoundDigital_MR::isPlaying(int channel) {
|
||||
if (channel == -1)
|
||||
return false;
|
||||
|
||||
assert(channel >= 0 && channel < ARRAYSIZE(_sounds));
|
||||
|
||||
if (!_sounds[channel].stream)
|
||||
return false;
|
||||
|
||||
return _mixer->isSoundHandleActive(_sounds[channel].handle);
|
||||
}
|
||||
|
||||
void SoundDigital_MR::stopSound(int channel) {
|
||||
if (channel == -1)
|
||||
return;
|
||||
|
||||
assert(channel >= 0 && channel < ARRAYSIZE(_sounds));
|
||||
_mixer->stopHandle(_sounds[channel].handle);
|
||||
_sounds[channel].stream = nullptr;
|
||||
}
|
||||
|
||||
void SoundDigital_MR::stopAllSounds() {
|
||||
for (int i = 0; i < ARRAYSIZE(_sounds); ++i) {
|
||||
if (isPlaying(i))
|
||||
stopSound(i);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundDigital_MR::beginFadeOut(int channel, int ticks) {
|
||||
if (isPlaying(channel))
|
||||
_sounds[channel].stream->beginFadeOut(ticks * _vm->tickLength());
|
||||
}
|
||||
|
||||
// static res
|
||||
|
||||
Audio::SeekableAudioStream *makeAUDStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
const SoundDigital_MR::AudioCodecs SoundDigital_MR::_supportedCodecs[] = {
|
||||
#ifdef USE_FLAC
|
||||
{ ".FLA", Audio::makeFLACStream },
|
||||
#endif // USE_FLAC
|
||||
#ifdef USE_VORBIS
|
||||
{ ".OGG", Audio::makeVorbisStream },
|
||||
#endif // USE_VORBIS
|
||||
#ifdef USE_MAD
|
||||
{ ".MP3", Audio::makeMP3Stream },
|
||||
#endif // USE_MAD
|
||||
{ ".AUD", makeAUDStream },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
118
engines/kyra/sound/sound_digital_mr.h
Normal file
118
engines/kyra/sound/sound_digital_mr.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/* 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 KYRA_SOUND_DIGITAL_MR_H
|
||||
#define KYRA_SOUND_DIGITAL_MR_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
} // End of namespace Common
|
||||
|
||||
namespace Audio {
|
||||
class SeekableAudioStream;
|
||||
} // End of namespace Audio
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
// Digital Audio
|
||||
class KyraAudioStream;
|
||||
class KyraEngine_MR;
|
||||
|
||||
/**
|
||||
* Digital audio output device.
|
||||
*
|
||||
* This is just used for Kyrandia 3.
|
||||
*/
|
||||
class SoundDigital_MR {
|
||||
public:
|
||||
SoundDigital_MR(KyraEngine_MR *vm, Audio::Mixer *mixer);
|
||||
~SoundDigital_MR();
|
||||
|
||||
/**
|
||||
* Plays a sound.
|
||||
*
|
||||
* @param filename file to be played
|
||||
* @param priority priority of the sound
|
||||
* @param type type
|
||||
* @param volume channel volume
|
||||
* @param loop true if the sound should loop (endlessly)
|
||||
* @param channel tell the sound player to use a specific channel for playback
|
||||
*
|
||||
* @return channel playing the sound
|
||||
*/
|
||||
int playSound(const char *filename, uint8 priority, Audio::Mixer::SoundType type, int volume = 255, bool loop = false, int channel = -1);
|
||||
|
||||
/**
|
||||
* Checks if a given channel is playing a sound.
|
||||
*
|
||||
* @param channel channel number to check
|
||||
* @return true if playing, else false
|
||||
*/
|
||||
bool isPlaying(int channel);
|
||||
|
||||
/**
|
||||
* Stop the playback of a sound in the given
|
||||
* channel.
|
||||
*
|
||||
* @param channel channel number
|
||||
*/
|
||||
void stopSound(int channel);
|
||||
|
||||
/**
|
||||
* Stops playback of all sounds.
|
||||
*/
|
||||
void stopAllSounds();
|
||||
|
||||
/**
|
||||
* Makes the sound in a given channel
|
||||
* fading out.
|
||||
*
|
||||
* @param channel channel number
|
||||
* @param ticks fadeout time
|
||||
*/
|
||||
void beginFadeOut(int channel, int ticks);
|
||||
private:
|
||||
KyraEngine_MR *_vm;
|
||||
Audio::Mixer *_mixer;
|
||||
|
||||
struct Sound {
|
||||
Audio::SoundHandle handle;
|
||||
|
||||
char filename[16];
|
||||
uint8 priority;
|
||||
KyraAudioStream *stream;
|
||||
} _sounds[4];
|
||||
|
||||
struct AudioCodecs {
|
||||
const char *fileext;
|
||||
Audio::SeekableAudioStream *(*streamFunc)(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
};
|
||||
|
||||
static const AudioCodecs _supportedCodecs[];
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
631
engines/kyra/sound/sound_intern.h
Normal file
631
engines/kyra/sound/sound_intern.h
Normal file
@@ -0,0 +1,631 @@
|
||||
/* 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 KYRA_SOUND_INTERN_H
|
||||
#define KYRA_SOUND_INTERN_H
|
||||
|
||||
|
||||
#include "kyra/sound/sound.h"
|
||||
#include "kyra/sound/sound_pc_v1.h"
|
||||
|
||||
#include "audio/midiparser.h"
|
||||
#include "audio/miles.h"
|
||||
#include "audio/softsynth/emumidi.h"
|
||||
#include "audio/softsynth/fmtowns_pc98/towns_audio.h"
|
||||
|
||||
#include "common/mutex.h"
|
||||
|
||||
class EuphonyPlayer;
|
||||
class TownsPC98_AudioDriver;
|
||||
|
||||
namespace Audio {
|
||||
class PCSpeakerStream;
|
||||
class MaxTrax;
|
||||
} // End of namespace Audio
|
||||
|
||||
namespace Common {
|
||||
class MacResManager;
|
||||
} // End of namespace Common
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class MidiOutput;
|
||||
|
||||
/**
|
||||
* MIDI output device.
|
||||
*
|
||||
* This device supports both MT-32 MIDI, as used in
|
||||
* Kyrandia 1 and 2, and GM MIDI, as used in Kyrandia 2.
|
||||
*/
|
||||
class SoundMidiPC : public Sound {
|
||||
public:
|
||||
SoundMidiPC(KyraEngine_v1 *vm, Audio::Mixer *mixer, MidiDriver *driver, kType type);
|
||||
~SoundMidiPC() override;
|
||||
|
||||
kType getMusicType() const override { return _type; }
|
||||
|
||||
bool init() override;
|
||||
|
||||
void updateVolumeSettings() override;
|
||||
|
||||
void initAudioResourceInfo(int set, void *info) override;
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint file) const override;
|
||||
void loadSoundFile(uint file) override;
|
||||
void loadSoundFile(const Common::Path &file) override;
|
||||
void loadSfxFile(const Common::Path &file) override;
|
||||
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
bool isPlaying() const override;
|
||||
|
||||
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
|
||||
void stopAllSoundEffects() override;
|
||||
|
||||
void beginFadeOut() override;
|
||||
|
||||
void pause(bool paused) override;
|
||||
private:
|
||||
static void onTimer(void *data);
|
||||
|
||||
// Our channel handling
|
||||
int _musicVolume, _sfxVolume;
|
||||
|
||||
uint32 _fadeStartTime;
|
||||
bool _fadeMusicOut;
|
||||
|
||||
// Midi file related
|
||||
Common::Path _mFileName, _sFileName;
|
||||
byte *_musicFile, *_sfxFile;
|
||||
|
||||
MidiParser *_music;
|
||||
MidiParser *_sfx[3];
|
||||
|
||||
const SoundResourceInfo_PC *res() const {return _resInfo[_currentResourceSet]; }
|
||||
SoundResourceInfo_PC *_resInfo[3];
|
||||
int _currentResourceSet;
|
||||
|
||||
// misc
|
||||
kType _type;
|
||||
Common::Path getFileName(const Common::Path &str);
|
||||
|
||||
bool _nativeMT32;
|
||||
MidiDriver *_driver;
|
||||
Audio::MidiDriver_Miles_Midi *_output;
|
||||
|
||||
Common::Mutex _mutex;
|
||||
};
|
||||
|
||||
class SoundTowns_LoK : public Sound {
|
||||
public:
|
||||
SoundTowns_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer);
|
||||
~SoundTowns_LoK() override;
|
||||
|
||||
kType getMusicType() const override { return kTowns; }
|
||||
|
||||
bool init() override;
|
||||
void process() override;
|
||||
|
||||
void initAudioResourceInfo(int set, void *info) override;
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint file) const override;
|
||||
void loadSoundFile(uint file) override;
|
||||
void loadSoundFile(const Common::Path &) override {}
|
||||
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
|
||||
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
|
||||
void stopAllSoundEffects() override;
|
||||
|
||||
void beginFadeOut() override;
|
||||
|
||||
void updateVolumeSettings() override;
|
||||
void enableMusic(int enable) override;
|
||||
|
||||
private:
|
||||
bool loadInstruments();
|
||||
void playEuphonyTrack(uint32 offset, int loop);
|
||||
|
||||
void fadeOutSoundEffects();
|
||||
|
||||
int _lastTrack;
|
||||
Audio::SoundHandle _sfxHandle;
|
||||
|
||||
uint8 *_musicTrackData;
|
||||
|
||||
uint _sfxFileIndex;
|
||||
uint8 *_sfxFileData;
|
||||
uint8 _sfxChannel;
|
||||
|
||||
EuphonyPlayer *_player;
|
||||
|
||||
bool _cdaPlaying;
|
||||
|
||||
const SoundResourceInfo_Towns *res() const {return _resInfo[_currentResourceSet]; }
|
||||
SoundResourceInfo_Towns *_resInfo[3];
|
||||
int _currentResourceSet;
|
||||
|
||||
const uint8 *_musicFadeTable;
|
||||
const uint8 *_sfxBTTable;
|
||||
const uint8 *_sfxWDTable;
|
||||
};
|
||||
|
||||
class SoundPC98_LoK : public Sound {
|
||||
public:
|
||||
SoundPC98_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer);
|
||||
~SoundPC98_LoK() override;
|
||||
|
||||
kType getMusicType() const override { return kPC98; }
|
||||
|
||||
bool init() override;
|
||||
|
||||
void initAudioResourceInfo(int set, void *info) override;
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint file) const override;
|
||||
void loadSoundFile(uint file) override;
|
||||
void loadSoundFile(const Common::Path &file) override;
|
||||
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
void beginFadeOut() override;
|
||||
|
||||
int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) override { return -1; }
|
||||
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
|
||||
|
||||
void updateVolumeSettings() override;
|
||||
|
||||
private:
|
||||
int _lastTrack;
|
||||
uint8 *_musicTrackData;
|
||||
uint8 *_sfxTrackData;
|
||||
TownsPC98_AudioDriver *_driver;
|
||||
|
||||
const char *resPattern() {return _resInfo[_currentResourceSet]->c_str(); }
|
||||
Common::String *_resInfo[3];
|
||||
int _currentResourceSet;
|
||||
};
|
||||
|
||||
class SoundTownsPC98_v2 : public Sound {
|
||||
public:
|
||||
SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer);
|
||||
~SoundTownsPC98_v2() override;
|
||||
|
||||
kType getMusicType() const override { return _vm->gameFlags().platform == Common::kPlatformFMTowns ? kTowns : kPC98; }
|
||||
|
||||
bool init() override;
|
||||
void process() override;
|
||||
|
||||
void initAudioResourceInfo(int set, void *info) override;
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint file) const override;
|
||||
void loadSoundFile(uint file) override {}
|
||||
void loadSoundFile(const Common::Path &file) override;
|
||||
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
void beginFadeOut() override;
|
||||
|
||||
int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume = 255, uint8 priority = 255, bool isSfx = true) override;
|
||||
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
|
||||
|
||||
void updateVolumeSettings() override;
|
||||
|
||||
private:
|
||||
Audio::AudioStream *_currentSFX;
|
||||
int _lastTrack;
|
||||
bool _useFmSfx;
|
||||
|
||||
uint8 *_musicTrackData;
|
||||
uint8 *_sfxTrackData;
|
||||
TownsPC98_AudioDriver *_driver;
|
||||
|
||||
const SoundResourceInfo_TownsPC98V2 *res() const { return _resInfo[_currentResourceSet]; }
|
||||
SoundResourceInfo_TownsPC98V2 *_resInfo[3];
|
||||
int _currentResourceSet;
|
||||
};
|
||||
|
||||
// PC Speaker MIDI driver
|
||||
class MidiDriver_PCSpeaker : public MidiDriver_Emulated {
|
||||
public:
|
||||
MidiDriver_PCSpeaker(Audio::Mixer *mixer);
|
||||
~MidiDriver_PCSpeaker() override;
|
||||
|
||||
// MidiDriver interface
|
||||
void close() override {}
|
||||
|
||||
void send(uint32 data) override;
|
||||
|
||||
MidiChannel *allocateChannel() override { return 0; }
|
||||
MidiChannel *getPercussionChannel() override { return 0; }
|
||||
|
||||
// MidiDriver_Emulated interface
|
||||
void generateSamples(int16 *buffer, int numSamples) override;
|
||||
|
||||
// AudioStream interface
|
||||
bool isStereo() const override { return false; }
|
||||
int getRate() const override { return _rate; }
|
||||
private:
|
||||
Common::Mutex _mutex;
|
||||
Audio::PCSpeakerStream *_speaker;
|
||||
int _rate;
|
||||
|
||||
struct Channel {
|
||||
uint8 pitchBendLow, pitchBendHigh;
|
||||
uint8 hold;
|
||||
uint8 modulation;
|
||||
uint8 voiceProtect;
|
||||
uint8 noteCount;
|
||||
} _channel[2];
|
||||
|
||||
void resetController(int channel);
|
||||
|
||||
struct Note {
|
||||
bool enabled;
|
||||
uint8 hardwareChannel;
|
||||
uint8 midiChannel;
|
||||
uint8 note;
|
||||
bool processHold;
|
||||
uint8 flags;
|
||||
uint8 hardwareFlags;
|
||||
uint16 priority;
|
||||
int16 modulation;
|
||||
uint16 precedence;
|
||||
} _note[2];
|
||||
|
||||
void noteOn(int channel, int note);
|
||||
void noteOff(int channel, int note);
|
||||
|
||||
void turnNoteOn(int note);
|
||||
void overwriteNote(int note);
|
||||
void turnNoteOff(int note);
|
||||
|
||||
void setupTone(int note);
|
||||
|
||||
uint16 _countdown;
|
||||
uint8 _hardwareChannel[1];
|
||||
bool _modulationFlag;
|
||||
|
||||
uint8 _timerValue;
|
||||
void onTimer() override;
|
||||
|
||||
static const uint8 _noteTable1[];
|
||||
static const uint8 _noteTable2[];
|
||||
};
|
||||
|
||||
// for StaticResource (maybe we can find a nicer way to handle it)
|
||||
struct AmigaSfxTable {
|
||||
uint8 note;
|
||||
uint8 patch;
|
||||
uint16 duration;
|
||||
uint8 volume;
|
||||
uint8 pan;
|
||||
};
|
||||
|
||||
class SoundAmiga_LoK : public Sound {
|
||||
public:
|
||||
SoundAmiga_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer);
|
||||
~SoundAmiga_LoK() override;
|
||||
|
||||
kType getMusicType() const override { return kAmiga; } //FIXME
|
||||
|
||||
bool init() override;
|
||||
|
||||
void initAudioResourceInfo(int set, void *info) override;
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint file) const override;
|
||||
void loadSoundFile(uint file) override;
|
||||
void loadSoundFile(const Common::Path &) override {}
|
||||
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
void beginFadeOut() override;
|
||||
|
||||
int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) override { return -1; }
|
||||
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
|
||||
|
||||
protected:
|
||||
Audio::MaxTrax *_driver;
|
||||
Audio::SoundHandle _musicHandle;
|
||||
enum FileType { kFileNone = -1, kFileIntro = 0, kFileGame = 1, kFileFinal = 2 } _fileLoaded;
|
||||
|
||||
const AmigaSfxTable *_tableSfxIntro;
|
||||
int _tableSfxIntro_Size;
|
||||
|
||||
const AmigaSfxTable *_tableSfxGame;
|
||||
int _tableSfxGame_Size;
|
||||
};
|
||||
|
||||
class SoundMacRes;
|
||||
class HalestormDriver;
|
||||
class SoundMac : public Sound {
|
||||
public:
|
||||
SoundMac(KyraEngine_v1 *vm, Audio::Mixer *mixer);
|
||||
~SoundMac() override;
|
||||
|
||||
kType getMusicType() const override;
|
||||
|
||||
bool init() override { return init(musicEnabled() == 1); }
|
||||
bool init(bool hiQuality);
|
||||
void initAudioResourceInfo(int, void*) override {}
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint) const override { return true; }
|
||||
void loadSoundFile(uint) override {}
|
||||
void loadSoundFile(const Common::Path &) override {}
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
void playSoundEffect(uint16 track, uint8) override;
|
||||
bool isPlaying() const override;
|
||||
void beginFadeOut() override;
|
||||
void updateVolumeSettings() override;
|
||||
void enableMusic(int enable) override;
|
||||
|
||||
private:
|
||||
void setQuality(bool hi);
|
||||
|
||||
SoundMacRes *_res;
|
||||
HalestormDriver *_driver;
|
||||
const int _talkieFlag;
|
||||
bool _ready;
|
||||
|
||||
const uint16 *_resIDMusic;
|
||||
int _currentResourceSet;
|
||||
|
||||
static const uint16 _resIDMusicIntro[4];
|
||||
static const uint16 _resIDMusicIngame[35];
|
||||
static const uint8 _musicLoopTable[35];
|
||||
static const uint16 _resIDSfxIntro[2][39];
|
||||
static const uint16 _resIDSfxIngame[2][39];
|
||||
|
||||
struct SoundEffectDef {
|
||||
uint8 note;
|
||||
uint8 number;
|
||||
uint16 rate;
|
||||
uint8 unk;
|
||||
};
|
||||
|
||||
static const SoundEffectDef _soundEffectDefsIntro[16];
|
||||
static const SoundEffectDef _soundEffectDefsIngame[120];
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
class SoundTowns_Darkmoon : public Sound, public TownsAudioInterfacePluginDriver {
|
||||
public:
|
||||
SoundTowns_Darkmoon(KyraEngine_v1 *vm, Audio::Mixer *mixer);
|
||||
~SoundTowns_Darkmoon() override;
|
||||
|
||||
kType getMusicType() const override { return kTowns; }
|
||||
|
||||
bool init() override;
|
||||
|
||||
void timerCallback(int timerId) override;
|
||||
|
||||
void initAudioResourceInfo(int set, void *info) override;
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint file) const override;
|
||||
void loadSoundFile(uint file) override;
|
||||
void loadSoundFile(const Common::Path &name) override;
|
||||
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
bool isPlaying() const override;
|
||||
|
||||
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
|
||||
void stopAllSoundEffects() override;
|
||||
|
||||
void beginFadeOut() override;
|
||||
|
||||
void updateVolumeSettings() override;
|
||||
|
||||
int checkTrigger() override;
|
||||
|
||||
void resetTrigger() override;
|
||||
|
||||
private:
|
||||
struct SoundTableEntry {
|
||||
int8 type;
|
||||
int32 para1;
|
||||
int16 para2;
|
||||
} _soundTable[120];
|
||||
|
||||
const char *const *_fileList;
|
||||
uint _fileListLen;
|
||||
|
||||
uint8 _lastSfxChan;
|
||||
uint8 _lastEnvChan;
|
||||
uint8 *_pcmData;
|
||||
uint32 _pcmDataSize;
|
||||
uint8 _pcmVol;
|
||||
|
||||
int _timer;
|
||||
int _timerSwitch;
|
||||
|
||||
SoundResourceInfo_TownsEoB *_resource[3];
|
||||
|
||||
TownsAudioInterface *_intf;
|
||||
};
|
||||
|
||||
class AudioMaster2;
|
||||
class SoundAmiga_EoB: public Sound {
|
||||
public:
|
||||
SoundAmiga_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer);
|
||||
~SoundAmiga_EoB() override;
|
||||
|
||||
kType getMusicType() const override;
|
||||
|
||||
bool init() override;
|
||||
void initAudioResourceInfo(int set, void *info) override;
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint file) const override { return false; }
|
||||
void loadSoundFile(uint) override {}
|
||||
void loadSoundFile(const Common::Path &file) override;
|
||||
void unloadSoundFile(const Common::String &file) override;
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
|
||||
void beginFadeOut() override { beginFadeOut(160); }
|
||||
void beginFadeOut(int delay) override;
|
||||
void updateVolumeSettings() override;
|
||||
int checkTrigger() override;
|
||||
|
||||
private:
|
||||
uint8 *_fileBuffer;
|
||||
|
||||
KyraEngine_v1 *_vm;
|
||||
AudioMaster2 *_driver;
|
||||
SoundResourceInfo_AmigaEoB *_resInfo[3];
|
||||
Common::String _lastSound;
|
||||
|
||||
int _currentResourceSet;
|
||||
|
||||
bool _ready;
|
||||
};
|
||||
|
||||
class MLALF98;
|
||||
class SoundPC98_EoB : public Sound {
|
||||
public:
|
||||
SoundPC98_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer);
|
||||
~SoundPC98_EoB() override;
|
||||
|
||||
kType getMusicType() const override;
|
||||
|
||||
bool init() override;
|
||||
void initAudioResourceInfo(int set, void *info) override;
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint file) const override { return false; }
|
||||
void loadSoundFile(uint file) override;
|
||||
void loadSoundFile(const Common::Path &file) override {}
|
||||
void loadSfxFile(const Common::Path &file) override;
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
void playSoundEffect(uint16 track, uint8) override;
|
||||
void beginFadeOut() override {}
|
||||
void updateVolumeSettings() override;
|
||||
|
||||
private:
|
||||
KyraEngine_v1 *_vm;
|
||||
MLALF98 *_driver;
|
||||
|
||||
SoundResourceInfo_PC *_resInfo[3];
|
||||
int _currentResourceSet;
|
||||
|
||||
uint32 _sfxDelay;
|
||||
|
||||
bool _ready;
|
||||
};
|
||||
|
||||
class CapcomPC98AudioDriver;
|
||||
class SoundPC98_Darkmoon : public Sound {
|
||||
public:
|
||||
SoundPC98_Darkmoon(KyraEngine_v1 *vm, MidiDriver::DeviceHandle dev, Audio::Mixer *mixer);
|
||||
~SoundPC98_Darkmoon() override;
|
||||
|
||||
kType getMusicType() const override;
|
||||
kType getSfxType() const override;
|
||||
|
||||
bool init() override;
|
||||
|
||||
void initAudioResourceInfo(int set, void *info) override;
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint file) const override { return true; }
|
||||
void loadSoundFile(uint file) override;
|
||||
void loadSoundFile(const Common::Path &name) override;
|
||||
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
bool isPlaying() const override;
|
||||
|
||||
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
|
||||
void stopAllSoundEffects() override;
|
||||
|
||||
void beginFadeOut() override;
|
||||
|
||||
void pause(bool paused) override;
|
||||
|
||||
void updateVolumeSettings() override;
|
||||
|
||||
int checkTrigger() override;
|
||||
void resetTrigger() override {} // This sound class is for EOB II only, this method is not needed there.
|
||||
|
||||
private:
|
||||
void restartBackgroundMusic();
|
||||
const uint8 *getData(uint16 track) const;
|
||||
|
||||
KyraEngine_v1 *_vm;
|
||||
CapcomPC98AudioDriver *_driver;
|
||||
uint8 *_soundData, *_fileBuffer;
|
||||
|
||||
int _lastTrack;
|
||||
|
||||
const SoundResourceInfo_PC *res() const {return _resInfo[_currentResourceSet]; }
|
||||
SoundResourceInfo_PC *_resInfo[3];
|
||||
int _currentResourceSet;
|
||||
|
||||
Common::Path _soundFileLoaded;
|
||||
|
||||
MidiDriver::DeviceHandle _dev;
|
||||
kType _drvType;
|
||||
bool _ready;
|
||||
};
|
||||
|
||||
class SegaAudioDriver;
|
||||
class SoundSegaCD_EoB : public Sound {
|
||||
public:
|
||||
SoundSegaCD_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer);
|
||||
~SoundSegaCD_EoB() override;
|
||||
|
||||
kType getMusicType() const override;
|
||||
|
||||
bool init() override;
|
||||
void initAudioResourceInfo(int, void*) override {}
|
||||
void selectAudioResourceSet(int) override {}
|
||||
bool hasSoundFile(uint file) const override { return false; }
|
||||
void loadSoundFile(uint file) override {}
|
||||
void loadSoundFile(const Common::Path &file) override {}
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
void playSoundEffect(uint16 track, uint8 volume) override;
|
||||
bool isPlaying() const override;
|
||||
void beginFadeOut() override {}
|
||||
void updateVolumeSettings() override;
|
||||
|
||||
private:
|
||||
void loadPCMData();
|
||||
void loadFMData();
|
||||
|
||||
KyraEngine_v1 *_vm;
|
||||
SegaAudioDriver *_driver;
|
||||
|
||||
uint8 _pcmOffsets[8];
|
||||
uint16 _fmOffsets[140];
|
||||
const uint8 *_fmData;
|
||||
int _lastSoundEffect;
|
||||
bool _ready;
|
||||
|
||||
static const uint8 _fmTrackMap[140];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
97
engines/kyra/sound/sound_lok.cpp
Normal file
97
engines/kyra/sound/sound_lok.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/* 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 "kyra/engine/kyra_lok.h"
|
||||
#include "kyra/sound/sound.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
void KyraEngine_LoK::snd_playSoundEffect(int track, int volume) {
|
||||
if (_flags.platform == Common::kPlatformPC98) {
|
||||
if (track < 16 || track > 119)
|
||||
track = 58;
|
||||
else
|
||||
track -= 16;
|
||||
} else if (_flags.platform == Common::kPlatformMacintosh && track >= 97 && track <= 99) {
|
||||
_sound->playTrack(track - 79);
|
||||
_lastMusicCommand = -1;
|
||||
return;
|
||||
} else if (_flags.platform == Common::kPlatformFMTowns && track == 49) {
|
||||
snd_playWanderScoreViaMap(56, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
KyraEngine_v1::snd_playSoundEffect(track);
|
||||
}
|
||||
|
||||
void KyraEngine_LoK::snd_playWanderScoreViaMap(int command, int restart) {
|
||||
if (restart)
|
||||
_lastMusicCommand = -1;
|
||||
|
||||
if (_flags.platform == Common::kPlatformFMTowns) {
|
||||
if (command >= 35 && command <= 38) {
|
||||
snd_playSoundEffect(command - 20);
|
||||
} else if (command >= 2) {
|
||||
if (_lastMusicCommand != command)
|
||||
// the original does -2 here we handle this inside _sound->playTrack()
|
||||
_sound->playTrack(command);
|
||||
} else {
|
||||
_sound->beginFadeOut();
|
||||
}
|
||||
_lastMusicCommand = command;
|
||||
} else if (_flags.platform == Common::kPlatformPC98) {
|
||||
if (command == 1) {
|
||||
_sound->beginFadeOut();
|
||||
} else if ((command >= 2 && command < 53) || command == 55) {
|
||||
if (_lastMusicCommand != command)
|
||||
_sound->playTrack(command);
|
||||
} else {
|
||||
_sound->haltTrack();
|
||||
}
|
||||
_lastMusicCommand = command;
|
||||
} else {
|
||||
KyraEngine_v1::snd_playWanderScoreViaMap(command, restart);
|
||||
}
|
||||
}
|
||||
|
||||
void KyraEngine_LoK::snd_playVoiceFile(int id) {
|
||||
Common::String vocFile = Common::String::format("%03d", id);
|
||||
_speechPlayTime = _sound->voicePlay(vocFile.c_str(), &_speechHandle);
|
||||
}
|
||||
|
||||
void KyraEngine_LoK::snd_voiceWaitForFinish(bool ingame) {
|
||||
while (_sound->voiceIsPlaying() && !skipFlag()) {
|
||||
if (ingame)
|
||||
delay(10, true);
|
||||
else
|
||||
_system->delayMillis(10);
|
||||
}
|
||||
}
|
||||
|
||||
uint32 KyraEngine_LoK::snd_getVoicePlayTime() {
|
||||
if (!snd_voiceIsPlaying())
|
||||
return 0;
|
||||
return (_speechPlayTime != -1 ? _speechPlayTime : 0);
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
302
engines/kyra/sound/sound_lol.cpp
Normal file
302
engines/kyra/sound/sound_lol.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_LOL
|
||||
|
||||
#include "kyra/engine/lol.h"
|
||||
#include "kyra/sound/sound.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
bool LoLEngine::snd_playCharacterSpeech(int id, int8 speaker, int) {
|
||||
if (!speechEnabled())
|
||||
return false;
|
||||
|
||||
if (speaker < 65) {
|
||||
if (_characters[speaker].flags & 1)
|
||||
speaker = (int)_characters[speaker].name[0];
|
||||
else
|
||||
speaker = 0;
|
||||
}
|
||||
|
||||
if (_lastSpeechId == id && speaker == _lastSpeaker)
|
||||
return true;
|
||||
|
||||
_lastSpeechId = id;
|
||||
_lastSpeaker = speaker;
|
||||
_nextSpeechId = _nextSpeaker = -1;
|
||||
|
||||
Common::String pattern1;
|
||||
Common::String file1;
|
||||
Common::String file2;
|
||||
Common::String file3;
|
||||
|
||||
SpeechList newSpeechList;
|
||||
|
||||
Common::String pattern2 = Common::String::format("%02d", id & 0x4000 ? 0 : _curTlkFile);
|
||||
|
||||
if (id & 0x4000) {
|
||||
pattern1 = Common::String::format("%03X", id & 0x3FFF);
|
||||
} else if (id < 1000) {
|
||||
pattern1 = Common::String::format("%03d", id);
|
||||
} else {
|
||||
file3 = Common::String::format("@%04d%c.%s", id - 1000, (char)speaker, pattern2.c_str());
|
||||
if (_sound->isVoicePresent(file3.c_str()))
|
||||
newSpeechList.push_back(_sound->getVoiceStream(file3.c_str()));
|
||||
}
|
||||
|
||||
if (file3.empty()) {
|
||||
for (char i = 0; ; i++) {
|
||||
char symbol = '0' + i;
|
||||
file1 = Common::String::format("%s%c%c.%s", pattern1.c_str(), (char)speaker, symbol, pattern2.c_str());
|
||||
file2 = Common::String::format("%s%c%c.%s", pattern1.c_str(), '_', symbol, pattern2.c_str());
|
||||
if (_sound->isVoicePresent(file1.c_str()))
|
||||
newSpeechList.push_back(_sound->getVoiceStream(file1.c_str()));
|
||||
else if (_sound->isVoicePresent(file2.c_str()))
|
||||
newSpeechList.push_back(_sound->getVoiceStream(file2.c_str()));
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newSpeechList.empty())
|
||||
return false;
|
||||
|
||||
while (_sound->voiceIsPlaying(&_speechHandle))
|
||||
delay(_tickLength, true);
|
||||
|
||||
while (_sound->allVoiceChannelsPlaying())
|
||||
delay(_tickLength);
|
||||
|
||||
for (SpeechList::iterator i = _speechList.begin(); i != _speechList.end(); ++i)
|
||||
delete *i;
|
||||
_speechList.clear();
|
||||
_speechList = newSpeechList;
|
||||
|
||||
_activeVoiceFileTotalTime = 0;
|
||||
for (SpeechList::iterator i = _speechList.begin(); i != _speechList.end();) {
|
||||
// Just in case any file loading failed: Remove the bad streams here.
|
||||
if (!*i)
|
||||
i = _speechList.erase(i);
|
||||
else
|
||||
_activeVoiceFileTotalTime += (*i++)->getLength().msecs();
|
||||
}
|
||||
|
||||
_sound->playVoiceStream(*_speechList.begin(), &_speechHandle);
|
||||
_speechList.pop_front();
|
||||
|
||||
if (!_activeVoiceFileTotalTime)
|
||||
return false;
|
||||
|
||||
_tim->_abortFlag = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int LoLEngine::snd_updateCharacterSpeech() {
|
||||
if (_sound->voiceIsPlaying(&_speechHandle))
|
||||
return 2;
|
||||
|
||||
if (_speechList.begin() != _speechList.end()) {
|
||||
_sound->playVoiceStream(*_speechList.begin(), &_speechHandle);
|
||||
_speechList.pop_front();
|
||||
return 2;
|
||||
|
||||
} else if (_nextSpeechId != -1) {
|
||||
_lastSpeechId = _lastSpeaker = -1;
|
||||
_activeVoiceFileTotalTime = 0;
|
||||
if (snd_playCharacterSpeech(_nextSpeechId, _nextSpeaker, 0))
|
||||
return 2;
|
||||
}
|
||||
|
||||
_lastSpeechId = _lastSpeaker = -1;
|
||||
_activeVoiceFileTotalTime = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LoLEngine::snd_stopSpeech(bool setFlag) {
|
||||
if (!_sound->voiceIsPlaying(&_speechHandle))
|
||||
return;
|
||||
|
||||
//_dlgTimer = 0;
|
||||
_sound->voiceStop(&_speechHandle);
|
||||
_activeVoiceFileTotalTime = 0;
|
||||
_nextSpeechId = _nextSpeaker = -1;
|
||||
|
||||
for (SpeechList::iterator i = _speechList.begin(); i != _speechList.end(); ++i)
|
||||
delete *i;
|
||||
_speechList.clear();
|
||||
|
||||
if (setFlag)
|
||||
_tim->_abortFlag = 1;
|
||||
}
|
||||
|
||||
void LoLEngine::snd_playSoundEffect(int track, int volume) {
|
||||
if ((track == 1 && (_lastSfxTrack == -1 || _lastSfxTrack == 1)) || shouldQuit())
|
||||
return;
|
||||
|
||||
_lastSfxTrack = track;
|
||||
if (track == -1 || track >= _ingameSoundIndexSize / 2)
|
||||
return;
|
||||
|
||||
volume &= 0xFF;
|
||||
int16 prIndex = _ingameSoundIndex[track * 2 + 1];
|
||||
uint16 priority = (prIndex > 0) ? (prIndex * volume) >> 8 : -prIndex;
|
||||
|
||||
static const uint8 volTable1[] = { 223, 159, 95, 47, 15, 0 };
|
||||
static const uint8 volTable2[] = { 255, 191, 127, 63, 30, 0 };
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (volTable1[i] < volume) {
|
||||
volume = volTable2[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int16 vocIndex = _ingameSoundIndex[track * 2];
|
||||
|
||||
bool hasVocFile = false;
|
||||
if (vocIndex != -1) {
|
||||
if (!_ingameSoundList[vocIndex].equalsIgnoreCase("EMPTY"))
|
||||
hasVocFile = true;
|
||||
}
|
||||
|
||||
if (hasVocFile) {
|
||||
if (_sound->isVoicePresent(_ingameSoundList[vocIndex].c_str()))
|
||||
_sound->voicePlay(_ingameSoundList[vocIndex].c_str(), 0, volume, priority, true);
|
||||
} else if (_flags.platform == Common::kPlatformDOS) {
|
||||
if (_sound->getSfxType() == Sound::kMidiMT32)
|
||||
track = (track < _ingameMT32SoundIndexSize) ? (_ingameMT32SoundIndex[track] - 1) : -1;
|
||||
else if (_sound->getSfxType() == Sound::kMidiGM)
|
||||
track = (track < _ingameGMSoundIndexSize) ? (_ingameGMSoundIndex[track] - 1) : -1;
|
||||
else if (_sound->getSfxType() == Sound::kPCSpkr)
|
||||
track = (track < _ingamePCSpeakerSoundIndexSize) ? (_ingamePCSpeakerSoundIndex[track] - 1) : -1;
|
||||
|
||||
if (track == 168)
|
||||
track = 167;
|
||||
|
||||
if (track != -1)
|
||||
KyraEngine_v1::snd_playSoundEffect(track, volume);
|
||||
}
|
||||
}
|
||||
|
||||
bool LoLEngine::snd_processEnvironmentalSoundEffect(int soundId, int block) {
|
||||
if (!KyraRpgEngine::snd_processEnvironmentalSoundEffect(soundId, block))
|
||||
return false;
|
||||
|
||||
if (block != _currentBlock) {
|
||||
static const int8 blockShiftTable[] = { -32, -31, 1, 33, 32, 31, -1, -33 };
|
||||
uint16 cbl = _currentBlock;
|
||||
|
||||
for (int i = 3; i > 0; i--) {
|
||||
int dir = calcMonsterDirection(cbl & 0x1F, cbl >> 5, block & 0x1F, block >> 5);
|
||||
cbl = (cbl + blockShiftTable[dir]) & 0x3FF;
|
||||
if (cbl == block)
|
||||
break;
|
||||
if (testWallFlag(cbl, 0, 1))
|
||||
_environmentSfxVol >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!soundId || _sceneUpdateRequired)
|
||||
return false;
|
||||
|
||||
return snd_processEnvironmentalSoundEffect(0, 0);
|
||||
}
|
||||
|
||||
void LoLEngine::snd_queueEnvironmentalSoundEffect(int soundId, int block) {
|
||||
if (_envSfxUseQueue && _envSfxNumTracksInQueue < 10) {
|
||||
_envSfxQueuedTracks[_envSfxNumTracksInQueue] = soundId;
|
||||
_envSfxQueuedBlocks[_envSfxNumTracksInQueue] = block;
|
||||
_envSfxNumTracksInQueue++;
|
||||
} else {
|
||||
snd_processEnvironmentalSoundEffect(soundId, block);
|
||||
}
|
||||
}
|
||||
|
||||
void LoLEngine::snd_playQueuedEffects() {
|
||||
for (int i = 0; i < _envSfxNumTracksInQueue; i++)
|
||||
snd_processEnvironmentalSoundEffect(_envSfxQueuedTracks[i], _envSfxQueuedBlocks[i]);
|
||||
_envSfxNumTracksInQueue = 0;
|
||||
}
|
||||
|
||||
void LoLEngine::snd_loadSoundFile(int track) {
|
||||
if (!_sound->musicEnabled() || _flags.platform != Common::kPlatformDOS)
|
||||
return;
|
||||
snd_stopMusic();
|
||||
int t = (track - 250) * 3;
|
||||
if (t < 0 || (_curMusicFileIndex == _musicTrackMap[t] && _curMusicFileExt == (char)_musicTrackMap[t + 1]))
|
||||
return;
|
||||
_sound->loadSoundFile(Common::Path(Common::String::format("LORE%02d%c", _musicTrackMap[t], (char)_musicTrackMap[t + 1])));
|
||||
_curMusicFileIndex = _musicTrackMap[t];
|
||||
_curMusicFileExt = (char)_musicTrackMap[t + 1];
|
||||
}
|
||||
|
||||
int LoLEngine::snd_playTrack(int track) {
|
||||
if (track == -1)
|
||||
return _lastMusicTrack;
|
||||
|
||||
int res = _lastMusicTrack;
|
||||
_lastMusicTrack = track;
|
||||
|
||||
if (_sound->musicEnabled()) {
|
||||
if (_flags.platform == Common::kPlatformDOS) {
|
||||
snd_loadSoundFile(track);
|
||||
int t = (track - 250) * 3;
|
||||
_sound->playTrack(_musicTrackMap[t + 2]);
|
||||
} else {
|
||||
_sound->playTrack(track - 249);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int LoLEngine::snd_stopMusic() {
|
||||
if (_sound->musicEnabled()) {
|
||||
if (_sound->isPlaying()) {
|
||||
_sound->beginFadeOut();
|
||||
_system->delayMillis(3 * _tickLength);
|
||||
}
|
||||
|
||||
_sound->haltTrack();
|
||||
}
|
||||
return snd_playTrack(-1);
|
||||
}
|
||||
|
||||
int LoLEngine::convertVolumeToMixer(int value) {
|
||||
value -= 2;
|
||||
return (value * Audio::Mixer::kMaxMixerVolume) / 100;
|
||||
}
|
||||
|
||||
int LoLEngine::convertVolumeFromMixer(int value) {
|
||||
return (value * 100) / Audio::Mixer::kMaxMixerVolume + 2;
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif // ENABLE_LOL
|
||||
386
engines/kyra/sound/sound_mac_lok.cpp
Normal file
386
engines/kyra/sound/sound_mac_lok.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
/* 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 "kyra/engine/util.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
#include "kyra/sound/sound_intern.h"
|
||||
#include "kyra/sound/sound_mac_res.h"
|
||||
#include "kyra/sound/drivers/halestorm.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/macresman.h"
|
||||
#include "common/compression/stuffit.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
|
||||
#define HS_16BITOUTPUT false
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
SoundMacRes::SoundMacRes(KyraEngine_v1 *vm) : _resMan(0), _stuffItArchive(nullptr), _isTalkie(vm->gameFlags().isTalkie) {
|
||||
_resMan = new Common::MacResManager[2];
|
||||
|
||||
if (vm->gameFlags().useInstallerPackage) {
|
||||
_stuffItArchive = vm->resource()->getCachedArchive("Install Legend of Kyrandia");
|
||||
if (!_stuffItArchive)
|
||||
error("SoundMacRes::SoundMacRes(): Failed to load Legend of Kyrandia installer file");
|
||||
}
|
||||
}
|
||||
|
||||
SoundMacRes::~SoundMacRes() {
|
||||
delete[] _resMan;
|
||||
}
|
||||
|
||||
bool SoundMacRes::init() {
|
||||
if (!_resMan)
|
||||
return false;
|
||||
|
||||
_kyraMacExe = _stuffItArchive ? Common::Path("Legend of Kyrandia\xaa") : Util::findMacResourceFile("Legend of Kyrandia");
|
||||
if (_kyraMacExe.empty() && _isTalkie)
|
||||
_kyraMacExe = Util::findMacResourceFile("LK");
|
||||
|
||||
if (_kyraMacExe.empty()) {
|
||||
warning("SoundMacRes::init(): Legend of Kyrandia resource fork not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// This will also test whether the resource containers are available.
|
||||
if (!setQuality(true))
|
||||
return false;
|
||||
|
||||
// Test actual resource fork reading...
|
||||
Common::SeekableReadStream *test = getResource(2, MKTAG('S', 'M', 'O', 'D'));
|
||||
if (!test) {
|
||||
warning("SoundMacRes::init(): Resource fork read test failed for 'Legend of Kyrandia' executable");
|
||||
return false;
|
||||
}
|
||||
delete test;
|
||||
|
||||
test = getResource(202, MKTAG('S', 'O', 'N', 'G'));
|
||||
if (!test) {
|
||||
warning("SoundMacRes::init(): Resource fork read test failed for 'HQ_Music.res'");
|
||||
return false;
|
||||
}
|
||||
delete test;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *SoundMacRes::getResource(uint16 id, uint32 type) {
|
||||
Common::StackLock lock(_mutex);
|
||||
Common::SeekableReadStream *res = nullptr;
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if ((res = _resMan[i].getResource(type, id)))
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool SoundMacRes::setQuality(bool hi) {
|
||||
Common::StackLock lock(_mutex);
|
||||
Common::Path s[2];
|
||||
s[0] = hi ? "HQ_Music.res" : "LQ_Music.res";
|
||||
s[1] = _kyraMacExe;
|
||||
int err = 0;
|
||||
|
||||
if (_stuffItArchive) {
|
||||
for (int i = 0; i < 2; ++i)
|
||||
err |= (_resMan[i].open(s[i], *_stuffItArchive) ? 0 : (1 << i));
|
||||
} else {
|
||||
for (int i = 0; i < 2; ++i)
|
||||
err |= (_resMan[i].open(s[i]) ? 0 : (1 << i));
|
||||
}
|
||||
|
||||
if (err) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (err & (1 << i))
|
||||
warning("SoundMacRes::setQuality(): Error opening resource container: '%s'", s[i].toString().c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SoundMac::SoundMac(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer), _driver(nullptr), _res(nullptr), _currentResourceSet(-1), _resIDMusic(nullptr), _talkieFlag(vm->gameFlags().isTalkie ? 1 : 0), _ready(false) {
|
||||
}
|
||||
|
||||
SoundMac::~SoundMac() {
|
||||
delete _driver;
|
||||
delete _res;
|
||||
}
|
||||
|
||||
Sound::kType SoundMac::getMusicType() const {
|
||||
return kMac;
|
||||
}
|
||||
|
||||
bool SoundMac::init(bool hiQuality) {
|
||||
if (_ready)
|
||||
return true;
|
||||
|
||||
_res = new SoundMacRes(_vm);
|
||||
if (!(_res && _res->init()))
|
||||
return false;
|
||||
|
||||
_driver = new HalestormDriver(_res, _mixer);
|
||||
|
||||
if (!(_driver && _driver->init(hiQuality, (hiQuality && _talkieFlag) ? HalestormDriver::kSimple : HalestormDriver::kNone, 1 + _talkieFlag, HS_16BITOUTPUT)))
|
||||
return false;
|
||||
|
||||
setQuality(hiQuality);
|
||||
|
||||
_ready = true;
|
||||
updateVolumeSettings();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundMac::selectAudioResourceSet(int set) {
|
||||
if (set < 0 || set > 2 || set == _currentResourceSet)
|
||||
return;
|
||||
|
||||
_currentResourceSet = set;
|
||||
|
||||
if (set == 0)
|
||||
_resIDMusic = _resIDMusicIntro;
|
||||
else if (set == 1)
|
||||
_resIDMusic = _resIDMusicIngame;
|
||||
}
|
||||
|
||||
void SoundMac::playTrack(uint8 track) {
|
||||
if (!_musicEnabled || !_ready)
|
||||
return;
|
||||
|
||||
int loop = 0;
|
||||
|
||||
if (_currentResourceSet == kMusicIntro) {
|
||||
track -= 2;
|
||||
assert(track < 4);
|
||||
} else if (track == 0xff || track == 3) {
|
||||
return;
|
||||
} else if (track == 0 || track == 1) {
|
||||
beginFadeOut();
|
||||
return;
|
||||
} else if (_currentResourceSet == kMusicFinale && track == 2) {
|
||||
_driver->doCommand(HalestormDriver::kSongPlayLoop, 0x12c);
|
||||
return;
|
||||
} else if (_currentResourceSet == kMusicFinale && track == 4) {
|
||||
_driver->doCommand(HalestormDriver::kSongPlayLoop, 0x12d);
|
||||
return;
|
||||
} else {
|
||||
track -= 11;
|
||||
assert(track < 35);
|
||||
loop = _musicLoopTable[track];
|
||||
}
|
||||
|
||||
_driver->doCommand(loop ? HalestormDriver::kSongPlayLoop : HalestormDriver::kSongPlayOnce, _resIDMusic[track]);
|
||||
}
|
||||
|
||||
void SoundMac::haltTrack() {
|
||||
if (_ready)
|
||||
_driver->doCommand(HalestormDriver::kSongAbort);
|
||||
}
|
||||
|
||||
void SoundMac::playSoundEffect(uint16 track, uint8) {
|
||||
if (!_sfxEnabled || !_ready)
|
||||
return;
|
||||
|
||||
if (_currentResourceSet == kMusicIntro) {
|
||||
if (track > 21 && track < 38)
|
||||
_driver->startSoundEffect(_resIDSfxIntro[_talkieFlag][_soundEffectDefsIntro[track - 22].number]);
|
||||
} else {
|
||||
const SoundEffectDef *se = &_soundEffectDefsIngame[track];
|
||||
if (se->note)
|
||||
_driver->enqueueSoundEffect(_resIDSfxIngame[_talkieFlag][se->number], se->rate, se->note);
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundMac::isPlaying() const {
|
||||
return _ready && _driver->doCommand(HalestormDriver::kSongIsPlaying);
|
||||
}
|
||||
|
||||
void SoundMac::beginFadeOut() {
|
||||
if (!_ready)
|
||||
return;
|
||||
|
||||
if (!isPlaying())
|
||||
return;
|
||||
|
||||
_driver->doCommand(HalestormDriver::kSongFadeOut, 30);
|
||||
while (_driver->doCommand(HalestormDriver::kSongFadeGetState) >= 16)
|
||||
_vm->delay(8);
|
||||
_driver->doCommand(HalestormDriver::kSongAbort);
|
||||
_driver->doCommand(HalestormDriver::kSongFadeReset, 256);
|
||||
}
|
||||
|
||||
void SoundMac::updateVolumeSettings() {
|
||||
if (!_ready)
|
||||
return;
|
||||
|
||||
bool mute = ConfMan.hasKey("mute") ? ConfMan.getBool("mute") : false;
|
||||
_driver->setMusicVolume(CLIP<int>(mute ? 0 : ConfMan.getInt("music_volume"), 0, Audio::Mixer::kMaxMixerVolume));
|
||||
_driver->setSoundEffectVolume(CLIP<int>(mute ? 0 : ConfMan.getInt("sfx_volume"), 0, Audio::Mixer::kMaxMixerVolume));
|
||||
}
|
||||
|
||||
void SoundMac::enableMusic(int enable) {
|
||||
if (enable && enable != _musicEnabled)
|
||||
setQuality(enable == 1);
|
||||
_musicEnabled = enable;
|
||||
}
|
||||
|
||||
void SoundMac::setQuality(bool hi) {
|
||||
static const uint16 resIDs[2][30] = {
|
||||
{
|
||||
0x1b5b, 0x1b5c, 0x1b5e, 0x1b62, 0x1b63, 0x1b6b, 0x1b6c, 0x1b6d,
|
||||
0x1b6e, 0x1b6f, 0x1b70, 0x1b71, 0x1b72, 0x1b73, 0x1b74, 0x1b75,
|
||||
0x1b76, 0x1b77, 0x1b78, 0x1b79, 0x1b7a, 0x1b7b, 0x1b7c, 0x1b7d,
|
||||
0x1b7e, 0x1b8a, 0x1bbc, 0x1bbd, 0x1bbe, 0xffff
|
||||
},
|
||||
{
|
||||
0x1b97, 0x1b98, 0x1b9a, 0x1b9e, 0x1b9f, 0x1b6b, 0x1b6c, 0x1b6d,
|
||||
0x1b6e, 0x1b6f, 0x1b70, 0x1b71, 0x1b72, 0x1b73, 0x1b74, 0x1b75,
|
||||
0x1b76, 0x1b77, 0x1b78, 0x1b79, 0x1b7a, 0x1b7b, 0x1b7c, 0x1b7d,
|
||||
0x1b7e, 0x1b8a, 0x1bbc, 0x1bbd, 0x1bbe, 0xffff
|
||||
}
|
||||
};
|
||||
|
||||
if (!(_driver && _res))
|
||||
return;
|
||||
|
||||
_driver->doCommand(HalestormDriver::kSongAbort);
|
||||
_driver->stopAllSoundEffects();
|
||||
_driver->releaseSamples();
|
||||
|
||||
_res->setQuality(hi);
|
||||
|
||||
if (hi) {
|
||||
_driver->changeSystemVoices(7 - _talkieFlag, 4, 1 + _talkieFlag);
|
||||
_driver->doCommand(HalestormDriver::kSetRateAndIntrplMode, 3 + (_talkieFlag << 1));
|
||||
} else {
|
||||
_driver->changeSystemVoices(4, 3 + _talkieFlag, 1 + _talkieFlag);
|
||||
_driver->doCommand(HalestormDriver::kSetRateAndIntrplMode, 2 + _talkieFlag);
|
||||
}
|
||||
|
||||
_driver->registerSamples(resIDs[_talkieFlag], true);
|
||||
}
|
||||
|
||||
const uint16 SoundMac::_resIDMusicIntro[4] {
|
||||
0x00c8, 0x00c9, 0x00ca, 0x00cb
|
||||
};
|
||||
|
||||
const uint16 SoundMac::_resIDMusicIngame[35] = {
|
||||
0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b,
|
||||
0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073,
|
||||
0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x01f4,
|
||||
0x01f5, 0x01f6, 0x01f7, 0x01f8, 0x01f9, 0x01fa, 0x01fb, 0x01fc,
|
||||
0x01fd, 0x01fe, 0x01ff
|
||||
};
|
||||
|
||||
|
||||
const uint8 SoundMac::_musicLoopTable[35] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x00, 0x00
|
||||
};
|
||||
|
||||
const uint16 SoundMac::_resIDSfxIntro[2][39] = {
|
||||
{
|
||||
0x1b58, 0x1b59, 0x1b5a, 0x1b5b, 0x1b5c, 0x1b5d, 0x1b5e, 0x1b5f,
|
||||
0x1b60, 0x1b61, 0x1b62, 0x1b63, 0x1b64, 0x1b65, 0x1b66, 0x1b67,
|
||||
0x1b68, 0x1b69, 0x1b6a, 0x1b6d, 0x1b6c, 0x1b7a, 0x1bbc, 0x1bbd,
|
||||
0x1bbe, 0x1b71, 0x1b72, 0x1b73, 0x1b74, 0x1b75, 0x1b76, 0x1b77,
|
||||
0x1b78, 0x1b79, 0x1b7a, 0x1b7b, 0x1b7c, 0x1b7d, 0x1b7e
|
||||
},
|
||||
{
|
||||
0x1b94, 0x1b95, 0x1b96, 0x1b97, 0x1b8b, 0x1b99, 0x1b9a, 0x1b9b,
|
||||
0x1b9c, 0x1b9d, 0x1b9e, 0x1b9f, 0x1ba0, 0x1ba1, 0x1ba2, 0x1ba3,
|
||||
0x1ba4, 0x1b69, 0x1b6a, 0x1b6d, 0x1b6c, 0x1b7a, 0x1bbc, 0x1bbd,
|
||||
0x1bbe, 0x1b71, 0x1b72, 0x1b73, 0x1b74, 0x1b75, 0x1b76, 0x1b77,
|
||||
0x1b78, 0x1b79, 0x1b7a, 0x1b7b, 0x1b7c, 0x1b7d, 0x1b7e
|
||||
}
|
||||
};
|
||||
|
||||
const uint16 SoundMac::_resIDSfxIngame[2][39] = {
|
||||
{
|
||||
0x1b58, 0x1b59, 0x1b5a, 0x1b5b, 0x1b5c, 0x1b5d, 0x1b5e, 0x1b5f,
|
||||
0x1b60, 0x1b61, 0x1b62, 0x1b63, 0x1b64, 0x1b65, 0x1b66, 0x1b67,
|
||||
0x1b68, 0x1b69, 0x1b6a, 0x1b6b, 0x1b6c, 0x1b6d, 0x1b6e, 0x1b6f,
|
||||
0x1b70, 0x1b71, 0x1b72, 0x1b73, 0x1b74, 0x1b75, 0x1b76, 0x1b77,
|
||||
0x1b78, 0x1b8a, 0x1b7a, 0x1b7b, 0x1b7c, 0x1b7d, 0x1b7e
|
||||
},
|
||||
{
|
||||
0x1b94, 0x1b95, 0x1b96, 0x1b97, 0x1b98, 0x1b99, 0x1b9a, 0x1b9b,
|
||||
0x1b9c, 0x1b9d, 0x1b9e, 0x1b9f, 0x1ba0, 0x1ba1, 0x1ba2, 0x1ba3,
|
||||
0x1ba4, 0x1b69, 0x1b6a, 0x1b6b, 0x1b6c, 0x1b6d, 0x1b6e, 0x1b6f,
|
||||
0x1b70, 0x1b71, 0x1b72, 0x1b73, 0x1b74, 0x1b75, 0x1b76, 0x1b77,
|
||||
0x1b78, 0x1b8a, 0x1b7a, 0x1b7b, 0x1b7c, 0x1b7d, 0x1b7e
|
||||
}
|
||||
};
|
||||
|
||||
const SoundMac::SoundEffectDef SoundMac::_soundEffectDefsIntro[16] = {
|
||||
{ 0x3c, 0x19, 0x252c, 0x6e }, { 0x3c, 0x19, 0x252c, 0x6e }, { 0x3c, 0x19, 0x252c, 0x6e }, { 0x3c, 0x13, 0x1B91, 0x6e },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x16, 0x2677, 0x6e }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x17, 0x1198, 0x6e }, { 0x3c, 0x19, 0x252c, 0x6e },
|
||||
{ 0x3c, 0x18, 0x22d1, 0x6e }, { 0x3c, 0x19, 0x252c, 0x6e }, { 0x45, 0x03, 0x0224, 0x6e }, { 0x3c, 0x16, 0x2677, 0x6e }
|
||||
};
|
||||
|
||||
const SoundMac::SoundEffectDef SoundMac::_soundEffectDefsIngame[120] = {
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x01, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x3c, 0x13, 0x0156, 0x78 }, { 0x3c, 0x14, 0x272c, 0x78 }, { 0x3c, 0x15, 0x1b91, 0x78 }, { 0x3c, 0x16, 0x1e97, 0x78 },
|
||||
{ 0x3c, 0x17, 0x122b, 0x78 }, { 0x3c, 0x16, 0x1e97, 0x78 }, { 0x45, 0x03, 0x0224, 0x78 }, { 0x3c, 0x16, 0x1e97, 0x78 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x19, 0x252c, 0x78 }, { 0x2c, 0x04, 0x0910, 0x78 }, { 0x3c, 0x19, 0x252c, 0x78 },
|
||||
{ 0x3c, 0x1a, 0x3aeb, 0x78 }, { 0x25, 0x1b, 0x138b, 0x78 }, { 0x18, 0x03, 0x0f52, 0x78 }, { 0x3e, 0x1c, 0x0622, 0x78 },
|
||||
{ 0x3b, 0x1c, 0x0754, 0x78 }, { 0x16, 0x03, 0x206f, 0x78 }, { 0x3c, 0x19, 0x252c, 0x78 }, { 0x3c, 0x1d, 0x09ea, 0x78 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x14, 0x272c, 0x78 },
|
||||
{ 0x3c, 0x1e, 0x036e, 0x78 }, { 0x3c, 0x17, 0x122b, 0x78 }, { 0x4e, 0x0b, 0x0991, 0x78 }, { 0x47, 0x1b, 0x02bc, 0x78 },
|
||||
{ 0x4c, 0x1b, 0x0211, 0x78 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x13, 0x0156, 0x78 }, { 0x3c, 0x13, 0x0156, 0x78 },
|
||||
{ 0x3c, 0x1f, 0x0e9e, 0x78 }, { 0x3c, 0x20, 0x010c, 0x78 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x19, 0x252c, 0x78 },
|
||||
{ 0x3c, 0x21, 0x0f7c, 0x78 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x2a, 0x0b, 0x4c47, 0x78 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x1b, 0x0528, 0x78 }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x2c, 0x04, 0x0910, 0x78 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x22, 0x0aee, 0x78 },
|
||||
{ 0x3c, 0x16, 0x1e97, 0x78 }, { 0x3c, 0x15, 0x1b91, 0x78 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x14, 0x272c, 0x78 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x22, 0x0aee, 0x78 },
|
||||
{ 0x3c, 0x14, 0x272c, 0x78 }, { 0x32, 0x23, 0x1419, 0x9c }, { 0x3c, 0x19, 0x171c, 0x78 }, { 0x3c, 0x14, 0x272c, 0x78 },
|
||||
{ 0x3e, 0x1c, 0x0622, 0x78 }, { 0x43, 0x13, 0x0201, 0x78 }, { 0x3c, 0x24, 0x1243, 0x5a }, { 0x3e, 0x20, 0x00ee, 0x78 },
|
||||
{ 0x3c, 0x19, 0x252c, 0x78 }, { 0x29, 0x04, 0x19ea, 0x78 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x20, 0x010c, 0x78 }, { 0x3c, 0x25, 0x30b6, 0x78 }, { 0x3c, 0x19, 0x252c, 0x78 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 },
|
||||
{ 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x16, 0x1e97, 0x78 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x1a, 0x3aeb, 0x78 },
|
||||
{ 0x1b, 0x04, 0x39f3, 0x78 }, { 0x30, 0x23, 0x1699, 0x50 }, { 0x3c, 0x15, 0x1b91, 0x78 }, { 0x29, 0x06, 0x19ea, 0x50 },
|
||||
{ 0x3c, 0x19, 0x252c, 0x78 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x1a, 0x3aeb, 0x78 },
|
||||
{ 0x3c, 0x19, 0x252c, 0x78 }, { 0x3c, 0x26, 0x0713, 0x78 }, { 0x3c, 0x26, 0x0713, 0x78 }, { 0x3c, 0x14, 0x272c, 0x78 },
|
||||
{ 0x30, 0x23, 0x1699, 0x50 }, { 0x30, 0x23, 0x1699, 0x50 }, { 0x00, 0x00, 0x0000, 0x00 }, { 0x3c, 0x13, 0x0156, 0x78 }
|
||||
};
|
||||
|
||||
#undef HS_16BITOUTPUT
|
||||
#undef HS_INTERPOLATION
|
||||
|
||||
} // End of namespace Kyra
|
||||
59
engines/kyra/sound/sound_mac_res.h
Normal file
59
engines/kyra/sound/sound_mac_res.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* 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 KYRA_SOUND_MACRES_H
|
||||
#define KYRA_SOUND_MACRES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/mutex.h"
|
||||
|
||||
#include "kyra/sound/drivers/halestorm.h"
|
||||
|
||||
namespace Common {
|
||||
class Archive;
|
||||
class MacResManager;
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class KyraEngine_v1;
|
||||
|
||||
class SoundMacRes final : public HalestormLoader {
|
||||
public:
|
||||
SoundMacRes(KyraEngine_v1 *vm);
|
||||
~SoundMacRes() override;
|
||||
bool init();
|
||||
bool setQuality(bool hi);
|
||||
Common::SeekableReadStream *getResource(uint16 id, uint32 type) override;
|
||||
|
||||
private:
|
||||
Common::Path _kyraMacExe;
|
||||
Common::MacResManager *_resMan;
|
||||
Common::Archive *_stuffItArchive;
|
||||
Common::Mutex _mutex;
|
||||
const bool _isTalkie;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
234
engines/kyra/sound/sound_pc98_darkmoon.cpp
Normal file
234
engines/kyra/sound/sound_pc98_darkmoon.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#include "kyra/sound/sound_intern.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
#include "kyra/sound/drivers/capcom98.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
SoundPC98_Darkmoon::SoundPC98_Darkmoon(KyraEngine_v1 *vm, MidiDriver::DeviceHandle dev, Audio::Mixer *mixer) : Sound(vm, mixer),
|
||||
_vm(vm), _driver(nullptr), _soundData(nullptr), _currentResourceSet(-1), _ready(false), _dev(dev), _drvType(kPC98), _lastTrack(-1) {
|
||||
|
||||
memset(&_resInfo, 0, sizeof(_resInfo));
|
||||
_soundData = new uint8[20600];
|
||||
memset(_soundData, 0, 20600);
|
||||
_fileBuffer = new uint8[10500];
|
||||
memset(_fileBuffer, 0, 10500);
|
||||
|
||||
MusicType type = MidiDriver::getMusicType(dev);
|
||||
if (type == MT_MT32)
|
||||
_drvType = kMidiMT32;
|
||||
else if (type == MT_GM)
|
||||
_drvType = kMidiGM;
|
||||
}
|
||||
|
||||
SoundPC98_Darkmoon::~SoundPC98_Darkmoon() {
|
||||
delete _driver;
|
||||
delete[] _soundData;
|
||||
delete[] _fileBuffer;
|
||||
for (int i = 0; i < 3; i++)
|
||||
initAudioResourceInfo(i, nullptr);
|
||||
}
|
||||
|
||||
Sound::kType SoundPC98_Darkmoon::getMusicType() const {
|
||||
return _drvType;
|
||||
}
|
||||
|
||||
Sound::kType SoundPC98_Darkmoon::getSfxType() const {
|
||||
return kPC98;
|
||||
}
|
||||
|
||||
bool SoundPC98_Darkmoon::init() {
|
||||
_driver = new CapcomPC98AudioDriver(_mixer, _dev);
|
||||
_ready = (_soundData && _driver && _driver->isUsable());
|
||||
return _ready;
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::initAudioResourceInfo(int set, void *info) {
|
||||
if (set < kMusicIntro || set > kMusicFinale)
|
||||
return;
|
||||
delete _resInfo[set];
|
||||
_resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : nullptr;
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::selectAudioResourceSet(int set) {
|
||||
if (set < kMusicIntro || set > kMusicFinale || set == _currentResourceSet || !_ready)
|
||||
return;
|
||||
if (_resInfo[set])
|
||||
_currentResourceSet = set;
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::loadSoundFile(uint file) {
|
||||
if (!_ready)
|
||||
return;
|
||||
|
||||
if (file < res()->fileListSize)
|
||||
loadSoundFile(res()->fileList[file]);
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::loadSoundFile(const Common::Path &name) {
|
||||
if (!_ready)
|
||||
return;
|
||||
|
||||
haltTrack();
|
||||
stopAllSoundEffects();
|
||||
|
||||
Common::Path path(name);
|
||||
path.appendInPlace(_drvType == kPC98 ? ".SDO" : ".SDM");
|
||||
if (!_ready || _soundFileLoaded == path)
|
||||
return;
|
||||
|
||||
Common::SeekableReadStream *in = _vm->resource()->createReadStream(path);
|
||||
if (!in)
|
||||
error("SoundPC98_Darkmoon::loadSoundFile(): Failed to load sound file '%s'", path.toString().c_str());
|
||||
|
||||
uint16 sz = in->readUint16LE();
|
||||
uint8 cmp = in->readByte();
|
||||
in->seek(1, SEEK_CUR);
|
||||
uint32 outSize = in->readUint32LE();
|
||||
if ((cmp == 0 && outSize > 10500) || (cmp != 0 && outSize > 20600))
|
||||
error("SoundPC98_Darkmoon::loadSoundFile(): Failed to load sound file '%s'", path.toString().c_str());
|
||||
sz -= in->pos();
|
||||
in->seek(2, SEEK_CUR);
|
||||
|
||||
memset(_fileBuffer, 0, 10500);
|
||||
uint16 readSize = in->read(_fileBuffer, 10500);
|
||||
assert(sz == readSize);
|
||||
delete in;
|
||||
|
||||
memset(_soundData, 0, 20600);
|
||||
if (cmp == 0) {
|
||||
memcpy(_soundData, _fileBuffer, outSize);
|
||||
} else if (cmp == 3) {
|
||||
Screen::decodeFrame3(_fileBuffer, _soundData, outSize, true);
|
||||
} else if (cmp == 4) {
|
||||
Screen::decodeFrame4(_fileBuffer, _soundData, outSize);
|
||||
} else {
|
||||
error("SoundPC98_Darkmoon::loadSoundFile(): Failed to load sound file '%s'", path.toString().c_str());
|
||||
}
|
||||
|
||||
uint16 instrOffs = READ_LE_UINT16(_soundData);
|
||||
if (instrOffs >= 20600)
|
||||
error("SoundPC98_Darkmoon::loadSoundFile(): Failed to load sound file '%s'", path.toString().c_str());
|
||||
|
||||
_driver->loadFMInstruments(_soundData + instrOffs);
|
||||
_driver->reset();
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::playTrack(uint8 track) {
|
||||
if (track == 0 || track == 2)
|
||||
_lastTrack = track;
|
||||
playSoundEffect(track, 127);
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::haltTrack() {
|
||||
if (!_ready)
|
||||
return;
|
||||
_driver->stopSong();
|
||||
_lastTrack = -1;
|
||||
}
|
||||
|
||||
bool SoundPC98_Darkmoon::isPlaying() const {
|
||||
return _ready && _driver && _driver->songIsPlaying();
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::playSoundEffect(uint16 track, uint8 vol) {
|
||||
if (!_ready)
|
||||
return;
|
||||
|
||||
if (track == 0 || track == 2) {
|
||||
restartBackgroundMusic();
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8 *data = getData(track);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (track < 52 || track > 67) {
|
||||
if (_sfxEnabled)
|
||||
_driver->startSoundEffect(data, vol);
|
||||
} else if (_musicEnabled) {
|
||||
_lastTrack = track;
|
||||
_driver->startSong(data, vol, false);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::stopAllSoundEffects() {
|
||||
if (_ready)
|
||||
_driver->stopSoundEffect();
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::beginFadeOut() {
|
||||
_driver->fadeOut();
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::pause(bool paused) {
|
||||
if (_ready && paused)
|
||||
_driver->allNotesOff();
|
||||
}
|
||||
|
||||
int SoundPC98_Darkmoon::checkTrigger() {
|
||||
return _driver ? _driver->checkSoundMarker() : 99;
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::restartBackgroundMusic() {
|
||||
if (_lastTrack == -1) {
|
||||
haltTrack();
|
||||
stopAllSoundEffects();
|
||||
} else {
|
||||
_lastTrack = -1;
|
||||
const uint8 *data = getData(0);
|
||||
if (!data)
|
||||
return;
|
||||
if (_musicEnabled)
|
||||
_driver->startSong(data, 127, true);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8 *SoundPC98_Darkmoon::getData(uint16 track) const {
|
||||
if (!_ready || track >= 120)
|
||||
return nullptr;
|
||||
|
||||
uint16 offset = READ_LE_UINT16(&_soundData[(track + 1) << 1]);
|
||||
return (offset < 20600) ? &_soundData[offset] : nullptr;
|
||||
}
|
||||
|
||||
void SoundPC98_Darkmoon::updateVolumeSettings() {
|
||||
if (!_driver || !_ready)
|
||||
return;
|
||||
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
|
||||
_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
127
engines/kyra/sound/sound_pc98_eob.cpp
Normal file
127
engines/kyra/sound/sound_pc98_eob.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#include "kyra/sound/sound_intern.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
#include "kyra/sound/drivers/mlalf98.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
SoundPC98_EoB::SoundPC98_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer),
|
||||
_vm(vm), _driver(0), _currentResourceSet(-1), _sfxDelay(0), _ready(false) {
|
||||
memset(_resInfo, 0, sizeof(_resInfo));
|
||||
}
|
||||
|
||||
SoundPC98_EoB::~SoundPC98_EoB() {
|
||||
delete _driver;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
initAudioResourceInfo(i, 0);
|
||||
}
|
||||
|
||||
Sound::kType SoundPC98_EoB::getMusicType() const {
|
||||
return kPC98;
|
||||
}
|
||||
|
||||
bool SoundPC98_EoB::init() {
|
||||
_driver = new MLALF98(_mixer, MLALF98::kType9801_86);
|
||||
_ready = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundPC98_EoB::initAudioResourceInfo(int set, void *info) {
|
||||
delete _resInfo[set];
|
||||
_resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0;
|
||||
}
|
||||
|
||||
void SoundPC98_EoB::selectAudioResourceSet(int set) {
|
||||
if (set == _currentResourceSet || !_ready)
|
||||
return;
|
||||
|
||||
if (!_resInfo[set])
|
||||
return;
|
||||
|
||||
_currentResourceSet = set;
|
||||
}
|
||||
|
||||
void SoundPC98_EoB::loadSoundFile(uint file) {
|
||||
if (!_ready)
|
||||
return;
|
||||
|
||||
if (file >= _resInfo[_currentResourceSet]->fileListSize)
|
||||
return;
|
||||
|
||||
Common::SeekableReadStream *s = _vm->resource()->createReadStream(_resInfo[_currentResourceSet]->fileList[file]);
|
||||
_driver->loadMusicData(s);
|
||||
delete s;
|
||||
}
|
||||
|
||||
void SoundPC98_EoB::loadSfxFile(const Common::Path &file) {
|
||||
if (!_ready)
|
||||
return;
|
||||
|
||||
Common::SeekableReadStream *s = _vm->resource()->createReadStream(file);
|
||||
_driver->loadSoundEffectData(s);
|
||||
delete s;
|
||||
}
|
||||
|
||||
void SoundPC98_EoB::playTrack(uint8 track) {
|
||||
if (!_musicEnabled || !_ready)
|
||||
return;
|
||||
|
||||
_driver->allChannelsOff();
|
||||
loadSoundFile(track);
|
||||
_driver->startMusic(0);
|
||||
}
|
||||
|
||||
void SoundPC98_EoB::haltTrack() {
|
||||
if (!_ready)
|
||||
return;
|
||||
playTrack(0);
|
||||
}
|
||||
|
||||
void SoundPC98_EoB::playSoundEffect(uint16 track, uint8) {
|
||||
if (_currentResourceSet != kMusicIngame || !_sfxEnabled || !_ready || track >= 120 || (track != 28 && _sfxDelay > _vm->_system->getMillis()))
|
||||
return;
|
||||
_driver->startSoundEffect(track);
|
||||
if (track == 28)
|
||||
_sfxDelay = _vm->_system->getMillis() + 1440;
|
||||
}
|
||||
|
||||
void SoundPC98_EoB::updateVolumeSettings() {
|
||||
if (!_driver || !_ready)
|
||||
return;
|
||||
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
|
||||
_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
145
engines/kyra/sound/sound_pc98_lok.cpp
Normal file
145
engines/kyra/sound/sound_pc98_lok.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/* 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 "kyra/sound/sound_intern.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
|
||||
#include "audio/softsynth/fmtowns_pc98/towns_pc98_driver.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
SoundPC98_LoK::SoundPC98_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
|
||||
Sound(vm, mixer), _musicTrackData(nullptr), _sfxTrackData(nullptr), _lastTrack(-1), _driver(nullptr), _currentResourceSet(0) {
|
||||
memset(&_resInfo, 0, sizeof(_resInfo));
|
||||
}
|
||||
|
||||
SoundPC98_LoK::~SoundPC98_LoK() {
|
||||
delete[] _musicTrackData;
|
||||
delete[] _sfxTrackData;
|
||||
delete _driver;
|
||||
for (int i = 0; i < 3; i++)
|
||||
initAudioResourceInfo(i, nullptr);
|
||||
}
|
||||
|
||||
bool SoundPC98_LoK::init() {
|
||||
_driver = new TownsPC98_AudioDriver(_mixer, TownsPC98_AudioDriver::kType26);
|
||||
bool reslt = _driver->init();
|
||||
updateVolumeSettings();
|
||||
|
||||
return reslt;
|
||||
}
|
||||
|
||||
void SoundPC98_LoK::initAudioResourceInfo(int set, void *info) {
|
||||
if (set >= kMusicIntro && set <= kMusicFinale) {
|
||||
delete _resInfo[set];
|
||||
_resInfo[set] = info ? new Common::String(((SoundResourceInfo_PC98*)info)->pattern) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundPC98_LoK::selectAudioResourceSet(int set) {
|
||||
if (set >= kMusicIntro && set <= kMusicFinale) {
|
||||
if (_resInfo[set])
|
||||
_currentResourceSet = set;
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundPC98_LoK::hasSoundFile(uint file) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundPC98_LoK::loadSoundFile(uint) {
|
||||
if (_currentResourceSet == kMusicIntro) {
|
||||
delete[] _sfxTrackData;
|
||||
_sfxTrackData = nullptr;
|
||||
|
||||
int dataSize = 0;
|
||||
const uint8 *tmp = _vm->staticres()->loadRawData(k1PC98IntroSfx, dataSize);
|
||||
|
||||
if (!tmp) {
|
||||
warning("Could not load static intro sound effects data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
_sfxTrackData = new uint8[dataSize];
|
||||
memcpy(_sfxTrackData, tmp, dataSize);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundPC98_LoK::loadSoundFile(const Common::Path &file) {
|
||||
delete[] _sfxTrackData;
|
||||
_sfxTrackData = _vm->resource()->fileData(file, nullptr);
|
||||
}
|
||||
|
||||
void SoundPC98_LoK::playTrack(uint8 track) {
|
||||
track -= 1;
|
||||
|
||||
if (track == _lastTrack && _musicEnabled)
|
||||
return;
|
||||
|
||||
beginFadeOut();
|
||||
|
||||
Common::String musicFile = Common::String::format(resPattern(), track);
|
||||
delete[] _musicTrackData;
|
||||
_musicTrackData = _vm->resource()->fileData(musicFile.c_str(), nullptr);
|
||||
if (_musicEnabled)
|
||||
_driver->loadMusicData(_musicTrackData);
|
||||
|
||||
_lastTrack = track;
|
||||
}
|
||||
|
||||
void SoundPC98_LoK::haltTrack() {
|
||||
_lastTrack = -1;
|
||||
_driver->reset();
|
||||
}
|
||||
|
||||
void SoundPC98_LoK::beginFadeOut() {
|
||||
if (!_driver->musicPlaying())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
_driver->fadeStep();
|
||||
_vm->delay(32);
|
||||
}
|
||||
haltTrack();
|
||||
}
|
||||
|
||||
void SoundPC98_LoK::playSoundEffect(uint16 track, uint8) {
|
||||
if (!_sfxTrackData)
|
||||
return;
|
||||
|
||||
_driver->loadSoundEffectData(_sfxTrackData, track);
|
||||
}
|
||||
|
||||
void SoundPC98_LoK::updateVolumeSettings() {
|
||||
if (!_driver)
|
||||
return;
|
||||
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
|
||||
_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
272
engines/kyra/sound/sound_pc98_v2.cpp
Normal file
272
engines/kyra/sound/sound_pc98_v2.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/* 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 "kyra/sound/sound_intern.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
|
||||
#include "audio/softsynth/fmtowns_pc98/towns_pc98_driver.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "backends/audiocd/audiocd.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
|
||||
Sound(vm, mixer), _currentSFX(nullptr), _musicTrackData(nullptr), _sfxTrackData(nullptr), _lastTrack(-1), _driver(nullptr), _useFmSfx(false), _currentResourceSet(0) {
|
||||
memset(&_resInfo, 0, sizeof(_resInfo));
|
||||
}
|
||||
|
||||
SoundTownsPC98_v2::~SoundTownsPC98_v2() {
|
||||
delete[] _musicTrackData;
|
||||
delete[] _sfxTrackData;
|
||||
delete _driver;
|
||||
for (int i = 0; i < 3; i++)
|
||||
initAudioResourceInfo(i, nullptr);
|
||||
}
|
||||
|
||||
bool SoundTownsPC98_v2::init() {
|
||||
_driver = new TownsPC98_AudioDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
|
||||
TownsPC98_AudioDriver::kType86 : TownsPC98_AudioDriver::kTypeTowns);
|
||||
|
||||
if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
|
||||
if (_resInfo[_currentResourceSet])
|
||||
if (_resInfo[_currentResourceSet]->cdaTableSize) {
|
||||
if (!_vm->existExtractedCDAudioFiles()
|
||||
&& !_vm->isDataAndCDAudioReadFromSameCD()) {
|
||||
_vm->warnMissingExtractedCDAudio();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize CD for audio
|
||||
bool hasRealCD = g_system->getAudioCDManager()->open();
|
||||
|
||||
// FIXME: While checking for 'track1.XXX(X)' looks like
|
||||
// a good idea, we should definitely not be doing this
|
||||
// here. Basically our filenaming scheme could change
|
||||
// or we could add support for other audio formats. Also
|
||||
// this misses the possibility that we play the tracks
|
||||
// right off CD. So we should find another way to
|
||||
// check if we have access to CD audio.
|
||||
Resource *r = _vm->resource();
|
||||
if (_musicEnabled &&
|
||||
(hasRealCD || r->exists("track1.mp3") || r->exists("track1.ogg") || r->exists("track1.flac") || r->exists("track1.fla")
|
||||
|| r->exists("track01.mp3") || r->exists("track01.ogg") || r->exists("track01.flac") || r->exists("track01.fla")))
|
||||
_musicEnabled = 2;
|
||||
else
|
||||
_musicEnabled = 1;
|
||||
_useFmSfx = false;
|
||||
|
||||
} else {
|
||||
_useFmSfx = true;
|
||||
}
|
||||
|
||||
bool reslt = _driver->init();
|
||||
updateVolumeSettings();
|
||||
return reslt;
|
||||
}
|
||||
|
||||
void SoundTownsPC98_v2::initAudioResourceInfo(int set, void *info) {
|
||||
if (set >= kMusicIntro && set <= kMusicFinale) {
|
||||
delete _resInfo[set];
|
||||
_resInfo[set] = info ? new SoundResourceInfo_TownsPC98V2(*(SoundResourceInfo_TownsPC98V2*)info) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundTownsPC98_v2::selectAudioResourceSet(int set) {
|
||||
if (set >= kMusicIntro && set <= kMusicFinale) {
|
||||
if (_resInfo[set])
|
||||
_currentResourceSet = set;
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundTownsPC98_v2::hasSoundFile(uint file) const {
|
||||
if (file < res()->fileListSize)
|
||||
return (res()->fileList[file] != nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoundTownsPC98_v2::loadSoundFile(const Common::Path &file) {
|
||||
delete[] _sfxTrackData;
|
||||
_sfxTrackData = _vm->resource()->fileData(file, nullptr);
|
||||
}
|
||||
|
||||
void SoundTownsPC98_v2::process() {
|
||||
g_system->getAudioCDManager()->update();
|
||||
}
|
||||
|
||||
void SoundTownsPC98_v2::playTrack(uint8 track) {
|
||||
if (track == _lastTrack && _musicEnabled)
|
||||
return;
|
||||
|
||||
int trackNum = -1;
|
||||
if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
|
||||
for (uint i = 0; i < res()->cdaTableSize >> 1; i++) {
|
||||
if (track == (uint8)READ_LE_UINT16(&res()->cdaTable[i * 2])) {
|
||||
trackNum = (int8)READ_LE_UINT16(&res()->cdaTable[i * 2 + 1]) - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beginFadeOut();
|
||||
|
||||
Common::String musicFile = res()->pattern ? Common::String::format(res()->pattern, track) : (res()->fileList ? res()->fileList[track] : nullptr);
|
||||
if (musicFile.empty())
|
||||
return;
|
||||
|
||||
delete[] _musicTrackData;
|
||||
|
||||
_musicTrackData = _vm->resource()->fileData(musicFile.c_str(), nullptr);
|
||||
_driver->loadMusicData(_musicTrackData, true);
|
||||
|
||||
if (_musicEnabled == 2 && trackNum != -1) {
|
||||
g_system->getAudioCDManager()->play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0);
|
||||
g_system->getAudioCDManager()->update();
|
||||
} else if (_musicEnabled) {
|
||||
_driver->cont();
|
||||
}
|
||||
|
||||
_lastTrack = track;
|
||||
}
|
||||
|
||||
void SoundTownsPC98_v2::haltTrack() {
|
||||
_lastTrack = -1;
|
||||
g_system->getAudioCDManager()->stop();
|
||||
g_system->getAudioCDManager()->update();
|
||||
_driver->reset();
|
||||
}
|
||||
|
||||
void SoundTownsPC98_v2::beginFadeOut() {
|
||||
if (!_driver->musicPlaying())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
_driver->fadeStep();
|
||||
_vm->delay(32);
|
||||
}
|
||||
|
||||
haltTrack();
|
||||
}
|
||||
|
||||
int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool) {
|
||||
static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
|
||||
static const char patternHOF[] = "%s.PCM";
|
||||
static const char patternLOL[] = "%s.VOC";
|
||||
|
||||
int h = 0;
|
||||
if (_currentSFX) {
|
||||
while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h].handle))
|
||||
h++;
|
||||
|
||||
if (h >= kNumChannelHandles) {
|
||||
h = 0;
|
||||
while (h < kNumChannelHandles && _soundChannels[h].priority > priority)
|
||||
++h;
|
||||
if (h < kNumChannelHandles)
|
||||
voiceStop(&_soundChannels[h].handle);
|
||||
}
|
||||
|
||||
if (h >= kNumChannelHandles)
|
||||
return 0;
|
||||
}
|
||||
|
||||
Common::String fileName = Common::String::format( _vm->game() == GI_LOL ? patternLOL : patternHOF, file);
|
||||
|
||||
uint8 *data = _vm->resource()->fileData(fileName.c_str(), nullptr);
|
||||
uint8 *src = data;
|
||||
if (!src)
|
||||
return 0;
|
||||
|
||||
uint16 sfxRate = rates[READ_LE_UINT16(src)];
|
||||
src += 2;
|
||||
bool compressed = (READ_LE_UINT16(src) & 1) ? true : false;
|
||||
src += 2;
|
||||
uint32 outsize = READ_LE_UINT32(src);
|
||||
uint8 *sfx = (uint8 *)malloc(outsize);
|
||||
uint8 *dst = sfx;
|
||||
src += 4;
|
||||
|
||||
if (compressed) {
|
||||
for (uint32 i = outsize; i;) {
|
||||
uint8 cnt = *src++;
|
||||
if (cnt & 0x80) {
|
||||
cnt &= 0x7F;
|
||||
memset(dst, *src++, cnt);
|
||||
} else {
|
||||
memcpy(dst, src, cnt);
|
||||
src += cnt;
|
||||
}
|
||||
dst += cnt;
|
||||
i -= cnt;
|
||||
}
|
||||
} else {
|
||||
memcpy(dst, src, outsize);
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < outsize; i++) {
|
||||
uint8 cmd = sfx[i];
|
||||
if (cmd & 0x80) {
|
||||
cmd = ~cmd;
|
||||
} else {
|
||||
cmd |= 0x80;
|
||||
if (cmd == 0xFF)
|
||||
cmd--;
|
||||
}
|
||||
if (cmd < 0x80)
|
||||
cmd = 0x80 - cmd;
|
||||
sfx[i] = cmd;
|
||||
}
|
||||
|
||||
_currentSFX = Audio::makeRawStream(sfx, outsize, sfxRate * 10, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN);
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundChannels[h].handle, _currentSFX, -1, volume);
|
||||
_soundChannels[h].priority = priority;
|
||||
if (handle)
|
||||
*handle = _soundChannels[h].handle;
|
||||
|
||||
delete[] data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void SoundTownsPC98_v2::playSoundEffect(uint16 track, uint8) {
|
||||
if (!_useFmSfx || !_sfxTrackData)
|
||||
return;
|
||||
|
||||
_driver->loadSoundEffectData(_sfxTrackData, track);
|
||||
}
|
||||
|
||||
void SoundTownsPC98_v2::updateVolumeSettings() {
|
||||
if (!_driver)
|
||||
return;
|
||||
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
|
||||
_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
455
engines/kyra/sound/sound_pc_midi.cpp
Normal file
455
engines/kyra/sound/sound_pc_midi.cpp
Normal file
@@ -0,0 +1,455 @@
|
||||
/* 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 "kyra/sound/sound_intern.h"
|
||||
|
||||
#include "kyra/resource/resource.h"
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "gui/message.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
SoundMidiPC::SoundMidiPC(KyraEngine_v1 *vm, Audio::Mixer *mixer, MidiDriver *driver, kType type) : Sound(vm, mixer) {
|
||||
_driver = driver;
|
||||
_output = nullptr;
|
||||
|
||||
_musicFile = _sfxFile = nullptr;
|
||||
_currentResourceSet = 0;
|
||||
memset(&_resInfo, 0, sizeof(_resInfo));
|
||||
|
||||
_music = MidiParser::createParser_XMIDI(MidiParser::defaultXMidiCallback, nullptr, 0);
|
||||
assert(_music);
|
||||
_music->property(MidiParser::mpDisableAllNotesOffMidiEvents, true);
|
||||
_music->property(MidiParser::mpDisableAutoStartPlayback, true);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
_sfx[i] = MidiParser::createParser_XMIDI(MidiParser::defaultXMidiCallback, nullptr, i + 1);
|
||||
assert(_sfx[i]);
|
||||
_sfx[i]->property(MidiParser::mpDisableAllNotesOffMidiEvents, true);
|
||||
_sfx[i]->property(MidiParser::mpDisableAutoStartPlayback, true);
|
||||
}
|
||||
|
||||
_musicVolume = _sfxVolume = 0;
|
||||
_fadeMusicOut = false;
|
||||
_fadeStartTime = 0;
|
||||
|
||||
_type = type;
|
||||
assert(_type == kMidiMT32 || _type == kMidiGM || _type == kPCSpkr);
|
||||
|
||||
// Only General MIDI isn't a Roland MT-32 MIDI implemenation,
|
||||
// even the PC Speaker driver is a Roland MT-32 based MIDI implementation.
|
||||
// Thus we set "_nativeMT32" for all types except Gerneral MIDI to true.
|
||||
_nativeMT32 = (_type != kMidiGM);
|
||||
|
||||
// KYRA1 does not include any General MIDI tracks, thus we have
|
||||
// to overwrite the internal type with MT32 to get the correct
|
||||
// file extension.
|
||||
if (_vm->game() == GI_KYRA1 && _type == kMidiGM)
|
||||
_type = kMidiMT32;
|
||||
|
||||
// Display a warning about possibly wrong sound when the user only has
|
||||
// a General MIDI device, but the game is setup to use Roland MT32 MIDI.
|
||||
// (This will only happen in The Legend of Kyrandia 1 though, all other
|
||||
// supported games include special General MIDI tracks).
|
||||
if (_type == kMidiMT32 && !_nativeMT32) {
|
||||
::GUI::MessageDialog dialog(_("You appear to be using a General MIDI device,\n"
|
||||
"but your game only supports Roland MT32 MIDI.\n"
|
||||
"We try to map the Roland MT32 instruments to\n"
|
||||
"General MIDI ones. It is still possible that\n"
|
||||
"some tracks sound incorrect."));
|
||||
dialog.runModal();
|
||||
}
|
||||
}
|
||||
|
||||
SoundMidiPC::~SoundMidiPC() {
|
||||
Common::StackLock lock(_mutex);
|
||||
_output->setTimerCallback(nullptr, nullptr);
|
||||
|
||||
delete _music;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
delete _sfx[i];
|
||||
_output->stopAllNotes();
|
||||
|
||||
delete _output; // This automatically frees _driver (!)
|
||||
|
||||
if (_musicFile != _sfxFile)
|
||||
delete[] _sfxFile;
|
||||
|
||||
delete[] _musicFile;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
initAudioResourceInfo(i, nullptr);
|
||||
}
|
||||
|
||||
bool SoundMidiPC::init() {
|
||||
_output = Audio::MidiDriver_Miles_MIDI_create(_type == kMidiGM ? MT_GM : MT_MT32, "");
|
||||
assert(_output);
|
||||
int returnCode = _output->open(_driver, _nativeMT32);
|
||||
if (returnCode > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
updateVolumeSettings();
|
||||
|
||||
_music->setMidiDriver(_output);
|
||||
_music->setTempo(_output->getBaseTempo());
|
||||
_music->setTimerRate(_output->getBaseTempo());
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
_sfx[i]->setMidiDriver(_output);
|
||||
_sfx[i]->setTempo(_output->getBaseTempo());
|
||||
_sfx[i]->setTimerRate(_output->getBaseTempo());
|
||||
}
|
||||
|
||||
_output->setTimerCallback(this, SoundMidiPC::onTimer);
|
||||
|
||||
// Load MT-32 and GM initialization files
|
||||
const char* midiFile = nullptr;
|
||||
const char* pakFile = nullptr;
|
||||
if (_nativeMT32 && _type == kMidiMT32) {
|
||||
if (_vm->game() == GI_KYRA1) {
|
||||
midiFile = "INTRO";
|
||||
} else if (_vm->game() == GI_KYRA2) {
|
||||
midiFile = "HOF_SYX";
|
||||
pakFile = "AUDIO.PAK";
|
||||
} else if (_vm->game() == GI_LOL) {
|
||||
midiFile = "LOREINTR";
|
||||
|
||||
if (_vm->gameFlags().isDemo) {
|
||||
if (_vm->gameFlags().isTalkie) {
|
||||
pakFile = "ISTARTUP.PAK";
|
||||
} else if (_vm->resource()->exists("INTROVOC.PAK")) {
|
||||
// Intro demo
|
||||
pakFile = "INTROVOC.PAK";
|
||||
} else {
|
||||
// Kyra2 SEQ player based demo
|
||||
pakFile = "GENERAL.PAK";
|
||||
midiFile = "LOLSYSEX";
|
||||
}
|
||||
} else {
|
||||
if (_vm->gameFlags().isTalkie)
|
||||
pakFile = (_vm->_flags.lang == Common::FR_FRA) ? "FRE/STARTUP.PAK" : (_vm->_flags.lang == Common::DE_DEU ? "GER/STARTUP.PAK" : "ENG/STARTUP.PAK");
|
||||
else
|
||||
pakFile = "INTROVOC.PAK";
|
||||
}
|
||||
}
|
||||
} else if (_type == kMidiGM && _vm->game() == GI_LOL) {
|
||||
if (_vm->gameFlags().isDemo && _vm->resource()->exists("INTROVOC.PAK")) {
|
||||
// Intro demo
|
||||
midiFile = "LOREINTR";
|
||||
pakFile = "INTROVOC.PAK";
|
||||
} else {
|
||||
midiFile = "LOLSYSEX";
|
||||
pakFile = "GENERAL.PAK";
|
||||
}
|
||||
}
|
||||
|
||||
if (!midiFile)
|
||||
return true;
|
||||
|
||||
if (pakFile)
|
||||
_vm->resource()->loadPakFile(pakFile);
|
||||
|
||||
loadSoundFile(midiFile);
|
||||
playTrack(0);
|
||||
|
||||
Common::Event event;
|
||||
while (isPlaying() && !_vm->shouldQuit()) {
|
||||
_vm->screen()->updateBackendScreen(true);
|
||||
_vm->_eventMan->pollEvent(event);
|
||||
_vm->_system->delayMillis(10);
|
||||
}
|
||||
|
||||
if (pakFile)
|
||||
_vm->resource()->unloadPakFile(pakFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundMidiPC::updateVolumeSettings() {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
if (!_output)
|
||||
return;
|
||||
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
const int newMusVol = (mute ? 0 : ConfMan.getInt("music_volume"));
|
||||
_sfxVolume = (mute ? 0 : ConfMan.getInt("sfx_volume"));
|
||||
|
||||
_output->setSourceVolume(0, newMusVol);
|
||||
_musicVolume = newMusVol;
|
||||
|
||||
for (int i = 1; i < 4; ++i)
|
||||
_output->setSourceVolume(i, _sfxVolume);
|
||||
}
|
||||
|
||||
void SoundMidiPC::initAudioResourceInfo(int set, void *info) {
|
||||
if (set >= kMusicIntro && set <= kMusicFinale) {
|
||||
delete _resInfo[set];
|
||||
_resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMidiPC::selectAudioResourceSet(int set) {
|
||||
if (set >= kMusicIntro && set <= kMusicFinale) {
|
||||
if (_resInfo[set])
|
||||
_currentResourceSet = set;
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundMidiPC::hasSoundFile(uint file) const {
|
||||
if (file < res()->fileListSize)
|
||||
return (res()->fileList[file] != nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoundMidiPC::loadSoundFile(uint file) {
|
||||
if (file < res()->fileListSize)
|
||||
loadSoundFile(res()->fileList[file]);
|
||||
}
|
||||
|
||||
void SoundMidiPC::loadSoundFile(const Common::Path &file) {
|
||||
Common::StackLock lock(_mutex);
|
||||
Common::Path path = getFileName(file);
|
||||
|
||||
if (_mFileName == path)
|
||||
return;
|
||||
|
||||
if (!_vm->resource()->exists(path))
|
||||
return;
|
||||
|
||||
haltTrack();
|
||||
if (_vm->game() == GI_KYRA1) {
|
||||
stopAllSoundEffects();
|
||||
}
|
||||
|
||||
delete[] _musicFile;
|
||||
uint32 fileSize = 0;
|
||||
_musicFile = _vm->resource()->fileData(path, &fileSize);
|
||||
_mFileName = path;
|
||||
|
||||
_music->loadMusic(_musicFile, fileSize);
|
||||
|
||||
// WORKAROUND The track playing during the character selection screen has a
|
||||
// bug: towards the end of the track, pitch bend events are sent on two
|
||||
// channels, but pitch bend is not reset to neutral when the track loops.
|
||||
// This causes two instruments to be out of tune after the track loops.
|
||||
// This occurs in both the MT-32 and GM versions, but in the GM version the
|
||||
// pitch bend is smaller, so it is much less noticeable. It was fixed in the
|
||||
// CD version for MT-32 by adding pitch bend neutral events to the end of
|
||||
// the track.
|
||||
// It is fixed here for the MT-32 floppy version and both GM versions by
|
||||
// moving the for loop event (indicating the start of the loop) before the
|
||||
// pitch bend neutral events at the start of the track; position is swapped
|
||||
// with the first pitch bend neutral event. (The pitch bend neutral events
|
||||
// are sent in a different order, but that makes no practical difference.)
|
||||
// The initial pitch bend neutral events are then sent again when the track
|
||||
// loops.
|
||||
if (path == "LOREINTR.XMI" && fileSize >= 0x6221 && _musicFile[0x6210] == 0xE1) {
|
||||
// MT-32 floppy version.
|
||||
|
||||
// Overwrite first pitch bend event with for loop event.
|
||||
_musicFile[0x6210] = 0xB6;
|
||||
_musicFile[0x6211] = 0x74;
|
||||
_musicFile[0x6212] = 0x00;
|
||||
|
||||
// Write pitch event in the old location of the for loop event.
|
||||
_musicFile[0x621F] = 0xE1;
|
||||
_musicFile[0x6220] = 0x00;
|
||||
_musicFile[0x6221] = 0x40;
|
||||
} else if (path == "LOREINTR.C55" && fileSize >= 0x216D && _musicFile[0x215C] == 0xE0) {
|
||||
// GM floppy and CD version.
|
||||
|
||||
// Overwrite first pitch bend event with for loop event.
|
||||
_musicFile[0x215C] = 0xB9;
|
||||
_musicFile[0x215D] = 0x74;
|
||||
_musicFile[0x215E] = 0x00;
|
||||
|
||||
// Write pitch event in the old location of the for loop event.
|
||||
_musicFile[0x216B] = 0xE0;
|
||||
_musicFile[0x216C] = 0x00;
|
||||
_musicFile[0x216D] = 0x40;
|
||||
}
|
||||
|
||||
// Since KYRA1 uses the same file for SFX and Music
|
||||
// we setup sfx to play from music file as well
|
||||
if (_vm->game() == GI_KYRA1) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
_sfx[i]->loadMusic(_musicFile, fileSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMidiPC::loadSfxFile(const Common::Path &file) {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
// Kyrandia 1 doesn't use a special sfx file
|
||||
if (_vm->game() == GI_KYRA1)
|
||||
return;
|
||||
|
||||
Common::Path path = getFileName(file);
|
||||
|
||||
if (_sFileName == path)
|
||||
return;
|
||||
|
||||
if (!_vm->resource()->exists(path))
|
||||
return;
|
||||
|
||||
stopAllSoundEffects();
|
||||
|
||||
delete[] _sfxFile;
|
||||
|
||||
uint32 fileSize = 0;
|
||||
_sfxFile = _vm->resource()->fileData(path, &fileSize);
|
||||
_sFileName = path;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
_sfx[i]->loadMusic(_sfxFile, fileSize);
|
||||
_sfx[i]->stopPlaying();
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMidiPC::playTrack(uint8 track) {
|
||||
if (!_musicEnabled)
|
||||
return;
|
||||
|
||||
haltTrack();
|
||||
|
||||
Common::StackLock lock(_mutex);
|
||||
_fadeMusicOut = false;
|
||||
|
||||
_output->setSourceVolume(0, _musicVolume);
|
||||
|
||||
if (_music->setTrack(track))
|
||||
_music->startPlaying();
|
||||
}
|
||||
|
||||
void SoundMidiPC::haltTrack() {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
_music->stopPlaying();
|
||||
_output->deinitSource(0);
|
||||
}
|
||||
|
||||
bool SoundMidiPC::isPlaying() const {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
return _music->isPlaying();
|
||||
}
|
||||
|
||||
void SoundMidiPC::playSoundEffect(uint16 track, uint8) {
|
||||
if (!_sfxEnabled)
|
||||
return;
|
||||
|
||||
Common::StackLock lock(_mutex);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (!_sfx[i]->isPlaying()) {
|
||||
if (_sfx[i]->setTrack(track))
|
||||
_sfx[i]->startPlaying();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMidiPC::stopAllSoundEffects() {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
_sfx[i]->stopPlaying();
|
||||
_output->deinitSource(i+1);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMidiPC::beginFadeOut() {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
_fadeMusicOut = true;
|
||||
_fadeStartTime = _vm->_system->getMillis();
|
||||
}
|
||||
|
||||
void SoundMidiPC::pause(bool paused) {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
if (paused) {
|
||||
_music->pausePlaying();
|
||||
for (int i = 0; i < 3; i++)
|
||||
_sfx[i]->pausePlaying();
|
||||
if (_output)
|
||||
_output->stopAllNotes();
|
||||
} else {
|
||||
_music->resumePlaying();
|
||||
for (int i = 0; i < 3; ++i)
|
||||
_sfx[i]->resumePlaying();
|
||||
// Possible TODO (IMHO unnecessary): restore notes and/or update _fadeStartTime
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMidiPC::onTimer(void *data) {
|
||||
SoundMidiPC *midi = (SoundMidiPC *)data;
|
||||
|
||||
Common::StackLock lock(midi->_mutex);
|
||||
|
||||
if (midi->_fadeMusicOut) {
|
||||
static const uint32 musicFadeTime = 1 * 1000;
|
||||
|
||||
if (midi->_fadeStartTime + musicFadeTime > midi->_vm->_system->getMillis()) {
|
||||
int volume = (byte)((musicFadeTime - (midi->_vm->_system->getMillis() - midi->_fadeStartTime)) * midi->_musicVolume / musicFadeTime);
|
||||
midi->_output->setSourceVolume(0, volume);
|
||||
} else {
|
||||
midi->haltTrack();
|
||||
midi->stopAllSoundEffects();
|
||||
|
||||
midi->_fadeMusicOut = false;
|
||||
|
||||
// Restore music volume
|
||||
midi->_output->setSourceVolume(0, midi->_musicVolume);
|
||||
}
|
||||
}
|
||||
|
||||
midi->_music->onTimer();
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
midi->_sfx[i]->onTimer();
|
||||
}
|
||||
}
|
||||
|
||||
Common::Path SoundMidiPC::getFileName(const Common::Path &str) {
|
||||
Common::Path file(str);
|
||||
if (_type == kMidiMT32)
|
||||
file.appendInPlace(".XMI");
|
||||
else if (_type == kMidiGM)
|
||||
file.appendInPlace(".C55");
|
||||
else if (_type == kPCSpkr)
|
||||
file.appendInPlace(".PCS");
|
||||
|
||||
if (_vm->resource()->exists(file))
|
||||
return file;
|
||||
|
||||
return str.append(".XMI");
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
266
engines/kyra/sound/sound_pc_v1.cpp
Normal file
266
engines/kyra/sound/sound_pc_v1.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
/* 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 "kyra/sound/sound_intern.h"
|
||||
#include "kyra/sound/drivers/pc_base.h"
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
// Kyra 1 sound triggers. Most noticeably, these are used towards the end of
|
||||
// the game, in the castle, to cycle between different songs. The same music is
|
||||
// used in other places throughout the game, but the player is less likely to
|
||||
// spend enough time there to notice.
|
||||
|
||||
const int SoundPC_v1::_kyra1SoundTriggers[] = {
|
||||
0, 4, 5, 3
|
||||
};
|
||||
|
||||
const int SoundPC_v1::_kyra1NumSoundTriggers = ARRAYSIZE(SoundPC_v1::_kyra1SoundTriggers);
|
||||
|
||||
SoundPC_v1::SoundPC_v1(KyraEngine_v1 *vm, Audio::Mixer *mixer, kType type)
|
||||
: Sound(vm, mixer), _driver(nullptr), _trackEntries(), _soundDataPtr(nullptr), _type(type), _version(-1) {
|
||||
memset(_trackEntries, 0, sizeof(_trackEntries));
|
||||
|
||||
_soundTriggers = nullptr;
|
||||
_numSoundTriggers = 0;
|
||||
_sfxPlayingSound = -1;
|
||||
_soundFileLoaded.clear();
|
||||
_currentResourceSet = 0;
|
||||
memset(&_resInfo, 0, sizeof(_resInfo));
|
||||
|
||||
switch (vm->game()) {
|
||||
case GI_LOL:
|
||||
_version = (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie) ? 3 : 4;
|
||||
break;
|
||||
case GI_KYRA2:
|
||||
_version = 4;
|
||||
break;
|
||||
case GI_KYRA1:
|
||||
_version = 3;
|
||||
_soundTriggers = _kyra1SoundTriggers;
|
||||
_numSoundTriggers = _kyra1NumSoundTriggers;
|
||||
break;
|
||||
case GI_EOB2:
|
||||
_version = 2;
|
||||
break;
|
||||
case GI_EOB1:
|
||||
_version = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
// Correct the type to someting we support. NullSound is treated as a silent AdLib driver.
|
||||
if (_type != kAdLib && _type != kPCSpkr && _type != kPCjr)
|
||||
_type = kAdLib;
|
||||
_driver = (type == kAdLib) ? (_version > 0 ? PCSoundDriver::createAdLib(mixer, _version) : nullptr) : PCSoundDriver::createPCSpk(mixer, _type == kPCjr);
|
||||
#else
|
||||
_type = kAdLib;
|
||||
if (_version > 0)
|
||||
_driver = PCSoundDriver::createAdLib(mixer, _version);
|
||||
#endif
|
||||
assert(_driver);
|
||||
}
|
||||
|
||||
SoundPC_v1::~SoundPC_v1() {
|
||||
delete _driver;
|
||||
delete[] _soundDataPtr;
|
||||
for (int i = 0; i < 3; i++)
|
||||
initAudioResourceInfo(i, nullptr);
|
||||
}
|
||||
|
||||
bool SoundPC_v1::init() {
|
||||
_driver->initDriver();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundPC_v1::process() {
|
||||
int trigger = _driver->getSoundTrigger();
|
||||
|
||||
if (trigger < _numSoundTriggers) {
|
||||
int soundId = _soundTriggers[trigger];
|
||||
|
||||
if (soundId)
|
||||
playTrack(soundId);
|
||||
} else {
|
||||
warning("Unknown sound trigger %d", trigger);
|
||||
// TODO: At this point, we really want to clear the trigger...
|
||||
}
|
||||
}
|
||||
|
||||
void SoundPC_v1::updateVolumeSettings() {
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
int newMusicVolume = mute ? 0 : ConfMan.getInt("music_volume");
|
||||
//newMusicVolume = (newMusicVolume * 145) / Audio::Mixer::kMaxMixerVolume + 110;
|
||||
newMusicVolume = CLIP(newMusicVolume, 0, 255);
|
||||
|
||||
int newSfxVolume = mute ? 0 : ConfMan.getInt("sfx_volume");
|
||||
//newSfxVolume = (newSfxVolume * 200) / Audio::Mixer::kMaxMixerVolume + 55;
|
||||
newSfxVolume = CLIP(newSfxVolume, 0, 255);
|
||||
|
||||
_driver->setMusicVolume(newMusicVolume);
|
||||
_driver->setSfxVolume(newSfxVolume);
|
||||
}
|
||||
|
||||
void SoundPC_v1::playTrack(uint8 track) {
|
||||
if (_musicEnabled) {
|
||||
// WORKAROUND: There is a bug in the Kyra 1 "Pool of Sorrow"
|
||||
// music which causes the channels to get progressively out of
|
||||
// sync for each loop. To avoid that, we declare that all four
|
||||
// of the song channels have to jump "in sync".
|
||||
|
||||
if (track == 4 && _soundFileLoaded.equalsIgnoreCase("KYRA1B.ADL"))
|
||||
_driver->setSyncJumpMask(0x000F);
|
||||
else
|
||||
_driver->setSyncJumpMask(0);
|
||||
play(track, 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundPC_v1::haltTrack() {
|
||||
play(0, 0);
|
||||
play(0, 0);
|
||||
//_vm->_system->delayMillis(3 * 60);
|
||||
}
|
||||
|
||||
bool SoundPC_v1::isPlaying() const {
|
||||
return _driver->isChannelPlaying(0);
|
||||
}
|
||||
|
||||
void SoundPC_v1::playSoundEffect(uint16 track, uint8 volume) {
|
||||
if (_sfxEnabled)
|
||||
play(track, volume);
|
||||
}
|
||||
|
||||
void SoundPC_v1::play(uint8 track, uint8 volume) {
|
||||
uint16 soundId = 0;
|
||||
|
||||
if (_version == 4)
|
||||
soundId = READ_LE_UINT16(&_trackEntries[track<<1]);
|
||||
else
|
||||
soundId = _trackEntries[track];
|
||||
|
||||
if ((soundId == 0xFFFF && _version == 4) || (soundId == 0xFF && _version < 4) || !_soundDataPtr)
|
||||
return;
|
||||
|
||||
_driver->startSound(soundId, volume);
|
||||
}
|
||||
|
||||
void SoundPC_v1::beginFadeOut() {
|
||||
play(_version > 2 ? 1 : 15, 0xFF);
|
||||
}
|
||||
|
||||
int SoundPC_v1::checkTrigger() {
|
||||
return _driver->getSoundTrigger();
|
||||
}
|
||||
|
||||
void SoundPC_v1::resetTrigger() {
|
||||
_driver->resetSoundTrigger();
|
||||
}
|
||||
|
||||
void SoundPC_v1::initAudioResourceInfo(int set, void *info) {
|
||||
if (set >= kMusicIntro && set <= kMusicFinale) {
|
||||
delete _resInfo[set];
|
||||
_resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundPC_v1::selectAudioResourceSet(int set) {
|
||||
if (set >= kMusicIntro && set <= kMusicFinale) {
|
||||
if (_resInfo[set])
|
||||
_currentResourceSet = set;
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundPC_v1::hasSoundFile(uint file) const {
|
||||
if (file < res()->fileListSize)
|
||||
return (res()->fileList[file] != nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoundPC_v1::loadSoundFile(uint file) {
|
||||
if (_version == 1 && (_type == kPCSpkr || _type == kPCjr))
|
||||
file += 1;
|
||||
if (file < res()->fileListSize)
|
||||
internalLoadFile(res()->fileList[file]);
|
||||
}
|
||||
|
||||
void SoundPC_v1::loadSoundFile(const Common::Path &file) {
|
||||
internalLoadFile(file);
|
||||
}
|
||||
|
||||
void SoundPC_v1::internalLoadFile(const Common::Path &file) {
|
||||
Common::Path path(file);
|
||||
path.appendInPlace((_version == 1) ? ".DAT" : (_type == kPCSpkr ? ".SND" : ".ADL"));
|
||||
if (_soundFileLoaded == path)
|
||||
return;
|
||||
|
||||
if (_soundDataPtr)
|
||||
haltTrack();
|
||||
|
||||
uint8 *fileData = nullptr; uint32 fileSize = 0;
|
||||
|
||||
fileData = _vm->resource()->fileData(path, &fileSize);
|
||||
if (!fileData) {
|
||||
warning("Couldn't find music file: '%s'", path.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
playSoundEffect(0);
|
||||
playSoundEffect(0);
|
||||
|
||||
_driver->stopAllChannels();
|
||||
|
||||
int soundDataSize = fileSize;
|
||||
uint8 *p = fileData;
|
||||
|
||||
if (_version == 4) {
|
||||
memcpy(_trackEntries, p, 500);
|
||||
p += 500;
|
||||
soundDataSize -= 500;
|
||||
} else {
|
||||
memcpy(_trackEntries, p, 120);
|
||||
p += 120;
|
||||
soundDataSize -= 120;
|
||||
}
|
||||
|
||||
uint8 *oldData = _soundDataPtr;
|
||||
_soundDataPtr = new uint8[soundDataSize];
|
||||
assert(_soundDataPtr);
|
||||
|
||||
memcpy(_soundDataPtr, p, soundDataSize);
|
||||
_driver->setSoundData(_soundDataPtr, soundDataSize);
|
||||
|
||||
delete[] fileData;
|
||||
delete[] oldData;
|
||||
|
||||
_soundFileLoaded = path;
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
105
engines/kyra/sound/sound_pc_v1.h
Normal file
105
engines/kyra/sound/sound_pc_v1.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* 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 KYRA_SOUND_ADLIB_H
|
||||
#define KYRA_SOUND_ADLIB_H
|
||||
|
||||
#include "kyra/sound/sound.h"
|
||||
|
||||
#include "common/mutex.h"
|
||||
|
||||
namespace Kyra {
|
||||
class PCSoundDriver;
|
||||
|
||||
/**
|
||||
* AdLib/PC Speaker (early version) implementation of the
|
||||
* sound output device.
|
||||
*
|
||||
* It uses a special sound file format special to EoB I, II,
|
||||
* Dune II, Kyrandia 1 and 2 and LoL. EoB I has a slightly
|
||||
* different (oldest) file format, EoB II, Dune II and
|
||||
* Kyrandia 1 have the exact same format, Kyrandia 2 and
|
||||
* LoL have a slightly different format.
|
||||
*
|
||||
* For PC Speaker this is a little different. Only the EoB
|
||||
* games use the old driver with this data file format. The
|
||||
* newer games use a MIDI-like driver (see pcspeaker_v2.cpp).
|
||||
*
|
||||
* See AdLibDriver / PCSpeakerDriver for more information.
|
||||
* @see AdLibDriver
|
||||
*/
|
||||
class SoundPC_v1 : public Sound {
|
||||
public:
|
||||
SoundPC_v1(KyraEngine_v1 *vm, Audio::Mixer *mixer, kType type);
|
||||
~SoundPC_v1() override;
|
||||
|
||||
kType getMusicType() const override { return _type; }
|
||||
|
||||
bool init() override;
|
||||
void process() override;
|
||||
|
||||
void updateVolumeSettings() override;
|
||||
|
||||
void initAudioResourceInfo(int set, void *info) override;
|
||||
void selectAudioResourceSet(int set) override;
|
||||
bool hasSoundFile(uint file) const override;
|
||||
void loadSoundFile(uint file) override;
|
||||
void loadSoundFile(const Common::Path &file) override;
|
||||
|
||||
void playTrack(uint8 track) override;
|
||||
void haltTrack() override;
|
||||
bool isPlaying() const override;
|
||||
|
||||
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
|
||||
|
||||
void beginFadeOut() override;
|
||||
|
||||
int checkTrigger() override;
|
||||
void resetTrigger() override;
|
||||
private:
|
||||
void internalLoadFile(const Common::Path &file);
|
||||
|
||||
void play(uint8 track, uint8 volume);
|
||||
|
||||
const SoundResourceInfo_PC *res() const {return _resInfo[_currentResourceSet]; }
|
||||
SoundResourceInfo_PC *_resInfo[3];
|
||||
int _currentResourceSet;
|
||||
|
||||
PCSoundDriver *_driver;
|
||||
|
||||
int _version;
|
||||
kType _type;
|
||||
uint8 _trackEntries[500];
|
||||
uint8 *_soundDataPtr;
|
||||
int _sfxPlayingSound;
|
||||
|
||||
Common::Path _soundFileLoaded;
|
||||
|
||||
int _numSoundTriggers;
|
||||
const int *_soundTriggers;
|
||||
|
||||
static const int _kyra1NumSoundTriggers;
|
||||
static const int _kyra1SoundTriggers[];
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
176
engines/kyra/sound/sound_segacd_eob.cpp
Normal file
176
engines/kyra/sound/sound_segacd_eob.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#include "kyra/sound/drivers/segacd.h"
|
||||
#include "kyra/sound/sound_intern.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "backends/audiocd/audiocd.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
SoundSegaCD_EoB::SoundSegaCD_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer),
|
||||
_vm(vm), _driver(0), _fmData(0), _lastSoundEffect(-1), _ready(false) {
|
||||
memset(_pcmOffsets, 0, sizeof(_pcmOffsets));
|
||||
memset(_fmOffsets, 0, sizeof(_fmOffsets));
|
||||
}
|
||||
|
||||
SoundSegaCD_EoB::~SoundSegaCD_EoB() {
|
||||
delete _driver;
|
||||
delete[] _fmData;
|
||||
}
|
||||
|
||||
Sound::kType SoundSegaCD_EoB::getMusicType() const {
|
||||
return kSegaCD;
|
||||
}
|
||||
|
||||
bool SoundSegaCD_EoB::init() {
|
||||
_driver = new SegaAudioDriver(_mixer);
|
||||
g_system->getAudioCDManager()->open();
|
||||
|
||||
loadPCMData();
|
||||
loadFMData();
|
||||
|
||||
_ready = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundSegaCD_EoB::playTrack(uint8 track) {
|
||||
if (!_ready)
|
||||
return;
|
||||
|
||||
if (!_musicEnabled) {
|
||||
haltTrack();
|
||||
return;
|
||||
}
|
||||
|
||||
int loop = track >> 6;
|
||||
track &= 0x7F;
|
||||
|
||||
g_system->getAudioCDManager()->play(track - 1, loop - 1, 0, 0);
|
||||
g_system->getAudioCDManager()->update();
|
||||
}
|
||||
|
||||
void SoundSegaCD_EoB::haltTrack() {
|
||||
if (!_ready)
|
||||
return;
|
||||
g_system->getAudioCDManager()->stop();
|
||||
}
|
||||
|
||||
void SoundSegaCD_EoB::playSoundEffect(uint16 track, uint8 volume) {
|
||||
if (!_sfxEnabled || !_ready)
|
||||
return;
|
||||
|
||||
uint8 flags = track >> 8;
|
||||
track &= 0xFF;
|
||||
|
||||
if (flags & 0x80) {
|
||||
track--;
|
||||
assert(track < ARRAYSIZE(_pcmOffsets));
|
||||
for (uint8 i = 0; i < 8; ++i)
|
||||
_driver->startPCMSound(i, _pcmOffsets[track]);
|
||||
|
||||
} else {
|
||||
uint8 snd = (flags & 0x40) ? track : _fmTrackMap[track];
|
||||
if (snd == 0 || snd > 135)
|
||||
return;
|
||||
|
||||
_driver->startFMSound(&_fmData[_fmOffsets[snd - 1]], volume, (SegaAudioDriver::PrioFlags)flags);
|
||||
_lastSoundEffect = track;
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundSegaCD_EoB::isPlaying() const {
|
||||
return g_system->getAudioCDManager()->isPlaying();
|
||||
}
|
||||
|
||||
void SoundSegaCD_EoB::updateVolumeSettings() {
|
||||
if (!_driver || !_ready)
|
||||
return;
|
||||
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
|
||||
_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
|
||||
}
|
||||
|
||||
void SoundSegaCD_EoB::loadPCMData() {
|
||||
uint32 dataSize;
|
||||
uint8 *data1 = _vm->resource()->fileData("PCM", &dataSize);
|
||||
if (!data1)
|
||||
error("SoundSegaCD_EoB::loadPCMData: File not found: '%s'", "PCM");
|
||||
|
||||
uint8 *data2 = new uint8[0x100];
|
||||
memset(data2, 0xFF, 0x100);
|
||||
data2[0] = 0x80;
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(_pcmOffsets); ++i)
|
||||
_pcmOffsets[i] = data1[(i << 2) + 2];
|
||||
|
||||
_driver->loadPCMData(0, data1 + 64, dataSize - 64);
|
||||
_driver->loadPCMData(0xFF00, data2, 0x100);
|
||||
|
||||
delete[] data1;
|
||||
delete[] data2;
|
||||
}
|
||||
|
||||
void SoundSegaCD_EoB::loadFMData() {
|
||||
Common::SeekableReadStreamEndian *in = _vm->resource()->createEndianAwareReadStream("FMSE");
|
||||
if (!in)
|
||||
error("SoundSegaCD_EoB::loadFMData: File not found: '%s'", "FMSE");
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(_fmOffsets); ++i)
|
||||
_fmOffsets[i] = (in->readUint32() - ARRAYSIZE(_fmOffsets) * 4) & 0xFFFF;
|
||||
|
||||
uint32 dataSize = in->size() - in->pos();
|
||||
uint8 *data = new uint8[dataSize];
|
||||
in->read(data, dataSize);
|
||||
delete[] _fmData;
|
||||
_fmData = data;
|
||||
|
||||
delete in;
|
||||
}
|
||||
|
||||
const uint8 SoundSegaCD_EoB::_fmTrackMap[140] = {
|
||||
0x00, 0x05, 0x40, 0x01, 0x02, 0x02, 0x06, 0x07, 0x12, 0x0a,
|
||||
0x11, 0x1f, 0x1e, 0x12, 0x09, 0x0c, 0x0b, 0x17, 0x21, 0x0d,
|
||||
0x00, 0x14, 0x00, 0x16, 0x00, 0x00, 0x26, 0x0f, 0x13, 0x10,
|
||||
0x00, 0x00, 0x27, 0x00, 0x1a, 0x28, 0x39, 0x46, 0x33, 0x4a,
|
||||
0x3b, 0x48, 0x33, 0x47, 0x38, 0x4d, 0x3e, 0x45, 0x36, 0x41,
|
||||
0x3a, 0x49, 0x46, 0x38, 0x44, 0x37, 0x42, 0x34, 0x4b, 0x3c,
|
||||
0x41, 0x3b, 0x40, 0x38, 0x47, 0x39, 0x4d, 0x35, 0x4c, 0x3d,
|
||||
0x4e, 0x3d, 0x42, 0x43, 0x36, 0x32, 0x00, 0x60, 0x1f, 0x82,
|
||||
0x1c, 0x29, 0x00, 0x00, 0x00, 0x65, 0x24, 0x60, 0x62, 0x6d,
|
||||
0x00, 0x78, 0x70, 0x74, 0x7e, 0x7d, 0x66, 0x81, 0x6a, 0x67,
|
||||
0x80, 0x68, 0x64, 0x6c, 0x77, 0x77, 0x77, 0x61, 0x61, 0x61,
|
||||
0x71, 0x79, 0x7f, 0x73, 0x7a, 0x7b, 0x71, 0x7c, 0x6e, 0x0e,
|
||||
0x75, 0x76, 0x78, 0x6b, 0x30, 0x2f, 0x03, 0x04, 0x23, 0x2b,
|
||||
0x5a, 0x1a, 0x1c, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif // ENABLE_EOB
|
||||
293
engines/kyra/sound/sound_towns_darkmoon.cpp
Normal file
293
engines/kyra/sound/sound_towns_darkmoon.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#include "kyra/sound/sound_intern.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "backends/audiocd/audiocd.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
SoundTowns_Darkmoon::SoundTowns_Darkmoon(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer) {
|
||||
_intf = new TownsAudioInterface(mixer, this);
|
||||
_pcmData = 0;
|
||||
_pcmDataSize = 0;
|
||||
_pcmVol = 0;
|
||||
_timer = 0;
|
||||
_timerSwitch = 0;
|
||||
_fileList = nullptr;
|
||||
_fileListLen = 0;
|
||||
_lastEnvChan = _lastSfxChan = 0;
|
||||
memset(_resource, 0, sizeof(_resource));
|
||||
memset(_soundTable, 0, sizeof(_soundTable));
|
||||
}
|
||||
|
||||
SoundTowns_Darkmoon::~SoundTowns_Darkmoon() {
|
||||
for (int i = 0; i < 3; i++)
|
||||
initAudioResourceInfo(i, 0);
|
||||
delete _intf;
|
||||
delete[] _pcmData;
|
||||
}
|
||||
|
||||
bool SoundTowns_Darkmoon::init() {
|
||||
if (!_intf->init())
|
||||
return false;
|
||||
|
||||
_intf->callback(21, 255, 1);
|
||||
_intf->callback(21, 0, 1);
|
||||
_intf->callback(22, 255, 221);
|
||||
|
||||
_intf->callback(70, 0x31);
|
||||
_intf->callback(33, 1);
|
||||
_intf->callback(8, 0x47, 127);
|
||||
_intf->callback(67, 1, 127, 127);
|
||||
|
||||
_intf->setSoundEffectChanMask(-1);
|
||||
|
||||
_lastSfxChan = 0x46;
|
||||
_lastEnvChan = 0x40;
|
||||
|
||||
updateVolumeSettings();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::timerCallback(int timerId) {
|
||||
switch (timerId) {
|
||||
case 1:
|
||||
_timerSwitch = (_timerSwitch + 1) % 4;
|
||||
if (!_timerSwitch)
|
||||
_timer++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::initAudioResourceInfo(int set, void *info) {
|
||||
delete _resource[set];
|
||||
_resource[set] = info ? new SoundResourceInfo_TownsEoB(*(SoundResourceInfo_TownsEoB*)info) : 0;
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::selectAudioResourceSet(int set) {
|
||||
delete[] _pcmData;
|
||||
|
||||
if (!_resource[set] || !_resource[kMusicIngame])
|
||||
return;
|
||||
|
||||
_fileList = _resource[set]->fileList;
|
||||
_fileListLen = _resource[set]->numFiles;
|
||||
|
||||
_pcmDataSize = _resource[kMusicIngame]->pcmDataSize;
|
||||
_pcmData = new uint8[_pcmDataSize];
|
||||
_pcmVol = _resource[set]->pcmVolume;
|
||||
memcpy(_pcmData, _resource[kMusicIngame]->pcmData, _pcmDataSize);
|
||||
|
||||
if (set == kMusicIngame)
|
||||
return;
|
||||
|
||||
memcpy(_pcmData, _resource[set]->pcmData, _resource[set]->pcmDataSize);
|
||||
}
|
||||
|
||||
bool SoundTowns_Darkmoon::hasSoundFile(uint file) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::loadSoundFile(uint file) {
|
||||
if (file < _fileListLen)
|
||||
loadSoundFile(_fileList[file]);
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::loadSoundFile(const Common::Path &name) {
|
||||
Common::SeekableReadStream *s = _vm->resource()->createReadStream(name.append(".SDT"));
|
||||
if (!s)
|
||||
error("Failed to load sound file '%s.SDT'", name.toString().c_str());
|
||||
|
||||
for (int i = 0; i < 120; i++) {
|
||||
_soundTable[i].type = s->readSByte();
|
||||
_soundTable[i].para1 = s->readSint32LE();
|
||||
_soundTable[i].para2 = s->readSint16LE();
|
||||
}
|
||||
|
||||
delete s;
|
||||
|
||||
uint32 bytesLeft;
|
||||
uint8 *pmb = _vm->resource()->fileData(name.append(".PMB"), &bytesLeft);
|
||||
|
||||
_vm->delay(300);
|
||||
|
||||
if (pmb) {
|
||||
uint8 *src = pmb + 8;
|
||||
for (int i = 0; i < 32; i++)
|
||||
_intf->callback(5, 0x40, i, &src[i << 7]);
|
||||
|
||||
_intf->callback(35, -1);
|
||||
src += 0x1000;
|
||||
bytesLeft -= 0x1008;
|
||||
|
||||
while (bytesLeft) {
|
||||
_intf->callback(34, src);
|
||||
uint32 len = READ_LE_UINT16(&src[12]) + 32;
|
||||
src = src + len;
|
||||
bytesLeft -= len;
|
||||
}
|
||||
|
||||
delete[] pmb;
|
||||
} else {
|
||||
warning("Sound file '%s.PMB' not found.", name.toString().c_str());
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::playTrack(uint8 track) {
|
||||
if (track >= 120 || !_sfxEnabled)
|
||||
return;
|
||||
|
||||
uint8 *pcm = 0;
|
||||
|
||||
switch (_soundTable[track].type) {
|
||||
case -1:
|
||||
if (track == 0)
|
||||
haltTrack();
|
||||
else if (track == 2)
|
||||
beginFadeOut();
|
||||
break;
|
||||
|
||||
case 0:
|
||||
if (_soundTable[track].para1 == -1 || (uint32)_soundTable[track].para1 > _pcmDataSize)
|
||||
return;
|
||||
|
||||
pcm = _pcmData + _soundTable[track].para1;
|
||||
WRITE_LE_UINT16(&pcm[24], _soundTable[track].para2 * 98 / 1000);
|
||||
|
||||
_intf->callback(39, 0x47);
|
||||
_intf->callback(37, 0x47, 60, track == 11 ? 127 : _pcmVol, pcm);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
resetTrigger();
|
||||
g_system->getAudioCDManager()->play(_soundTable[track].para1 - 1, 1, 0, 0);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_lastSfxChan ^= 3;
|
||||
_intf->callback(39, _lastSfxChan);
|
||||
_intf->callback(4, _lastSfxChan, _soundTable[track].para1);
|
||||
_intf->callback(1, _lastSfxChan, _soundTable[track].para2, 127);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::haltTrack() {
|
||||
_intf->callback(39, 0x47);
|
||||
_intf->callback(39, 0x46);
|
||||
_intf->callback(39, 0x45);
|
||||
|
||||
g_system->getAudioCDManager()->stop();
|
||||
}
|
||||
|
||||
bool SoundTowns_Darkmoon::isPlaying() const {
|
||||
return g_system->getAudioCDManager()->isPlaying();
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::playSoundEffect(uint16 track, uint8 volume) {
|
||||
if (!_sfxEnabled)
|
||||
return;
|
||||
|
||||
if (volume == 255)
|
||||
return playTrack(track);
|
||||
|
||||
uint8 *pcm = 0;
|
||||
|
||||
switch (_soundTable[track].type) {
|
||||
case 0:
|
||||
if (_soundTable[track].para1 == -1 || (uint32)_soundTable[track].para1 > _pcmDataSize)
|
||||
return;
|
||||
|
||||
pcm = _pcmData + _soundTable[track].para1;
|
||||
WRITE_LE_UINT16(&pcm[24], _soundTable[track].para2 * 98 / 1000);
|
||||
|
||||
_intf->callback(39, 0x47);
|
||||
_intf->callback(37, 0x47, 60, volume, pcm);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_intf->callback(2, _lastEnvChan);
|
||||
_intf->callback(4, _lastEnvChan, _soundTable[track].para1);
|
||||
_intf->callback(1, _lastEnvChan, _soundTable[track].para2, volume);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (++_lastEnvChan == 0x43)
|
||||
_lastEnvChan = 0x40;
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::stopAllSoundEffects() {
|
||||
_intf->callback(39, 0x42);
|
||||
_intf->callback(39, 0x41);
|
||||
_intf->callback(39, 0x40);
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::beginFadeOut() {
|
||||
for (int vol = 127; vol >= 0; vol -= 2) {
|
||||
_intf->callback(67, 1, vol, vol);
|
||||
_vm->delay(16);
|
||||
}
|
||||
|
||||
_intf->callback(67, 1, 0, 0);
|
||||
_intf->callback(70, 1);
|
||||
|
||||
g_system->getAudioCDManager()->stop();
|
||||
|
||||
_intf->callback(70, 0x31);
|
||||
_intf->callback(67, 1, 127, 127);
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::updateVolumeSettings() {
|
||||
bool mute = (ConfMan.hasKey("mute")) ? ConfMan.getBool("mute") : false;
|
||||
_intf->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
|
||||
}
|
||||
|
||||
int SoundTowns_Darkmoon::checkTrigger() {
|
||||
return _timer;
|
||||
}
|
||||
|
||||
void SoundTowns_Darkmoon::resetTrigger() {
|
||||
_timer = 0;
|
||||
_timerSwitch = 0;
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
402
engines/kyra/sound/sound_towns_lok.cpp
Normal file
402
engines/kyra/sound/sound_towns_lok.cpp
Normal file
@@ -0,0 +1,402 @@
|
||||
/* 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 "kyra/sound/sound_intern.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
|
||||
#include "audio/softsynth/fmtowns_pc98/towns_euphony.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "backends/audiocd/audiocd.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
SoundTowns_LoK::SoundTowns_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer)
|
||||
: Sound(vm, mixer), _lastTrack(-1), _musicTrackData(nullptr), _sfxFileData(nullptr), _cdaPlaying(false),
|
||||
_sfxFileIndex((uint)-1), _musicFadeTable(nullptr), _sfxWDTable(nullptr), _sfxBTTable(nullptr), _sfxChannel(0x46), _currentResourceSet(0) {
|
||||
memset(&_resInfo, 0, sizeof(_resInfo));
|
||||
_player = new EuphonyPlayer(_mixer);
|
||||
}
|
||||
|
||||
SoundTowns_LoK::~SoundTowns_LoK() {
|
||||
g_system->getAudioCDManager()->stop();
|
||||
haltTrack();
|
||||
delete _player;
|
||||
delete[] _musicTrackData;
|
||||
delete[] _sfxFileData;
|
||||
for (int i = 0; i < 3; i++)
|
||||
initAudioResourceInfo(i, nullptr);
|
||||
}
|
||||
|
||||
bool SoundTowns_LoK::init() {
|
||||
if (!_vm->existExtractedCDAudioFiles()
|
||||
&& !_vm->isDataAndCDAudioReadFromSameCD()) {
|
||||
_vm->warnMissingExtractedCDAudio();
|
||||
}
|
||||
int unused = 0;
|
||||
_musicFadeTable = _vm->staticres()->loadRawData(k1TownsMusicFadeTable, unused);
|
||||
_sfxWDTable = _vm->staticres()->loadRawData(k1TownsSFXwdTable, unused);
|
||||
_sfxBTTable = _vm->staticres()->loadRawData(k1TownsSFXbtTable, unused);
|
||||
_musicTrackData = new uint8[50570];
|
||||
|
||||
if (!_player->init())
|
||||
return false;
|
||||
|
||||
if (!loadInstruments())
|
||||
return false;
|
||||
|
||||
/*_player->driver()->intf()->callback(68);
|
||||
_player->driver()->intf()->callback(70, 0x33);*/
|
||||
_player->driver()->setOutputVolume(1, 118, 118);
|
||||
|
||||
// Initialize CD for audio
|
||||
g_system->getAudioCDManager()->open();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::process() {
|
||||
g_system->getAudioCDManager()->update();
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::playTrack(uint8 track) {
|
||||
if (track < 2)
|
||||
return;
|
||||
track -= 2;
|
||||
|
||||
uint tTableIndex = 3 * track;
|
||||
|
||||
assert(tTableIndex + 2 < res()->cdaTableSize);
|
||||
|
||||
int trackNum = (int)READ_LE_UINT32(&res()->cdaTable[tTableIndex + 2]);
|
||||
int32 loop = (int32)READ_LE_UINT32(&res()->cdaTable[tTableIndex + 1]);
|
||||
|
||||
if (track == _lastTrack && _musicEnabled)
|
||||
return;
|
||||
|
||||
beginFadeOut();
|
||||
|
||||
if (_musicEnabled == 2 && trackNum != -1) {
|
||||
_player->driver()->setOutputVolume(1, 118, 118);
|
||||
g_system->getAudioCDManager()->play(trackNum + 1, loop ? -1 : 1, 0, 0);
|
||||
g_system->getAudioCDManager()->update();
|
||||
_cdaPlaying = true;
|
||||
} else if (_musicEnabled) {
|
||||
playEuphonyTrack(READ_LE_UINT32(&res()->cdaTable[tTableIndex]), loop);
|
||||
_cdaPlaying = false;
|
||||
}
|
||||
|
||||
_lastTrack = track;
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::haltTrack() {
|
||||
_lastTrack = -1;
|
||||
g_system->getAudioCDManager()->stop();
|
||||
g_system->getAudioCDManager()->update();
|
||||
_cdaPlaying = false;
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
_player->driver()->channelVolume(i, 0);
|
||||
for (int i = 0x40; i < 0x46; i++)
|
||||
_player->driver()->channelVolume(i, 0);
|
||||
for (int i = 0; i < 32; i++)
|
||||
_player->configPart_enable(i, 0);
|
||||
_player->stop();
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::initAudioResourceInfo(int set, void *info) {
|
||||
if (set >= kMusicIntro && set <= kMusicFinale) {
|
||||
delete _resInfo[set];
|
||||
_resInfo[set] = info ? new SoundResourceInfo_Towns(*(SoundResourceInfo_Towns*)info) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::selectAudioResourceSet(int set) {
|
||||
if (set >= kMusicIntro && set <= kMusicFinale) {
|
||||
if (_resInfo[set])
|
||||
_currentResourceSet = set;
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundTowns_LoK::hasSoundFile(uint file) const {
|
||||
if (file < res()->fileListSize)
|
||||
return (res()->fileList[file] != nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::loadSoundFile(uint file) {
|
||||
if (_sfxFileIndex == file || file >= res()->fileListSize)
|
||||
return;
|
||||
_sfxFileIndex = file;
|
||||
delete[] _sfxFileData;
|
||||
_sfxFileData = _vm->resource()->fileData(res()->fileList[file], nullptr);
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::playSoundEffect(uint16 track, uint8) {
|
||||
if (!_sfxEnabled || !_sfxFileData)
|
||||
return;
|
||||
|
||||
if (track == 0 || track == 10) {
|
||||
stopAllSoundEffects();
|
||||
return;
|
||||
} else if (track == 1) {
|
||||
fadeOutSoundEffects();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 note = 60;
|
||||
if (_sfxFileIndex == 5) {
|
||||
if (track == 16) {
|
||||
note = 62;
|
||||
track = 15;
|
||||
} else if (track == 17) {
|
||||
note = 64;
|
||||
track = 15;
|
||||
} else if (track == 18) {
|
||||
note = 65;
|
||||
track = 15;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 *fileBody = _sfxFileData + 0x01B8;
|
||||
int32 offset = (int32)READ_LE_UINT32(_sfxFileData + (track - 0x0B) * 4);
|
||||
if (offset == -1)
|
||||
return;
|
||||
|
||||
if (!_player->driver()->soundEffectIsPlaying(_sfxChannel ^ 1)) {
|
||||
_sfxChannel ^= 1;
|
||||
} else if (_player->driver()->soundEffectIsPlaying(_sfxChannel)) {
|
||||
_sfxChannel ^= 1;
|
||||
_player->driver()->stopSoundEffect(_sfxChannel);
|
||||
}
|
||||
|
||||
uint32 *sfxHeader = (uint32 *)(fileBody + offset);
|
||||
uint32 sfxHeaderID = READ_LE_UINT32(sfxHeader);
|
||||
uint32 playbackBufferSize = sfxHeaderID == 1 ? 30704 : READ_LE_UINT32(&sfxHeader[3]);
|
||||
|
||||
uint8 *sfxPlaybackBuffer = new uint8[playbackBufferSize + 32];
|
||||
memcpy(sfxPlaybackBuffer, fileBody + offset, 32);
|
||||
|
||||
uint8 *dst = sfxPlaybackBuffer + 32;
|
||||
memset(dst, 0x80, playbackBufferSize);
|
||||
|
||||
uint8 *sfxBody = ((uint8 *)sfxHeader) + 0x20;
|
||||
|
||||
if (!sfxHeaderID) {
|
||||
memcpy(dst, sfxBody, playbackBufferSize);
|
||||
} else if (sfxHeaderID == 1) {
|
||||
Screen::decodeFrame4(sfxBody, dst, playbackBufferSize);
|
||||
} else if (_sfxWDTable) {
|
||||
uint8 *tgt = dst;
|
||||
uint32 sfx_BtTable_Offset = 0;
|
||||
uint32 sfx_WdTable_Offset = 0;
|
||||
uint32 sfx_WdTable_Number = 5;
|
||||
uint32 inSize = READ_LE_UINT32(&sfxHeader[1]);
|
||||
|
||||
for (uint32 i = 0; i < inSize; i++) {
|
||||
sfx_WdTable_Offset = (sfx_WdTable_Number * 3 << 9) + sfxBody[i] * 6;
|
||||
sfx_WdTable_Number = READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset);
|
||||
|
||||
sfx_BtTable_Offset += (int16)READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset + 2);
|
||||
*tgt++ = _sfxBTTable[((sfx_BtTable_Offset >> 2) & 0xFF)];
|
||||
|
||||
sfx_BtTable_Offset += (int16)READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset + 4);
|
||||
*tgt++ = _sfxBTTable[((sfx_BtTable_Offset >> 2) & 0xFF)];
|
||||
}
|
||||
}
|
||||
|
||||
_player->driver()->channelVolume(_sfxChannel, 127);
|
||||
_player->driver()->channelPan(_sfxChannel, 0x40);
|
||||
_player->driver()->channelPitch(_sfxChannel, 0);
|
||||
_player->driver()->playSoundEffect(_sfxChannel, note, 127, sfxPlaybackBuffer);
|
||||
delete[] sfxPlaybackBuffer;
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::updateVolumeSettings() {
|
||||
if (!_player)
|
||||
return;
|
||||
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
_player->driver()->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
|
||||
_player->driver()->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::enableMusic(int enable) {
|
||||
if (enable && enable != _musicEnabled && _lastTrack != -1)
|
||||
haltTrack();
|
||||
_musicEnabled = enable;
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::stopAllSoundEffects() {
|
||||
_player->driver()->channelVolume(0x46, 0);
|
||||
_player->driver()->channelVolume(0x47, 0);
|
||||
_player->driver()->stopSoundEffect(0x46);
|
||||
_player->driver()->stopSoundEffect(0x47);
|
||||
_sfxChannel = 0x46;
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::beginFadeOut() {
|
||||
if (_cdaPlaying) {
|
||||
for (int i = 118; i > 103; i--) {
|
||||
_player->driver()->setOutputVolume(1, i, i);
|
||||
_vm->delay(2 * _vm->tickLength());
|
||||
}
|
||||
|
||||
for (int i = 103; i > 83; i -= 2) {
|
||||
_player->driver()->setOutputVolume(1, i, i);
|
||||
_vm->delay(2 * _vm->tickLength());
|
||||
}
|
||||
|
||||
for (int i = 83; i > 58; i -= 2) {
|
||||
_player->driver()->setOutputVolume(1, i, i);
|
||||
_vm->delay(_vm->tickLength());
|
||||
}
|
||||
|
||||
for (int i = 58; i > 0; i--) {
|
||||
_player->driver()->setOutputVolume(1, i, i);
|
||||
_vm->delay(1);
|
||||
}
|
||||
|
||||
_player->driver()->setOutputVolume(1, 0, 0);
|
||||
|
||||
} else {
|
||||
if (_lastTrack == -1 || !_player->isPlaying())
|
||||
return;
|
||||
|
||||
uint32 ticks = 2;
|
||||
int tickAdv = 0;
|
||||
|
||||
uint16 fadeVolCur[12];
|
||||
uint16 fadeVolStep[12];
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
fadeVolCur[i] = READ_LE_UINT16(&_musicFadeTable[(_lastTrack * 12 + i) * 2]);
|
||||
fadeVolStep[i] = fadeVolCur[i] / 50;
|
||||
fadeVolCur[i + 6] = READ_LE_UINT16(&_musicFadeTable[(_lastTrack * 12 + 6 + i) * 2]);
|
||||
fadeVolStep[i + 6] = fadeVolCur[i + 6] / 30;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
for (int ii = 0; ii < 6; ii++)
|
||||
_player->driver()->channelVolume(ii, fadeVolCur[ii]);
|
||||
for (int ii = 0x40; ii < 0x46; ii++)
|
||||
_player->driver()->channelVolume(ii, fadeVolCur[ii - 0x3A]);
|
||||
|
||||
for (int ii = 0; ii < 6; ii++) {
|
||||
fadeVolCur[ii] -= fadeVolStep[ii];
|
||||
if (fadeVolCur[ii] < 10)
|
||||
fadeVolCur[ii] = 0;
|
||||
fadeVolCur[ii + 6] -= fadeVolStep[ii + 6];
|
||||
if (fadeVolCur[ii + 6] < 10)
|
||||
fadeVolCur[ii + 6] = 0;
|
||||
}
|
||||
|
||||
if (++tickAdv == 3) {
|
||||
tickAdv = 0;
|
||||
ticks += 2;
|
||||
}
|
||||
_vm->delay(ticks * _vm->tickLength());
|
||||
}
|
||||
}
|
||||
|
||||
haltTrack();
|
||||
}
|
||||
|
||||
bool SoundTowns_LoK::loadInstruments() {
|
||||
uint8 *twm = _vm->resource()->fileData("twmusic.pak", nullptr);
|
||||
if (!twm)
|
||||
return false;
|
||||
|
||||
Screen::decodeFrame4(twm, _musicTrackData, 50570);
|
||||
for (int i = 0; i < 128; i++)
|
||||
_player->driver()->loadInstrument(0, i, &_musicTrackData[i * 48 + 8]);
|
||||
|
||||
Screen::decodeFrame4(twm + 3232, _musicTrackData, 50570);
|
||||
for (int i = 0; i < 32; i++)
|
||||
_player->driver()->loadInstrument(0x40, i, &_musicTrackData[i * 128 + 8]);
|
||||
|
||||
_player->driver()->unloadWaveTable(-1);
|
||||
uint8 *src = &_musicTrackData[32 * 128 + 8];
|
||||
for (int i = 0; i < 10; i++) {
|
||||
_player->driver()->loadWaveTable(src);
|
||||
src = src + READ_LE_UINT16(&src[12]) + 32;
|
||||
}
|
||||
|
||||
_player->driver()->reserveSoundEffectChannels(2);
|
||||
|
||||
delete[] twm;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::playEuphonyTrack(uint32 offset, int loop) {
|
||||
uint8 *twm = _vm->resource()->fileData("twmusic.pak", nullptr);
|
||||
Screen::decodeFrame4(twm + 19312 + offset, _musicTrackData, 50570);
|
||||
delete[] twm;
|
||||
|
||||
const uint8 *src = _musicTrackData + 852;
|
||||
for (int i = 0; i < 32; i++)
|
||||
_player->configPart_enable(i, *src++);
|
||||
for (int i = 0; i < 32; i++)
|
||||
_player->configPart_setType(i, *src++);
|
||||
for (int i = 0; i < 32; i++)
|
||||
_player->configPart_remap(i, *src++);
|
||||
for (int i = 0; i < 32; i++)
|
||||
_player->configPart_adjustVolume(i, *src++);
|
||||
for (int i = 0; i < 32; i++)
|
||||
_player->configPart_setTranspose(i, *src++);
|
||||
|
||||
src = _musicTrackData + 1748;
|
||||
for (int i = 0; i < 6; i++)
|
||||
_player->driver()->assignPartToChannel(i, *src++);
|
||||
for (int i = 0x40; i < 0x46; i++)
|
||||
_player->driver()->assignPartToChannel(i, *src++);
|
||||
|
||||
uint32 trackSize = READ_LE_UINT32(_musicTrackData + 2048);
|
||||
uint8 startTick = _musicTrackData[2052];
|
||||
|
||||
_player->setTempo(_musicTrackData[2053]);
|
||||
|
||||
src = _musicTrackData + 2054;
|
||||
uint32 l = READ_LE_UINT32(src + trackSize);
|
||||
trackSize += (l + 4);
|
||||
l = READ_LE_UINT32(src + trackSize);
|
||||
trackSize += (l + 4);
|
||||
|
||||
_player->setLoopStatus(loop);
|
||||
_player->startTrack(src, trackSize, startTick);
|
||||
}
|
||||
|
||||
void SoundTowns_LoK::fadeOutSoundEffects() {
|
||||
for (int i = 127; i > 0; i-= 12) {
|
||||
_player->driver()->channelVolume(0x46, i);
|
||||
_player->driver()->channelVolume(0x47, i);
|
||||
_vm->delay(_vm->tickLength());
|
||||
}
|
||||
stopAllSoundEffects();
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
Reference in New Issue
Block a user