diff --git a/libraries/dumb/include/dumb.h b/libraries/dumb/include/dumb.h index 8ac820229..fa65cb702 100644 --- a/libraries/dumb/include/dumb.h +++ b/libraries/dumb/include/dumb.h @@ -802,6 +802,8 @@ DUH *make_duh( void DUMBEXPORT duh_set_length(DUH *duh, int32 length); +extern short *(*dumb_decode_vorbis)(int outlen, const void *oggstream, int sizebytes); + #ifdef __cplusplus } diff --git a/libraries/dumb/src/it/readxm.c b/libraries/dumb/src/it/readxm.c index b26359f64..fde1838c8 100644 --- a/libraries/dumb/src/it/readxm.c +++ b/libraries/dumb/src/it/readxm.c @@ -28,7 +28,7 @@ #include #include -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: @@ -830,7 +830,7 @@ static int it_xm_read_sample_data(IT_SAMPLE *sample, unsigned char roguebytes, D sample->loop_start >>= 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) { free(sample->data); diff --git a/libraries/zmusic/mididevices/mididevice.h b/libraries/zmusic/mididevices/mididevice.h index 209321c2f..5074c8427 100644 --- a/libraries/zmusic/mididevices/mididevice.h +++ b/libraries/zmusic/mididevices/mididevice.h @@ -20,14 +20,6 @@ struct MidiHeader 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 { public: diff --git a/libraries/zmusic/zmusic/mididefs.h b/libraries/zmusic/zmusic/mididefs.h index 3b56e67a8..97bdc8a76 100644 --- a/libraries/zmusic/zmusic/mididefs.h +++ b/libraries/zmusic/zmusic/mididefs.h @@ -44,3 +44,12 @@ enum EMidiDevice 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; +}; + diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index 751a59f22..2b020cbfa 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -61,7 +61,6 @@ EXTERN_CVAR (Bool, snd_pitched) EXTERN_CVAR (Color, am_wallcolor) EXTERN_CVAR (Color, am_fdwallcolor) EXTERN_CVAR (Color, am_cdwallcolor) -EXTERN_CVAR (Float, spc_amp) EXTERN_CVAR (Bool, wi_percents) EXTERN_CVAR (Int, gl_texture_hqresizemode) EXTERN_CVAR (Int, gl_texture_hqresizemult) @@ -324,6 +323,7 @@ void FGameConfigFile::DoGlobalSetup () vsync->ResetToDefault (); } } + /* spc_amp no longer exists if (last < 206) { // spc_amp is now a float, not an int. if (spc_amp > 16) @@ -331,6 +331,7 @@ void FGameConfigFile::DoGlobalSetup () spc_amp = spc_amp / 16.f; } } + */ if (last < 207) { // Now that snd_midiprecache works again, you probably don't want it on. FBaseCVar *precache = FindCVar ("snd_midiprecache", NULL); diff --git a/src/sound/backend/i_sound.cpp b/src/sound/backend/i_sound.cpp index cbbed41c1..b36ab360a 100644 --- a/src/sound/backend/i_sound.cpp +++ b/src/sound/backend/i_sound.cpp @@ -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() { return "No stream stats available."; diff --git a/src/sound/backend/i_sound.h b/src/sound/backend/i_sound.h index 6323e361d..f45a6f925 100644 --- a/src/sound/backend/i_sound.h +++ b/src/sound/backend/i_sound.h @@ -58,7 +58,7 @@ enum EStartSoundFlags class SoundStream { public: - virtual ~SoundStream (); + virtual ~SoundStream () {} enum { // For CreateStream @@ -77,8 +77,6 @@ public: virtual bool SetPaused(bool paused) = 0; virtual unsigned int GetPosition() = 0; virtual bool IsEnded() = 0; - virtual bool SetPosition(unsigned int pos); - virtual bool SetOrder(int order); virtual FString GetStats(); }; diff --git a/src/sound/music/i_music.cpp b/src/sound/music/i_music.cpp index e57ceebbc..9abe79901 100644 --- a/src/sound/music/i_music.cpp +++ b/src/sound/music/i_music.cpp @@ -47,8 +47,9 @@ #include "stats.h" #include "vm.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 @@ -261,11 +262,6 @@ FString MusInfo::GetStats() return "No stats available for this song"; } -MusInfo *MusInfo::GetOPLDumper(const char *filename) -{ - return nullptr; -} - MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate) { return nullptr; @@ -371,6 +367,7 @@ static EMIDIType IdentifyMIDIType(uint32_t *id, int size) MusInfo *I_RegisterSong (FileReader &reader, MidiDeviceSetting *device) { MusInfo *info = nullptr; + StreamSource *streamsource = nullptr; const char *fmt; 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('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'))) { - info = XA_OpenSong(reader); + streamsource = XA_OpenSong(reader); } // Check for game music 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 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) { diff --git a/src/sound/music/i_music.h b/src/sound/music/i_music.h index 6376c5cd7..26da5f077 100644 --- a/src/sound/music/i_music.h +++ b/src/sound/music/i_music.h @@ -78,7 +78,6 @@ public: 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 FString GetStats(); - virtual MusInfo *GetOPLDumper(const char *filename); virtual MusInfo *GetWaveDumper(const char *filename, int rate); virtual void ChangeSettingInt(const char *setting, int value); // FluidSynth settings virtual void ChangeSettingNum(const char *setting, double value); // " diff --git a/src/sound/music/i_musicinterns.h b/src/sound/music/i_musicinterns.h index 2dc41a538..c0c36d3ec 100644 --- a/src/sound/music/i_musicinterns.h +++ b/src/sound/music/i_musicinterns.h @@ -110,45 +110,31 @@ protected: // 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: - StreamSong (FileReader &reader); - ~StreamSong (); - void Play (bool looping, int subsong); - void Pause (); - void Resume (); - void Stop (); - bool IsPlaying (); - bool IsValid () const { return m_Stream != NULL; } - bool SetPosition (unsigned int pos); - bool SetSubsong (int subsong); - FString GetStats(); + + StreamSource (int outputRate) { m_OutputRate = outputRate; } + virtual ~StreamSource () {} + virtual void SetPlayMode(bool looping) { m_Looping = looping; } + virtual bool Start() { return true; } + virtual bool SetPosition(unsigned position) { return false; } + virtual bool SetSubsong(int subsong) { return false; } + virtual bool GetData(void *buffer, size_t len) = 0; + virtual SoundStreamInfo GetFormat() { return {65536, m_OutputRate, 2 }; } // Default format is: System's output sample rate, 32 bit float, stereo + virtual FString GetStats() { return ""; } + 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: - StreamSong () : m_Stream(NULL) {} - - SoundStream *m_Stream; + StreamSource() = default; }; -// 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 ----------------------- @@ -191,14 +177,17 @@ bool WildMidi_SetupConfig(WildMidiConfig* config, const char* args); // 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); -MusInfo *GME_OpenSong(FileReader &reader, const char *fmt); -MusInfo *SndFile_OpenSong(FileReader &fr); -MusInfo* XA_OpenSong(FileReader& reader); // -------------------------------------------------------------------------- diff --git a/src/sound/music/midi_cvars.cpp b/src/sound/music/midi_cvars.cpp index 8b67aaa37..d90765085 100644 --- a/src/sound/music/midi_cvars.cpp +++ b/src/sound/music/midi_cvars.cpp @@ -915,3 +915,11 @@ bool WildMidi_SetupConfig(WildMidiConfig* config, const char* args) // This one is for Win32 MMAPI. 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); +} diff --git a/src/sound/musicformats/music_dumb.cpp b/src/sound/musicformats/music_dumb.cpp index c39bc8385..bad3471b5 100644 --- a/src/sound/musicformats/music_dumb.cpp +++ b/src/sound/musicformats/music_dumb.cpp @@ -44,19 +44,31 @@ #include "../dumb/include/dumb.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 ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- -class input_mod : public StreamSong +class DumbSong : public StreamSource { public: - input_mod(DUH *myduh); - ~input_mod(); + DumbSong(DUH *myduh); + ~DumbSong(); //bool SetPosition(int ms); - bool SetSubsong(int subsong); - void Play(bool looping, int subsong); - FString GetStats(); + bool SetSubsong(int subsong) override; + bool Start() override; + SoundStreamInfo GetFormat() override; + FString GetStats() override; FString Codec; FString TrackerVersion; @@ -71,6 +83,7 @@ protected: double delta; double length; bool eof; + bool started = false; size_t written; DUH *duh; DUH_SIGRENDERER *sr; @@ -79,7 +92,8 @@ protected: bool open2(long pos); long render(double volume, double delta, long samples, sample_t **buffer); 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) @@ -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; @@ -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; 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; int headsize; @@ -770,7 +784,7 @@ MusInfo *MOD_OpenSong(FileReader &reader) }; dumbfile_mem_status filestate; DUMBFILE *f = NULL; - input_mod *state = NULL; + DumbSong *state = NULL; bool is_it = false; bool is_dos = true; @@ -920,17 +934,10 @@ MusInfo *MOD_OpenSong(FileReader &reader) { MOD_SetAutoChip(duh); } - state = new input_mod(duh); - if (!state->IsValid()) - { - delete state; - state = NULL; - } - else - { - if (is_it) ReadIT(filestate.ptr, size, state, false); - else ReadDUH(duh, state, false, is_dos); - } + state = new DumbSong(duh); + + if (is_it) ReadIT(filestate.ptr, size, state, false); + else ReadDUH(duh, state, false, is_dos); } 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 (state->eof) + if (eof) { memset(buffer, 0, sizebytes); return false; } - std::lock_guard lock(state->crit_sec); + std::lock_guard lock_(crit_sec); + while (sizebytes > 0) { - int written = state->decode_run(buffer, sizebytes / 8); + int written = decode_run(buffer, (unsigned)sizebytes / 8); if (written < 0) { 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; sr = NULL; eof = false; @@ -1009,57 +1017,60 @@ input_mod::input_mod(DUH *myduh) { srate = (int)GSnd->GetOutputRate(); } - m_Stream = GSnd->CreateStream(read, 32*1024, SoundStream::Float, srate, this); 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 (duh) unload_duh(duh); } //========================================================================== // -// input_mod :: Play +// DumbSong GetFormat // //========================================================================== -void input_mod::Play(bool looping, int order) +SoundStreamInfo DumbSong::GetFormat() { - m_Status = STATE_Stopped; - m_Looping = looping; - - start_order = order; - if (open2(0) && m_Stream->Play(m_Looping, 1)) - { - m_Status = STATE_Playing; - } + return { 32*1024, srate, 2 }; } //========================================================================== // -// 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) { - return false; + return true; + } + if (!started) + { + start_order = order; + return true; } std::lock_guard lock(crit_sec); 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) { @@ -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); @@ -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 // 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; DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr); + if (itsr == nullptr) return 0; int dt = int(delta * 65536.0 + 0.5); long samples = long((((LONG_LONG)itsr->time_left << 16) | itsr->sub_time_left) / dt); 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(); DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr); @@ -1211,13 +1223,3 @@ FString input_mod::GetStats() 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); -} diff --git a/src/sound/musicformats/music_gme.cpp b/src/sound/musicformats/music_gme.cpp index f3bead4a0..4f87b1c90 100644 --- a/src/sound/musicformats/music_gme.cpp +++ b/src/sound/musicformats/music_gme.cpp @@ -37,6 +37,7 @@ // Uncomment if you are using the DLL version of GME. //#define GME_DLL +#include #include "i_musicinterns.h" #include #include @@ -47,14 +48,17 @@ // TYPES ------------------------------------------------------------------- -class GMESong : public StreamSong +class GMESong : public StreamSource { public: GMESong(Music_Emu *emu, int sample_rate); ~GMESong(); - bool SetSubsong(int subsong); - void Play(bool looping, int subsong); - FString GetStats(); + bool SetSubsong(int subsong) override; + bool Start() override; + void ChangeSettingNum(const char *name, double val) override; + FString GetStats() override; + bool GetData(void *buffer, size_t len) override; + SoundStreamInfo GetFormat() override; protected: std::mutex CritSec; @@ -62,13 +66,11 @@ protected: gme_info_t *TrackInfo; int SampleRate; int CurrTrack; + bool started = false; bool StartTrack(int track, bool getcritsec=true); bool GetTrackInfo(); int CalcSongLength(); - void GMEDepthChanged(float val); - - static bool Read(SoundStream *stream, void *buff, int len, void *userdata); }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -82,13 +84,6 @@ protected: // PUBLIC DATA DEFINITIONS ------------------------------------------------- // 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 ------------------------------------------------ @@ -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_err_t err; @@ -152,7 +147,7 @@ MusInfo *GME_OpenSong(FileReader &reader, const char *fmt) reader.Seek(fpos, FileReader::SeekSet); 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 return new GMESong(emu, sample_rate); } @@ -169,7 +164,12 @@ GMESong::GMESong(Music_Emu *emu, int sample_rate) SampleRate = sample_rate; CurrTrack = 0; 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() { - Stop(); - if (m_Stream != NULL) - { - delete m_Stream; - m_Stream = NULL; - } if (TrackInfo != NULL) { 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) - gme_set_stereo_depth(Emu, clamp(val, 0.f, 1.f)); + if (Emu != nullptr && !stricmp(name, "gme.stereodepth")) + { + gme_set_stereo_depth(Emu, clamp((float)val, 0.f, 1.f)); + } } - //========================================================================== // // GMESong :: Play // //========================================================================== -void GMESong::Play(bool looping, int track) +bool GMESong::Start() { - m_Status = STATE_Stopped; - m_Looping = looping; - if (StartTrack(track) && m_Stream->Play(looping, 1)) - { - m_Status = STATE_Playing; - } + return StartTrack(CurrTrack); } + //========================================================================== // // GMESong :: SetSubsong @@ -236,7 +227,12 @@ bool GMESong::SetSubsong(int track) { if (CurrTrack == track) { - return false; + return true; + } + if (!started) + { + CurrTrack = track; + return true; } 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; - GMESong *song = (GMESong *)userdata; - std::lock_guard lock(song->CritSec); - if (gme_track_ended(song->Emu)) + std::lock_guard lock(CritSec); + if (gme_track_ended(Emu)) { - if (song->m_Looping) + if (m_Looping) { - song->StartTrack(song->CurrTrack, false); + StartTrack(CurrTrack, false); } else { - memset(buff, 0, len); + memset(buffer, 0, len); return false; } } - err = gme_play(song->Emu, len >> 1, (short *)buff); + err = gme_play(Emu, int(len >> 1), (short *)buffer); return (err == NULL); } diff --git a/src/sound/musicformats/music_libsndfile.cpp b/src/sound/musicformats/music_libsndfile.cpp index e0891342f..7c1e3588b 100644 --- a/src/sound/musicformats/music_libsndfile.cpp +++ b/src/sound/musicformats/music_libsndfile.cpp @@ -44,14 +44,14 @@ // TYPES ------------------------------------------------------------------- -class SndFileSong : public StreamSong +class SndFileSong : public StreamSource { public: SndFileSong(FileReader &reader, SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass); ~SndFileSong(); - bool SetSubsong(int subsong); - void Play(bool looping, int subsong); - FString GetStats(); + FString GetStats() override; + SoundStreamInfo GetFormat() override; + bool GetData(void *buffer, size_t len) override; protected: std::mutex CritSec; @@ -64,8 +64,6 @@ protected: uint32_t Loop_End; int CalcSongLength(); - - static bool Read(SoundStream *stream, void *buff, int len, void *userdata); }; // 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); @@ -303,7 +301,11 @@ SndFileSong::SndFileSong(FileReader &reader, SoundDecoder *decoder, uint32_t loo Reader = std::move(reader); Decoder = decoder; 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() { - Stop(); - if (m_Stream != nullptr) - { - delete m_Stream; - m_Stream = nullptr; - } if (Decoder != nullptr) { 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 @@ -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; - SndFileSong *song = (SndFileSong *)userdata; - std::lock_guard lock(song->CritSec); + std::lock_guard lock(CritSec); - size_t len = size_t(ilen); - size_t currentpos = song->Decoder->getSampleOffset(); - size_t framestoread = len / (song->Channels*2); + size_t currentpos = Decoder->getSampleOffset(); + size_t framestoread = len / (Channels*2); bool err = false; - if (!song->m_Looping) + if (!m_Looping) { - size_t maxpos = song->Decoder->getSampleLength(); + size_t maxpos = Decoder->getSampleLength(); if (currentpos == maxpos) { memset(buff, 0, len); @@ -404,36 +370,36 @@ bool SndFileSong::Read(SoundStream *stream, void *vbuff, int ilen, void *userdat } 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); } else { - size_t got = song->Decoder->read(buff, len); + size_t got = Decoder->read(buff, len); err = (got != len); } } 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. - 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 - if (currentpos < song->Loop_End) + if (currentpos < Loop_End) { - size_t endblock = (song->Loop_End - currentpos) * song->Channels * 2; - size_t endlen = song->Decoder->read(buff, endblock); + size_t endblock = (Loop_End - currentpos) * Channels * 2; + size_t endlen = Decoder->read(buff, endblock); // Even if zero bytes was read give it a chance to start from the beginning buff += endlen; len -= endlen; } - song->Decoder->seek(song->Loop_Start, false, true); + Decoder->seek(Loop_Start, false, true); } while (len > 0) { - size_t readlen = song->Decoder->read(buff, len); + size_t readlen = Decoder->read(buff, len); if (readlen == 0) { return false; @@ -442,7 +408,7 @@ bool SndFileSong::Read(SoundStream *stream, void *vbuff, int ilen, void *userdat len -= readlen; if (len > 0) { - song->Decoder->seek(song->Loop_Start, false, true); + Decoder->seek(Loop_Start, false, true); } } } diff --git a/src/sound/musicformats/music_opl.cpp b/src/sound/musicformats/music_opl.cpp index 8cf2edceb..93327d7d1 100644 --- a/src/sound/musicformats/music_opl.cpp +++ b/src/sound/musicformats/music_opl.cpp @@ -35,59 +35,78 @@ #include "../libraries/oplsynth/oplsynth/opl.h" #include "../libraries/oplsynth/oplsynth/opl_mus_player.h" -static bool OPL_Active; - EXTERN_CVAR (Int, opl_numchips) 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) { - int samples = int(OPL_SAMPLE_RATE / 14); - const char* error; + current_opl_core = opl_core; + if (args != NULL && *args >= '0' && *args < '4') current_opl_core = *args - '0'; - int core = getOPLCore(args); - 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; + Music = nullptr ;// new OPLmusicFile(reader, current_opl_core); } +//========================================================================== +// +// +// +//========================================================================== + +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 () { - OPL_Active = false; - Stop (); if (Music != NULL) { delete Music; } } -bool OPLMUSSong::IsValid () const -{ - return m_Stream != NULL; -} +//========================================================================== +// +// +// +//========================================================================== void OPLMUSSong::ChangeSettingInt(const char * name, int val) { @@ -95,28 +114,31 @@ void OPLMUSSong::ChangeSettingInt(const char * name, int 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; - m_Looping = looping; - - Music->SetLooping (looping); + Music->SetLooping (m_Looping); Music->Restart (); - - if (m_Stream == NULL || m_Stream->Play (true, snd_musicvolume)) - { - m_Status = STATE_Playing; - } + return true; } -bool OPLMUSSong::FillStream (SoundStream *stream, void *buff, int len, void *userdata) +//========================================================================== +// +// +// +//========================================================================== + +bool OPLMUSSong::GetData(void *buffer, size_t len) { - OPLMUSSong *song = (OPLMUSSong *)userdata; - return song->Music->ServiceStream (buff, len); + return Music->ServiceStream(buffer, int(len)) ? len : 0; } +StreamSource *OPL_OpenSong(FileReader &reader, const char *args) +{ + return new OPLMUSSong(reader, args); +} diff --git a/src/sound/musicformats/music_stream.cpp b/src/sound/musicformats/music_stream.cpp index 01f75fb4b..ccf24700d 100644 --- a/src/sound/musicformats/music_stream.cpp +++ b/src/sound/musicformats/music_stream.cpp @@ -1,9 +1,10 @@ /* ** 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 2019 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -33,18 +34,50 @@ #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) { m_Status = STATE_Stopped; 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 () { Stop (); - if (m_Stream != NULL) + if (m_Stream != nullptr) { 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 () @@ -111,9 +154,9 @@ bool StreamSong::IsPlaying () 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 { @@ -123,9 +166,9 @@ bool StreamSong::SetPosition(unsigned int pos) bool StreamSong::SetSubsong(int subsong) { - if (m_Stream != NULL) + if (m_Stream != nullptr) { - return m_Stream->SetOrder(subsong); + return m_Source->SetSubsong(subsong); } else { @@ -135,9 +178,39 @@ bool StreamSong::SetSubsong(int subsong) FString StreamSong::GetStats() { + FString s1, s2; 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; +} + diff --git a/src/sound/musicformats/music_xa.cpp b/src/sound/musicformats/music_xa.cpp index 38a4fe270..ada0abe31 100644 --- a/src/sound/musicformats/music_xa.cpp +++ b/src/sound/musicformats/music_xa.cpp @@ -227,18 +227,16 @@ static void getNextXABlock(xa_data *xad, bool looping ) // //========================================================================== -class XASong : public StreamSong +class XASong : public StreamSource { public: XASong(FileReader & readr); - ~XASong(); - bool SetSubsong(int subsong); - void Play(bool looping, int subsong); + SoundStreamInfo GetFormat() override; + bool Start() override; + bool GetData(void *buffer, size_t len) override; protected: 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.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0; 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. } -//========================================================================== -// -// XASong - Destructor -// -//========================================================================== - -XASong::~XASong() +SoundStreamInfo XASong::GetFormat() { - Stop(); - if (m_Stream != nullptr) - { - delete m_Stream; - m_Stream = nullptr; - } + auto SampleRate = xad.blockIs18K? 18900 : 37800; + return { 64*1024, SampleRate, 2}; } - //========================================================================== // // XASong :: Play // //========================================================================== -void XASong::Play(bool looping, int track) +bool XASong::Start() { - m_Status = STATE_Stopped; - m_Looping = looping; - if (xad.finished && looping) + if (xad.finished && m_Looping) { xad.reader.Seek(XA_DATA_START, FileReader::SeekSet); xad.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0; xad.finished = false; } - if (m_Stream->Play(looping, 1)) - { - m_Status = STATE_Playing; - } + return true; } //========================================================================== // -// XASong :: SetSubsong +// XASong :: Read // //========================================================================== -bool XASong::SetSubsong(int track) +bool XASong::GetData(void *vbuff, size_t len) { - return false; -} - -//========================================================================== -// -// XASong :: Read STATIC -// -//========================================================================== - -bool XASong::Read(SoundStream *stream, void *vbuff, int ilen, void *userdata) -{ - auto self = (XASong*)userdata; + auto olen = len; float *dest = (float*)vbuff; - while (ilen > 0) + while (len > 0) { - auto ptr = self->xad.committed; - auto block = self->xad.block + ptr; + auto ptr = xad.committed; + auto block = xad.block + ptr; if (ptr < kBufSize) { // 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; 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++; *dest++ = f; *dest++ = f; - ilen -= 8; + len -= 8; ptr++; } - self->xad.committed = ptr; + xad.committed = ptr; } else { 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); dest += tocopy / 4; - ilen -= (int)tocopy; - self->xad.committed += tocopy / 4; + len -= tocopy; + 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 - getNextXABlock(&self->xad, self->m_Looping); + getNextXABlock(&xad, m_Looping); // repeat until done. } 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); }