mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-26 05:51:20 +00:00
Merge branch 'zmusic_dll'
# Conflicts: # libraries/zmusic/zmusic/configuration.cpp
This commit is contained in:
commit
4253fb6b4a
52 changed files with 1102 additions and 775 deletions
|
@ -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
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -703,7 +703,7 @@ Instruments::~Instruments()
|
|||
drumset[i] = NULL;
|
||||
}
|
||||
}
|
||||
if (sfreader != nullptr) delete sfreader;
|
||||
if (sfreader != nullptr) sfreader->close();
|
||||
sfreader = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1326,7 +1326,7 @@ void Instruments::load_patch(struct _mdi *mdi, unsigned short patchid)
|
|||
Instruments::~Instruments()
|
||||
{
|
||||
FreePatches();
|
||||
delete sfreader;
|
||||
sfreader->close();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "zmusic/zmusic_internal.h"
|
||||
#include "sndfile_decoder.h"
|
||||
#include "mpg123_decoder.h"
|
||||
|
||||
|
@ -79,3 +80,65 @@ std::vector<uint8_t> 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;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,10 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
|
||||
|
|
|
@ -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<MidiOutDeviceInternal> & AlsaSequencer::GetInternalDevices()
|
|||
return internalDevices;
|
||||
}
|
||||
|
||||
const std::vector<MidiOutDevice> & AlsaSequencer::GetDevices() {
|
||||
return externalDevices;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -65,7 +65,6 @@ public:
|
|||
}
|
||||
|
||||
int EnumerateDevices();
|
||||
const std::vector<MidiOutDevice> &GetDevices();
|
||||
const std::vector<MidiOutDeviceInternal> &GetInternalDevices();
|
||||
|
||||
snd_seq_t *handle = nullptr;
|
||||
|
@ -75,7 +74,6 @@ public:
|
|||
|
||||
private:
|
||||
std::vector<MidiOutDeviceInternal> internalDevices;
|
||||
std::vector<MidiOutDevice> externalDevices;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <mutex>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -35,8 +35,11 @@
|
|||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include "midisource.h"
|
||||
#include "zmusic/zmusic_internal.h"
|
||||
#include "zmusic/m_swap.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include "midisource.h"
|
||||
#include "zmusic/zmusic_internal.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
**
|
||||
*/
|
||||
|
||||
#include "zmusic/zmusic.h"
|
||||
#include "zmusic/zmusic_internal.h"
|
||||
#include "zmusic/musinfo.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
*/
|
||||
|
||||
#include "zmusic/musinfo.h"
|
||||
#include "zmusic/zmusic_internal.h"
|
||||
#include "streamsources/streamsource.h"
|
||||
|
||||
class StreamSong : public MusInfo
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
|
@ -31,13 +31,18 @@
|
|||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#include <mmsystem.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#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<MidiOutDevice> &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<MidiOutDevice> empty;
|
||||
return empty;
|
||||
#endif // HAVE_SYSTEM_MIDI && __linux__
|
||||
std::vector<MidiOutDevice> 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<class valtype>
|
||||
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<int>(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;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#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.
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <string>
|
||||
#include <mutex>
|
||||
#include "mididefs.h"
|
||||
#include "zmusic/zmusic_internal.h"
|
||||
|
||||
// The base music class. Everything is derived from this --------------------
|
||||
|
||||
|
|
|
@ -1,19 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../music_common/fileio.h"
|
||||
#include "zmusic_internal.h"
|
||||
#include <vector>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
#include <string>
|
||||
#include <zlib.h>
|
||||
#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<uint8_t> &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<uint8_t> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<uint8_t> 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;
|
||||
}
|
||||
|
|
|
@ -1,122 +1,200 @@
|
|||
#pragma once
|
||||
|
||||
#include "mididefs.h"
|
||||
#include "../../music_common/fileio.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
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<MidiOutDevice> &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
|
60
libraries/zmusic/zmusic/zmusic_internal.h
Normal file
60
libraries/zmusic/zmusic/zmusic_internal.h
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -35,8 +35,10 @@
|
|||
#ifndef __I_SOUND__
|
||||
#define __I_SOUND__
|
||||
|
||||
#include <vector>
|
||||
#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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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<SoundHandle,bool> 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<SoundDecoder> 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<SoundHandle,bool> 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<uint8_t> 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)
|
||||
{
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
#include <zlib.h>
|
||||
|
||||
#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<float>(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<FSoundFontReader*>(handle)->open_interface(fn);
|
||||
}
|
||||
|
||||
static void mus_sfaddpath(void *handle, const char* path)
|
||||
{
|
||||
reinterpret_cast<FSoundFontReader*>(handle)->AddPath(path);
|
||||
}
|
||||
|
||||
static void mus_sfclose(void* handle)
|
||||
{
|
||||
reinterpret_cast<FSoundFontReader*>(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<uint8_t> 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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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 <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#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 = "<Driver not installed>";
|
||||
else if (res == MMSYSERR_NOMEM)
|
||||
text = "<No memory for description>";
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
#include "vm.h"
|
||||
#include "i_system.h"
|
||||
#include "s_music.h"
|
||||
#include "mididevices/mididevice.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<FName, MidiDeviceSetting> 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
|
||||
|
|
|
@ -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<FileReaderInterface*>(zr->handle)->Gets(buff, n); };
|
||||
zcr->read = [](ZMusicCustomReader* zr, void* buff, int32_t size) { return reinterpret_cast<FileReaderInterface*>(zr->handle)->Read(buff, (long)size); };
|
||||
zcr->seek = [](ZMusicCustomReader* zr, long offset, int whence) { return reinterpret_cast<FileReaderInterface*>(zr->handle)->Seek(offset, whence); };
|
||||
zcr->tell = [](ZMusicCustomReader* zr) { return reinterpret_cast<FileReaderInterface*>(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<FileReaderInterface*>(zr->handle);
|
||||
delete zr;
|
||||
};
|
||||
return zcr;
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue