- uncoupled the stream sources from the low level implementation. The entire setup had the stream sources depend on the SoundStream class, severely limiting reusability. This was changed that there is one SoundStream class that uses the StreamSources as mere data source that has no knowledge and no connection to the underlying system, similar to how the MIDI system works. With this there are only 3 top level music classes left - MIDIStreamer, StreamSong and CDSong.

Also made the decode_vorbis function in DUMB a function pointer so that the library does not depend on high level code and can just ignore the vorbis case if no supported.
This commit is contained in:
Christoph Oelckers 2019-09-28 20:33:25 +02:00
parent cfe89ef6e6
commit d94b63b486
17 changed files with 389 additions and 379 deletions

View file

@ -802,6 +802,8 @@ DUH *make_duh(
void DUMBEXPORT duh_set_length(DUH *duh, int32 length); void DUMBEXPORT duh_set_length(DUH *duh, int32 length);
extern short *(*dumb_decode_vorbis)(int outlen, const void *oggstream, int sizebytes);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -28,7 +28,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
extern short *DUMBCALLBACK dumb_decode_vorbis(int outlen, const void *oggstream, int sizebytes); short * (*dumb_decode_vorbis)(int outlen, const void *oggstream, int sizebytes);
/** TODO: /** TODO:
@ -830,7 +830,7 @@ static int it_xm_read_sample_data(IT_SAMPLE *sample, unsigned char roguebytes, D
sample->loop_start >>= 1; sample->loop_start >>= 1;
sample->loop_end >>= 1; sample->loop_end >>= 1;
} }
output = dumb_decode_vorbis(outlen, (char *)sample->data + 4, datasizebytes - 4); output = dumb_decode_vorbis? dumb_decode_vorbis(outlen, (char *)sample->data + 4, datasizebytes - 4) : NULL;
if (output != NULL) if (output != NULL)
{ {
free(sample->data); free(sample->data);

View file

@ -20,14 +20,6 @@ struct MidiHeader
MidiHeader *lpNext; MidiHeader *lpNext;
}; };
struct SoundStreamInfo
{
// Format is always 32 bit float. If mBufferSize is 0, the song doesn't use streaming but plays through a different interface.
int mBufferSize;
int mSampleRate;
int mNumChannels;
};
class MIDIDevice class MIDIDevice
{ {
public: public:

View file

@ -44,3 +44,12 @@ enum EMidiDevice
MDEV_COUNT MDEV_COUNT
}; };
struct SoundStreamInfo
{
// Format is always 32 bit float. If mBufferSize is 0, the song doesn't use streaming but plays through a different interface.
int mBufferSize;
int mSampleRate;
int mNumChannels;
};

View file

@ -61,7 +61,6 @@ EXTERN_CVAR (Bool, snd_pitched)
EXTERN_CVAR (Color, am_wallcolor) EXTERN_CVAR (Color, am_wallcolor)
EXTERN_CVAR (Color, am_fdwallcolor) EXTERN_CVAR (Color, am_fdwallcolor)
EXTERN_CVAR (Color, am_cdwallcolor) EXTERN_CVAR (Color, am_cdwallcolor)
EXTERN_CVAR (Float, spc_amp)
EXTERN_CVAR (Bool, wi_percents) EXTERN_CVAR (Bool, wi_percents)
EXTERN_CVAR (Int, gl_texture_hqresizemode) EXTERN_CVAR (Int, gl_texture_hqresizemode)
EXTERN_CVAR (Int, gl_texture_hqresizemult) EXTERN_CVAR (Int, gl_texture_hqresizemult)
@ -324,6 +323,7 @@ void FGameConfigFile::DoGlobalSetup ()
vsync->ResetToDefault (); vsync->ResetToDefault ();
} }
} }
/* spc_amp no longer exists
if (last < 206) if (last < 206)
{ // spc_amp is now a float, not an int. { // spc_amp is now a float, not an int.
if (spc_amp > 16) if (spc_amp > 16)
@ -331,6 +331,7 @@ void FGameConfigFile::DoGlobalSetup ()
spc_amp = spc_amp / 16.f; spc_amp = spc_amp / 16.f;
} }
} }
*/
if (last < 207) if (last < 207)
{ // Now that snd_midiprecache works again, you probably don't want it on. { // Now that snd_midiprecache works again, you probably don't want it on.
FBaseCVar *precache = FindCVar ("snd_midiprecache", NULL); FBaseCVar *precache = FindCVar ("snd_midiprecache", NULL);

View file

@ -379,20 +379,6 @@ void SoundRenderer::DrawWaveDebug(int mode)
{ {
} }
SoundStream::~SoundStream ()
{
}
bool SoundStream::SetPosition(unsigned int pos)
{
return false;
}
bool SoundStream::SetOrder(int order)
{
return false;
}
FString SoundStream::GetStats() FString SoundStream::GetStats()
{ {
return "No stream stats available."; return "No stream stats available.";

View file

@ -58,7 +58,7 @@ enum EStartSoundFlags
class SoundStream class SoundStream
{ {
public: public:
virtual ~SoundStream (); virtual ~SoundStream () {}
enum enum
{ // For CreateStream { // For CreateStream
@ -77,8 +77,6 @@ public:
virtual bool SetPaused(bool paused) = 0; virtual bool SetPaused(bool paused) = 0;
virtual unsigned int GetPosition() = 0; virtual unsigned int GetPosition() = 0;
virtual bool IsEnded() = 0; virtual bool IsEnded() = 0;
virtual bool SetPosition(unsigned int pos);
virtual bool SetOrder(int order);
virtual FString GetStats(); virtual FString GetStats();
}; };

View file

@ -47,8 +47,9 @@
#include "stats.h" #include "stats.h"
#include "vm.h" #include "vm.h"
#include "s_music.h" #include "s_music.h"
#include "..//libraries/zmusic/midisources/midisource.h" #include "../libraries/zmusic/midisources/midisource.h"
EXTERN_CVAR(Float, gme_stereodepth)
#define GZIP_ID1 31 #define GZIP_ID1 31
@ -261,11 +262,6 @@ FString MusInfo::GetStats()
return "No stats available for this song"; return "No stats available for this song";
} }
MusInfo *MusInfo::GetOPLDumper(const char *filename)
{
return nullptr;
}
MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate) MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate)
{ {
return nullptr; return nullptr;
@ -371,6 +367,7 @@ static EMIDIType IdentifyMIDIType(uint32_t *id, int size)
MusInfo *I_RegisterSong (FileReader &reader, MidiDeviceSetting *device) MusInfo *I_RegisterSong (FileReader &reader, MidiDeviceSetting *device)
{ {
MusInfo *info = nullptr; MusInfo *info = nullptr;
StreamSource *streamsource = nullptr;
const char *fmt; const char *fmt;
uint32_t id[32/4]; uint32_t id[32/4];
@ -446,26 +443,28 @@ MusInfo *I_RegisterSong (FileReader &reader, MidiDeviceSetting *device)
(id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL (id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL
(id[0] == MAKE_ID('A','D','L','I') && *((uint8_t *)id + 4) == 'B')) // Martin Fernandez's modified IMF (id[0] == MAKE_ID('A','D','L','I') && *((uint8_t *)id + 4) == 'B')) // Martin Fernandez's modified IMF
{ {
info = new OPLMUSSong (reader, device != nullptr? device->args.GetChars() : ""); streamsource = OPL_OpenSong(reader, device != nullptr? device->args.GetChars() : "");
} }
else if ((id[0] == MAKE_ID('R', 'I', 'F', 'F') && id[2] == MAKE_ID('C', 'D', 'X', 'A'))) else if ((id[0] == MAKE_ID('R', 'I', 'F', 'F') && id[2] == MAKE_ID('C', 'D', 'X', 'A')))
{ {
info = XA_OpenSong(reader); streamsource = XA_OpenSong(reader);
} }
// Check for game music // Check for game music
else if ((fmt = GME_CheckFormat(id[0])) != nullptr && fmt[0] != '\0') else if ((fmt = GME_CheckFormat(id[0])) != nullptr && fmt[0] != '\0')
{ {
info = GME_OpenSong(reader, fmt); streamsource = GME_OpenSong(reader, fmt, gme_stereodepth);
} }
// Check for module formats // Check for module formats
else else
{ {
info = MOD_OpenSong(reader); streamsource = MOD_OpenSong(reader);
} }
if (info == nullptr) if (info == nullptr && streamsource == nullptr)
{ {
info = SndFile_OpenSong(reader); streamsource = SndFile_OpenSong(reader);
} }
if (streamsource) info = OpenStreamSong(streamsource);
if (info == nullptr) if (info == nullptr)
{ {

View file

@ -78,7 +78,6 @@ public:
virtual void Update(); virtual void Update();
virtual int GetDeviceType() const { return MDEV_DEFAULT; } // MDEV_DEFAULT stands in for anything that cannot change playback parameters which needs a restart. virtual int GetDeviceType() const { return MDEV_DEFAULT; } // MDEV_DEFAULT stands in for anything that cannot change playback parameters which needs a restart.
virtual FString GetStats(); virtual FString GetStats();
virtual MusInfo *GetOPLDumper(const char *filename);
virtual MusInfo *GetWaveDumper(const char *filename, int rate); virtual MusInfo *GetWaveDumper(const char *filename, int rate);
virtual void ChangeSettingInt(const char *setting, int value); // FluidSynth settings virtual void ChangeSettingInt(const char *setting, int value); // FluidSynth settings
virtual void ChangeSettingNum(const char *setting, double value); // " virtual void ChangeSettingNum(const char *setting, double value); // "

View file

@ -110,45 +110,31 @@ protected:
// Anything supported by the sound system out of the box -------------------- // Anything supported by the sound system out of the box --------------------
class StreamSong : public MusInfo class StreamSource
{ {
protected:
bool m_Looping = true;
int m_OutputRate;
public: public:
StreamSong (FileReader &reader);
~StreamSong (); StreamSource (int outputRate) { m_OutputRate = outputRate; }
void Play (bool looping, int subsong); virtual ~StreamSource () {}
void Pause (); virtual void SetPlayMode(bool looping) { m_Looping = looping; }
void Resume (); virtual bool Start() { return true; }
void Stop (); virtual bool SetPosition(unsigned position) { return false; }
bool IsPlaying (); virtual bool SetSubsong(int subsong) { return false; }
bool IsValid () const { return m_Stream != NULL; } virtual bool GetData(void *buffer, size_t len) = 0;
bool SetPosition (unsigned int pos); virtual SoundStreamInfo GetFormat() { return {65536, m_OutputRate, 2 }; } // Default format is: System's output sample rate, 32 bit float, stereo
bool SetSubsong (int subsong); virtual FString GetStats() { return ""; }
FString GetStats(); virtual void ChangeSettingInt(const char *name, int value) { }
virtual void ChangeSettingNum(const char *name, double value) { }
virtual void ChangeSettingString(const char *name, const char *value) { }
protected: protected:
StreamSong () : m_Stream(NULL) {} StreamSource() = default;
SoundStream *m_Stream;
}; };
// MUS file played by a software OPL2 synth and streamed through the sound system
class OPLMUSSong : public StreamSong
{
public:
OPLMUSSong (FileReader &reader, const char *args);
~OPLMUSSong ();
void Play (bool looping, int subsong);
bool IsPlaying ();
bool IsValid () const;
void ChangeSettingInt (const char *, int) override;
protected:
static bool FillStream (SoundStream *stream, void *buff, int len, void *userdata);
OPLmusicFile *Music;
};
// CD track/disk played through the multimedia system ----------------------- // CD track/disk played through the multimedia system -----------------------
@ -191,14 +177,17 @@ bool WildMidi_SetupConfig(WildMidiConfig* config, const char* args);
// Module played via foo_dumb ----------------------------------------------- // Module played via foo_dumb -----------------------------------------------
MusInfo *MOD_OpenSong(FileReader &reader); StreamSource *MOD_OpenSong(FileReader &reader);
StreamSource *GME_OpenSong(FileReader &reader, const char *fmt, float depth);
StreamSource *SndFile_OpenSong(FileReader &fr);
StreamSource* XA_OpenSong(FileReader& reader);
StreamSource *OPL_OpenSong(FileReader &reader, const char *args);
// Music played via Game Music Emu ------------------------------------------ // stream song ------------------------------------------
MusInfo *OpenStreamSong(StreamSource *source);
const char *GME_CheckFormat(uint32_t header); const char *GME_CheckFormat(uint32_t header);
MusInfo *GME_OpenSong(FileReader &reader, const char *fmt);
MusInfo *SndFile_OpenSong(FileReader &fr);
MusInfo* XA_OpenSong(FileReader& reader);
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------

View file

@ -915,3 +915,11 @@ bool WildMidi_SetupConfig(WildMidiConfig* config, const char* args)
// This one is for Win32 MMAPI. // This one is for Win32 MMAPI.
CVAR(Bool, snd_midiprecache, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR(Bool, snd_midiprecache, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
// GME
CUSTOM_CVAR(Float, gme_stereodepth, 0.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (currSong != nullptr)
currSong->ChangeSettingNum("GME.stereodepth", *self);
}

View file

@ -44,19 +44,31 @@
#include "../dumb/include/dumb.h" #include "../dumb/include/dumb.h"
#include "../dumb/include/internal/it.h" #include "../dumb/include/internal/it.h"
//==========================================================================
//
// dumb_decode_vorbis
//
//==========================================================================
static short *DUMBCALLBACK dumb_decode_vorbis_(int outlen, const void *oggstream, int sizebytes)
{
return GSnd->DecodeSample(outlen, oggstream, sizebytes, CODEC_Vorbis);
}
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
// TYPES ------------------------------------------------------------------- // TYPES -------------------------------------------------------------------
class input_mod : public StreamSong class DumbSong : public StreamSource
{ {
public: public:
input_mod(DUH *myduh); DumbSong(DUH *myduh);
~input_mod(); ~DumbSong();
//bool SetPosition(int ms); //bool SetPosition(int ms);
bool SetSubsong(int subsong); bool SetSubsong(int subsong) override;
void Play(bool looping, int subsong); bool Start() override;
FString GetStats(); SoundStreamInfo GetFormat() override;
FString GetStats() override;
FString Codec; FString Codec;
FString TrackerVersion; FString TrackerVersion;
@ -71,6 +83,7 @@ protected:
double delta; double delta;
double length; double length;
bool eof; bool eof;
bool started = false;
size_t written; size_t written;
DUH *duh; DUH *duh;
DUH_SIGRENDERER *sr; DUH_SIGRENDERER *sr;
@ -79,7 +92,8 @@ protected:
bool open2(long pos); bool open2(long pos);
long render(double volume, double delta, long samples, sample_t **buffer); long render(double volume, double delta, long samples, sample_t **buffer);
int decode_run(void *buffer, unsigned int size); int decode_run(void *buffer, unsigned int size);
static bool read(SoundStream *stream, void *buff, int len, void *userdata); bool GetData(void *buffer, size_t len) override;
}; };
#pragma pack(1) #pragma pack(1)
@ -165,7 +179,7 @@ static inline uint64_t time_to_samples(double p_time,int p_sample_rate)
// //
//========================================================================== //==========================================================================
static void ReadDUH(DUH * duh, input_mod *info, bool meta, bool dos) static void ReadDUH(DUH * duh, DumbSong *info, bool meta, bool dos)
{ {
if (!duh) return; if (!duh) return;
@ -249,7 +263,7 @@ static void ReadDUH(DUH * duh, input_mod *info, bool meta, bool dos)
// //
//========================================================================== //==========================================================================
static bool ReadIT(const uint8_t * ptr, unsigned size, input_mod *info, bool meta) static bool ReadIT(const uint8_t * ptr, unsigned size, DumbSong *info, bool meta)
{ {
PITFILEHEADER pifh = (PITFILEHEADER) ptr; PITFILEHEADER pifh = (PITFILEHEADER) ptr;
if ((!ptr) || (size < 0x100)) return false; if ((!ptr) || (size < 0x100)) return false;
@ -759,7 +773,7 @@ static void MOD_SetAutoChip(DUH *duh)
// //
//========================================================================== //==========================================================================
MusInfo *MOD_OpenSong(FileReader &reader) StreamSource *MOD_OpenSong(FileReader &reader)
{ {
DUH *duh = 0; DUH *duh = 0;
int headsize; int headsize;
@ -770,7 +784,7 @@ MusInfo *MOD_OpenSong(FileReader &reader)
}; };
dumbfile_mem_status filestate; dumbfile_mem_status filestate;
DUMBFILE *f = NULL; DUMBFILE *f = NULL;
input_mod *state = NULL; DumbSong *state = NULL;
bool is_it = false; bool is_it = false;
bool is_dos = true; bool is_dos = true;
@ -920,17 +934,10 @@ MusInfo *MOD_OpenSong(FileReader &reader)
{ {
MOD_SetAutoChip(duh); MOD_SetAutoChip(duh);
} }
state = new input_mod(duh); state = new DumbSong(duh);
if (!state->IsValid())
{ if (is_it) ReadIT(filestate.ptr, size, state, false);
delete state; else ReadDUH(duh, state, false, is_dos);
state = NULL;
}
else
{
if (is_it) ReadIT(filestate.ptr, size, state, false);
else ReadDUH(duh, state, false, is_dos);
}
} }
else else
{ {
@ -946,22 +953,22 @@ MusInfo *MOD_OpenSong(FileReader &reader)
//========================================================================== //==========================================================================
// //
// input_mod :: read static // DumbSong :: read static
// //
//========================================================================== //==========================================================================
bool input_mod::read(SoundStream *stream, void *buffer, int sizebytes, void *userdata) bool DumbSong::GetData(void *buffer, size_t sizebytes)
{ {
input_mod *state = (input_mod *)userdata; if (eof)
if (state->eof)
{ {
memset(buffer, 0, sizebytes); memset(buffer, 0, sizebytes);
return false; return false;
} }
std::lock_guard<std::mutex> lock(state->crit_sec); std::lock_guard<std::mutex> lock_(crit_sec);
while (sizebytes > 0) while (sizebytes > 0)
{ {
int written = state->decode_run(buffer, sizebytes / 8); int written = decode_run(buffer, (unsigned)sizebytes / 8);
if (written < 0) if (written < 0)
{ {
return false; return false;
@ -987,12 +994,13 @@ bool input_mod::read(SoundStream *stream, void *buffer, int sizebytes, void *use
//========================================================================== //==========================================================================
// //
// input_mod constructor // DumbSong constructor
// //
//========================================================================== //==========================================================================
input_mod::input_mod(DUH *myduh) DumbSong::DumbSong(DUH *myduh)
{ {
dumb_decode_vorbis = dumb_decode_vorbis_;
duh = myduh; duh = myduh;
sr = NULL; sr = NULL;
eof = false; eof = false;
@ -1009,57 +1017,60 @@ input_mod::input_mod(DUH *myduh)
{ {
srate = (int)GSnd->GetOutputRate(); srate = (int)GSnd->GetOutputRate();
} }
m_Stream = GSnd->CreateStream(read, 32*1024, SoundStream::Float, srate, this);
delta = 65536.0 / srate; delta = 65536.0 / srate;
} }
//========================================================================== //==========================================================================
// //
// input_mod destructor // DumbSong destructor
// //
//========================================================================== //==========================================================================
input_mod::~input_mod() DumbSong::~DumbSong()
{ {
Stop();
if (m_Stream != NULL)
{
delete m_Stream;
m_Stream = NULL;
}
if (sr) duh_end_sigrenderer(sr); if (sr) duh_end_sigrenderer(sr);
if (duh) unload_duh(duh); if (duh) unload_duh(duh);
} }
//========================================================================== //==========================================================================
// //
// input_mod :: Play // DumbSong GetFormat
// //
//========================================================================== //==========================================================================
void input_mod::Play(bool looping, int order) SoundStreamInfo DumbSong::GetFormat()
{ {
m_Status = STATE_Stopped; return { 32*1024, srate, 2 };
m_Looping = looping;
start_order = order;
if (open2(0) && m_Stream->Play(m_Looping, 1))
{
m_Status = STATE_Playing;
}
} }
//========================================================================== //==========================================================================
// //
// input_mod :: SetSubsong // DumbSong :: Play
// //
//========================================================================== //==========================================================================
bool input_mod::SetSubsong(int order) bool DumbSong::Start()
{
started = open2(0);
return started;
}
//==========================================================================
//
// DumbSong :: SetSubsong
//
//==========================================================================
bool DumbSong::SetSubsong(int order)
{ {
if (order == start_order) if (order == start_order)
{ {
return false; return true;
}
if (!started)
{
start_order = order;
return true;
} }
std::lock_guard<std::mutex> lock(crit_sec); std::lock_guard<std::mutex> lock(crit_sec);
DUH_SIGRENDERER *oldsr = sr; DUH_SIGRENDERER *oldsr = sr;
@ -1076,11 +1087,11 @@ bool input_mod::SetSubsong(int order)
//========================================================================== //==========================================================================
// //
// input_mod :: open2 // DumbSong :: open2
// //
//========================================================================== //==========================================================================
bool input_mod::open2(long pos) bool DumbSong::open2(long pos)
{ {
if (start_order != 0) if (start_order != 0)
{ {
@ -1110,11 +1121,11 @@ bool input_mod::open2(long pos)
//========================================================================== //==========================================================================
// //
// input_mod :: render // DumbSong :: render
// //
//========================================================================== //==========================================================================
long input_mod::render(double volume, double delta, long samples, sample_t **buffer) long DumbSong::render(double volume, double delta, long samples, sample_t **buffer)
{ {
long written = duh_sigrenderer_generate_samples(sr, volume, delta, samples, buffer); long written = duh_sigrenderer_generate_samples(sr, volume, delta, samples, buffer);
@ -1139,18 +1150,19 @@ long input_mod::render(double volume, double delta, long samples, sample_t **buf
//========================================================================== //==========================================================================
// //
// input_mod :: decode_run // DumbSong :: decode_run
// //
// Given a buffer of 32-bit PCM stereo pairs and a size specified in // Given a buffer of 32-bit PCM stereo pairs and a size specified in
// samples, returns the number of samples written to the buffer. // samples, returns the number of samples written to the buffer.
// //
//========================================================================== //==========================================================================
int input_mod::decode_run(void *buffer, unsigned int size) int DumbSong::decode_run(void *buffer, unsigned int size)
{ {
if (eof) return 0; if (eof) return 0;
DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr); DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr);
if (itsr == nullptr) return 0;
int dt = int(delta * 65536.0 + 0.5); int dt = int(delta * 65536.0 + 0.5);
long samples = long((((LONG_LONG)itsr->time_left << 16) | itsr->sub_time_left) / dt); long samples = long((((LONG_LONG)itsr->time_left << 16) | itsr->sub_time_left) / dt);
if (samples == 0 || samples > (long)size) samples = size; if (samples == 0 || samples > (long)size) samples = size;
@ -1170,11 +1182,11 @@ retry:
//========================================================================== //==========================================================================
// //
// input_mod :: GetStats // DumbSong :: GetStats
// //
//========================================================================== //==========================================================================
FString input_mod::GetStats() FString DumbSong::GetStats()
{ {
//return StreamSong::GetStats(); //return StreamSong::GetStats();
DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr); DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr);
@ -1211,13 +1223,3 @@ FString input_mod::GetStats()
return out; return out;
} }
//==========================================================================
//
// dumb_decode_vorbis
//
//==========================================================================
extern "C" short *DUMBCALLBACK dumb_decode_vorbis(int outlen, const void *oggstream, int sizebytes)
{
return GSnd->DecodeSample(outlen, oggstream, sizebytes, CODEC_Vorbis);
}

View file

@ -37,6 +37,7 @@
// Uncomment if you are using the DLL version of GME. // Uncomment if you are using the DLL version of GME.
//#define GME_DLL //#define GME_DLL
#include <algorithm>
#include "i_musicinterns.h" #include "i_musicinterns.h"
#include <gme/gme.h> #include <gme/gme.h>
#include <mutex> #include <mutex>
@ -47,14 +48,17 @@
// TYPES ------------------------------------------------------------------- // TYPES -------------------------------------------------------------------
class GMESong : public StreamSong class GMESong : public StreamSource
{ {
public: public:
GMESong(Music_Emu *emu, int sample_rate); GMESong(Music_Emu *emu, int sample_rate);
~GMESong(); ~GMESong();
bool SetSubsong(int subsong); bool SetSubsong(int subsong) override;
void Play(bool looping, int subsong); bool Start() override;
FString GetStats(); void ChangeSettingNum(const char *name, double val) override;
FString GetStats() override;
bool GetData(void *buffer, size_t len) override;
SoundStreamInfo GetFormat() override;
protected: protected:
std::mutex CritSec; std::mutex CritSec;
@ -62,13 +66,11 @@ protected:
gme_info_t *TrackInfo; gme_info_t *TrackInfo;
int SampleRate; int SampleRate;
int CurrTrack; int CurrTrack;
bool started = false;
bool StartTrack(int track, bool getcritsec=true); bool StartTrack(int track, bool getcritsec=true);
bool GetTrackInfo(); bool GetTrackInfo();
int CalcSongLength(); int CalcSongLength();
void GMEDepthChanged(float val);
static bool Read(SoundStream *stream, void *buff, int len, void *userdata);
}; };
// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -82,13 +84,6 @@ protected:
// PUBLIC DATA DEFINITIONS ------------------------------------------------- // PUBLIC DATA DEFINITIONS -------------------------------------------------
// Currently not used. // Currently not used.
CVAR (Float, spc_amp, 1.875f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CUSTOM_CVAR(Float, gme_stereodepth, 0.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (currSong != nullptr)
currSong->GMEDepthChanged(self);
}
// PRIVATE DATA DEFINITIONS ------------------------------------------------ // PRIVATE DATA DEFINITIONS ------------------------------------------------
@ -111,7 +106,7 @@ const char *GME_CheckFormat(uint32_t id)
// //
//========================================================================== //==========================================================================
MusInfo *GME_OpenSong(FileReader &reader, const char *fmt) StreamSource *GME_OpenSong(FileReader &reader, const char *fmt, float stereo_depth)
{ {
gme_type_t type; gme_type_t type;
gme_err_t err; gme_err_t err;
@ -152,7 +147,7 @@ MusInfo *GME_OpenSong(FileReader &reader, const char *fmt)
reader.Seek(fpos, FileReader::SeekSet); reader.Seek(fpos, FileReader::SeekSet);
return nullptr; return nullptr;
} }
gme_set_stereo_depth(emu, clamp(*gme_stereodepth, 0.f, 1.f)); gme_set_stereo_depth(emu, std::min(std::max(stereo_depth, 0.f), 1.f));
gme_set_fade(emu, -1); // Enable infinite loop gme_set_fade(emu, -1); // Enable infinite loop
return new GMESong(emu, sample_rate); return new GMESong(emu, sample_rate);
} }
@ -169,7 +164,12 @@ GMESong::GMESong(Music_Emu *emu, int sample_rate)
SampleRate = sample_rate; SampleRate = sample_rate;
CurrTrack = 0; CurrTrack = 0;
TrackInfo = NULL; TrackInfo = NULL;
m_Stream = GSnd->CreateStream(Read, 32*1024, 0, sample_rate, this); }
SoundStreamInfo GMESong::GetFormat()
{
return { 32*1024, SampleRate, -2 };
} }
//========================================================================== //==========================================================================
@ -180,12 +180,6 @@ GMESong::GMESong(Music_Emu *emu, int sample_rate)
GMESong::~GMESong() GMESong::~GMESong()
{ {
Stop();
if (m_Stream != NULL)
{
delete m_Stream;
m_Stream = NULL;
}
if (TrackInfo != NULL) if (TrackInfo != NULL)
{ {
gme_free_info(TrackInfo); gme_free_info(TrackInfo);
@ -203,29 +197,26 @@ GMESong::~GMESong()
// //
//========================================================================== //==========================================================================
void GMESong::GMEDepthChanged(float val) void GMESong::ChangeSettingNum(const char *name, double val)
{ {
if (Emu != nullptr) if (Emu != nullptr && !stricmp(name, "gme.stereodepth"))
gme_set_stereo_depth(Emu, clamp(val, 0.f, 1.f)); {
gme_set_stereo_depth(Emu, clamp((float)val, 0.f, 1.f));
}
} }
//========================================================================== //==========================================================================
// //
// GMESong :: Play // GMESong :: Play
// //
//========================================================================== //==========================================================================
void GMESong::Play(bool looping, int track) bool GMESong::Start()
{ {
m_Status = STATE_Stopped; return StartTrack(CurrTrack);
m_Looping = looping;
if (StartTrack(track) && m_Stream->Play(looping, 1))
{
m_Status = STATE_Playing;
}
} }
//========================================================================== //==========================================================================
// //
// GMESong :: SetSubsong // GMESong :: SetSubsong
@ -236,7 +227,12 @@ bool GMESong::SetSubsong(int track)
{ {
if (CurrTrack == track) if (CurrTrack == track)
{ {
return false; return true;
}
if (!started)
{
CurrTrack = track;
return true;
} }
return StartTrack(track); return StartTrack(track);
} }
@ -353,24 +349,23 @@ int GMESong::CalcSongLength()
// //
//========================================================================== //==========================================================================
bool GMESong::Read(SoundStream *stream, void *buff, int len, void *userdata) bool GMESong::GetData(void *buffer, size_t len)
{ {
gme_err_t err; gme_err_t err;
GMESong *song = (GMESong *)userdata;
std::lock_guard<std::mutex> lock(song->CritSec); std::lock_guard<std::mutex> lock(CritSec);
if (gme_track_ended(song->Emu)) if (gme_track_ended(Emu))
{ {
if (song->m_Looping) if (m_Looping)
{ {
song->StartTrack(song->CurrTrack, false); StartTrack(CurrTrack, false);
} }
else else
{ {
memset(buff, 0, len); memset(buffer, 0, len);
return false; return false;
} }
} }
err = gme_play(song->Emu, len >> 1, (short *)buff); err = gme_play(Emu, int(len >> 1), (short *)buffer);
return (err == NULL); return (err == NULL);
} }

View file

@ -44,14 +44,14 @@
// TYPES ------------------------------------------------------------------- // TYPES -------------------------------------------------------------------
class SndFileSong : public StreamSong class SndFileSong : public StreamSource
{ {
public: public:
SndFileSong(FileReader &reader, SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass); SndFileSong(FileReader &reader, SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass);
~SndFileSong(); ~SndFileSong();
bool SetSubsong(int subsong); FString GetStats() override;
void Play(bool looping, int subsong); SoundStreamInfo GetFormat() override;
FString GetStats(); bool GetData(void *buffer, size_t len) override;
protected: protected:
std::mutex CritSec; std::mutex CritSec;
@ -64,8 +64,6 @@ protected:
uint32_t Loop_End; uint32_t Loop_End;
int CalcSongLength(); int CalcSongLength();
static bool Read(SoundStream *stream, void *buff, int len, void *userdata);
}; };
// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -267,7 +265,7 @@ void FindLoopTags(FileReader &fr, uint32_t *start, bool *startass, uint32_t *end
// //
//========================================================================== //==========================================================================
MusInfo *SndFile_OpenSong(FileReader &fr) StreamSource *SndFile_OpenSong(FileReader &fr)
{ {
fr.Seek(0, FileReader::SeekSet); fr.Seek(0, FileReader::SeekSet);
@ -303,7 +301,11 @@ SndFileSong::SndFileSong(FileReader &reader, SoundDecoder *decoder, uint32_t loo
Reader = std::move(reader); Reader = std::move(reader);
Decoder = decoder; Decoder = decoder;
Channels = iChannels == ChannelConfig_Stereo? 2:1; Channels = iChannels == ChannelConfig_Stereo? 2:1;
m_Stream = GSnd->CreateStream(Read, snd_streambuffersize * 1024, iChannels == ChannelConfig_Stereo? 0 : SoundStream::Mono, SampleRate, this); }
SoundStreamInfo SndFileSong::GetFormat()
{
return { snd_streambuffersize * 1024, SampleRate, -Channels };
} }
//========================================================================== //==========================================================================
@ -314,46 +316,12 @@ SndFileSong::SndFileSong(FileReader &reader, SoundDecoder *decoder, uint32_t loo
SndFileSong::~SndFileSong() SndFileSong::~SndFileSong()
{ {
Stop();
if (m_Stream != nullptr)
{
delete m_Stream;
m_Stream = nullptr;
}
if (Decoder != nullptr) if (Decoder != nullptr)
{ {
delete Decoder; delete Decoder;
} }
} }
//==========================================================================
//
// SndFileSong :: Play
//
//==========================================================================
void SndFileSong::Play(bool looping, int track)
{
m_Status = STATE_Stopped;
m_Looping = looping;
if (m_Stream->Play(looping, 1))
{
m_Status = STATE_Playing;
}
}
//==========================================================================
//
// SndFileSong :: SetSubsong
//
//==========================================================================
bool SndFileSong::SetSubsong(int track)
{
return false;
}
//========================================================================== //==========================================================================
// //
// SndFileSong :: GetStats // SndFileSong :: GetStats
@ -384,19 +352,17 @@ FString SndFileSong::GetStats()
// //
//========================================================================== //==========================================================================
bool SndFileSong::Read(SoundStream *stream, void *vbuff, int ilen, void *userdata) bool SndFileSong::GetData(void *vbuff, size_t len)
{ {
char *buff = (char*)vbuff; char *buff = (char*)vbuff;
SndFileSong *song = (SndFileSong *)userdata; std::lock_guard<std::mutex> lock(CritSec);
std::lock_guard<std::mutex> lock(song->CritSec);
size_t len = size_t(ilen); size_t currentpos = Decoder->getSampleOffset();
size_t currentpos = song->Decoder->getSampleOffset(); size_t framestoread = len / (Channels*2);
size_t framestoread = len / (song->Channels*2);
bool err = false; bool err = false;
if (!song->m_Looping) if (!m_Looping)
{ {
size_t maxpos = song->Decoder->getSampleLength(); size_t maxpos = Decoder->getSampleLength();
if (currentpos == maxpos) if (currentpos == maxpos)
{ {
memset(buff, 0, len); memset(buff, 0, len);
@ -404,36 +370,36 @@ bool SndFileSong::Read(SoundStream *stream, void *vbuff, int ilen, void *userdat
} }
if (currentpos + framestoread > maxpos) if (currentpos + framestoread > maxpos)
{ {
size_t got = song->Decoder->read(buff, (maxpos - currentpos) * song->Channels * 2); size_t got = Decoder->read(buff, (maxpos - currentpos) * Channels * 2);
memset(buff + got, 0, len - got); memset(buff + got, 0, len - got);
} }
else else
{ {
size_t got = song->Decoder->read(buff, len); size_t got = Decoder->read(buff, len);
err = (got != len); err = (got != len);
} }
} }
else else
{ {
// This looks a bit more complicated than necessary because libmpg123 will not read the full requested length for the last block in the file. // This looks a bit more complicated than necessary because libmpg123 will not read the full requested length for the last block in the file.
if (currentpos + framestoread > song->Loop_End) if (currentpos + framestoread > Loop_End)
{ {
// Loop can be very short, make sure the current position doesn't exceed it // Loop can be very short, make sure the current position doesn't exceed it
if (currentpos < song->Loop_End) if (currentpos < Loop_End)
{ {
size_t endblock = (song->Loop_End - currentpos) * song->Channels * 2; size_t endblock = (Loop_End - currentpos) * Channels * 2;
size_t endlen = song->Decoder->read(buff, endblock); size_t endlen = Decoder->read(buff, endblock);
// Even if zero bytes was read give it a chance to start from the beginning // Even if zero bytes was read give it a chance to start from the beginning
buff += endlen; buff += endlen;
len -= endlen; len -= endlen;
} }
song->Decoder->seek(song->Loop_Start, false, true); Decoder->seek(Loop_Start, false, true);
} }
while (len > 0) while (len > 0)
{ {
size_t readlen = song->Decoder->read(buff, len); size_t readlen = Decoder->read(buff, len);
if (readlen == 0) if (readlen == 0)
{ {
return false; return false;
@ -442,7 +408,7 @@ bool SndFileSong::Read(SoundStream *stream, void *vbuff, int ilen, void *userdat
len -= readlen; len -= readlen;
if (len > 0) if (len > 0)
{ {
song->Decoder->seek(song->Loop_Start, false, true); Decoder->seek(Loop_Start, false, true);
} }
} }
} }

View file

@ -35,59 +35,78 @@
#include "../libraries/oplsynth/oplsynth/opl.h" #include "../libraries/oplsynth/oplsynth/opl.h"
#include "../libraries/oplsynth/oplsynth/opl_mus_player.h" #include "../libraries/oplsynth/oplsynth/opl_mus_player.h"
static bool OPL_Active;
EXTERN_CVAR (Int, opl_numchips) EXTERN_CVAR (Int, opl_numchips)
EXTERN_CVAR(Int, opl_core) EXTERN_CVAR(Int, opl_core)
int getOPLCore(const char* args)
{
int current_opl_core = opl_core;
if (args != NULL && *args >= '0' && *args < '4') current_opl_core = *args - '0';
return current_opl_core;
}
//==========================================================================
//
// OPL file played by a software OPL2 synth and streamed through the sound system
//
//==========================================================================
class OPLMUSSong : public StreamSource
{
public:
OPLMUSSong (FileReader &reader, const char *args);
~OPLMUSSong ();
bool Start() override;
void ChangeSettingInt(const char *name, int value) override;
SoundStreamInfo GetFormat() override;
protected:
bool GetData(void *buffer, size_t len) override;
OPLmusicFile *Music;
int current_opl_core;
};
//==========================================================================
//
//
//
//==========================================================================
OPLMUSSong::OPLMUSSong (FileReader &reader, const char *args) OPLMUSSong::OPLMUSSong (FileReader &reader, const char *args)
{ {
int samples = int(OPL_SAMPLE_RATE / 14); current_opl_core = opl_core;
const char* error; if (args != NULL && *args >= '0' && *args < '4') current_opl_core = *args - '0';
int core = getOPLCore(args); Music = nullptr ;// new OPLmusicFile(reader, current_opl_core);
auto data = reader.Read();
Music = new OPLmusicFile (data.Data(), data.Size(), core, opl_numchips, error);
if (!Music->IsValid())
{
Printf(PRINT_BOLD, "%s", error? error : "Invalid OPL format\n");
delete Music;
return;
}
m_Stream = GSnd->CreateStream (FillStream, samples*4,
(core == 0 ? SoundStream::Mono : 0) | SoundStream::Float, int(OPL_SAMPLE_RATE), this);
if (m_Stream == NULL)
{
Printf (PRINT_BOLD, "Could not create music stream.\n");
delete Music;
return;
}
OPL_Active = true;
} }
//==========================================================================
//
//
//
//==========================================================================
SoundStreamInfo OPLMUSSong::GetFormat()
{
int samples = int(OPL_SAMPLE_RATE / 14);
return { samples * 4, int(OPL_SAMPLE_RATE), current_opl_core == 0? 1:2 };
}
//==========================================================================
//
//
//
//==========================================================================
OPLMUSSong::~OPLMUSSong () OPLMUSSong::~OPLMUSSong ()
{ {
OPL_Active = false;
Stop ();
if (Music != NULL) if (Music != NULL)
{ {
delete Music; delete Music;
} }
} }
bool OPLMUSSong::IsValid () const //==========================================================================
{ //
return m_Stream != NULL; //
} //
//==========================================================================
void OPLMUSSong::ChangeSettingInt(const char * name, int val) void OPLMUSSong::ChangeSettingInt(const char * name, int val)
{ {
@ -95,28 +114,31 @@ void OPLMUSSong::ChangeSettingInt(const char * name, int val)
Music->ResetChips (val); Music->ResetChips (val);
} }
bool OPLMUSSong::IsPlaying () //==========================================================================
{ //
return m_Status == STATE_Playing; //
} //
//==========================================================================
void OPLMUSSong::Play (bool looping, int subsong) bool OPLMUSSong::Start()
{ {
m_Status = STATE_Stopped; Music->SetLooping (m_Looping);
m_Looping = looping;
Music->SetLooping (looping);
Music->Restart (); Music->Restart ();
return true;
if (m_Stream == NULL || m_Stream->Play (true, snd_musicvolume))
{
m_Status = STATE_Playing;
}
} }
bool OPLMUSSong::FillStream (SoundStream *stream, void *buff, int len, void *userdata) //==========================================================================
//
//
//
//==========================================================================
bool OPLMUSSong::GetData(void *buffer, size_t len)
{ {
OPLMUSSong *song = (OPLMUSSong *)userdata; return Music->ServiceStream(buffer, int(len)) ? len : 0;
return song->Music->ServiceStream (buff, len);
} }
StreamSource *OPL_OpenSong(FileReader &reader, const char *args)
{
return new OPLMUSSong(reader, args);
}

View file

@ -1,9 +1,10 @@
/* /*
** music_stream.cpp ** music_stream.cpp
** Wrapper around the sound system's streaming feature for music. ** Plays a streaming song from a StreamSource
** **
**--------------------------------------------------------------------------- **---------------------------------------------------------------------------
** Copyright 2008 Randy Heit ** Copyright 2008 Randy Heit
** Copyright 2019 Christoph Oelckers
** All rights reserved. ** All rights reserved.
** **
** Redistribution and use in source and binary forms, with or without ** Redistribution and use in source and binary forms, with or without
@ -33,18 +34,50 @@
#include "i_musicinterns.h" #include "i_musicinterns.h"
class StreamSong : public MusInfo
{
public:
StreamSong (StreamSource *source);
~StreamSong ();
void Play (bool looping, int subsong) override;
void Pause () override;
void Resume () override;
void Stop () override;
bool IsPlaying () override;
bool IsValid () const override { return m_Stream != nullptr && m_Source != nullptr; }
bool SetPosition (unsigned int pos) override;
bool SetSubsong (int subsong) override;
FString GetStats() override;
void ChangeSettingInt(const char *name, int value) override { if (m_Source) m_Source->ChangeSettingInt(name, value); }
void ChangeSettingNum(const char *name, double value) override { if (m_Source) m_Source->ChangeSettingNum(name, value); }
void ChangeSettingString(const char *name, const char *value) override { if(m_Source) m_Source->ChangeSettingString(name, value); }
protected:
SoundStream *m_Stream = nullptr;
StreamSource *m_Source = nullptr;
private:
static bool FillStream (SoundStream *stream, void *buff, int len, void *userdata);
};
void StreamSong::Play (bool looping, int subsong) void StreamSong::Play (bool looping, int subsong)
{ {
m_Status = STATE_Stopped; m_Status = STATE_Stopped;
m_Looping = looping; m_Looping = looping;
if (m_Stream->Play (m_Looping, 1)) if (m_Stream != nullptr && m_Source != nullptr)
{ {
if (subsong != 0) m_Source->SetPlayMode(looping);
m_Source->SetSubsong(subsong);
if (m_Source->Start())
{ {
m_Stream->SetOrder (subsong); m_Status = STATE_Playing;
m_Stream->Play(m_Looping, 1);
} }
m_Status = STATE_Playing;
} }
} }
@ -78,16 +111,26 @@ void StreamSong::Stop ()
StreamSong::~StreamSong () StreamSong::~StreamSong ()
{ {
Stop (); Stop ();
if (m_Stream != NULL) if (m_Stream != nullptr)
{ {
delete m_Stream; delete m_Stream;
m_Stream = NULL; m_Stream = nullptr;
}
if (m_Source != nullptr)
{
delete m_Source;
m_Source = nullptr;
} }
} }
StreamSong::StreamSong (FileReader &reader) StreamSong::StreamSong (StreamSource *source)
{ {
m_Stream = GSnd->OpenStream (reader, SoundStream::Loop); m_Source = source;
auto fmt = source->GetFormat();
int flags = fmt.mNumChannels < 0? 0 : SoundStream::Float;
if (abs(fmt.mNumChannels) < 2) flags |= SoundStream::Mono;
m_Stream = GSnd->CreateStream(FillStream, fmt.mBufferSize, flags, fmt.mSampleRate, this);
} }
bool StreamSong::IsPlaying () bool StreamSong::IsPlaying ()
@ -111,9 +154,9 @@ bool StreamSong::IsPlaying ()
bool StreamSong::SetPosition(unsigned int pos) bool StreamSong::SetPosition(unsigned int pos)
{ {
if (m_Stream != NULL) if (m_Source != nullptr)
{ {
return m_Stream->SetPosition(pos); return m_Source->SetPosition(pos);
} }
else else
{ {
@ -123,9 +166,9 @@ bool StreamSong::SetPosition(unsigned int pos)
bool StreamSong::SetSubsong(int subsong) bool StreamSong::SetSubsong(int subsong)
{ {
if (m_Stream != NULL) if (m_Stream != nullptr)
{ {
return m_Stream->SetOrder(subsong); return m_Source->SetSubsong(subsong);
} }
else else
{ {
@ -135,9 +178,39 @@ bool StreamSong::SetSubsong(int subsong)
FString StreamSong::GetStats() FString StreamSong::GetStats()
{ {
FString s1, s2;
if (m_Stream != NULL) if (m_Stream != NULL)
{ {
return m_Stream->GetStats(); s1 = m_Stream->GetStats();
} }
return "No song loaded\n"; if (m_Source != NULL)
{
s2 = m_Source->GetStats();
}
if (s1.IsEmpty() && s2.IsEmpty()) return "No song loaded\n";
if (s1.IsEmpty()) return s2;
if (s2.IsEmpty()) return s1;
return FStringf("%s\n%s", s1.GetChars(), s2.GetChars());
} }
bool StreamSong::FillStream (SoundStream *stream, void *buff, int len, void *userdata)
{
StreamSong *song = (StreamSong *)userdata;
bool written = song->m_Source->GetData(buff, len);
if (!written)
{
memset((char*)buff, 0, len);
return false;
}
return true;
}
MusInfo *OpenStreamSong(StreamSource *source)
{
auto song = new StreamSong(source);
if (song->IsValid()) return song;
delete song;
return nullptr;
}

View file

@ -227,18 +227,16 @@ static void getNextXABlock(xa_data *xad, bool looping )
// //
//========================================================================== //==========================================================================
class XASong : public StreamSong class XASong : public StreamSource
{ {
public: public:
XASong(FileReader & readr); XASong(FileReader & readr);
~XASong(); SoundStreamInfo GetFormat() override;
bool SetSubsong(int subsong); bool Start() override;
void Play(bool looping, int subsong); bool GetData(void *buffer, size_t len) override;
protected: protected:
xa_data xad; xa_data xad;
static bool Read(SoundStream *stream, void *buff, int len, void *userdata);
}; };
//========================================================================== //==========================================================================
@ -253,81 +251,51 @@ XASong::XASong(FileReader &reader)
xad.reader = std::move(reader); xad.reader = std::move(reader);
xad.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0; xad.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0;
getNextXABlock(&xad, false); getNextXABlock(&xad, false);
auto SampleRate = xad.blockIs18K? 18900 : 37800;
m_Stream = GSnd->CreateStream(Read, 64 * 1024, SoundStream::Float, SampleRate, this); // create a floating point stereo stream.
} }
//========================================================================== SoundStreamInfo XASong::GetFormat()
//
// XASong - Destructor
//
//==========================================================================
XASong::~XASong()
{ {
Stop(); auto SampleRate = xad.blockIs18K? 18900 : 37800;
if (m_Stream != nullptr) return { 64*1024, SampleRate, 2};
{
delete m_Stream;
m_Stream = nullptr;
}
} }
//========================================================================== //==========================================================================
// //
// XASong :: Play // XASong :: Play
// //
//========================================================================== //==========================================================================
void XASong::Play(bool looping, int track) bool XASong::Start()
{ {
m_Status = STATE_Stopped; if (xad.finished && m_Looping)
m_Looping = looping;
if (xad.finished && looping)
{ {
xad.reader.Seek(XA_DATA_START, FileReader::SeekSet); xad.reader.Seek(XA_DATA_START, FileReader::SeekSet);
xad.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0; xad.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0;
xad.finished = false; xad.finished = false;
} }
if (m_Stream->Play(looping, 1)) return true;
{
m_Status = STATE_Playing;
}
} }
//========================================================================== //==========================================================================
// //
// XASong :: SetSubsong // XASong :: Read
// //
//========================================================================== //==========================================================================
bool XASong::SetSubsong(int track) bool XASong::GetData(void *vbuff, size_t len)
{ {
return false; auto olen = len;
}
//==========================================================================
//
// XASong :: Read STATIC
//
//==========================================================================
bool XASong::Read(SoundStream *stream, void *vbuff, int ilen, void *userdata)
{
auto self = (XASong*)userdata;
float *dest = (float*)vbuff; float *dest = (float*)vbuff;
while (ilen > 0) while (len > 0)
{ {
auto ptr = self->xad.committed; auto ptr = xad.committed;
auto block = self->xad.block + ptr; auto block = xad.block + ptr;
if (ptr < kBufSize) if (ptr < kBufSize)
{ {
// commit the data // commit the data
if (self->xad.blockIsMono) if (xad.blockIsMono)
{ {
size_t numsamples = ilen / 8; size_t numsamples = len / 8;
size_t availdata = kBufSize - ptr; size_t availdata = kBufSize - ptr;
for(size_t tocopy = std::min(numsamples, availdata); tocopy > 0; tocopy--) for(size_t tocopy = std::min(numsamples, availdata); tocopy > 0; tocopy--)
@ -335,35 +303,36 @@ bool XASong::Read(SoundStream *stream, void *vbuff, int ilen, void *userdata)
float f = *block++; float f = *block++;
*dest++ = f; *dest++ = f;
*dest++ = f; *dest++ = f;
ilen -= 8; len -= 8;
ptr++; ptr++;
} }
self->xad.committed = ptr; xad.committed = ptr;
} }
else else
{ {
size_t availdata = (kBufSize - ptr) * 4; size_t availdata = (kBufSize - ptr) * 4;
size_t tocopy = std::min(availdata, (size_t)ilen); size_t tocopy = std::min(availdata, len);
memcpy(dest, block, tocopy); memcpy(dest, block, tocopy);
dest += tocopy / 4; dest += tocopy / 4;
ilen -= (int)tocopy; len -= tocopy;
self->xad.committed += tocopy / 4; xad.committed += tocopy / 4;
} }
} }
if (self->xad.finished) if (xad.finished)
{ {
// fill the rest with 0's. memset(dest, 0, len);
return true;
} }
if (ilen > 0) if (len > 0)
{ {
// we ran out of data and need more // we ran out of data and need more
getNextXABlock(&self->xad, self->m_Looping); getNextXABlock(&xad, m_Looping);
// repeat until done. // repeat until done.
} }
else break; else break;
} }
return !self->xad.finished; return !xad.finished;
} }
//========================================================================== //==========================================================================
@ -372,7 +341,7 @@ bool XASong::Read(SoundStream *stream, void *vbuff, int ilen, void *userdata)
// //
//========================================================================== //==========================================================================
MusInfo *XA_OpenSong(FileReader &reader) StreamSource *XA_OpenSong(FileReader &reader)
{ {
return new XASong(reader); return new XASong(reader);
} }