mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
It contains defines that should be available from scummsys.h. Also, it contains defines to support ancient and now unsupported versions of MSVC (2013 and earlier)
403 lines
9.2 KiB
C++
403 lines
9.2 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
// Disable symbol overrides for FILE and fseek as those are used in the
|
|
// mikmod headers.
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
|
|
|
|
// On Windows, unlink and setjmp/longjmp may also be triggered.
|
|
#if defined(WIN32)
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_chdir
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_getcwd
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_unlink
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_setjmp
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_longjmp
|
|
#endif
|
|
|
|
#include "common/ptr.h"
|
|
#include "common/stream.h"
|
|
#include "common/textconsole.h"
|
|
#include "common/util.h"
|
|
|
|
#include "audio/audiostream.h"
|
|
#include "audio/decoders/raw.h"
|
|
|
|
#ifdef USE_OPENMPT
|
|
|
|
#include <libopenmpt/libopenmpt.h>
|
|
|
|
#elif defined(USE_MIKMOD)
|
|
|
|
#include <mikmod.h>
|
|
|
|
#endif
|
|
|
|
#ifdef USE_OPENMPT
|
|
|
|
static size_t memoryReaderRead(void *stream, void *dst, size_t bytes) {
|
|
Common::SeekableReadStream *reader = (Common::SeekableReadStream *)stream;
|
|
|
|
if (!reader) {
|
|
return 0;
|
|
}
|
|
|
|
uint32 receivedBytes = reader->read(dst, bytes);
|
|
|
|
if (receivedBytes < bytes) {
|
|
return 0;
|
|
}
|
|
|
|
return receivedBytes;
|
|
}
|
|
|
|
static int memoryReaderSeek(void *stream, int64_t offset, int whence) {
|
|
Common::SeekableReadStream *reader = (Common::SeekableReadStream *)stream;
|
|
|
|
if (!reader) {
|
|
return -1;
|
|
}
|
|
|
|
bool ret = reader->seek(offset, whence);
|
|
|
|
if (ret)
|
|
return 0;
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int64_t memoryReaderTell(void *stream) {
|
|
Common::SeekableReadStream *reader = (Common::SeekableReadStream *)stream;
|
|
|
|
if (reader) {
|
|
return reader->pos();
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
#elif defined(USE_MIKMOD)
|
|
|
|
typedef struct MikMemoryReader {
|
|
MREADER core;
|
|
Common::SeekableReadStream *stream;
|
|
} MikMemoryReader;
|
|
|
|
static BOOL memoryReaderEof(MREADER *reader);
|
|
static BOOL memoryReaderRead(MREADER *reader, void *ptr, size_t size);
|
|
static int memoryReaderGet(MREADER *reader);
|
|
static int memoryReaderSeek(MREADER *reader, long offset, int whence);
|
|
static long memoryReaderTell(MREADER *reader);
|
|
|
|
MREADER *createMikMemoryReader(Common::SeekableReadStream *stream) {
|
|
MikMemoryReader *reader = (MikMemoryReader *)calloc(1, sizeof(MikMemoryReader));
|
|
|
|
if (reader) {
|
|
reader->core.Eof = &memoryReaderEof;
|
|
reader->core.Read = &memoryReaderRead;
|
|
reader->core.Get = &memoryReaderGet;
|
|
reader->core.Seek = &memoryReaderSeek;
|
|
reader->core.Tell = &memoryReaderTell;
|
|
reader->stream = stream;
|
|
}
|
|
|
|
return (MREADER *)reader;
|
|
}
|
|
|
|
static BOOL memoryReaderEof(MREADER *reader) {
|
|
MikMemoryReader *mr = (MikMemoryReader *)reader;
|
|
|
|
if (!mr)
|
|
return 1;
|
|
|
|
if (mr->stream && mr->stream->eos() == true)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static BOOL memoryReaderRead(MREADER *reader, void *ptr, size_t size) {
|
|
MikMemoryReader *mr;
|
|
mr = (MikMemoryReader *)reader;
|
|
|
|
if (!mr || !mr->stream)
|
|
return 0;
|
|
|
|
uint32 receivedBytes = mr->stream->read(ptr, size);
|
|
|
|
if (receivedBytes < size)
|
|
return 0; // not enough remaining bytes (or error)
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int memoryReaderGet(MREADER *reader) {
|
|
MikMemoryReader *mr;
|
|
mr = (MikMemoryReader *)reader;
|
|
|
|
if (!mr->stream)
|
|
return -1;
|
|
|
|
return mr->stream->readByte();
|
|
}
|
|
|
|
static int memoryReaderSeek(MREADER *reader, long offset, int whence) {
|
|
MikMemoryReader *mr;
|
|
mr = (MikMemoryReader *)reader;
|
|
|
|
if (!reader || !mr->stream)
|
|
return -1;
|
|
|
|
return mr->stream->seek(offset, whence);
|
|
}
|
|
|
|
static long memoryReaderTell(MREADER *reader) {
|
|
if (reader)
|
|
return ((MikMemoryReader *)reader)->stream->pos();
|
|
|
|
return 0;
|
|
}
|
|
// End memory wrappper
|
|
|
|
#endif // USE_MIKMOD
|
|
|
|
#ifdef USE_OPENMPT
|
|
|
|
namespace Audio {
|
|
class UniversalTrackerMod : public RewindableAudioStream {
|
|
public:
|
|
UniversalTrackerMod(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate);
|
|
~UniversalTrackerMod();
|
|
|
|
// ImpulseTrackerMod functions
|
|
bool isLoaded() {
|
|
return _mpt_load_successful;
|
|
}
|
|
|
|
int readBuffer(int16 *buffer, const int numSamples) override {
|
|
openmpt_module_read_interleaved_stereo(_mod, getRate(), numSamples / 2, buffer);
|
|
|
|
return numSamples;
|
|
}
|
|
|
|
bool isStereo() const override {
|
|
return true;
|
|
}
|
|
|
|
int getRate() const override {
|
|
return _sampleRate;
|
|
}
|
|
|
|
bool endOfData() const override {
|
|
return _stream->eos();
|
|
}
|
|
|
|
bool endOfStream() const override {
|
|
return endOfData();
|
|
}
|
|
|
|
// RewindableAudioStream API
|
|
bool rewind() override {
|
|
openmpt_module_set_position_seconds(_mod, 0.0);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
DisposeAfterUse::Flag _dispose;
|
|
bool _mpt_load_successful = false;
|
|
Common::SeekableReadStream *_stream;
|
|
openmpt_module *_mod = nullptr;
|
|
int _sampleRate;
|
|
};
|
|
|
|
UniversalTrackerMod::UniversalTrackerMod(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate) {
|
|
if (!stream) {
|
|
warning("UniversalTrackerMod::UniversalTrackerMod(): Input file/stream is invalid.");
|
|
return;
|
|
}
|
|
|
|
int mod_err;
|
|
const char *mod_err_str = NULL;
|
|
|
|
_stream = stream;
|
|
_dispose = disposeAfterUse;
|
|
_sampleRate = rate;
|
|
|
|
openmpt_stream_callbacks stream_callbacks;
|
|
stream_callbacks.read = &memoryReaderRead;
|
|
stream_callbacks.seek = &memoryReaderSeek;
|
|
stream_callbacks.tell = &memoryReaderTell;
|
|
|
|
_mod = openmpt_module_create2(stream_callbacks, _stream, NULL, NULL, NULL, NULL, &mod_err, &mod_err_str, NULL);
|
|
|
|
if (!_mod) {
|
|
mod_err_str = openmpt_error_string(mod_err);
|
|
warning("UniversalTrackerMod::UniversalTrackerMod(): Parsing mod error: %s ", mod_err_str);
|
|
openmpt_free_string(mod_err_str);
|
|
return;
|
|
}
|
|
|
|
_mpt_load_successful = true;
|
|
}
|
|
|
|
UniversalTrackerMod::~UniversalTrackerMod() {
|
|
if (_mod)
|
|
openmpt_module_destroy(_mod);
|
|
|
|
if (_dispose == DisposeAfterUse::Flag::YES)
|
|
delete _stream;
|
|
}
|
|
|
|
} // End of namespace Audio
|
|
|
|
#endif // #ifdef USE_OPENMPT
|
|
|
|
#ifdef USE_MIKMOD
|
|
|
|
namespace Audio {
|
|
class UniversalTrackerMod : public RewindableAudioStream {
|
|
public:
|
|
UniversalTrackerMod(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate);
|
|
~UniversalTrackerMod();
|
|
|
|
// ImpulseTrackerMod functions
|
|
bool isLoaded() {
|
|
return _mikmod_load_successful;
|
|
}
|
|
|
|
// AudioStream API
|
|
int readBuffer(int16 *buffer, const int numSamples) override {
|
|
// Multiplied by 2 as VC_WriteBytes function expects 8 byte integer arrays, whereas buffer needs 16 ones.
|
|
VC_WriteBytes((SBYTE *)buffer, numSamples * 2);
|
|
|
|
return numSamples;
|
|
}
|
|
|
|
bool isStereo() const override {
|
|
return true;
|
|
}
|
|
int getRate() const override {
|
|
return _sampleRate;
|
|
}
|
|
bool endOfData() const override {
|
|
return !Player_Active();
|
|
}
|
|
bool endOfStream() const override {
|
|
return endOfData();
|
|
}
|
|
|
|
// RewindableAudioStream API
|
|
bool rewind() override {
|
|
Player_SetPosition(0);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
DisposeAfterUse::Flag _dispose;
|
|
bool _mikmod_load_successful = false;
|
|
Common::SeekableReadStream *_stream;
|
|
MREADER *_reader = nullptr;
|
|
MODULE *_mod = nullptr;
|
|
int _sampleRate;
|
|
};
|
|
|
|
UniversalTrackerMod::UniversalTrackerMod(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate) {
|
|
if (!stream) {
|
|
warning("UniversalTrackerMod::UniversalTrackerMod(): Input file/stream is invalid.");
|
|
return;
|
|
}
|
|
|
|
_sampleRate = rate;
|
|
|
|
MikMod_InitThreads();
|
|
MikMod_RegisterDriver(&drv_nos);
|
|
|
|
// Set flags
|
|
md_mode |= DMODE_SOFT_MUSIC | DMODE_NOISEREDUCTION;
|
|
md_mixfreq = getRate();
|
|
|
|
if (MikMod_Init("")) {
|
|
warning("UniversalTrackerMod::UniversalTrackerMod(): Could not initialize sound, reason: %s",
|
|
MikMod_strerror(MikMod_errno));
|
|
return;
|
|
}
|
|
|
|
// Loading only impulse tracker loader!
|
|
MikMod_RegisterLoader(&load_it);
|
|
|
|
_stream = stream;
|
|
_dispose = disposeAfterUse;
|
|
|
|
// Load mod using custom loader class!
|
|
_reader = createMikMemoryReader(_stream);
|
|
_mod = Player_LoadGeneric(_reader, 64, 0);
|
|
if (!_mod) {
|
|
warning("UniversalTrackerMod::UniversalTrackerMod(): Parsing mod error: %s", MikMod_strerror(MikMod_errno));
|
|
return;
|
|
}
|
|
|
|
// Start mikmod playing, ie fill VC_Driver buffer with data
|
|
Player_Start(_mod);
|
|
_mikmod_load_successful = true;
|
|
}
|
|
|
|
UniversalTrackerMod::~UniversalTrackerMod() {
|
|
Player_Stop();
|
|
|
|
if (_mod)
|
|
Player_Free(_mod);
|
|
|
|
if (_reader)
|
|
free(_reader);
|
|
|
|
if (_dispose == DisposeAfterUse::Flag::YES)
|
|
delete _stream;
|
|
|
|
MikMod_Exit();
|
|
}
|
|
|
|
|
|
} // End of namespace Audio
|
|
|
|
#endif // #ifdef USE_MIKMOD
|
|
|
|
namespace Audio {
|
|
|
|
RewindableAudioStream *makeUniversalTrackerStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate) {
|
|
|
|
#if !defined(USE_OPENMPT) && !defined(USE_MIKMOD)
|
|
warning("Modplayer Support not compiled in");
|
|
return nullptr;
|
|
#else
|
|
|
|
UniversalTrackerMod *impulseTrackerMod = new UniversalTrackerMod(stream, disposeAfterUse, rate);
|
|
|
|
if (!impulseTrackerMod->isLoaded()) {
|
|
delete impulseTrackerMod;
|
|
return nullptr;
|
|
}
|
|
|
|
return impulseTrackerMod;
|
|
|
|
#endif
|
|
}
|
|
|
|
}
|