diff --git a/libraries/dumb/include/dumb.h b/libraries/dumb/include/dumb.h index fa65cb7021..14cf184dfb 100644 --- a/libraries/dumb/include/dumb.h +++ b/libraries/dumb/include/dumb.h @@ -802,9 +802,6 @@ 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 } #endif diff --git a/libraries/dumb/src/it/readxm.c b/libraries/dumb/src/it/readxm.c index fde1838c8c..e3c082e91a 100644 --- a/libraries/dumb/src/it/readxm.c +++ b/libraries/dumb/src/it/readxm.c @@ -28,7 +28,7 @@ #include #include -short * (*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? dumb_decode_vorbis(outlen, (char *)sample->data + 4, datasizebytes - 4) : NULL; + output = dumb_decode_vorbis(outlen, (char *)sample->data + 4, datasizebytes - 4); if (output != NULL) { free(sample->data); diff --git a/libraries/music_common/fileio.h b/libraries/music_common/fileio.h index 1a26f2db4f..dcea971bc7 100644 --- a/libraries/music_common/fileio.h +++ b/libraries/music_common/fileio.h @@ -60,8 +60,7 @@ protected: virtual ~FileInterface() {} public: virtual char* gets(char* buff, int n) = 0; - virtual long read(void* buff, int32_t size, int32_t nitems) = 0; - long read(void* buff, int32_t size) { return read(buff, 1, size); } + virtual long read(void* buff, int32_t size) = 0; virtual long seek(long offset, int whence) = 0; virtual long tell() = 0; virtual void close() @@ -101,10 +100,10 @@ struct StdioFileReader : public FileInterface if (!f) return nullptr; return fgets(buff, n, f); } - long read(void* buff, int32_t size, int32_t nitems) override + long read(void* buff, int32_t size) override { if (!f) return 0; - return (long)fread(buff, size, nitems, f); + return (long)fread(buff, 1, size, f); } long seek(long offset, int whence) override { @@ -165,14 +164,14 @@ struct MemoryReader : public FileInterface *p++ = 0; return strbuf; } - long read(void* buff, int32_t size, int32_t nitems) override + long read(void* buff, int32_t size) override { - long len = long(size) * nitems; + long len = long(size); if (len > mLength - mPos) len = mLength - mPos; if (len < 0) len = 0; memcpy(buff, mData + mPos, len); mPos += len; - return len / size; + return len; } long seek(long offset, int whence) override { @@ -217,15 +216,18 @@ struct VectorReader : public MemoryReader mLength = (long)mVector.size(); mPos = 0; } - - + VectorReader(const uint8_t* data, size_t size) + { + mVector.resize(size); + memcpy(mVector.data(), data, size); + } }; //========================================================================== // -// The follpwing two functions are needed to allow using UTF-8 in the file interface. -// fopen on Windows is only safe for ASCII, +// The following two functions are needed to allow using UTF-8 in the file interface. +// fopen on Windows is only safe for ASCII. // //========================================================================== @@ -271,10 +273,12 @@ inline bool fileExists(const char *fn) class SoundFontReaderInterface { -public: +protected: virtual ~SoundFontReaderInterface() {} +public: virtual struct FileInterface* open_file(const char* fn) = 0; virtual void add_search_path(const char* path) = 0; + virtual void close() { delete this; } }; @@ -375,6 +379,7 @@ public: } }; - +MusicIO::SoundFontReaderInterface* ClientOpenSoundFont(const char* name, int type); } + diff --git a/libraries/oplsynth/oplio.cpp b/libraries/oplsynth/oplio.cpp index e1b6e86443..e97d726f1a 100644 --- a/libraries/oplsynth/oplio.cpp +++ b/libraries/oplsynth/oplio.cpp @@ -58,6 +58,18 @@ int OPLio::Init(int core, uint32_t numchips, bool stereo, bool initopl3) uint32_t i; IsOPL3 = (core == 1 || core == 2 || core == 3); + using CoreInit = OPLEmul* (*)(bool); + static CoreInit inits[] = + { + YM3812Create, + DBOPLCreate, + JavaOPLCreate, + NukedOPL3Create, + }; + + if (core < 0) core = 0; + if (core > 3) core = 3; + memset(chips, 0, sizeof(chips)); if (IsOPL3) { @@ -65,7 +77,7 @@ int OPLio::Init(int core, uint32_t numchips, bool stereo, bool initopl3) } for (i = 0; i < numchips; ++i) { - OPLEmul *chip = IsOPL3 ? (core == 1 ? DBOPLCreate(stereo) : (core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo); + OPLEmul* chip = inits[core](stereo); if (chip == NULL) { break; diff --git a/libraries/timidity/common.cpp b/libraries/timidity/common.cpp index 6e77611a3d..53f099f6b3 100644 --- a/libraries/timidity/common.cpp +++ b/libraries/timidity/common.cpp @@ -34,6 +34,7 @@ namespace Timidity /* This'll allocate memory or die. */ void *safe_malloc(size_t count) { +#if 0 char buffer[80]; void *p; if (count > (1 << 21)) @@ -51,6 +52,15 @@ void *safe_malloc(size_t count) throw std::runtime_error(buffer); } return 0; // Unreachable. +#else + // No, we cannot throw exceptions here - this code can get called from real-time worker threads. + auto p = malloc(count); + if (!p) + { + abort(); // we must abort, though... + } + return p; +#endif } diff --git a/libraries/timidity/instrum.cpp b/libraries/timidity/instrum.cpp index bd6224cc42..9240de4560 100644 --- a/libraries/timidity/instrum.cpp +++ b/libraries/timidity/instrum.cpp @@ -703,7 +703,7 @@ Instruments::~Instruments() drumset[i] = NULL; } } - if (sfreader != nullptr) delete sfreader; + if (sfreader != nullptr) sfreader->close(); sfreader = nullptr; } diff --git a/libraries/timidityplus/common.cpp b/libraries/timidityplus/common.cpp index ce275c16b5..c5d4b0318c 100644 --- a/libraries/timidityplus/common.cpp +++ b/libraries/timidityplus/common.cpp @@ -39,7 +39,7 @@ void *safe_malloc(size_t count) auto p = malloc(count); if (p == nullptr) { - // I_FatalError("Out of memory"); + abort(); } return p; } @@ -54,7 +54,7 @@ void *safe_realloc(void *ptr, size_t count) auto p = realloc(ptr, count); if (p == nullptr) { - // I_FatalError("Out of memory"); + abort(); } return p; } @@ -65,7 +65,7 @@ char *safe_strdup(const char *s) auto p = strdup(s); if (p == nullptr) { - // I_FatalError("Out of memory"); + abort(); } return p; } @@ -149,7 +149,7 @@ void skip(timidity_file *tf, size_t len) int tf_getc(timidity_file *tf) { unsigned char c; - auto read = tf_read(&c, 1, 1, tf); + auto read = tf_read(&c, 1, tf); return read == 0 ? EOF : c; } diff --git a/libraries/timidityplus/instrum.cpp b/libraries/timidityplus/instrum.cpp index b133af280e..94e55ad739 100644 --- a/libraries/timidityplus/instrum.cpp +++ b/libraries/timidityplus/instrum.cpp @@ -73,7 +73,7 @@ Instruments::~Instruments() free_tone_bank(); free_instrument_map(); - if (sfreader != nullptr) delete sfreader; + if (sfreader != nullptr) sfreader->close(); } void Instruments::free_instrument(Instrument *ip) @@ -545,21 +545,21 @@ void Instruments::apply_bank_parameter(Instrument *ip, ToneBankElement *tone) #define READ_CHAR(thing) { \ uint8_t tmpchar; \ \ - if (tf_read(&tmpchar, 1, 1, tf) != 1) \ + if (tf_read(&tmpchar, 1, tf) != 1) \ goto fail; \ thing = tmpchar; \ } #define READ_SHORT(thing) { \ uint16_t tmpshort; \ \ - if (tf_read(&tmpshort, 2, 1, tf) != 1) \ + if (tf_read(&tmpshort, 2, tf) != 2) \ goto fail; \ thing = LE_SHORT(tmpshort); \ } #define READ_LONG(thing) { \ int32_t tmplong; \ \ - if (tf_read(&tmplong, 4, 1, tf) != 1) \ + if (tf_read(&tmplong, 4, tf) != 4) \ goto fail; \ thing = LE_LONG(tmplong); \ } @@ -661,7 +661,7 @@ Instrument *Instruments::load_gus_instrument(char *name, ToneBank *bank, int dr, skip(tf, 127); tmp[0] = tf_getc(tf); } - if ((tf_read(tmp + 1, 1, 238, tf) != 238) + if ((tf_read(tmp + 1, 238, tf) != 238) || (memcmp(tmp, "GF1PATCH110\0ID#000002", 22) && memcmp(tmp, "GF1PATCH100\0ID#000002", 22))) { /* don't know what the differences are */ @@ -689,7 +689,7 @@ Instrument *Instruments::load_gus_instrument(char *name, ToneBank *bank, int dr, memset(ip->sample, 0, sizeof(Sample) * ip->samples); for (i = 0; i < ip->samples; i++) { skip(tf, 7); /* Skip the wave name */ - if (tf_read(&fractions, 1, 1, tf) != 1) { + if (tf_read(&fractions, 1, tf) != 1) { fail: printMessage(CMSG_ERROR, VERB_NORMAL, "Error reading sample %d", i); for (j = 0; j < i; j++) @@ -738,7 +738,7 @@ Instrument *Instruments::load_gus_instrument(char *name, ToneBank *bank, int dr, else sp->panning = (uint8_t)(panning & 0x7f); /* envelope, tremolo, and vibrato */ - if (tf_read(tmp, 1, 18, tf) != 18) + if (tf_read(tmp, 18, tf) != 18) goto fail; if (!tmp[13] || !tmp[14]) { sp->tremolo_sweep_increment = sp->tremolo_phase_increment = 0; @@ -845,7 +845,7 @@ Instrument *Instruments::load_gus_instrument(char *name, ToneBank *bank, int dr, /* Then read the sample data */ sp->data = (sample_t *)safe_malloc(sp->data_length + 4); sp->data_alloced = 1; - if ((j = tf_read(sp->data, 1, sp->data_length, tf)) != (int)sp->data_length) { + if ((j = tf_read(sp->data, sp->data_length, tf)) != (int)sp->data_length) { printMessage(CMSG_ERROR, VERB_NORMAL, "Too small this patch length: %d < %d", j, sp->data_length); goto fail; } diff --git a/libraries/timidityplus/sffile.cpp b/libraries/timidityplus/sffile.cpp index 254f0bfac6..ecc32c53a5 100644 --- a/libraries/timidityplus/sffile.cpp +++ b/libraries/timidityplus/sffile.cpp @@ -66,7 +66,7 @@ namespace TimidityPlus static int READCHUNK(SFChunk *vp, timidity_file *tf) { - if (tf_read(vp, 8, 1, tf) != 1) + if (tf_read(vp, 8, tf) != 8) return -1; vp->size = LE_LONG(vp->size); return 1; @@ -74,7 +74,7 @@ static int READCHUNK(SFChunk *vp, timidity_file *tf) static int READDW(uint32_t *vp, timidity_file *tf) { - if (tf_read(vp, 4, 1, tf) != 1) + if (tf_read(vp, 4, tf) != 4) return -1; *vp = LE_LONG(*vp); return 1; @@ -82,7 +82,7 @@ static int READDW(uint32_t *vp, timidity_file *tf) static int READW(uint16_t *vp, timidity_file *tf) { - if (tf_read(vp, 2, 1, tf) != 1) + if (tf_read(vp, 2, tf) != 2) return -1; *vp = LE_SHORT(*vp); return 1; @@ -92,7 +92,7 @@ static int READSTR(char *str, timidity_file *tf) { int n; - if (tf_read(str, 20, 1, tf) != 1) + if (tf_read(str, 20, tf) != 20) return -1; str[19] = '\0'; n = (int)strlen(str); @@ -102,8 +102,8 @@ static int READSTR(char *str, timidity_file *tf) return n; } -#define READID(var,tf) tf_read(var, 4, 1, tf) -#define READB(var,tf) tf_read(&var, 1, 1, tf) +#define READID(var,tf) tf_read(var, 4, tf) +#define READB(var,tf) tf_read(&var, 1, tf) #define SKIPB(tf) skip(tf, 1) #define SKIPW(tf) skip(tf, 2) #define SKIPDW(tf) skip(tf, 4) @@ -327,7 +327,7 @@ int Instruments::process_info(int size, SFInfo *sf, timidity_file *fd) case INAM_ID: /* name of the font */ sf->sf_name = (char*)safe_malloc(chunk.size + 1); - tf_read(sf->sf_name, 1, chunk.size, fd); + tf_read(sf->sf_name, chunk.size, fd); sf->sf_name[chunk.size] = 0; printMessage(CMSG_INFO, VERB_DEBUG, " name %s", sf->sf_name); diff --git a/libraries/timidityplus/smplfile.cpp b/libraries/timidityplus/smplfile.cpp index 2480dd450f..fd71891890 100644 --- a/libraries/timidityplus/smplfile.cpp +++ b/libraries/timidityplus/smplfile.cpp @@ -204,20 +204,20 @@ int Instruments::get_next_importer(char *sample_file, int start, int count, Samp /* from instrum.c */ #define READ_CHAR(thing) \ - if (1 != tf_read(&tmpchar, 1, 1, tf)) goto fail; \ + if (1 != tf_read(&tmpchar, 1, tf)) goto fail; \ thing = tmpchar; #define READ_SHORT_LE(thing) \ - if (1 != tf_read(&tmpshort, 2, 1, tf)) goto fail; \ + if (2 != tf_read(&tmpshort, 2, tf)) goto fail; \ thing = LE_SHORT(tmpshort); #define READ_LONG_LE(thing) \ - if (1 != tf_read(&tmplong, 4, 1, tf)) goto fail; \ + if (4 != tf_read(&tmplong, 4, tf)) goto fail; \ thing = LE_LONG(tmplong); #define READ_SHORT_BE(thing) \ - if (1 != tf_read(&tmpshort, 2, 1, tf)) goto fail; \ + if (2 != tf_read(&tmpshort, 2, tf)) goto fail; \ thing = BE_SHORT(tmpshort); #define READ_LONG_BE(thing) \ - if (1 != tf_read(&tmplong, 4, 1, tf)) goto fail; \ + if (4 != tf_read(&tmplong, 4, tf)) goto fail; \ thing = BE_LONG(tmplong); const uint8_t pan_mono[] = {64}; /* center */ @@ -280,7 +280,7 @@ int Instruments::import_wave_discriminant(char *sample_file) if ((tf = open_file(sample_file, sfreader)) == NULL) return 1; - if (tf_read(buf, 12, 1, tf) != 1 + if (tf_read(buf, 12, tf) != 12 || memcmp(&buf[0], "RIFF", 4) != 0 || memcmp(&buf[8], "WAVE", 4) != 0) { tf_close(tf); @@ -311,7 +311,7 @@ int Instruments::import_wave_load(char *sample_file, Instrument *inst) if ((tf = open_file(sample_file, sfreader)) == NULL) return 1; - if (tf_read(buf, 12, 1, tf) != 1 + if (tf_read(buf, 12, tf) != 12 || memcmp(&buf[0], "RIFF", 4) != 0 || memcmp(&buf[8], "WAVE", 4) != 0) { tf_close(tf); @@ -321,7 +321,7 @@ int Instruments::import_wave_load(char *sample_file, Instrument *inst) state = chunk_flags = 0; type_index = 4, type_size = 8; for(;;) { - if (tf_read(&buf[type_index], type_size, 1, tf) != 1) + if (tf_read(&buf[type_index], type_size, tf) != type_size) break; chunk_size = LE_LONG(xbuf.i[2]); if (memcmp(&buf[4 + 0], "fmt ", 4) == 0) @@ -553,7 +553,7 @@ int Instruments::import_aiff_discriminant(char *sample_file) if ((tf = open_file(sample_file, sfreader)) == NULL) return 1; - if (tf_read(buf, 12, 1, tf) != 1 + if (tf_read(buf, 12, tf) != 12 || memcmp(&buf[0], "FORM", 4) != 0 || memcmp(&buf[8], "AIF", 3) != 0 || (buf[8 + 3] != 'F' && buf[8 + 3] != 'C')) { @@ -593,7 +593,7 @@ int Instruments::import_aiff_load(char *sample_file, Instrument *inst) if ((tf = open_file(sample_file, sfreader)) == NULL) return 1; - if (tf_read(buf, 12, 1, tf) != 1 + if (tf_read(buf, 12, tf) != 12 || memcmp(&buf[0], "FORM", 4) != 0 || memcmp(&buf[8], "AIF", 3) != 0 || (buf[8 + 3] != 'F' && buf[8 + 3] != 'C')) { @@ -608,7 +608,7 @@ int Instruments::import_aiff_load(char *sample_file, Instrument *inst) sound.common = &common; marker_data = NULL; for(;;) { - if (tf_read(&buf[type_index], type_size, 1, tf) != 1) + if (tf_read(&buf[type_index], type_size, tf) != type_size) break; chunk_size = BE_LONG(xbuf.i[2]); if (memcmp(&buf[4 + 0], "COMM", 4) == 0) @@ -666,7 +666,7 @@ int Instruments::import_aiff_load(char *sample_file, Instrument *inst) else if (inst->instname == NULL && memcmp(&buf[4 + 0], "NAME", 4) == 0) { inst->instname = (char*)malloc(chunk_size + 1); - if (tf_read(inst->instname, chunk_size, 1, tf) != 1) + if (tf_read(inst->instname, chunk_size, tf) != chunk_size) { chunk_flags |= AIFF_CHUNKFLAG_READERR; break; @@ -744,7 +744,7 @@ int Instruments::import_aiff_load(char *sample_file, Instrument *inst) READ_SHORT_BE(comm->numChannels); READ_LONG_BE(comm->numSampleFrames); READ_SHORT_BE(comm->sampleSize); - if (tf_read(sampleRate, 10, 1, tf) != 1) + if (tf_read(sampleRate, 10, tf) != 10) goto fail; comm->sampleRate = ConvertFromIeeeExtended(sampleRate); csize -= 8 + 10; @@ -758,7 +758,7 @@ int Instruments::import_aiff_load(char *sample_file, Instrument *inst) uint8_t compressionNameLength; READ_CHAR(compressionNameLength); - if (tf_read(compressionName, compressionNameLength, 1, tf) != 1) + if (tf_read(compressionName, compressionNameLength, tf) != compressionNameLength) goto fail; compressionName[compressionNameLength] = '\0'; printMessage(CMSG_WARNING, VERB_VERBOSE, "AIFF-C unknown compression type: %s", compressionName); @@ -924,7 +924,7 @@ static int AIFFGetMarkerPosition(int16_t id, const AIFFMarkerData *markers, uint #define WAVE_BUF_SIZE (1 << 11) /* should be power of 2 */ #define READ_WAVE_SAMPLE(dest, b, s) \ - if (tf_read(dest, (b) * (s), 1, tf) != 1) \ + if (tf_read(dest, (b) * (s), tf) != (b) * (s)) \ goto fail #define READ_WAVE_FRAME(dest, b, f) \ READ_WAVE_SAMPLE(dest, b, (f) * channels) diff --git a/libraries/timidityplus/sndfont.cpp b/libraries/timidityplus/sndfont.cpp index 13223efcab..854529c0d7 100644 --- a/libraries/timidityplus/sndfont.cpp +++ b/libraries/timidityplus/sndfont.cpp @@ -550,7 +550,7 @@ Instrument *Instruments::load_from_file(SFInsts *rec, InstList *ip) sample->data_alloced = 1; tf_seek(rec->tf, sp->start, SEEK_SET); - tf_read(sample->data, sp->len, 1, rec->tf); + tf_read(sample->data, sp->len, rec->tf); #ifdef _BIG_ENDIAN_ tmp = (int16_t*)sample->data; diff --git a/libraries/timidityplus/timiditypp/common.h b/libraries/timidityplus/timiditypp/common.h index 1c6116b7cd..503d0710a4 100644 --- a/libraries/timidityplus/timiditypp/common.h +++ b/libraries/timidityplus/timiditypp/common.h @@ -39,9 +39,9 @@ inline char* tf_gets(char* buff, int n, timidity_file* tf) return tf->gets(buff, n); } -inline long tf_read(void* buff, int32_t size, int32_t nitems, timidity_file* tf) +inline long tf_read(void* buff, int32_t size, timidity_file* tf) { - return (long)tf->read(buff, size, nitems); + return (long)tf->read(buff, size); } inline long tf_seek(timidity_file* tf, long offset, int whence) diff --git a/libraries/wildmidi/wildmidi_lib.cpp b/libraries/wildmidi/wildmidi_lib.cpp index 3401e997a2..9286b98257 100644 --- a/libraries/wildmidi/wildmidi_lib.cpp +++ b/libraries/wildmidi/wildmidi_lib.cpp @@ -1326,7 +1326,7 @@ void Instruments::load_patch(struct _mdi *mdi, unsigned short patchid) Instruments::~Instruments() { FreePatches(); - delete sfreader; + sfreader->close(); } diff --git a/libraries/zmusic/decoder/sounddecoder.cpp b/libraries/zmusic/decoder/sounddecoder.cpp index 61600a83ec..3ddd27986f 100644 --- a/libraries/zmusic/decoder/sounddecoder.cpp +++ b/libraries/zmusic/decoder/sounddecoder.cpp @@ -33,6 +33,7 @@ */ +#include "zmusic/zmusic_internal.h" #include "sndfile_decoder.h" #include "mpg123_decoder.h" @@ -79,3 +80,65 @@ std::vector SoundDecoder::readAll() output.resize(total); return output; } + +//========================================================================== +// +// other callbacks +// +//========================================================================== +extern "C" +short* dumb_decode_vorbis(int outlen, const void* oggstream, int sizebytes) +{ + short* samples = (short*)calloc(1, outlen); + ChannelConfig chans; + SampleType type; + int srate; + + // The decoder will take ownership of the reader if it succeeds so this may not be a local variable. + MusicIO::MemoryReader* reader = new MusicIO::MemoryReader((const uint8_t*)oggstream, sizebytes); + + SoundDecoder* decoder = SoundDecoder::CreateDecoder(reader); + if (!decoder) + { + reader->close(); + return samples; + } + + decoder->getInfo(&srate, &chans, &type); + if (chans != ChannelConfig_Mono || type != SampleType_Int16) + { + delete decoder; + return samples; + } + + decoder->read((char*)samples, outlen); + delete decoder; + return samples; +} + +DLL_EXPORT struct SoundDecoder* CreateDecoder(const uint8_t* data, size_t size, bool isstatic) +{ + MusicIO::FileInterface* reader; + if (isstatic) reader = new MusicIO::MemoryReader(data, (long)size); + else reader = new MusicIO::VectorReader(data, size); + auto res = SoundDecoder::CreateDecoder(reader); + if (!res) reader->close(); + return res; +} + +DLL_EXPORT void SoundDecoder_GetInfo(struct SoundDecoder* decoder, int* samplerate, ChannelConfig* chans, SampleType* type) +{ + if (decoder) decoder->getInfo(samplerate, chans, type); + else if (samplerate) *samplerate = 0; +} + +DLL_EXPORT size_t SoundDecoder_Read(struct SoundDecoder* decoder, void* buffer, size_t length) +{ + if (decoder) return decoder->read((char*)buffer, length); + else return 0; +} + +DLL_EXPORT void SoundDecoder_Close(struct SoundDecoder* decoder) +{ + if (decoder) delete decoder; +} diff --git a/libraries/zmusic/mididevices/music_adlmidi_mididevice.cpp b/libraries/zmusic/mididevices/music_adlmidi_mididevice.cpp index e6fb89b29c..c98f336f58 100644 --- a/libraries/zmusic/mididevices/music_adlmidi_mididevice.cpp +++ b/libraries/zmusic/mididevices/music_adlmidi_mididevice.cpp @@ -36,7 +36,10 @@ #include +#include "zmusic/zmusic_internal.h" #include "mididevice.h" + +#ifdef HAVE_ADL #include "adlmidi.h" ADLConfig adlConfig; @@ -267,4 +270,10 @@ MIDIDevice *CreateADLMIDIDevice(const char *Args) return new ADLMIDIDevice(&config); } +#else +MIDIDevice* CreateADLMIDIDevice(const char* Args) +{ + throw std::runtime_error("ADL device not supported in this configuration"); +} +#endif diff --git a/libraries/zmusic/mididevices/music_alsa_state.cpp b/libraries/zmusic/mididevices/music_alsa_state.cpp index 064b38038a..bac72a24f2 100644 --- a/libraries/zmusic/mididevices/music_alsa_state.cpp +++ b/libraries/zmusic/mididevices/music_alsa_state.cpp @@ -130,21 +130,19 @@ int AlsaSequencer::EnumerateDevices() { if (!filter(pinfo)) { continue; } - externalDevices.emplace_back(); internalDevices.emplace_back(); - auto & item = externalDevices.back(); auto & itemInternal = internalDevices.back(); - itemInternal.ID = item.ID = index++; + itemInternal.ID = index++; const char *name = snd_seq_port_info_get_name(pinfo); int portNumber = snd_seq_port_info_get_port(pinfo); if(!name) { std::ostringstream out; out << "MIDI Port " << clientID << ":" << portNumber; - itemInternal.Name = item.Name = out.str(); + itemInternal.Name = out.str(); } else { - itemInternal.Name = item.Name = name; + itemInternal.Name = name; } itemInternal.ClientID = clientID; itemInternal.PortNumber = portNumber; @@ -159,8 +157,4 @@ const std::vector & AlsaSequencer::GetInternalDevices() return internalDevices; } -const std::vector & AlsaSequencer::GetDevices() { - return externalDevices; -} - #endif diff --git a/libraries/zmusic/mididevices/music_alsa_state.h b/libraries/zmusic/mididevices/music_alsa_state.h index 3955bdc2ce..a84f4f5c3d 100644 --- a/libraries/zmusic/mididevices/music_alsa_state.h +++ b/libraries/zmusic/mididevices/music_alsa_state.h @@ -65,7 +65,6 @@ public: } int EnumerateDevices(); - const std::vector &GetDevices(); const std::vector &GetInternalDevices(); snd_seq_t *handle = nullptr; @@ -75,7 +74,6 @@ public: private: std::vector internalDevices; - std::vector externalDevices; }; #endif diff --git a/libraries/zmusic/mididevices/music_fluidsynth_mididevice.cpp b/libraries/zmusic/mididevices/music_fluidsynth_mididevice.cpp index 16ffa3ab08..96044e96cc 100644 --- a/libraries/zmusic/mididevices/music_fluidsynth_mididevice.cpp +++ b/libraries/zmusic/mididevices/music_fluidsynth_mididevice.cpp @@ -37,6 +37,7 @@ #include #include #include +#include "zmusic/zmusic_internal.h" #include "mididevice.h" #include "zmusic/mus2midi.h" @@ -687,5 +688,12 @@ MIDIDevice *CreateFluidSynthMIDIDevice(int samplerate, const char *Args) Fluid_SetupConfig(Args, fluid_patchset, true); return new FluidSynthMIDIDevice(samplerate, fluid_patchset, musicCallbacks.Fluid_MessageFunc); } +#else + +MIDIDevice* CreateFluidSynthMIDIDevice(int samplerate, const char* Args) +{ + throw std::runtime_error("FlidSynth device not supported in this configuration"); +} + #endif // HAVE_FLUIDSYNTH diff --git a/libraries/zmusic/mididevices/music_opl_mididevice.cpp b/libraries/zmusic/mididevices/music_opl_mididevice.cpp index f7503f689a..8ed74463f8 100644 --- a/libraries/zmusic/mididevices/music_opl_mididevice.cpp +++ b/libraries/zmusic/mididevices/music_opl_mididevice.cpp @@ -35,8 +35,11 @@ // HEADER FILES ------------------------------------------------------------ +#include "zmusic/zmusic_internal.h" #include "mididevice.h" #include "zmusic/mus2midi.h" + +#ifdef HAVE_OPL #include "oplsynth/opl.h" #include "oplsynth/opl_mus_player.h" @@ -324,3 +327,10 @@ MIDIDevice* CreateOplMIDIDevice(const char *Args) if (Args != NULL && *Args >= '0' && *Args < '4') core = *Args - '0'; return new OPLMIDIDevice(core); } + +#else +MIDIDevice* CreateOplMIDIDevice(const char* Args) +{ + throw std::runtime_error("OPL device not supported in this configuration"); +} +#endif \ No newline at end of file diff --git a/libraries/zmusic/mididevices/music_opnmidi_mididevice.cpp b/libraries/zmusic/mididevices/music_opnmidi_mididevice.cpp index d00f050ae9..5144484607 100644 --- a/libraries/zmusic/mididevices/music_opnmidi_mididevice.cpp +++ b/libraries/zmusic/mididevices/music_opnmidi_mididevice.cpp @@ -35,6 +35,9 @@ // HEADER FILES ------------------------------------------------------------ #include "mididevice.h" +#include "zmusic/zmusic_internal.h" + +#ifdef HAVE_OPN #include "opnmidi.h" OpnConfig opnConfig; @@ -245,4 +248,9 @@ MIDIDevice *CreateOPNMIDIDevice(const char *Args) return new OPNMIDIDevice(bank); } - +#else +MIDIDevice* CreateOPNMIDIDevice(const char* Args) +{ + throw std::runtime_error("OPN device not supported in this configuration"); +} +#endif diff --git a/libraries/zmusic/mididevices/music_timidity_mididevice.cpp b/libraries/zmusic/mididevices/music_timidity_mididevice.cpp index b460a74a83..2d22192253 100644 --- a/libraries/zmusic/mididevices/music_timidity_mididevice.cpp +++ b/libraries/zmusic/mididevices/music_timidity_mididevice.cpp @@ -35,8 +35,11 @@ // HEADER FILES ------------------------------------------------------------ #include - #include "mididevice.h" +#include "zmusic/zmusic_internal.h" + +#ifdef HAVE_GUS + #include "timidity/timidity.h" #include "timidity/playmidi.h" #include "timidity/instrum.h" @@ -267,14 +270,20 @@ bool GUS_SetupConfig(const char* args) if (*args == 0) args = gusConfig.gus_config.c_str(); if (stricmp(gusConfig.loadedConfig.c_str(), args) == 0) return false; // aleady loaded - MusicIO::SoundFontReaderInterface *reader = nullptr; - if (musicCallbacks.OpenSoundFont) + MusicIO::SoundFontReaderInterface* reader = MusicIO::ClientOpenSoundFont(args, SF_GUS | SF_SF2); + if (!reader && MusicIO::fileExists(args)) { - reader = musicCallbacks.OpenSoundFont(args, SF_GUS | SF_SF2); - } - else if (MusicIO::fileExists(args)) - { - reader = new MusicIO::FileSystemSoundFontReader(args, true); + auto f = MusicIO::utf8_fopen(args, "rb"); + if (f) + { + char test[12] = {}; + fread(test, 1, 12, f); + fclose(f); + // If the passed file is an SF2 sound font we need to use the special reader that fakes a config for it. + if (memcmp(test, "RIFF", 4) == 0 && memcmp(test + 8, "sfbk", 4) == 0) + reader = new MusicIO::SF2Reader(args); + } + if (!reader) reader = new MusicIO::FileSystemSoundFontReader(args, true); } if (reader == nullptr) @@ -288,10 +297,16 @@ bool GUS_SetupConfig(const char* args) return true; } - - -MIDIDevice *CreateTimidityMIDIDevice(const char *Args, int samplerate) +# +MIDIDevice* CreateTimidityMIDIDevice(const char* Args, int samplerate) { GUS_SetupConfig(Args); return new TimidityMIDIDevice(samplerate); } + +#else +MIDIDevice* CreateTimidityMIDIDevice(const char* Args, int samplerate) +{ + throw std::runtime_error("GUS device not supported in this configuration"); +} +#endif diff --git a/libraries/zmusic/mididevices/music_timiditypp_mididevice.cpp b/libraries/zmusic/mididevices/music_timiditypp_mididevice.cpp index 57f45df714..825e80edb0 100644 --- a/libraries/zmusic/mididevices/music_timiditypp_mididevice.cpp +++ b/libraries/zmusic/mididevices/music_timiditypp_mididevice.cpp @@ -33,6 +33,9 @@ */ #include "mididevice.h" +#include "zmusic/zmusic_internal.h" + +#ifdef HAVE_TIMIDITY #include "timiditypp/timidity.h" #include "timiditypp/instrum.h" @@ -198,14 +201,20 @@ bool Timidity_SetupConfig(const char* args) if (*args == 0) args = timidityConfig.timidity_config.c_str(); if (stricmp(timidityConfig.loadedConfig.c_str(), args) == 0) return false; // aleady loaded - MusicIO::SoundFontReaderInterface* reader = nullptr; - if (musicCallbacks.OpenSoundFont) + MusicIO::SoundFontReaderInterface* reader = MusicIO::ClientOpenSoundFont(args, SF_GUS | SF_SF2); + if (!reader && MusicIO::fileExists(args)) { - reader = musicCallbacks.OpenSoundFont(args, SF_GUS | SF_SF2); - } - else if (MusicIO::fileExists(args)) - { - reader = new MusicIO::FileSystemSoundFontReader(args, true); + auto f = MusicIO::utf8_fopen(args, "rb"); + if (f) + { + char test[12] = {}; + fread(test, 1, 12, f); + fclose(f); + // If the passed file is an SF2 sound font we need to use the special reader that fakes a config for it. + if (memcmp(test, "RIFF", 4) == 0 && memcmp(test + 8, "sfbk", 4) == 0) + reader = new MusicIO::SF2Reader(args); + } + if (!reader) reader = new MusicIO::FileSystemSoundFontReader(args, true); } if (reader == nullptr) @@ -225,3 +234,9 @@ MIDIDevice *CreateTimidityPPMIDIDevice(const char *Args, int samplerate) return new TimidityPPMIDIDevice(samplerate); } +#else +MIDIDevice* CreateTimidityPPMIDIDevice(const char* Args, int samplerate) +{ + throw std::runtime_error("Timidity++ device not supported in this configuration"); +} +#endif \ No newline at end of file diff --git a/libraries/zmusic/mididevices/music_wildmidi_mididevice.cpp b/libraries/zmusic/mididevices/music_wildmidi_mididevice.cpp index 1bdd7e7388..ac33c32995 100644 --- a/libraries/zmusic/mididevices/music_wildmidi_mididevice.cpp +++ b/libraries/zmusic/mididevices/music_wildmidi_mididevice.cpp @@ -35,6 +35,10 @@ // HEADER FILES ------------------------------------------------------------ #include "mididevice.h" +#include "zmusic/zmusic_internal.h" + +#ifdef HAVE_WILDMIDI + #include "wildmidi/wildmidi_lib.h" // MACROS ------------------------------------------------------------------ @@ -244,12 +248,8 @@ bool WildMidi_SetupConfig(const char* args) if (*args == 0) args = wildMidiConfig.config.c_str(); if (stricmp(wildMidiConfig.loadedConfig.c_str(), args) == 0) return false; // aleady loaded - MusicIO::SoundFontReaderInterface* reader = nullptr; - if (musicCallbacks.OpenSoundFont) - { - reader = musicCallbacks.OpenSoundFont(args, SF_GUS); - } - else if (MusicIO::fileExists(args)) + MusicIO::SoundFontReaderInterface* reader = MusicIO::ClientOpenSoundFont(args, SF_GUS); + if (!reader && MusicIO::fileExists(args)) { reader = new MusicIO::FileSystemSoundFontReader(args, true); } @@ -279,3 +279,9 @@ MIDIDevice *CreateWildMIDIDevice(const char *Args, int samplerate) return new WildMIDIDevice(samplerate); } +#else +MIDIDevice* CreateWildMIDIDevice(const char* Args, int samplerate) +{ + throw std::runtime_error("WildMidi device not supported in this configuration"); +} +#endif \ No newline at end of file diff --git a/libraries/zmusic/midisources/midisource.cpp b/libraries/zmusic/midisources/midisource.cpp index 0fa3d0fdbe..d37b6f52cb 100644 --- a/libraries/zmusic/midisources/midisource.cpp +++ b/libraries/zmusic/midisources/midisource.cpp @@ -33,7 +33,7 @@ ** */ - +#include "zmusic/zmusic_internal.h" #include "midisource.h" @@ -418,7 +418,7 @@ extern int MUSHeaderSearch(const uint8_t *head, int len); // //========================================================================== -EMIDIType IdentifyMIDIType(uint32_t *id, int size) +DLL_EXPORT EMIDIType ZMusic_IdentifyMIDIType(uint32_t *id, int size) { // Check for MUS format // Tolerate sloppy wads by searching up to 32 bytes for the header @@ -467,23 +467,39 @@ EMIDIType IdentifyMIDIType(uint32_t *id, int size) // //========================================================================== -MIDISource *CreateMIDISource(const uint8_t *data, size_t length, EMIDIType miditype) +DLL_EXPORT ZMusic_MidiSource ZMusic_CreateMIDISource(const uint8_t *data, size_t length, EMIDIType miditype) { - switch (miditype) + try { - case MIDI_MUS: - return new MUSSong2(data, length); + MIDISource* source; + switch (miditype) + { + case MIDI_MUS: + source = new MUSSong2(data, length); + break; - case MIDI_MIDI: - return new MIDISong2(data, length); + case MIDI_MIDI: + source = new MIDISong2(data, length); + break; - case MIDI_HMI: - return new HMISong(data, length); + case MIDI_HMI: + source = new HMISong(data, length); + break; - case MIDI_XMI: - return new XMISong(data, length); + case MIDI_XMI: + source = new XMISong(data, length); + break; - default: + default: + SetError("Unable to identify MIDI data"); + source = nullptr; + break; + } + return source; + } + catch (const std::exception & ex) + { + SetError(ex.what()); return nullptr; } } diff --git a/libraries/zmusic/midisources/midisource_hmi.cpp b/libraries/zmusic/midisources/midisource_hmi.cpp index 96fba013e1..791954edab 100644 --- a/libraries/zmusic/midisources/midisource_hmi.cpp +++ b/libraries/zmusic/midisources/midisource_hmi.cpp @@ -37,6 +37,7 @@ #include #include #include "midisource.h" +#include "zmusic/zmusic_internal.h" #include "zmusic/m_swap.h" // MACROS ------------------------------------------------------------------ diff --git a/libraries/zmusic/midisources/midisource_smf.cpp b/libraries/zmusic/midisources/midisource_smf.cpp index 0d9f476997..8d223ec601 100644 --- a/libraries/zmusic/midisources/midisource_smf.cpp +++ b/libraries/zmusic/midisources/midisource_smf.cpp @@ -38,6 +38,7 @@ // HEADER FILES ------------------------------------------------------------ #include "midisource.h" +#include "zmusic/zmusic_internal.h" // MACROS ------------------------------------------------------------------ diff --git a/libraries/zmusic/musicformats/music_cd.cpp b/libraries/zmusic/musicformats/music_cd.cpp index 4ddd44c8a5..d494c9174c 100644 --- a/libraries/zmusic/musicformats/music_cd.cpp +++ b/libraries/zmusic/musicformats/music_cd.cpp @@ -31,7 +31,7 @@ ** */ -#include "zmusic/zmusic.h" +#include "zmusic/zmusic_internal.h" #include "zmusic/musinfo.h" #ifdef _WIN32 diff --git a/libraries/zmusic/musicformats/music_midi.cpp b/libraries/zmusic/musicformats/music_midi.cpp index f4f7eba18c..598dd69c1f 100644 --- a/libraries/zmusic/musicformats/music_midi.cpp +++ b/libraries/zmusic/musicformats/music_midi.cpp @@ -37,6 +37,7 @@ #include #include #include +#include "zmusic/zmusic_internal.h" #include "zmusic/musinfo.h" #include "mididevices/mididevice.h" #include "midisources/midisource.h" @@ -286,11 +287,9 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate) #endif // Intentional fall-through for systems without standard midi support -#ifdef HAVE_FLUIDSYNTH case MDEV_FLUIDSYNTH: dev = CreateFluidSynthMIDIDevice(samplerate, Args.c_str()); break; -#endif // HAVE_FLUIDSYNTH case MDEV_OPL: dev = CreateOplMIDIDevice(Args.c_str()); @@ -321,6 +320,8 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate) #ifdef HAVE_SYSTEM_MIDI else if (!checked[MDEV_STANDARD]) devtype = MDEV_STANDARD; #endif + else if (!checked[MDEV_ADL]) devtype = MDEV_ADL; + else if (!checked[MDEV_OPN]) devtype = MDEV_OPN; else if (!checked[MDEV_OPL]) devtype = MDEV_OPL; if (devtype == MDEV_DEFAULT) @@ -333,13 +334,15 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate) if (selectedDevice != requestedDevice && (selectedDevice != lastSelectedDevice || requestedDevice != lastRequestedDevice)) { static const char *devnames[] = { - "Windows Default", + "System Default", "OPL", "", "Timidity++", "FluidSynth", "GUS", - "WildMidi" + "WildMidi", + "ADL", + "OPN", }; lastRequestedDevice = requestedDevice; @@ -1014,9 +1017,18 @@ MusInfo* CreateMIDIStreamer(MIDISource *source, EMidiDevice devtype, const char* return me; } -void MIDIDumpWave(MIDISource* source, EMidiDevice devtype, const char *devarg, const char *outname, int subsong, int samplerate) +DLL_EXPORT bool ZMusic_MIDIDumpWave(ZMusic_MidiSource source, EMidiDevice devtype, const char *devarg, const char *outname, int subsong, int samplerate) { - MIDIStreamer me(devtype, devarg); - me.SetMIDISource(source); - me.DumpWave(outname, subsong, samplerate); + try + { + MIDIStreamer me(devtype, devarg); + me.SetMIDISource(source); + me.DumpWave(outname, subsong, samplerate); + return true; + } + catch (const std::exception & ex) + { + SetError(ex.what()); + return false; + } } diff --git a/libraries/zmusic/musicformats/music_stream.cpp b/libraries/zmusic/musicformats/music_stream.cpp index db59cd604a..19807435d3 100644 --- a/libraries/zmusic/musicformats/music_stream.cpp +++ b/libraries/zmusic/musicformats/music_stream.cpp @@ -33,6 +33,7 @@ */ #include "zmusic/musinfo.h" +#include "zmusic/zmusic_internal.h" #include "streamsources/streamsource.h" class StreamSong : public MusInfo diff --git a/libraries/zmusic/streamsources/music_libsndfile.cpp b/libraries/zmusic/streamsources/music_libsndfile.cpp index 8b801e2846..2d0933429f 100644 --- a/libraries/zmusic/streamsources/music_libsndfile.cpp +++ b/libraries/zmusic/streamsources/music_libsndfile.cpp @@ -338,6 +338,12 @@ void FindLoopTags(MusicIO::FileInterface *fr, uint32_t *start, bool *startass, u FindOggComments(fr, start, startass, end, endass); } +DLL_EXPORT void FindLoopTags(const uint8_t* data, size_t size, uint32_t* start, bool* startass, uint32_t* end, bool* endass) +{ + MusicIO::FileInterface* reader = new MusicIO::MemoryReader(data, (long)size); + FindLoopTags(reader, start, startass, end, endass); + reader->close(); +} //========================================================================== // diff --git a/libraries/zmusic/zmusic/configuration.cpp b/libraries/zmusic/zmusic/configuration.cpp index e83a303174..c59996ab42 100644 --- a/libraries/zmusic/zmusic/configuration.cpp +++ b/libraries/zmusic/zmusic/configuration.cpp @@ -31,13 +31,18 @@ **--------------------------------------------------------------------------- ** */ + +#ifdef _WIN32 +#include +#include +#endif #include #include "timidity/timidity.h" #include "timiditypp/timidity.h" #include "oplsynth/oplio.h" #include "../../libraries/dumb/include/dumb.h" -#include "zmusic.h" +#include "zmusic_internal.h" #include "musinfo.h" #include "midiconfig.h" #include "mididevices/music_alsa_state.h" @@ -55,25 +60,71 @@ struct Dummy MiscConfig miscConfig; Callbacks musicCallbacks; -void ZMusic_SetCallbacks(const Callbacks* cb) +class SoundFontWrapperInterface : public MusicIO::SoundFontReaderInterface { - dumb_decode_vorbis = cb->DumbVorbisDecode; - musicCallbacks = *cb; + void* handle; + +public: + SoundFontWrapperInterface(void* h) + { + handle = h; + } + + struct MusicIO::FileInterface* open_file(const char* fn) override + { + auto rd = musicCallbacks.SF_OpenFile(handle, fn); + if (rd) + { + auto fr = new CustomFileReader(rd); + if (fr) fr->filename = fn? fn : "timidity.cfg"; + return fr; + } + else return nullptr; + } + void add_search_path(const char* path) override + { + musicCallbacks.SF_AddToSearchPath(handle, path); + } + void close() override + { + musicCallbacks.SF_Close(handle); + delete this; + } +}; + +namespace MusicIO { + SoundFontReaderInterface* ClientOpenSoundFont(const char* name, int type) + { + if (!musicCallbacks.OpenSoundFont) return nullptr; + auto iface = musicCallbacks.OpenSoundFont(name, type); + if (!iface) return nullptr; + return new SoundFontWrapperInterface(iface); + } } -void ZMusic_SetGenMidi(const uint8_t* data) + +DLL_EXPORT void ZMusic_SetCallbacks(const Callbacks* cb) +{ + musicCallbacks = *cb; + // If not all these are set the sound font interface is not usable. + if (!cb->SF_AddToSearchPath || !cb->SF_OpenFile || !cb->SF_Close) + musicCallbacks.OpenSoundFont = nullptr; + +} + +DLL_EXPORT void ZMusic_SetGenMidi(const uint8_t* data) { memcpy(oplConfig.OPLinstruments, data, 175 * 36); oplConfig.genmidiset = true; } -void ZMusic_SetWgOpn(const void* data, unsigned len) +DLL_EXPORT void ZMusic_SetWgOpn(const void* data, unsigned len) { opnConfig.default_bank.resize(len); memcpy(opnConfig.default_bank.data(), data, len); } -void ZMusic_SetDmxGus(const void* data, unsigned len) +DLL_EXPORT void ZMusic_SetDmxGus(const void* data, unsigned len) { gusConfig.dmxgus.resize(len); memcpy(gusConfig.dmxgus.data(), data, len); @@ -95,18 +146,87 @@ int ZMusic_EnumerateMidiDevices() } -const std::vector &ZMusic_GetMidiDevices() +struct MidiDeviceList { -#if defined HAVE_SYSTEM_MIDI && defined __linux__ - auto &sequencer = AlsaSequencer::Get(); - return sequencer.GetDevices(); -#else // !HAVE_SYSTEM_MIDI || !__linux__ - static std::vector empty; - return empty; -#endif // HAVE_SYSTEM_MIDI && __linux__ + std::vector devices; + ~MidiDeviceList() + { + for (auto& device : devices) + { + free(device.Name); + } + } + void Build() + { +#ifdef HAVE_OPN + devices.push_back({ strdup("libOPN"), -8, MIDIDEV_FMSYNTH }); +#endif +#ifdef HAVE_ADL + devices.push_back({ strdup("libADL"), -7, MIDIDEV_FMSYNTH }); +#endif +#ifdef HAVE_WILDMIDI + devices.push_back({ strdup("WildMidi"), -6, MIDIDEV_SWSYNTH }); +#endif +#ifdef HAVE_FLUIDSYNTH + devices.push_back({ strdup("FluidSynth"), -5, MIDIDEV_SWSYNTH }); +#endif +#ifdef HAVE_GUS + devices.push_back({ strdup("GUS Emulation"), -4, MIDIDEV_SWSYNTH }); +#endif +#ifdef HAVE_OPL + devices.push_back({ strdup("OPL Synth Emulation"), -3, MIDIDEV_FMSYNTH }); +#endif +#ifdef HAVE_TIMIDITY + devices.push_back({ strdup("TiMidity++"), -2, MIDIDEV_SWSYNTH }); +#endif + +#ifdef HAVE_SYSTEM_MIDI +#ifdef __linux__ + auto& sequencer = AlsaSequencer::Get(); + sequencer.EnumerateDevices(); + auto& dev = sequencer.GetInternalDevices(); + for (auto& d : dev) + { + MidiOutDevice mdev = { strdup(d.Name.c_str()), d.ID, MIDIDEV_MAPPER }; // fixme: Correctly determine the type of the device. + devices.push_back(mdev); + } +#elif _WIN32 + UINT nummididevices = midiOutGetNumDevs(); + for (uint32_t id = 0; id < nummididevices; ++id) + { + MIDIOUTCAPSW caps; + MMRESULT res; + + res = midiOutGetDevCapsW(id, &caps, sizeof(caps)); + if (res == MMSYSERR_NOERROR) + { + auto len = wcslen(caps.szPname); + int size_needed = WideCharToMultiByte(CP_UTF8, 0, caps.szPname, (int)len, nullptr, 0, nullptr, nullptr); + char* outbuf = (char*)malloc(size_needed + 1); + WideCharToMultiByte(CP_UTF8, 0, caps.szPname, (int)len, outbuf, size_needed, nullptr, nullptr); + outbuf[size_needed] = 0; + + MidiOutDevice mdev = { outbuf, (int)id, (int)caps.wTechnology }; + devices.push_back(mdev); + } + } +#endif +#endif + } + +}; + +static MidiDeviceList devlist; + +DLL_EXPORT const MidiOutDevice* ZMusic_GetMidiDevices(int* pAmount) +{ + if (devlist.devices.size() == 0) devlist.Build(); + if (pAmount) *pAmount = (int)devlist.devices.size(); + return devlist.devices.data(); } + template void ChangeAndReturn(valtype &variable, valtype value, valtype *realv) { @@ -166,63 +286,62 @@ static void TimidityPlus_SetReverb() } -using namespace ZMusic; //========================================================================== // // change an integer value // //========================================================================== -bool ChangeMusicSetting(ZMusic::EIntConfigKey key, MusInfo *currSong, int value, int *pRealValue) +DLL_EXPORT bool ChangeMusicSettingInt(EIntConfigKey key, MusInfo *currSong, int value, int *pRealValue) { switch (key) { default: return false; - case adl_chips_count: + case zmusic_adl_chips_count: ChangeAndReturn(adlConfig.adl_chips_count, value, pRealValue); return devType() == MDEV_ADL; - case adl_emulator_id: + case zmusic_adl_emulator_id: ChangeAndReturn(adlConfig.adl_emulator_id, value, pRealValue); return devType() == MDEV_ADL; - case adl_run_at_pcm_rate: + case zmusic_adl_run_at_pcm_rate: ChangeAndReturn(adlConfig.adl_run_at_pcm_rate, value, pRealValue); return devType() == MDEV_ADL; - case adl_fullpan: + case zmusic_adl_fullpan: ChangeAndReturn(adlConfig.adl_fullpan, value, pRealValue); return devType() == MDEV_ADL; - case adl_bank: + case zmusic_adl_bank: ChangeAndReturn(adlConfig.adl_bank, value, pRealValue); return devType() == MDEV_ADL; - case adl_use_custom_bank: + case zmusic_adl_use_custom_bank: ChangeAndReturn(adlConfig.adl_use_custom_bank, value, pRealValue); return devType() == MDEV_ADL; - case adl_volume_model: + case zmusic_adl_volume_model: ChangeAndReturn(adlConfig.adl_volume_model, value, pRealValue); return devType() == MDEV_ADL; - case fluid_reverb: + case zmusic_fluid_reverb: if (currSong != NULL) currSong->ChangeSettingInt("fluidsynth.synth.reverb.active", value); ChangeAndReturn(fluidConfig.fluid_reverb, value, pRealValue); return false; - case fluid_chorus: + case zmusic_fluid_chorus: if (currSong != NULL) currSong->ChangeSettingInt("fluidsynth.synth.chorus.active", value); ChangeAndReturn(fluidConfig.fluid_chorus, value, pRealValue); return false; - case fluid_voices: + case zmusic_fluid_voices: if (value < 16) value = 16; else if (value > 4096) @@ -234,7 +353,7 @@ bool ChangeMusicSetting(ZMusic::EIntConfigKey key, MusInfo *currSong, int value, ChangeAndReturn(fluidConfig.fluid_voices, value, pRealValue); return false; - case fluid_interp: + case zmusic_fluid_interp: // Values are: 0 = FLUID_INTERP_NONE // 1 = FLUID_INTERP_LINEAR // 4 = FLUID_INTERP_4THORDER (the FluidSynth default) @@ -256,14 +375,14 @@ bool ChangeMusicSetting(ZMusic::EIntConfigKey key, MusInfo *currSong, int value, ChangeAndReturn(fluidConfig.fluid_interp, value, pRealValue); return false; - case fluid_samplerate: + case zmusic_fluid_samplerate: // This will only take effect for the next song. (Q: Is this even needed?) ChangeAndReturn(fluidConfig.fluid_samplerate, std::max(value, 0), pRealValue); return false; // I don't know if this setting even matters for us, since we aren't letting // FluidSynth drives its own output. - case fluid_threads: + case zmusic_fluid_threads: if (value < 1) value = 1; else if (value > 256) @@ -272,7 +391,7 @@ bool ChangeMusicSetting(ZMusic::EIntConfigKey key, MusInfo *currSong, int value, ChangeAndReturn(fluidConfig.fluid_threads, value, pRealValue); return false; - case fluid_chorus_voices: + case zmusic_fluid_chorus_voices: if (value < 0) value = 0; else if (value > 99) @@ -284,7 +403,7 @@ bool ChangeMusicSetting(ZMusic::EIntConfigKey key, MusInfo *currSong, int value, ChangeAndReturn(fluidConfig.fluid_chorus_voices, value, pRealValue); return false; - case fluid_chorus_type: + case zmusic_fluid_chorus_type: if (value != FLUID_CHORUS_MOD_SINE && value != FLUID_CHORUS_MOD_TRIANGLE) value = FLUID_CHORUS_DEFAULT_TYPE; @@ -294,7 +413,7 @@ bool ChangeMusicSetting(ZMusic::EIntConfigKey key, MusInfo *currSong, int value, ChangeAndReturn(fluidConfig.fluid_chorus_type, value, pRealValue); return false; - case opl_numchips: + case zmusic_opl_numchips: if (value <= 0) value = 1; else if (value > MAXOPL2CHIPS) @@ -306,143 +425,143 @@ bool ChangeMusicSetting(ZMusic::EIntConfigKey key, MusInfo *currSong, int value, ChangeAndReturn(oplConfig.numchips, value, pRealValue); return false; - case opl_core: + case zmusic_opl_core: if (value < 0) value = 0; else if (value > 3) value = 3; ChangeAndReturn(oplConfig.core, value, pRealValue); return devType() == MDEV_OPL; - case opl_fullpan: + case zmusic_opl_fullpan: ChangeAndReturn(oplConfig.fullpan, value, pRealValue); return false; - case opn_chips_count: + case zmusic_opn_chips_count: ChangeAndReturn(opnConfig.opn_chips_count, value, pRealValue); return devType() == MDEV_OPN; - case opn_emulator_id: + case zmusic_opn_emulator_id: ChangeAndReturn(opnConfig.opn_emulator_id, value, pRealValue); return devType() == MDEV_OPN; - case opn_run_at_pcm_rate: + case zmusic_opn_run_at_pcm_rate: ChangeAndReturn(opnConfig.opn_run_at_pcm_rate, value, pRealValue); return devType() == MDEV_OPN; - case opn_fullpan: + case zmusic_opn_fullpan: ChangeAndReturn(opnConfig.opn_fullpan, value, pRealValue); return devType() == MDEV_OPN; - case opn_use_custom_bank: + case zmusic_opn_use_custom_bank: ChangeAndReturn(opnConfig.opn_use_custom_bank, value, pRealValue); return devType() == MDEV_OPN; - case gus_dmxgus: + case zmusic_gus_dmxgus: ChangeAndReturn(gusConfig.gus_dmxgus, value, pRealValue); return devType() == MDEV_GUS; - case gus_midi_voices: + case zmusic_gus_midi_voices: ChangeAndReturn(gusConfig.midi_voices, value, pRealValue); return devType() == MDEV_GUS; - case gus_memsize: + case zmusic_gus_memsize: ChangeAndReturn(gusConfig.gus_memsize, value, pRealValue); return devType() == MDEV_GUS && gusConfig.gus_dmxgus; - case timidity_modulation_wheel: + case zmusic_timidity_modulation_wheel: ChangeVarSync(TimidityPlus::timidity_modulation_wheel, value); if (pRealValue) *pRealValue = value; return false; - case timidity_portamento: + case zmusic_timidity_portamento: ChangeVarSync(TimidityPlus::timidity_portamento, value); if (pRealValue) *pRealValue = value; return false; - case timidity_reverb: + case zmusic_timidity_reverb: if (value < 0 || value > 4) value = 0; else TimidityPlus_SetReverb(); local_timidity_reverb = value; if (pRealValue) *pRealValue = value; return false; - case timidity_reverb_level: + case zmusic_timidity_reverb_level: if (value < 0 || value > 127) value = 0; else TimidityPlus_SetReverb(); local_timidity_reverb_level = value; if (pRealValue) *pRealValue = value; return false; - case timidity_chorus: + case zmusic_timidity_chorus: ChangeVarSync(TimidityPlus::timidity_chorus, value); if (pRealValue) *pRealValue = value; return false; - case timidity_surround_chorus: + case zmusic_timidity_surround_chorus: ChangeVarSync(TimidityPlus::timidity_surround_chorus, value); if (pRealValue) *pRealValue = value; return devType() == MDEV_TIMIDITY; - case timidity_channel_pressure: + case zmusic_timidity_channel_pressure: ChangeVarSync(TimidityPlus::timidity_channel_pressure, value); if (pRealValue) *pRealValue = value; return false; - case timidity_lpf_def: + case zmusic_timidity_lpf_def: ChangeVarSync(TimidityPlus::timidity_lpf_def, value); if (pRealValue) *pRealValue = value; return false; - case timidity_temper_control: + case zmusic_timidity_temper_control: ChangeVarSync(TimidityPlus::timidity_temper_control, value); if (pRealValue) *pRealValue = value; return false; - case timidity_modulation_envelope: + case zmusic_timidity_modulation_envelope: ChangeVarSync(TimidityPlus::timidity_modulation_envelope, value); if (pRealValue) *pRealValue = value; return devType() == MDEV_TIMIDITY; - case timidity_overlap_voice_allow: + case zmusic_timidity_overlap_voice_allow: ChangeVarSync(TimidityPlus::timidity_overlap_voice_allow, value); if (pRealValue) *pRealValue = value; return false; - case timidity_drum_effect: + case zmusic_timidity_drum_effect: ChangeVarSync(TimidityPlus::timidity_drum_effect, value); if (pRealValue) *pRealValue = value; return false; - case timidity_pan_delay: + case zmusic_timidity_pan_delay: ChangeVarSync(TimidityPlus::timidity_pan_delay, value); if (pRealValue) *pRealValue = value; return false; - case timidity_key_adjust: + case zmusic_timidity_key_adjust: if (value < -24) value = -24; else if (value > 24) value = 24; ChangeVarSync(TimidityPlus::timidity_key_adjust, value); if (pRealValue) *pRealValue = value; return false; - case wildmidi_reverb: + case zmusic_wildmidi_reverb: if (currSong != NULL) currSong->ChangeSettingInt("wildmidi.reverb", value); wildMidiConfig.reverb = value; if (pRealValue) *pRealValue = value; return false; - case wildmidi_enhanced_resampling: + case zmusic_wildmidi_enhanced_resampling: if (currSong != NULL) currSong->ChangeSettingInt("wildmidi.resampling", value); wildMidiConfig.enhanced_resampling = value; if (pRealValue) *pRealValue = value; return false; - case snd_midiprecache: + case zmusic_snd_midiprecache: ChangeAndReturn(miscConfig.snd_midiprecache, value, pRealValue); return false; - case snd_streambuffersize: + case zmusic_snd_streambuffersize: if (value < 16) { value = 16; @@ -454,42 +573,42 @@ bool ChangeMusicSetting(ZMusic::EIntConfigKey key, MusInfo *currSong, int value, ChangeAndReturn(miscConfig.snd_streambuffersize, value, pRealValue); return false; - case mod_samplerate: + case zmusic_mod_samplerate: ChangeAndReturn(dumbConfig.mod_samplerate, value, pRealValue); return false; - case mod_volramp: + case zmusic_mod_volramp: ChangeAndReturn(dumbConfig.mod_volramp, value, pRealValue); return false; - case mod_interp: + case zmusic_mod_interp: ChangeAndReturn(dumbConfig.mod_interp, value, pRealValue); return false; - case mod_autochip: + case zmusic_mod_autochip: ChangeAndReturn(dumbConfig.mod_autochip, value, pRealValue); return false; - case mod_autochip_size_force: + case zmusic_mod_autochip_size_force: ChangeAndReturn(dumbConfig.mod_autochip_size_force, value, pRealValue); return false; - case mod_autochip_size_scan: + case zmusic_mod_autochip_size_scan: ChangeAndReturn(dumbConfig.mod_autochip_size_scan, value, pRealValue); return false; - case mod_autochip_scan_threshold: + case zmusic_mod_autochip_scan_threshold: ChangeAndReturn(dumbConfig.mod_autochip_scan_threshold, value, pRealValue); return false; - case snd_mididevice: + case zmusic_snd_mididevice: { bool change = miscConfig.snd_mididevice != value; miscConfig.snd_mididevice = value; return change; } - case snd_outputrate: + case zmusic_snd_outputrate: miscConfig.snd_outputrate = value; return false; @@ -497,14 +616,14 @@ bool ChangeMusicSetting(ZMusic::EIntConfigKey key, MusInfo *currSong, int value, return false; } -bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* currSong, float value, float *pRealValue) +DLL_EXPORT bool ChangeMusicSettingFloat(EFloatConfigKey key, MusInfo* currSong, float value, float *pRealValue) { switch (key) { default: return false; - case fluid_gain: + case zmusic_fluid_gain: if (value < 0) value = 0; else if (value > 10) @@ -516,7 +635,7 @@ bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* currSong, float va ChangeAndReturn(fluidConfig.fluid_gain, value, pRealValue); return false; - case fluid_reverb_roomsize: + case zmusic_fluid_reverb_roomsize: if (value < 0) value = 0; else if (value > 1.2f) @@ -528,7 +647,7 @@ bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* currSong, float va ChangeAndReturn(fluidConfig.fluid_reverb_roomsize, value, pRealValue); return false; - case fluid_reverb_damping: + case zmusic_fluid_reverb_damping: if (value < 0) value = 0; else if (value > 1) @@ -540,7 +659,7 @@ bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* currSong, float va ChangeAndReturn(fluidConfig.fluid_reverb_damping, value, pRealValue); return false; - case fluid_reverb_width: + case zmusic_fluid_reverb_width: if (value < 0) value = 0; else if (value > 100) @@ -552,7 +671,7 @@ bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* currSong, float va ChangeAndReturn(fluidConfig.fluid_reverb_width, value, pRealValue); return false; - case fluid_reverb_level: + case zmusic_fluid_reverb_level: if (value < 0) value = 0; else if (value > 1) @@ -564,7 +683,7 @@ bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* currSong, float va ChangeAndReturn(fluidConfig.fluid_reverb_level, value, pRealValue); return false; - case fluid_chorus_level: + case zmusic_fluid_chorus_level: if (value < 0) value = 0; else if (value > 1) @@ -576,7 +695,7 @@ bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* currSong, float va ChangeAndReturn(fluidConfig.fluid_chorus_level, value, pRealValue); return false; - case fluid_chorus_speed: + case zmusic_fluid_chorus_speed: if (value < 0.29f) value = 0.29f; else if (value > 5) @@ -589,7 +708,7 @@ bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* currSong, float va return false; // depth is in ms and actual maximum depends on the sample rate - case fluid_chorus_depth: + case zmusic_fluid_chorus_depth: if (value < 0) value = 0; else if (value > 21) @@ -601,7 +720,7 @@ bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* currSong, float va ChangeAndReturn(fluidConfig.fluid_chorus_depth, value, pRealValue); return false; - case timidity_drum_power: + case zmusic_timidity_drum_power: if (value < 0) value = 0; else if (value > MAX_AMPLIFICATION / 100.f) value = MAX_AMPLIFICATION / 100.f; ChangeVarSync(TimidityPlus::timidity_drum_power, value); @@ -609,39 +728,39 @@ bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* currSong, float va return false; // For testing mainly. - case timidity_tempo_adjust: + case zmusic_timidity_tempo_adjust: if (value < 0.25) value = 0.25; else if (value > 10) value = 10; ChangeVarSync(TimidityPlus::timidity_tempo_adjust, value); if (pRealValue) *pRealValue = value; return false; - case min_sustain_time: + case zmusic_min_sustain_time: if (value < 0) value = 0; ChangeVarSync(TimidityPlus::min_sustain_time, value); if (pRealValue) *pRealValue = value; return false; - case gme_stereodepth: + case zmusic_gme_stereodepth: if (currSong != nullptr) currSong->ChangeSettingNum("GME.stereodepth", value); ChangeAndReturn(miscConfig.gme_stereodepth, value, pRealValue); return false; - case mod_dumb_mastervolume: + case zmusic_mod_dumb_mastervolume: if (value < 0) value = 0; ChangeAndReturn(dumbConfig.mod_dumb_mastervolume, value, pRealValue); return false; - case snd_musicvolume: + case zmusic_snd_musicvolume: miscConfig.snd_musicvolume = value; return false; - case relative_volume: + case zmusic_relative_volume: miscConfig.relative_volume = value; return false; - case snd_mastervolume: + case zmusic_snd_mastervolume: miscConfig.snd_mastervolume = value; return false; @@ -649,42 +768,42 @@ bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* currSong, float va return false; } -bool ChangeMusicSetting(ZMusic::EStringConfigKey key, MusInfo* currSong, const char *value) +DLL_EXPORT bool ChangeMusicSettingString(EStringConfigKey key, MusInfo* currSong, const char *value) { switch (key) { default: return false; - case adl_custom_bank: + case zmusic_adl_custom_bank: adlConfig.adl_custom_bank = value; return devType() == MDEV_ADL; - case fluid_lib: + case zmusic_fluid_lib: fluidConfig.fluid_lib = value; return false; // only takes effect for next song. - case fluid_patchset: + case zmusic_fluid_patchset: fluidConfig.fluid_patchset = value; return devType() == MDEV_FLUIDSYNTH; - case opn_custom_bank: + case zmusic_opn_custom_bank: opnConfig.opn_custom_bank = value; return devType() == MDEV_OPN && opnConfig.opn_use_custom_bank; - case gus_config: + case zmusic_gus_config: gusConfig.gus_config = value; return devType() == MDEV_GUS; - case gus_patchdir: + case zmusic_gus_patchdir: gusConfig.gus_patchdir = value; return devType() == MDEV_GUS && gusConfig.gus_dmxgus; - case timidity_config: + case zmusic_timidity_config: timidityConfig.timidity_config = value; return devType() == MDEV_TIMIDITY; - case wildmidi_config: + case zmusic_wildmidi_config: wildMidiConfig.config = value; return devType() == MDEV_TIMIDITY; diff --git a/libraries/zmusic/zmusic/midiconfig.h b/libraries/zmusic/zmusic/midiconfig.h index b6bd9d60c7..49e5dcef84 100644 --- a/libraries/zmusic/zmusic/midiconfig.h +++ b/libraries/zmusic/zmusic/midiconfig.h @@ -3,7 +3,7 @@ #include #include #include -#include "zmusic.h" +#include "zmusic_internal.h" #include "../../libraries/music_common/fileio.h" // Note: Bools here are stored as ints to allow having a simpler interface. diff --git a/libraries/zmusic/zmusic/mididefs.h b/libraries/zmusic/zmusic/mididefs.h index 7f573544fe..29cda0d20c 100644 --- a/libraries/zmusic/zmusic/mididefs.h +++ b/libraries/zmusic/zmusic/mididefs.h @@ -10,29 +10,6 @@ enum inline constexpr uint8_t MEVENT_EVENTTYPE(uint32_t x) { return ((uint8_t)((x) >> 24)); } inline constexpr uint32_t MEVENT_EVENTPARM(uint32_t x) { return ((x) & 0xffffff); } -// These constants must match the corresponding values of the Windows headers -// to avoid readjustment in the native Windows device's playback functions -// and should not be changed. -enum EMidiDeviceClass -{ - MIDIDEV_MIDIPORT = 1, - MIDIDEV_SYNTH, - MIDIDEV_SQSYNTH, - MIDIDEV_FMSYNTH, - MIDIDEV_MAPPER, - MIDIDEV_WAVETABLE, - MIDIDEV_SWSYNTH -}; - -enum EMIDIType -{ - MIDI_NOTMIDI, - MIDI_MIDI, - MIDI_HMI, - MIDI_XMI, - MIDI_MUS -}; - enum EMidiEvent : uint8_t { MEVENT_TEMPO = 1, @@ -40,38 +17,6 @@ enum EMidiEvent : uint8_t MEVENT_LONGMSG = 128, }; -enum EMidiDevice -{ - MDEV_DEFAULT = -1, - MDEV_STANDARD = 0, - MDEV_OPL = 1, - MDEV_SNDSYS = 2, - MDEV_TIMIDITY = 3, - MDEV_FLUIDSYNTH = 4, - MDEV_GUS = 5, - MDEV_WILDMIDI = 6, - MDEV_ADL = 7, - MDEV_OPN = 8, - - MDEV_COUNT -}; - -enum ESoundFontTypes -{ - SF_SF2 = 1, - SF_GUS = 2, - SF_WOPL = 4, - SF_WOPN = 8 -}; - - -struct SoundStreamInfo -{ - int mBufferSize; // If mBufferSize is 0, the song doesn't use streaming but plays through a different interface. - int mSampleRate; - int mNumChannels; // If mNumChannels is negative, 16 bit integer format is used instead of floating point. -}; - #ifndef MAKE_ID #ifndef __BIG_ENDIAN__ #define MAKE_ID(a,b,c,d) ((uint32_t)((a)|((b)<<8)|((c)<<16)|((d)<<24))) diff --git a/libraries/zmusic/zmusic/musinfo.h b/libraries/zmusic/zmusic/musinfo.h index 3b934aab4d..c892d01414 100644 --- a/libraries/zmusic/zmusic/musinfo.h +++ b/libraries/zmusic/zmusic/musinfo.h @@ -3,6 +3,7 @@ #include #include #include "mididefs.h" +#include "zmusic/zmusic_internal.h" // The base music class. Everything is derived from this -------------------- diff --git a/libraries/zmusic/zmusic/sounddecoder.h b/libraries/zmusic/zmusic/sounddecoder.h index 4331891953..e0e7d5782e 100644 --- a/libraries/zmusic/zmusic/sounddecoder.h +++ b/libraries/zmusic/zmusic/sounddecoder.h @@ -1,19 +1,8 @@ #pragma once -#include "../../music_common/fileio.h" +#include "zmusic_internal.h" #include -enum SampleType -{ - SampleType_UInt8, - SampleType_Int16 -}; -enum ChannelConfig -{ - ChannelConfig_Mono, - ChannelConfig_Stereo -}; - struct SoundDecoder { static SoundDecoder* CreateDecoder(MusicIO::FileInterface* reader); @@ -37,4 +26,3 @@ protected: SoundDecoder(const SoundDecoder &rhs) = delete; SoundDecoder& operator=(const SoundDecoder &rhs) = delete; }; - diff --git a/libraries/zmusic/zmusic/zmusic.cpp b/libraries/zmusic/zmusic/zmusic.cpp index fd76505c25..84903af50c 100644 --- a/libraries/zmusic/zmusic/zmusic.cpp +++ b/libraries/zmusic/zmusic/zmusic.cpp @@ -38,7 +38,7 @@ #include #include #include "m_swap.h" -#include "zmusic.h" +#include "zmusic_internal.h" #include "midiconfig.h" #include "musinfo.h" #include "streamsources/streamsource.h" @@ -65,6 +65,7 @@ const char *GME_CheckFormat(uint32_t header); MusInfo* CDDA_OpenSong(MusicIO::FileInterface* reader); MusInfo* CD_OpenSong(int track, int id); MusInfo* CreateMIDIStreamer(MIDISource *source, EMidiDevice devtype, const char* args); + //========================================================================== // // ungzip @@ -148,7 +149,7 @@ static bool ungzip(uint8_t *data, int complen, std::vector &newdata) // //========================================================================== -MusInfo *ZMusic_OpenSong (MusicIO::FileInterface *reader, EMidiDevice device, const char *Args) +static MusInfo *ZMusic_OpenSongInternal (MusicIO::FileInterface *reader, EMidiDevice device, const char *Args) { MusInfo *info = nullptr; StreamSource *streamsource = nullptr; @@ -157,6 +158,7 @@ MusInfo *ZMusic_OpenSong (MusicIO::FileInterface *reader, EMidiDevice device, co if(reader->read(id, 32) != 32 || reader->seek(-32, SEEK_CUR) != 0) { + SetError("Unable to read header"); reader->close(); return nullptr; } @@ -190,16 +192,17 @@ MusInfo *ZMusic_OpenSong (MusicIO::FileInterface *reader, EMidiDevice device, co } } - EMIDIType miditype = IdentifyMIDIType(id, sizeof(id)); + EMIDIType miditype = ZMusic_IdentifyMIDIType(id, sizeof(id)); if (miditype != MIDI_NOTMIDI) { std::vector data(reader->filelength()); if (reader->read(data.data(), (long)data.size()) != (long)data.size()) { + SetError("Failed to read MIDI data"); reader->close(); return nullptr; } - auto source = CreateMIDISource(data.data(), data.size(), miditype); + auto source = ZMusic_CreateMIDISource(data.data(), data.size(), miditype); if (source == nullptr) { reader->close(); @@ -207,6 +210,7 @@ MusInfo *ZMusic_OpenSong (MusicIO::FileInterface *reader, EMidiDevice device, co } if (!source->isValid()) { + SetError("Invalid data in MIDI file"); delete source; return nullptr; } @@ -268,32 +272,72 @@ MusInfo *ZMusic_OpenSong (MusicIO::FileInterface *reader, EMidiDevice device, co { // File could not be identified as music. if (reader) reader->close(); + SetError("Unable to identify as music"); return nullptr; } if (info && !info->IsValid()) { delete info; + SetError("Unable to identify as music"); info = nullptr; } + if (reader) reader->close(); + return info; } - catch (...) + catch (const std::exception &ex) { // Make sure the reader is closed if this function abnormally terminates if (reader) reader->close(); - throw; + SetError(ex.what()); + return nullptr; } - if (reader) reader->close(); - return info; } +DLL_EXPORT ZMusic_MusicStream ZMusic_OpenSongFile(const char* filename, EMidiDevice device, const char* Args) +{ + auto f = MusicIO::utf8_fopen(filename, "rb"); + if (!f) + { + SetError("File not found"); + return nullptr; + } + auto fr = new MusicIO::StdioFileReader; + fr->f = f; + return ZMusic_OpenSongInternal(fr, device, Args); +} + +DLL_EXPORT ZMusic_MusicStream ZMusic_OpenSongMem(const void* mem, size_t size, EMidiDevice device, const char* Args) +{ + if (!mem || !size) + { + SetError("Invalid data"); + return nullptr; + } + // Data must be copied because it may be used as a streaming source and we cannot guarantee that the client memory stays valid. We also have no means to free it. + auto mr = new MusicIO::VectorReader((uint8_t*)mem, (long)size); + return ZMusic_OpenSongInternal(mr, device, Args); +} + +DLL_EXPORT ZMusic_MusicStream ZMusic_OpenSong(ZMusicCustomReader* reader, EMidiDevice device, const char* Args) +{ + if (!reader) + { + SetError("No reader protocol specified"); + return nullptr; + } + auto cr = new CustomFileReader(reader); // Oh no! We just put another wrapper around the client's wrapper! + return ZMusic_OpenSongInternal(cr, device, Args); +} + + //========================================================================== // // play CD music // //========================================================================== -MusInfo *ZMusic_OpenCDSong (int track, int id) +DLL_EXPORT MusInfo *ZMusic_OpenCDSong (int track, int id) { MusInfo *info = CD_OpenSong (track, id); @@ -301,6 +345,7 @@ MusInfo *ZMusic_OpenCDSong (int track, int id) { delete info; info = nullptr; + SetError("Unable to open CD Audio"); } return info; @@ -312,7 +357,7 @@ MusInfo *ZMusic_OpenCDSong (int track, int id) // //========================================================================== -bool ZMusic_FillStream(MusInfo* song, void* buff, int len) +DLL_EXPORT bool ZMusic_FillStream(MusInfo* song, void* buff, int len) { if (song == nullptr) return false; std::lock_guard lock(song->CritSec); @@ -325,10 +370,19 @@ bool ZMusic_FillStream(MusInfo* song, void* buff, int len) // //========================================================================== -void ZMusic_Start(MusInfo *song, int subsong, bool loop) +DLL_EXPORT bool ZMusic_Start(MusInfo *song, int subsong, bool loop) { - if (!song) return; - song->Play(loop, subsong); + if (!song) return true; // Starting a null song is not an error! It just won't play anything. + try + { + song->Play(loop, subsong); + return true; + } + catch (const std::exception & ex) + { + SetError(ex.what()); + return false; + } } //========================================================================== @@ -337,79 +391,107 @@ void ZMusic_Start(MusInfo *song, int subsong, bool loop) // //========================================================================== -void ZMusic_Pause(MusInfo *song) +DLL_EXPORT void ZMusic_Pause(MusInfo *song) { if (!song) return; song->Pause(); } -void ZMusic_Resume(MusInfo *song) +DLL_EXPORT void ZMusic_Resume(MusInfo *song) { if (!song) return; song->Resume(); } -void ZMusic_Update(MusInfo *song) +DLL_EXPORT void ZMusic_Update(MusInfo *song) { if (!song) return; song->Update(); } -bool ZMusic_IsPlaying(MusInfo *song) +DLL_EXPORT bool ZMusic_IsPlaying(MusInfo *song) { if (!song) return false; return song->IsPlaying(); } -void ZMusic_Stop(MusInfo *song) +DLL_EXPORT void ZMusic_Stop(MusInfo *song) { if (!song) return; std::lock_guard lock(song->CritSec); song->Stop(); } -bool ZMusic_SetSubsong(MusInfo *song, int subsong) +DLL_EXPORT bool ZMusic_SetSubsong(MusInfo *song, int subsong) { if (!song) return false; std::lock_guard lock(song->CritSec); return song->SetSubsong(subsong); } -bool ZMusic_IsLooping(MusInfo *song) +DLL_EXPORT bool ZMusic_IsLooping(MusInfo *song) { if (!song) return false; return song->m_Looping; } -bool ZMusic_IsMIDI(MusInfo *song) +DLL_EXPORT bool ZMusic_IsMIDI(MusInfo *song) { if (!song) return false; return song->IsMIDI(); } -SoundStreamInfo ZMusic_GetStreamInfo(MusInfo *song) +DLL_EXPORT void ZMusic_GetStreamInfo(MusInfo *song, SoundStreamInfo *fmt) { - if (!song) return {}; + if (!fmt) return; + if (!song) *fmt = {}; std::lock_guard lock(song->CritSec); - return song->GetStreamInfo(); + *fmt = song->GetStreamInfo(); } -void ZMusic_Close(MusInfo *song) +DLL_EXPORT void ZMusic_Close(MusInfo *song) { if (!song) return; delete song; } -void ZMusic_VolumeChanged(MusInfo *song) +DLL_EXPORT void ZMusic_VolumeChanged(MusInfo *song) { if (!song) return; std::lock_guard lock(song->CritSec); song->MusicVolumeChanged(); } -std::string ZMusic_GetStats(MusInfo *song) +static std::string staticErrorMessage; + +DLL_EXPORT const char *ZMusic_GetStats(MusInfo *song) { if (!song) return ""; std::lock_guard lock(song->CritSec); - return song->GetStats(); + staticErrorMessage = song->GetStats(); + return staticErrorMessage.c_str(); +} + +void SetError(const char* msg) +{ + staticErrorMessage = msg; +} + +DLL_EXPORT const char* ZMusic_GetLastError() +{ + return staticErrorMessage.c_str(); +} + +DLL_EXPORT bool ZMusic_WriteSMF(MIDISource* source, const char *fn, int looplimit) +{ + std::vector midi; + bool success; + + if (!source) return false; + source->CreateSMF(midi, 1); + auto f = MusicIO::utf8_fopen(fn, "wt"); + if (f == nullptr) return false; + success = (fwrite(&midi[0], 1, midi.size(), f) == midi.size()); + delete f; + return success; } diff --git a/libraries/zmusic/zmusic/zmusic.h b/libraries/zmusic/zmusic/zmusic.h index 6422512f61..56a61749dc 100644 --- a/libraries/zmusic/zmusic/zmusic.h +++ b/libraries/zmusic/zmusic/zmusic.h @@ -1,122 +1,200 @@ #pragma once -#include "mididefs.h" -#include "../../music_common/fileio.h" +#include +#include #include #include -namespace ZMusic // Namespaced because these conflict with the same-named CVARs +// These constants must match the corresponding values of the Windows headers +// to avoid readjustment in the native Windows device's playback functions +// and should not be changed. +enum EMidiDeviceClass { + MIDIDEV_MIDIPORT = 1, + MIDIDEV_SYNTH, + MIDIDEV_SQSYNTH, + MIDIDEV_FMSYNTH, + MIDIDEV_MAPPER, + MIDIDEV_WAVETABLE, + MIDIDEV_SWSYNTH +}; + +enum EMIDIType +{ + MIDI_NOTMIDI, + MIDI_MIDI, + MIDI_HMI, + MIDI_XMI, + MIDI_MUS +}; + +enum EMidiDevice +{ + MDEV_DEFAULT = -1, + MDEV_STANDARD = 0, + MDEV_OPL = 1, + MDEV_SNDSYS = 2, + MDEV_TIMIDITY = 3, + MDEV_FLUIDSYNTH = 4, + MDEV_GUS = 5, + MDEV_WILDMIDI = 6, + MDEV_ADL = 7, + MDEV_OPN = 8, + + MDEV_COUNT +}; + +enum ESoundFontTypes +{ + SF_SF2 = 1, + SF_GUS = 2, + SF_WOPL = 4, + SF_WOPN = 8 +}; + +struct SoundStreamInfo +{ + int mBufferSize; // If mBufferSize is 0, the song doesn't use streaming but plays through a different interface. + int mSampleRate; + int mNumChannels; // If mNumChannels is negative, 16 bit integer format is used instead of floating point. +}; + +enum SampleType +{ + SampleType_UInt8, + SampleType_Int16 +}; +enum ChannelConfig +{ + ChannelConfig_Mono, + ChannelConfig_Stereo +}; enum EIntConfigKey { - adl_chips_count, - adl_emulator_id, - adl_run_at_pcm_rate, - adl_fullpan, - adl_bank, - adl_use_custom_bank, - adl_volume_model, + zmusic_adl_chips_count, + zmusic_adl_emulator_id, + zmusic_adl_run_at_pcm_rate, + zmusic_adl_fullpan, + zmusic_adl_bank, + zmusic_adl_use_custom_bank, + zmusic_adl_volume_model, - fluid_reverb, - fluid_chorus, - fluid_voices, - fluid_interp, - fluid_samplerate, - fluid_threads, - fluid_chorus_voices, - fluid_chorus_type, + zmusic_fluid_reverb, + zmusic_fluid_chorus, + zmusic_fluid_voices, + zmusic_fluid_interp, + zmusic_fluid_samplerate, + zmusic_fluid_threads, + zmusic_fluid_chorus_voices, + zmusic_fluid_chorus_type, - opl_numchips, - opl_core, - opl_fullpan, + zmusic_opl_numchips, + zmusic_opl_core, + zmusic_opl_fullpan, - opn_chips_count, - opn_emulator_id, - opn_run_at_pcm_rate, - opn_fullpan, - opn_use_custom_bank, + zmusic_opn_chips_count, + zmusic_opn_emulator_id, + zmusic_opn_run_at_pcm_rate, + zmusic_opn_fullpan, + zmusic_opn_use_custom_bank, - gus_dmxgus, - gus_midi_voices, - gus_memsize, + zmusic_gus_dmxgus, + zmusic_gus_midi_voices, + zmusic_gus_memsize, - timidity_modulation_wheel, - timidity_portamento, - timidity_reverb, - timidity_reverb_level, - timidity_chorus, - timidity_surround_chorus, - timidity_channel_pressure, - timidity_lpf_def, - timidity_temper_control, - timidity_modulation_envelope, - timidity_overlap_voice_allow, - timidity_drum_effect, - timidity_pan_delay, - timidity_key_adjust, + zmusic_timidity_modulation_wheel, + zmusic_timidity_portamento, + zmusic_timidity_reverb, + zmusic_timidity_reverb_level, + zmusic_timidity_chorus, + zmusic_timidity_surround_chorus, + zmusic_timidity_channel_pressure, + zmusic_timidity_lpf_def, + zmusic_timidity_temper_control, + zmusic_timidity_modulation_envelope, + zmusic_timidity_overlap_voice_allow, + zmusic_timidity_drum_effect, + zmusic_timidity_pan_delay, + zmusic_timidity_key_adjust, - wildmidi_reverb, - wildmidi_enhanced_resampling, + zmusic_wildmidi_reverb, + zmusic_wildmidi_enhanced_resampling, - snd_midiprecache, + zmusic_snd_midiprecache, - mod_samplerate, - mod_volramp, - mod_interp, - mod_autochip, - mod_autochip_size_force, - mod_autochip_size_scan, - mod_autochip_scan_threshold, + zmusic_mod_samplerate, + zmusic_mod_volramp, + zmusic_mod_interp, + zmusic_mod_autochip, + zmusic_mod_autochip_size_force, + zmusic_mod_autochip_size_scan, + zmusic_mod_autochip_scan_threshold, - snd_streambuffersize, + zmusic_snd_streambuffersize, - snd_mididevice, - snd_outputrate, + zmusic_snd_mididevice, + zmusic_snd_outputrate, - NUM_INT_CONFIGS + NUM_ZMUSIC_INT_CONFIGS }; enum EFloatConfigKey { - fluid_gain, - fluid_reverb_roomsize, - fluid_reverb_damping, - fluid_reverb_width, - fluid_reverb_level, - fluid_chorus_level, - fluid_chorus_speed, - fluid_chorus_depth, + zmusic_fluid_gain, + zmusic_fluid_reverb_roomsize, + zmusic_fluid_reverb_damping, + zmusic_fluid_reverb_width, + zmusic_fluid_reverb_level, + zmusic_fluid_chorus_level, + zmusic_fluid_chorus_speed, + zmusic_fluid_chorus_depth, - timidity_drum_power, - timidity_tempo_adjust, - min_sustain_time, + zmusic_timidity_drum_power, + zmusic_timidity_tempo_adjust, + zmusic_min_sustain_time, - gme_stereodepth, - mod_dumb_mastervolume, + zmusic_gme_stereodepth, + zmusic_mod_dumb_mastervolume, - snd_musicvolume, - relative_volume, - snd_mastervolume, + zmusic_snd_musicvolume, + zmusic_relative_volume, + zmusic_snd_mastervolume, NUM_FLOAT_CONFIGS }; enum EStringConfigKey { - adl_custom_bank, - fluid_lib, - fluid_patchset, - opn_custom_bank, - gus_config, - gus_patchdir, - timidity_config, - wildmidi_config, + zmusic_adl_custom_bank, + zmusic_fluid_lib, + zmusic_fluid_patchset, + zmusic_opn_custom_bank, + zmusic_gus_config, + zmusic_gus_patchdir, + zmusic_timidity_config, + zmusic_wildmidi_config, NUM_STRING_CONFIGS }; -} + +struct ZMusicCustomReader +{ + void* handle; + char* (*gets)(struct ZMusicCustomReader* handle, char* buff, int n); + long (*read)(struct ZMusicCustomReader* handle, void* buff, int32_t size); + long (*seek)(struct ZMusicCustomReader* handle, long offset, int whence); + long (*tell)(struct ZMusicCustomReader* handle); + void (*close)(struct ZMusicCustomReader* handle); +}; + +struct MidiOutDevice +{ + char *Name; + int ID; + int Technology; +}; struct Callbacks { @@ -124,67 +202,115 @@ struct Callbacks // or to provide sound font data. // The message callbacks are optional, without them the output goes to stdout. - void (*WildMidi_MessageFunc)(const char* wmfmt, va_list args) = nullptr; - void (*GUS_MessageFunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr; - void (*Timidity_Messagefunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr; - int (*Fluid_MessageFunc)(const char *fmt, ...) = nullptr; - - // The sound font callbacks are for allowing the client to customize sound font management - // Without them only paths to real files can be used. - const char *(*PathForSoundfont)(const char *name, int type) = nullptr; - MusicIO::SoundFontReaderInterface *(*OpenSoundFont)(const char* name, int type) = nullptr; - + void (*WildMidi_MessageFunc)(const char* wmfmt, va_list args); + void (*GUS_MessageFunc)(int type, int verbosity_level, const char* fmt, ...); + void (*Timidity_Messagefunc)(int type, int verbosity_level, const char* fmt, ...); + int (*Fluid_MessageFunc)(const char *fmt, ...); + + // Retrieves the path to a soundfont identified by an identifier. Only needed if the client virtualizes the sound font names + const char *(*PathForSoundfont)(const char *name, int type); + + // The sound font callbacks are for allowing the client to customize sound font management and they are optional. + // They only need to be defined if the client virtualizes the sound font management and doesn't pass real paths to the music code. + // Without them only paths to real files can be used. If one of these gets set, all must be set. + + // This opens a sound font. Must return a handle with which the sound font's content can be read. + void *(*OpenSoundFont)(const char* name, int type); + + // Opens a file in the sound font. For GUS patch sets this will try to open each patch with this function. + // For other formats only the sound font's actual name can be requested. + // When passed NULL this must open the Timidity config file, if this is requested for an SF2 sound font it should be synthesized. + struct ZMusicCustomReader* (*SF_OpenFile)(void* handle, const char* fn); + + //Adds a path to the list of directories in which files must be looked for. + void (*SF_AddToSearchPath)(void* handle, const char* path); + + // Closes the sound font reader. + void (*SF_Close)(void* handle); + // Used to handle client-specific path macros. If not set, the path may not contain any special tokens that may need expansion. - std::string (*NicePath)(const char *path) = nullptr; - - // For playing modules with compressed samples. - short* (*DumbVorbisDecode)(int outlen, const void* oggstream, int sizebytes); - + const char *(*NicePath)(const char* path); }; -// Sets callbacks for functionality that the client needs to provide. -void ZMusic_SetCallbacks(const Callbacks *callbacks); -// Sets GenMidi data for OPL playback. If this isn't provided the OPL synth will not work. -void ZMusic_SetGenMidi(const uint8_t* data); -// Set default bank for OPN. Without this OPN only works with custom banks. -void ZMusic_SetWgOpn(const void* data, unsigned len); -// Set DMXGUS data for running the GUS synth in actual GUS mode. -void ZMusic_SetDmxGus(const void* data, unsigned len); -struct MidiOutDevice { - std::string Name; - int ID = -1; -}; -int ZMusic_EnumerateMidiDevices(); -const std::vector &ZMusic_GetMidiDevices(); +#ifndef ZMUSIC_INTERNAL +#define DLL_IMPORT // _declspec(dllimport) +// Note that the internal 'class' definitions are not C compatible! +typedef struct { int zm1; } *ZMusic_MidiSource; +typedef struct { int zm2; } *ZMusic_MusicStream; +struct SoundDecoder; +#endif -// These exports are needed by the MIDI dumpers which need to remain on the client side. -class MIDISource; // abstract for the client -class MusInfo; -EMIDIType IdentifyMIDIType(uint32_t *id, int size); -MIDISource *CreateMIDISource(const uint8_t *data, size_t length, EMIDIType miditype); -void MIDIDumpWave(MIDISource* source, EMidiDevice devtype, const char* devarg, const char* outname, int subsong, int samplerate); +#ifdef __cplusplus +extern "C" +{ +#endif + DLL_IMPORT const char* ZMusic_GetLastError(); -MusInfo *ZMusic_OpenSong (MusicIO::FileInterface *reader, EMidiDevice device, const char *Args); -MusInfo *ZMusic_OpenCDSong (int track, int cdid = 0); + // Sets callbacks for functionality that the client needs to provide. + DLL_IMPORT void ZMusic_SetCallbacks(const Callbacks* callbacks); + // Sets GenMidi data for OPL playback. If this isn't provided the OPL synth will not work. + DLL_IMPORT void ZMusic_SetGenMidi(const uint8_t* data); + // Set default bank for OPN. Without this OPN only works with custom banks. + DLL_IMPORT void ZMusic_SetWgOpn(const void* data, unsigned len); + // Set DMXGUS data for running the GUS synth in actual GUS mode. + DLL_IMPORT void ZMusic_SetDmxGus(const void* data, unsigned len); -bool ZMusic_FillStream(MusInfo* stream, void* buff, int len); -void ZMusic_Start(MusInfo *song, int subsong, bool loop); -void ZMusic_Pause(MusInfo *song); -void ZMusic_Resume(MusInfo *song); -void ZMusic_Update(MusInfo *song); -bool ZMusic_IsPlaying(MusInfo *song); -void ZMusic_Stop(MusInfo *song); -void ZMusic_Close(MusInfo *song); -bool ZMusic_SetSubsong(MusInfo *song, int subsong); -bool ZMusic_IsLooping(MusInfo *song); -bool ZMusic_IsMIDI(MusInfo *song); -void ZMusic_VolumeChanged(MusInfo *song); -SoundStreamInfo ZMusic_GetStreamInfo(MusInfo *song); -std::string ZMusic_GetStats(MusInfo *song); + // These exports are needed by the MIDI dumpers which need to remain on the client side because the need access to the client's file system. + DLL_IMPORT EMIDIType ZMusic_IdentifyMIDIType(uint32_t* id, int size); + DLL_IMPORT ZMusic_MidiSource ZMusic_CreateMIDISource(const uint8_t* data, size_t length, EMIDIType miditype); + DLL_IMPORT bool ZMusic_MIDIDumpWave(ZMusic_MidiSource source, EMidiDevice devtype, const char* devarg, const char* outname, int subsong, int samplerate); -// Configuration interface. The return value specifies if a music restart is needed. -// RealValue should be written back to the CVAR or whatever other method the client uses to store configuration state. -bool ChangeMusicSetting(ZMusic::EIntConfigKey key, MusInfo *song, int value, int *pRealValue = nullptr); -bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, MusInfo* song, float value, float *pRealValue = nullptr); -bool ChangeMusicSetting(ZMusic::EStringConfigKey key, MusInfo* song, const char *value); + DLL_IMPORT ZMusic_MusicStream ZMusic_OpenSong(struct ZMusicCustomReader* reader, EMidiDevice device, const char* Args); + DLL_IMPORT ZMusic_MusicStream ZMusic_OpenSongFile(const char *filename, EMidiDevice device, const char* Args); + DLL_IMPORT ZMusic_MusicStream ZMusic_OpenSongMem(const void *mem, size_t size, EMidiDevice device, const char* Args); + DLL_IMPORT ZMusic_MusicStream ZMusic_OpenCDSong(int track, int cdid = 0); + + DLL_IMPORT bool ZMusic_FillStream(ZMusic_MusicStream stream, void* buff, int len); + DLL_IMPORT bool ZMusic_Start(ZMusic_MusicStream song, int subsong, bool loop); + DLL_IMPORT void ZMusic_Pause(ZMusic_MusicStream song); + DLL_IMPORT void ZMusic_Resume(ZMusic_MusicStream song); + DLL_IMPORT void ZMusic_Update(ZMusic_MusicStream song); + DLL_IMPORT bool ZMusic_IsPlaying(ZMusic_MusicStream song); + DLL_IMPORT void ZMusic_Stop(ZMusic_MusicStream song); + DLL_IMPORT void ZMusic_Close(ZMusic_MusicStream song); + DLL_IMPORT bool ZMusic_SetSubsong(ZMusic_MusicStream song, int subsong); + DLL_IMPORT bool ZMusic_IsLooping(ZMusic_MusicStream song); + DLL_IMPORT bool ZMusic_IsMIDI(ZMusic_MusicStream song); + DLL_IMPORT void ZMusic_VolumeChanged(ZMusic_MusicStream song); + DLL_IMPORT bool ZMusic_WriteSMF(ZMusic_MidiSource source, const char* fn, int looplimit); + DLL_IMPORT void ZMusic_GetStreamInfo(ZMusic_MusicStream song, SoundStreamInfo *info); + // Configuration interface. The return value specifies if a music restart is needed. + // RealValue should be written back to the CVAR or whatever other method the client uses to store configuration state. + DLL_IMPORT bool ChangeMusicSettingInt(EIntConfigKey key, ZMusic_MusicStream song, int value, int* pRealValue); + DLL_IMPORT bool ChangeMusicSettingFloat(EFloatConfigKey key, ZMusic_MusicStream song, float value, float* pRealValue); + DLL_IMPORT bool ChangeMusicSettingString(EStringConfigKey key, ZMusic_MusicStream song, const char* value); + DLL_IMPORT const char *ZMusic_GetStats(ZMusic_MusicStream song); + + + DLL_IMPORT struct SoundDecoder* CreateDecoder(const uint8_t* data, size_t size, bool isstatic); + DLL_IMPORT void SoundDecoder_GetInfo(struct SoundDecoder* decoder, int* samplerate, ChannelConfig* chans, SampleType* type); + DLL_IMPORT size_t SoundDecoder_Read(struct SoundDecoder* decoder, void* buffer, size_t length); + DLL_IMPORT void SoundDecoder_Close(struct SoundDecoder* decoder); + DLL_IMPORT void FindLoopTags(const uint8_t* data, size_t size, uint32_t* start, bool* startass, uint32_t* end, bool* endass); + // The rest of the decoder interface is only useful for streaming music. + + DLL_IMPORT const MidiOutDevice *ZMusic_GetMidiDevices(int *pAmount); + +#ifdef __cplusplus +} + +inline bool ChangeMusicSetting(EIntConfigKey key, ZMusic_MusicStream song, int value, int* pRealValue = nullptr) +{ + return ChangeMusicSettingInt(key, song, value, pRealValue); +} +inline bool ChangeMusicSetting(EFloatConfigKey key, ZMusic_MusicStream song, float value, float* pRealValue = nullptr) +{ + return ChangeMusicSettingFloat(key, song, value, pRealValue); +} +inline bool ChangeMusicSetting(EStringConfigKey key, ZMusic_MusicStream song, const char* value) +{ + return ChangeMusicSettingString(key, song, value); +} + +#endif \ No newline at end of file diff --git a/libraries/zmusic/zmusic/zmusic_internal.h b/libraries/zmusic/zmusic/zmusic_internal.h new file mode 100644 index 0000000000..9f2b52d737 --- /dev/null +++ b/libraries/zmusic/zmusic/zmusic_internal.h @@ -0,0 +1,60 @@ +#pragma once +#define ZMUSIC_INTERNAL + +#define DLL_EXPORT // __declspec(dllexport) +#define DLL_IMPORT +typedef class MIDISource *ZMusic_MidiSource; +typedef class MusInfo *ZMusic_MusicStream; + +// Some MIDI backends are not compatible with LGPLv2, so with this #define the support level can be set. +// License can be: +// 0: LGPLv2. FluidSynth and GUS only. +// 1: GPLv2. Adds Timidity++ and OPL. +// 2: GPLv3. Adds ADL, OPN and WildMidi. +// + +// Default is GPLv3. Most Open Source games can comply with this. +// The notable exceptions are the Build engine games and Descent which are under a no profit clause. +// For these games LGPLv2 is the only viable choice. +#ifndef LICENSE +#define LICENSE 2 +#endif + +// Devices under a permissive license, or at most LGPLv2+ +#define HAVE_GUS +// FluidSynth is a configuration option + +#if LICENSE >= 1 +#define HAVE_TIMIDITY +#define HAVE_OPL +#endif + +#if LICENSE >= 2 +// MIDI Devices that cannot be licensed as LGPLv2. +#define HAVE_ADL +#define HAVE_OPN +#define HAVE_WILDMIDI +#endif + +#include "zmusic.h" +#include "../../music_common/fileio.h" + +void SetError(const char *text); + +struct CustomFileReader : public MusicIO::FileInterface +{ + ZMusicCustomReader* cr; + + CustomFileReader(ZMusicCustomReader* zr) : cr(zr) {} + virtual char* gets(char* buff, int n) { return cr->gets(cr, buff, n); } + virtual long read(void* buff, int32_t size) { return cr->read(cr, buff, size); } + virtual long seek(long offset, int whence) { return cr->seek(cr, offset, whence); } + virtual long tell() { return cr->tell(cr); } + virtual void close() + { + cr->close(cr); + delete this; + } + +}; + diff --git a/src/sound/backend/i_sound.cpp b/src/sound/backend/i_sound.cpp index 3c6585a443..c33b561bc1 100644 --- a/src/sound/backend/i_sound.cpp +++ b/src/sound/backend/i_sound.cpp @@ -39,7 +39,6 @@ #include "i_module.h" #include "cmdlib.h" -#include "zmusic/sounddecoder.h" #include "c_dispatch.h" #include "i_music.h" @@ -92,7 +91,7 @@ CUSTOM_CVAR(Float, snd_mastervolume, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) else if (self > 1.f) self = 1.f; - ChangeMusicSetting(ZMusic::snd_mastervolume, nullptr, self); + ChangeMusicSetting(zmusic_snd_mastervolume, nullptr, self); snd_sfxvolume.Callback(); snd_musicvolume.Callback(); } @@ -325,36 +324,6 @@ FString SoundRenderer::GatherStats () return "No stats for this sound renderer."; } -short *SoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType ctype) -{ - short *samples = (short*)calloc(1, outlen); - ChannelConfig chans; - SampleType type; - int srate; - - // The decoder will take ownership of the reader if it succeeds so this may not be a local variable. - MusicIO::MemoryReader *reader = new MusicIO::MemoryReader((const uint8_t*)coded, sizebytes); - - SoundDecoder *decoder = SoundDecoder::CreateDecoder(reader); - if (!decoder) - { - reader->close(); - return samples; - } - - decoder->getInfo(&srate, &chans, &type); - if(chans != ChannelConfig_Mono || type != SampleType_Int16) - { - DPrintf(DMSG_WARNING, "Sample is not 16-bit mono\n"); - delete decoder; - return samples; - } - - decoder->read((char*)samples, outlen); - delete decoder; - return samples; -} - void SoundRenderer::DrawWaveDebug(int mode) { } diff --git a/src/sound/backend/i_sound.h b/src/sound/backend/i_sound.h index 40b8a14253..bca2c32642 100644 --- a/src/sound/backend/i_sound.h +++ b/src/sound/backend/i_sound.h @@ -35,8 +35,10 @@ #ifndef __I_SOUND__ #define __I_SOUND__ +#include #include "i_soundinternal.h" #include "utility/zstring.h" +#include "zmusic/zmusic.h" class FileReader; struct FSoundChan; @@ -165,7 +167,6 @@ public: virtual void PrintStatus () = 0; virtual void PrintDriversList () = 0; virtual FString GatherStats (); - virtual short *DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type); virtual void DrawWaveDebug(int mode); }; diff --git a/src/sound/backend/i_soundinternal.h b/src/sound/backend/i_soundinternal.h index 823f672b7b..35e00f52b3 100644 --- a/src/sound/backend/i_soundinternal.h +++ b/src/sound/backend/i_soundinternal.h @@ -6,8 +6,6 @@ #include "vectors.h" #include "tarray.h" -#include "zmusic/sounddecoder.h" -#include "../../libraries/music_common/fileio.h" #include "tflags.h" enum EChanFlag @@ -140,9 +138,6 @@ struct FISoundChannel EChanFlags ChanFlags; }; - -void FindLoopTags(MusicIO::FileInterface *fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass); - class SoundStream; diff --git a/src/sound/backend/oalsound.cpp b/src/sound/backend/oalsound.cpp index 8a3a5f13be..6568333363 100644 --- a/src/sound/backend/oalsound.cpp +++ b/src/sound/backend/oalsound.cpp @@ -43,8 +43,6 @@ #include "i_module.h" #include "cmdlib.h" #include "m_fixed.h" -#include "zmusic/sounddecoder.h" -#include "filereadermusicinterface.h" const char *GetSampleTypeName(SampleType type); @@ -1099,18 +1097,14 @@ std::pair OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int /* Only downmix to mono if we can't spatialize multi-channel sounds. */ monoize = monoize && !AL.SOFT_source_spatialize; - auto mreader = new MusicIO::MemoryReader(sfxdata, length); - FindLoopTags(mreader, &loop_start, &startass, &loop_end, &endass); - mreader->seek(0, SEEK_SET); - std::unique_ptr decoder(SoundDecoder::CreateDecoder(mreader)); + FindLoopTags(sfxdata, length, &loop_start, &startass, &loop_end, &endass); + auto decoder = CreateDecoder(sfxdata, length, true); if (!decoder) { - delete mreader; return std::make_pair(retval, true); } - // the decode will take ownership of the reader here. - decoder->getInfo(&srate, &chans, &type); + SoundDecoder_GetInfo(decoder, &srate, &chans, &type); int samplesize = 1; if (chans == ChannelConfig_Mono || monoize) { @@ -1125,12 +1119,24 @@ std::pair OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int if (format == AL_NONE) { + SoundDecoder_Close(decoder); Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), GetSampleTypeName(type)); return std::make_pair(retval, true); } - auto data = decoder->readAll(); + std::vector data; + unsigned total = 0; + unsigned got; + + data.resize(total + 32768); + while ((got = (unsigned)SoundDecoder_Read(decoder, (char*)&data[total], data.size() - total)) > 0) + { + total += got; + data.resize(total * 2); + } + data.resize(total); + SoundDecoder_Close(decoder); if(chans != ChannelConfig_Mono && monoize) { diff --git a/src/sound/music/i_music.cpp b/src/sound/music/i_music.cpp index 1d3c0f7154..2208fc79c5 100644 --- a/src/sound/music/i_music.cpp +++ b/src/sound/music/i_music.cpp @@ -39,6 +39,7 @@ #include +#include "zmusic/zmusic.h" #include "m_argv.h" #include "w_wad.h" #include "c_dispatch.h" @@ -52,10 +53,7 @@ #include "i_soundfont.h" #include "s_music.h" #include "doomstat.h" -#include "zmusic/zmusic.h" -#include "streamsources/streamsource.h" #include "filereadermusicinterface.h" -#include "../libraries/zmusic/midisources/midisource.h" @@ -70,8 +68,6 @@ int nomusic = 0; #ifdef _WIN32 -void I_InitMusicWin32(); - #include "musicformats/win32/i_cd.h" //========================================================================== // @@ -123,7 +119,7 @@ CUSTOM_CVAR (Float, snd_musicvolume, 0.5f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) else { // Set general music volume. - ChangeMusicSetting(ZMusic::snd_musicvolume, nullptr, self); + ChangeMusicSetting(zmusic_snd_musicvolume, nullptr, self); if (GSnd != nullptr) { GSnd->SetMusicVolume(clamp(self * relative_volume * snd_mastervolume, 0, 1)); @@ -179,20 +175,11 @@ static void wm_printfunc(const char* wmfmt, va_list args) VPrintf(PRINT_HIGH, wmfmt, args); } -//========================================================================== -// -// other callbacks -// -//========================================================================== -static short* dumb_decode_vorbis_(int outlen, const void* oggstream, int sizebytes) +static FString strv; +static const char *mus_NicePath(const char* str) { - return GSnd->DecodeSample(outlen, oggstream, sizebytes, CODEC_Vorbis); -} - -static std::string mus_NicePath(const char* str) -{ - FString strv = NicePath(str); + strv = NicePath(str); return strv.GetChars(); } @@ -202,11 +189,26 @@ static const char* mus_pathToSoundFont(const char* sfname, int type) return info ? info->mFilename.GetChars() : nullptr; } -static MusicIO::SoundFontReaderInterface* mus_openSoundFont(const char* sfname, int type) +static void* mus_openSoundFont(const char* sfname, int type) { return sfmanager.OpenSoundFont(sfname, type); } +static ZMusicCustomReader* mus_sfopenfile(void* handle, const char* fn) +{ + return reinterpret_cast(handle)->open_interface(fn); +} + +static void mus_sfaddpath(void *handle, const char* path) +{ + reinterpret_cast(handle)->AddPath(path); +} + +static void mus_sfclose(void* handle) +{ + reinterpret_cast(handle)->close(); +} + //========================================================================== // @@ -270,15 +272,9 @@ void I_InitMusic (void) nomusic = !!Args->CheckParm("-nomusic") || !!Args->CheckParm("-nosound"); -// TODO: remove, move functionality to ZMusic_EnumerateMidiDevices -#ifdef _WIN32 - I_InitMusicWin32 (); -#endif // _WIN32 - - ZMusic_EnumerateMidiDevices(); snd_mididevice.Callback(); - Callbacks callbacks; + Callbacks callbacks{}; callbacks.Fluid_MessageFunc = Printf; callbacks.GUS_MessageFunc = callbacks.Timidity_Messagefunc = tim_printfunc; @@ -286,7 +282,9 @@ void I_InitMusic (void) callbacks.NicePath = mus_NicePath; callbacks.PathForSoundfont = mus_pathToSoundFont; callbacks.OpenSoundFont = mus_openSoundFont; - callbacks.DumbVorbisDecode = dumb_decode_vorbis_; + callbacks.SF_OpenFile = mus_sfopenfile; + callbacks.SF_AddToSearchPath = mus_sfaddpath; + callbacks.SF_Close = mus_sfclose; ZMusic_SetCallbacks(&callbacks); SetupGenMidi(); @@ -304,7 +302,7 @@ void I_InitMusic (void) void I_SetRelativeVolume(float vol) { relative_volume = (float)vol; - ChangeMusicSetting(ZMusic::relative_volume, nullptr, (float)vol); + ChangeMusicSetting(zmusic_relative_volume, nullptr, (float)vol); snd_musicvolume.Callback(); } //========================================================================== @@ -345,7 +343,7 @@ ADD_STAT(music) { if (mus_playing.handle != nullptr) { - return FString(ZMusic_GetStats(mus_playing.handle).c_str()); + return ZMusic_GetStats(mus_playing.handle); } return "No song playing"; } @@ -356,7 +354,7 @@ ADD_STAT(music) // //========================================================================== -static MIDISource *GetMIDISource(const char *fn) +static ZMusic_MidiSource GetMIDISource(const char *fn) { FString src = fn; if (src.Compare("*") == 0) src = mus_playing.name; @@ -378,7 +376,7 @@ static MIDISource *GetMIDISource(const char *fn) Printf("Unable to read lump %s\n", src.GetChars()); return nullptr; } - auto type = IdentifyMIDIType(id, 32); + auto type = ZMusic_IdentifyMIDIType(id, 32); if (type == MIDI_NOTMIDI) { Printf("%s is not MIDI-based.\n", src.GetChars()); @@ -386,11 +384,11 @@ static MIDISource *GetMIDISource(const char *fn) } auto data = wlump.Read(); - auto source = CreateMIDISource(data.Data(), data.Size(), type); + auto source = ZMusic_CreateMIDISource(data.Data(), data.Size(), type); if (source == nullptr) { - Printf("%s is not MIDI-based.\n", src.GetChars()); + Printf("Unable to open %s: %s\n", src.GetChars(), ZMusic_GetLastError()); return nullptr; } return source; @@ -434,13 +432,9 @@ UNSAFE_CCMD (writewave) auto savedsong = mus_playing; S_StopMusic(true); if (dev == MDEV_DEFAULT && snd_mididevice >= 0) dev = MDEV_FLUIDSYNTH; // The Windows system synth cannot dump a wave. - try + if (!ZMusic_MIDIDumpWave(source, dev, argv.argc() < 6 ? nullptr : argv[6], argv[2], argv.argc() < 4 ? 0 : (int)strtol(argv[3], nullptr, 10), argv.argc() < 5 ? 0 : (int)strtol(argv[4], nullptr, 10))) { - MIDIDumpWave(source, dev, argv.argc() < 6 ? nullptr : argv[6], argv[2], argv.argc() < 4 ? 0 : (int)strtol(argv[3], nullptr, 10), argv.argc() < 5 ? 0 : (int)strtol(argv[4], nullptr, 10)); - } - catch (const std::runtime_error& err) - { - Printf("MIDI dump failed: %s\n", err.what()); + Printf("MIDI dump of %s failed: %s\n",argv[1], ZMusic_GetLastError()); } S_ChangeMusic(savedsong.name, savedsong.baseorder, savedsong.loop, true); @@ -470,23 +464,13 @@ UNSAFE_CCMD(writemidi) return; } auto source = GetMIDISource(argv[1]); - if (source == nullptr) return; - - std::vector midi; - bool success; - - source->CreateSMF(midi, 1); - auto f = FileWriter::Open(argv[2]); - if (f == nullptr) + if (source == nullptr) { - Printf("Could not open %s.\n", argv[2]); + Printf("Unable to open %s: %s\n", argv[1], ZMusic_GetLastError()); return; } - success = (f->Write(&midi[0], midi.size()) == midi.size()); - delete f; - - if (!success) + if (!ZMusic_WriteSMF(source, argv[1], 1)) { - Printf("Could not write to music file %s.\n", argv[2]); + Printf("Unable to write %s\n", argv[1]); } } diff --git a/src/sound/music/i_soundfont.cpp b/src/sound/music/i_soundfont.cpp index f14e538424..f55ee208cf 100644 --- a/src/sound/music/i_soundfont.cpp +++ b/src/sound/music/i_soundfont.cpp @@ -135,30 +135,17 @@ FileReader FSoundFontReader::Open(const char *name, std::string& filename) // //========================================================================== -MusicIO::FileInterface* FSoundFontReader::open_interface(const char* name) +ZMusicCustomReader* FSoundFontReader::open_interface(const char* name) { std::string filename; FileReader fr = Open(name, filename); if (!fr.isOpen()) return nullptr; - auto fri = new FileReaderMusicInterface(fr); - fri->filename = std::move(filename); + auto fri = GetMusicReader(fr); return fri; } -//========================================================================== -// -// The file interface for the backend -// -//========================================================================== - -struct MusicIO::FileInterface* FSoundFontReader::open_file(const char* name) -{ - return open_interface(name); -} - - //========================================================================== // // Note that the file type has already been checked diff --git a/src/sound/music/i_soundfont.h b/src/sound/music/i_soundfont.h index 76e20d1b19..c2955620c6 100644 --- a/src/sound/music/i_soundfont.h +++ b/src/sound/music/i_soundfont.h @@ -19,8 +19,7 @@ struct FSoundFontInfo // //========================================================================== -class FSoundFontReader : public MusicIO::SoundFontReaderInterface -// Yes, it's 3 copies of essentially the same interface, but since we want to keep the 3 renderers as isolated modules we have to pull in their own implementations here. +class FSoundFontReader { protected: // This is only doable for loose config files that get set as sound fonts. All other cases read from a contained environment where this does not apply. @@ -52,15 +51,12 @@ public: } virtual FileReader Open(const char* name, std::string &filename); + virtual void close() + { + delete this; + } - // Timidity++ interface - struct MusicIO::FileInterface* open_file(const char* name) override; - void add_search_path(const char* name) override - { - return AddPath(name); - } - - MusicIO::FileInterface* open_interface(const char* name); + ZMusicCustomReader* open_interface(const char* name); }; diff --git a/src/sound/music/music_config.cpp b/src/sound/music/music_config.cpp index da6103b5ca..177e9f643f 100644 --- a/src/sound/music/music_config.cpp +++ b/src/sound/music/music_config.cpp @@ -48,18 +48,18 @@ #define FORWARD_CVAR(key) \ decltype(*self) newval; \ - auto ret = ChangeMusicSetting(ZMusic::key, mus_playing.handle, *self, &newval); \ + auto ret = ChangeMusicSetting(zmusic_##key, mus_playing.handle, *self, &newval); \ self = (decltype(*self))newval; \ if (ret) S_MIDIDeviceChanged(-1); #define FORWARD_BOOL_CVAR(key) \ int newval; \ - auto ret = ChangeMusicSetting(ZMusic::key, mus_playing.handle,*self, &newval); \ + auto ret = ChangeMusicSetting(zmusic_##key, mus_playing.handle,*self, &newval); \ self = !!newval; \ if (ret) S_MIDIDeviceChanged(-1); #define FORWARD_STRING_CVAR(key) \ - auto ret = ChangeMusicSetting(ZMusic::key, mus_playing.handle,*self); \ + auto ret = ChangeMusicSetting(zmusic_##key, mus_playing.handle,*self); \ if (ret) S_MIDIDeviceChanged(-1); diff --git a/src/sound/music/music_midi_base.cpp b/src/sound/music/music_midi_base.cpp index 9af4b7ebc7..f45bffcd2a 100644 --- a/src/sound/music/music_midi_base.cpp +++ b/src/sound/music/music_midi_base.cpp @@ -3,6 +3,7 @@ ** **--------------------------------------------------------------------------- ** Copyright 1998-2010 Randy Heit +** Copyright 2005-2020 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -31,13 +32,6 @@ ** */ -#ifdef _WIN32 - -#define WIN32_LEAN_AND_MEAN -#include -#include -#endif - #include "c_dispatch.h" #include "v_text.h" @@ -48,54 +42,17 @@ #define DEF_MIDIDEV -5 EXTERN_CVAR(Int, snd_mididevice) -static uint32_t nummididevices; -#define NUM_DEF_DEVICES 7 - -static void AddDefaultMidiDevices(FOptionValues *opt) +void I_BuildMIDIMenuList(FOptionValues* opt) { - FOptionValues::Pair *pair = &opt->mValues[opt->mValues.Reserve(NUM_DEF_DEVICES)]; - pair[0].Text = "FluidSynth"; - pair[0].Value = -5.0; - pair[1].Text = "TiMidity++"; - pair[1].Value = -2.0; - pair[2].Text = "WildMidi"; - pair[2].Value = -6.0; - pair[3].Text = "GUS"; - pair[3].Value = -4.0; - pair[4].Text = "OPL Synth Emulation"; - pair[4].Value = -3.0; - pair[5].Text = "libADL"; - pair[5].Value = -7.0; - pair[6].Text = "libOPN"; - pair[6].Value = -8.0; + int amount; + auto list = ZMusic_GetMidiDevices(&amount); -} - -#ifdef _WIN32 - -void I_InitMusicWin32 () -{ - nummididevices = midiOutGetNumDevs (); -} - -void I_BuildMIDIMenuList (FOptionValues *opt) -{ - AddDefaultMidiDevices(opt); - - for (uint32_t id = 0; id < nummididevices; ++id) + for (int i = 0; i < amount; i++) { - MIDIOUTCAPS caps; - MMRESULT res; - - res = midiOutGetDevCaps (id, &caps, sizeof(caps)); - assert(res == MMSYSERR_NOERROR); - if (res == MMSYSERR_NOERROR) - { - FOptionValues::Pair *pair = &opt->mValues[opt->mValues.Reserve(1)]; - pair->Text = caps.szPname; - pair->Value = (float)id; - } + FOptionValues::Pair* pair = &opt->mValues[opt->mValues.Reserve(1)]; + pair->Text = list[i].Name; + pair->Value = (float)list[i].ID; } } @@ -116,111 +73,46 @@ static void PrintMidiDevice (int id, const char *name, uint16_t tech, uint32_t s case MIDIDEV_WAVETABLE: Printf ("WAVETABLE"); break; case MIDIDEV_SWSYNTH: Printf ("SWSYNTH"); break; } - if (support & MIDICAPS_CACHE) - { - Printf (" CACHE"); - } - if (support & MIDICAPS_LRVOLUME) - { - Printf (" LRVOLUME"); - } - if (support & MIDICAPS_STREAM) - { - Printf (" STREAM"); - } - if (support & MIDICAPS_VOLUME) - { - Printf (" VOLUME"); - } Printf (TEXTCOLOR_NORMAL "\n"); } CCMD (snd_listmididevices) { - UINT id; - MIDIOUTCAPS caps; - MMRESULT res; + int amount; + auto list = ZMusic_GetMidiDevices(&amount); - PrintMidiDevice(-8, "libOPN", MIDIDEV_FMSYNTH, 0); - PrintMidiDevice(-7, "libADL", MIDIDEV_FMSYNTH, 0); - PrintMidiDevice (-6, "WildMidi", MIDIDEV_SWSYNTH, 0); - PrintMidiDevice (-5, "FluidSynth", MIDIDEV_SWSYNTH, 0); - PrintMidiDevice (-4, "Gravis Ultrasound Emulation", MIDIDEV_SWSYNTH, 0); - PrintMidiDevice (-3, "Emulated OPL FM Synth", MIDIDEV_FMSYNTH, 0); - PrintMidiDevice (-2, "TiMidity++", MIDIDEV_SWSYNTH, 0); - if (nummididevices != 0) + for (int i = 0; i < amount; i++) { - for (id = 0; id < nummididevices; ++id) - { - FString text; - res = midiOutGetDevCaps (id, &caps, sizeof(caps)); - if (res == MMSYSERR_NODRIVER) - text = ""; - else if (res == MMSYSERR_NOMEM) - text = ""; - else if (res == MMSYSERR_NOERROR) - text = caps.szPname; - else - continue; - - PrintMidiDevice (id, text, caps.wTechnology, caps.dwSupport); - } + PrintMidiDevice(list[i].ID, list[i].Name, list[i].Technology, 0); } } -#else // _WIN32 - -void I_BuildMIDIMenuList (FOptionValues *opt) -{ - AddDefaultMidiDevices(opt); - - auto devices = ZMusic_GetMidiDevices(); - - for (auto & device: devices) - { - FOptionValues::Pair *pair = &opt->mValues[opt->mValues.Reserve(1)]; - pair->Text = device.Name.c_str(); - pair->Value = (float)device.ID; - } -} - -CCMD (snd_listmididevices) -{ - Printf("%s-8. libOPN\n", -8 == snd_mididevice ? TEXTCOLOR_BOLD : ""); - Printf("%s-7. libADL\n", -7 == snd_mididevice ? TEXTCOLOR_BOLD : ""); - Printf("%s-6. WildMidi\n", -6 == snd_mididevice ? TEXTCOLOR_BOLD : ""); - Printf("%s-5. FluidSynth\n", -5 == snd_mididevice ? TEXTCOLOR_BOLD : ""); - Printf("%s-4. Gravis Ultrasound Emulation\n", -4 == snd_mididevice ? TEXTCOLOR_BOLD : ""); - Printf("%s-3. Emulated OPL FM Synth\n", -3 == snd_mididevice ? TEXTCOLOR_BOLD : ""); - Printf("%s-2. TiMidity++\n", -2 == snd_mididevice ? TEXTCOLOR_BOLD : ""); - - auto devices = ZMusic_GetMidiDevices(); - - for (auto & device: devices) - { - Printf("%s%d. %s\n", -2 == snd_mididevice ? TEXTCOLOR_BOLD : "", device.ID, device.Name.c_str()); - } -} -#endif CUSTOM_CVAR (Int, snd_mididevice, DEF_MIDIDEV, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) { - auto devices = ZMusic_GetMidiDevices(); - if ((self >= (signed)devices.size()) || (self < -8)) + int amount; + auto list = ZMusic_GetMidiDevices(&amount); + + bool found = false; + // The list is not necessarily contiguous so we need to check each entry. + for (int i = 0; i < amount; i++) + { + if (self == list[i].ID) + { + found = true; + break; + } + } + if (!found) { // Don't do repeated message spam if there is no valid device. - if (self != 0) + if (self != 0 && self != -1) { Printf("ID out of range. Using default device.\n"); } - self = DEF_MIDIDEV; + if (self != DEF_MIDIDEV) self = DEF_MIDIDEV; return; } - else if (self == -1) - { - self = DEF_MIDIDEV; - return; - } - bool change = ChangeMusicSetting(ZMusic::snd_mididevice, nullptr, self); + bool change = ChangeMusicSetting(zmusic_snd_mididevice, nullptr, self); if (change) S_MIDIDeviceChanged(self); } diff --git a/src/sound/s_advsound.cpp b/src/sound/s_advsound.cpp index 874ff70bef..273f05775c 100644 --- a/src/sound/s_advsound.cpp +++ b/src/sound/s_advsound.cpp @@ -49,7 +49,6 @@ #include "vm.h" #include "i_system.h" #include "s_music.h" -#include "mididevices/mididevice.h" // MACROS ------------------------------------------------------------------ diff --git a/src/sound/s_music.cpp b/src/sound/s_music.cpp index b1fee66766..d36f7678e9 100644 --- a/src/sound/s_music.cpp +++ b/src/sound/s_music.cpp @@ -139,7 +139,8 @@ static bool FillStream(SoundStream* stream, void* buff, int len, void* userdata) void S_CreateStream() { if (!mus_playing.handle) return; - auto fmt = ZMusic_GetStreamInfo(mus_playing.handle); + SoundStreamInfo fmt; + ZMusic_GetStreamInfo(mus_playing.handle, &fmt); if (fmt.mBufferSize > 0) { int flags = fmt.mNumChannels < 0 ? 0 : SoundStream::Float; @@ -171,7 +172,7 @@ void S_StopStream() // //========================================================================== -static void S_StartMusicPlaying(MusInfo* song, bool loop, float rel_vol, int subsong) +static bool S_StartMusicPlaying(ZMusic_MusicStream song, bool loop, float rel_vol, int subsong) { if (rel_vol > 0.f) { @@ -180,10 +181,14 @@ static void S_StartMusicPlaying(MusInfo* song, bool loop, float rel_vol, int sub I_SetRelativeVolume(saved_relative_volume * factor); } ZMusic_Stop(song); - ZMusic_Start(song, subsong, loop); + if (!ZMusic_Start(song, subsong, loop)) + { + return false; + } // Notify the sound system of the changed relative volume snd_musicvolume.Callback(); + return true; } @@ -418,15 +423,11 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) } else if (!ZMusic_IsPlaying(mus_playing.handle)) { - try + if (!ZMusic_Start(mus_playing.handle, looping, order)) { - ZMusic_Start(mus_playing.handle, looping, order); - S_CreateStream(); - } - catch (const std::runtime_error& err) - { - Printf("Unable to start %s: %s\n", mus_playing.name.GetChars(), err.what()); + Printf("Unable to start %s: %s\n", mus_playing.name.GetChars(), ZMusic_GetLastError()); } + S_CreateStream(); } return true; @@ -444,12 +445,16 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) } S_StopMusic (true); mus_playing.handle = ZMusic_OpenCDSong (track, id); + if (mus_playing.handle == nullptr) + { + Printf("Unable to start CD Audio for track #%d, ID %d\n", track, id); + } } else { int lumpnum = -1; int length = 0; - MusInfo *handle = nullptr; + ZMusic_MusicStream handle = nullptr; MidiDeviceSetting *devp = MidiDevices.CheckKey(musicname); // Strip off any leading file:// component. @@ -504,14 +509,11 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) } else { - try + auto mreader = GetMusicReader(reader); // this passes the file reader to the newly created wrapper. + mus_playing.handle = ZMusic_OpenSong(mreader, devp? (EMidiDevice)devp->device : MDEV_DEFAULT, devp? devp->args.GetChars() : ""); + if (mus_playing.handle == nullptr) { - auto mreader = new FileReaderMusicInterface(reader); - mus_playing.handle = ZMusic_OpenSong(mreader, devp? (EMidiDevice)devp->device : MDEV_DEFAULT, devp? devp->args.GetChars() : ""); - } - catch (const std::runtime_error& err) - { - Printf("Unable to load %s: %s\n", mus_playing.name.GetChars(), err.what()); + Printf("Unable to load %s: %s\n", mus_playing.name.GetChars(), ZMusic_GetLastError()); } } } @@ -523,16 +525,13 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) if (mus_playing.handle != 0) { // play it - try + if (!S_StartMusicPlaying(mus_playing.handle, looping, S_GetMusicVolume(musicname), order)) { - S_StartMusicPlaying(mus_playing.handle, looping, S_GetMusicVolume(musicname), order); - S_CreateStream(); - mus_playing.baseorder = order; - } - catch (const std::runtime_error& err) - { - Printf("Unable to start %s: %s\n", mus_playing.name.GetChars(), err.what()); + Printf("Unable to start %s: %s\n", mus_playing.name.GetChars(), ZMusic_GetLastError()); + return false; } + S_CreateStream(); + mus_playing.baseorder = order; return true; } return false; @@ -575,7 +574,7 @@ void S_RestartMusic () void S_MIDIDeviceChanged(int newdev) { - MusInfo* song = mus_playing.handle; + auto song = mus_playing.handle; if (song != nullptr && ZMusic_IsMIDI(song) && ZMusic_IsPlaying(song)) { // Reload the song to change the device diff --git a/src/sound/s_music.h b/src/sound/s_music.h index 76a0798749..b70bbf8b25 100644 --- a/src/sound/s_music.h +++ b/src/sound/s_music.h @@ -28,6 +28,7 @@ #ifndef __S_MUSIC__ #define __S_MUSIC__ +#include "zmusic/zmusic.h" #include "doomtype.h" #include "i_soundinternal.h" @@ -78,11 +79,10 @@ typedef TMap MidiDeviceMap; extern MusicAliasMap MusicAliases; extern MidiDeviceMap MidiDevices; -class MusInfo; struct MusPlayingInfo { FString name; - MusInfo* handle; + ZMusic_MusicStream handle; int baseorder; bool loop; FString LastSong; // last music that was played diff --git a/src/utility/filereadermusicinterface.h b/src/utility/filereadermusicinterface.h index 1de7665904..cde43f88e7 100644 --- a/src/utility/filereadermusicinterface.h +++ b/src/utility/filereadermusicinterface.h @@ -1,39 +1,22 @@ #pragma once -#include "../libraries/music_common/fileio.h" +#include "zmusic/zmusic.h" #include "files.h" -struct FileReaderMusicInterface : public MusicIO::FileInterface + +inline ZMusicCustomReader *GetMusicReader(FileReader& fr) { - FileReader fr; + auto zcr = new ZMusicCustomReader; - FileReaderMusicInterface(FileReader& fr_in) + zcr->handle = fr.GetInterface(); + zcr->gets = [](ZMusicCustomReader* zr, char* buff, int n) { return reinterpret_cast(zr->handle)->Gets(buff, n); }; + zcr->read = [](ZMusicCustomReader* zr, void* buff, int32_t size) { return reinterpret_cast(zr->handle)->Read(buff, (long)size); }; + zcr->seek = [](ZMusicCustomReader* zr, long offset, int whence) { return reinterpret_cast(zr->handle)->Seek(offset, whence); }; + zcr->tell = [](ZMusicCustomReader* zr) { return reinterpret_cast(zr->handle)->Tell(); }; + zcr->close = [](ZMusicCustomReader* zr) { - fr = std::move(fr_in); - } - char* gets(char* buff, int n) override - { - if (!fr.isOpen()) return nullptr; - return fr.Gets(buff, n); - } - long read(void* buff, int32_t size, int32_t nitems) override - { - if (!fr.isOpen()) return 0; - return (long)fr.Read(buff, size * nitems) / size; - } - long seek(long offset, int whence) override - { - if (!fr.isOpen()) return 0; - return (long)fr.Seek(offset, (FileReader::ESeek)whence); - } - long tell() override - { - if (!fr.isOpen()) return 0; - return (long)fr.Tell(); - } - FileReader& getReader() - { - return fr; - } -}; - + delete reinterpret_cast(zr->handle); + delete zr; + }; + return zcr; +} \ No newline at end of file diff --git a/src/utility/files.h b/src/utility/files.h index d363b51bf6..a82e5e8e2d 100644 --- a/src/utility/files.h +++ b/src/utility/files.h @@ -154,6 +154,14 @@ public: return *this; } + // This is for wrapping the actual reader for custom access where a managed FileReader won't work. + FileReaderInterface* GetInterface() + { + auto i = mReader; + mReader = nullptr; + return i; + } + ~FileReader() {