scummvm/audio/mods/universaltracker.cpp
Filippos Karapetis 7347a82df2 COMMON: Remove inttypes.h
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)
2024-07-04 00:03:15 +03:00

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