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
|
||||
Reference in New Issue
Block a user