- made the ZMusic interface more DLL friendly: Reworked all functions not to throw exceptions across the library boundary and made a few definitions internal.

Not complete yet.
This commit is contained in:
Christoph Oelckers 2020-01-01 20:01:38 +01:00
parent c24f9b42ba
commit 7923d25cce
11 changed files with 222 additions and 139 deletions

View file

@ -57,7 +57,7 @@ void *safe_malloc(size_t count)
auto p = malloc(count);
if (!p)
{
std::terminate(); // we must abort, though...
abort(); // we must abort, though...
}
return p;
#endif

View file

@ -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)
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;
}
}

View file

@ -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"
@ -1004,9 +1005,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;
}
}

View file

@ -37,7 +37,7 @@
#include "oplsynth/oplio.h"
#include "../../libraries/dumb/include/dumb.h"
#include "zmusic.h"
#include "zmusic_internal.h"
#include "musinfo.h"
#include "midiconfig.h"
@ -54,25 +54,25 @@ struct Dummy
MiscConfig miscConfig;
Callbacks musicCallbacks;
void ZMusic_SetCallbacks(const Callbacks* cb)
DLL_EXPORT void ZMusic_SetCallbacks(const Callbacks* cb)
{
dumb_decode_vorbis = cb->DumbVorbisDecode;
musicCallbacks = *cb;
}
void ZMusic_SetGenMidi(const uint8_t* data)
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);

View file

@ -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"
@ -157,6 +157,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 +191,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 +209,7 @@ MusInfo *ZMusic_OpenSong (MusicIO::FileInterface *reader, EMidiDevice device, co
}
if (!source->isValid())
{
SetError("Invalid data in MIDI file");
delete source;
return nullptr;
}
@ -268,23 +271,26 @@ 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;
}
//==========================================================================
@ -293,7 +299,7 @@ MusInfo *ZMusic_OpenSong (MusicIO::FileInterface *reader, EMidiDevice device, co
//
//==========================================================================
MusInfo *ZMusic_OpenCDSong (int track, int id)
DLL_EXPORT MusInfo *ZMusic_OpenCDSong (int track, int id)
{
MusInfo *info = CD_OpenSong (track, id);
@ -301,6 +307,7 @@ MusInfo *ZMusic_OpenCDSong (int track, int id)
{
delete info;
info = nullptr;
SetError("Unable to open CD Audio");
}
return info;
@ -312,7 +319,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 +332,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,51 +353,51 @@ 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();
@ -394,13 +410,13 @@ SoundStreamInfo ZMusic_GetStreamInfo(MusInfo *song)
return 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);
@ -413,3 +429,28 @@ std::string ZMusic_GetStats(MusInfo *song)
std::lock_guard<std::mutex> lock(song->CritSec);
return song->GetStats();
}
static std::string staticErrorMessage;
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;
}

View file

@ -140,42 +140,63 @@ struct Callbacks
};
// 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);
// These exports are needed by the MIDI dumpers which need to remain on the client side.
class MIDISource; // abstract for the client
#ifndef ZMUSIC_INTERNAL
#define DLL_IMPORT // _declspec(dllimport)
typedef struct { int zm1; } *ZMusic_MidiSource;
typedef struct { int zm2; } *ZMusic_MusicStream;
#endif
extern "C"
{
DLL_IMPORT const char* ZMusic_GetLastError();
// 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);
// 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);
DLL_IMPORT ZMusic_MusicStream ZMusic_OpenSong(MusicIO::FileInterface* reader, 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);
SoundStreamInfo ZMusic_GetStreamInfo(ZMusic_MusicStream song);
// 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.
}
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);
MusInfo *ZMusic_OpenSong (MusicIO::FileInterface *reader, EMidiDevice device, const char *Args);
MusInfo *ZMusic_OpenCDSong (int track, int cdid = 0);
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);
// 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);
#if 0
std::string ZMusic_GetStats(ZMusic_MusicStream song);
bool ChangeMusicSetting(ZMusic::EIntConfigKey key, ZMusic_MusicStream song, int value, int* pRealValue = nullptr);
bool ChangeMusicSetting(ZMusic::EFloatConfigKey key, ZMusic_MusicStream song, float value, float* pRealValue = nullptr);
bool ChangeMusicSetting(ZMusic::EStringConfigKey key, ZMusic_MusicStream song, const char* value);
#else
// Cannot be done yet.
std::string ZMusic_GetStats(MusInfo* song);
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);
#endif

View file

@ -0,0 +1,11 @@
#pragma once
#define ZMUSIC_INTERNAL
#define DLL_EXPORT // __declspec(dllexport)
#define DLL_IMPORT
typedef class MIDISource *ZMusic_MidiSource;
typedef class MusInfo *ZMusic_MusicStream;
#include "zmusic.h"
void SetError(const char *text);