From d7b9439df5d09d121c55a15f2bc25c360deeebe0 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Thu, 17 May 2018 21:33:28 +0200 Subject: dynamic instrument API --- include/adlmidi.h | 104 ++++++++++++++++++++++++++++++++++++ src/adldata.hh | 10 ++++ src/adlmidi.cpp | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ src/adlmidi_bankmap.h | 2 + src/adlmidi_bankmap.tcc | 19 +++++++ src/adlmidi_load.cpp | 79 ++++++++++++++++++++++++++-- src/adlmidi_private.hpp | 4 +- 7 files changed, 349 insertions(+), 5 deletions(-) diff --git a/include/adlmidi.h b/include/adlmidi.h index 19dd6b2..347f4b4 100644 --- a/include/adlmidi.h +++ b/include/adlmidi.h @@ -111,6 +111,46 @@ extern int adl_getBanksCount(); /* Returns pointer to array of names of every bank */ extern const char *const *adl_getBankNames(); +/* Reference to dynamic bank */ +typedef struct ADL_Bank +{ + void *pointer[3]; +} ADL_Bank; + +/* Identifier of dynamic bank */ +typedef struct ADL_BankId +{ + ADL_UInt8 percussive, msb, lsb; +} ADL_BankId; + +/* Flags for dynamic bank access */ +enum ADL_BankAccessFlags +{ + ADLMIDI_Bank_Create = 1, /* create bank, allocating memory as needed */ + ADLMIDI_Bank_CreateRt = 1|2, /* create bank, never allocating memory */ +}; + +typedef struct ADL_Instrument ADL_Instrument; + +#if defined(ADLMIDI_UNSTABLE_API) +/* Preallocates a minimum number of bank slots. Returns the actual capacity. */ +extern int adl_reserveBanks(struct ADL_MIDIPlayer *device, unsigned banks); +/* Gets the bank designated by the identifier, optionally creating if it does not exist. */ +extern int adl_getBank(struct ADL_MIDIPlayer *device, const ADL_BankId *id, int flags, ADL_Bank *bank); +/* Gets the identifier of a bank. */ +extern int adl_getBankId(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id); +/* Removes a bank. */ +extern int adl_removeBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/* Gets the first bank. */ +extern int adl_getFirstBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/* Iterates to the next bank. */ +extern int adl_getNextBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/* Gets the nth intrument in the bank [0..127]. */ +extern int adl_getInstrument(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins); +/* Sets the nth intrument in the bank [0..127]. */ +extern int adl_setInstrument(struct ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins); +#endif // defined(ADLMIDI_UNSTABLE_API) + /*Sets number of 4-operator channels between all chips. By default, it is automatically re-calculating every bank change. If you want to specify custom number of four operator channels, @@ -337,6 +377,70 @@ extern void adl_setNoteHook(struct ADL_MIDIPlayer *device, ADL_NoteHook noteHook /* Set debug message hook */ extern void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_DebugMessageHook debugMessageHook, void *userData); + +/**Instrument structures**/ + +enum +{ + ADLMIDI_InstrumentVersion = 0, +}; + +typedef enum ADL_InstrumentFlags +{ + /* Is two-operator single-voice instrument (no flags) */ + ADLMIDI_Ins_2op = 0x00, + /* Is true four-operator instrument */ + ADLMIDI_Ins_4op = 0x01, + /* Is pseudo four-operator (two 2-operator voices) instrument */ + ADLMIDI_Ins_Pseudo4op = 0x02, + /* Is a blank instrument entry */ + ADLMIDI_Ins_IsBlank = 0x04, + /* Mask of the flags range */ + ADLMIDI_Ins_ALL_MASK = 0x07, +} ADL_InstrumentFlags; + +typedef struct ADL_Operator +{ + /* AM/Vib/Env/Ksr/FMult characteristics */ + ADL_UInt8 avekf_20; + /* Key Scale Level / Total level register data */ + ADL_UInt8 ksl_l_40; + /* Attack / Decay */ + ADL_UInt8 atdec_60; + /* Systain and Release register data */ + ADL_UInt8 susrel_80; + /* Wave form */ + ADL_UInt8 waveform_E0; +} ADL_Operator; + +typedef struct ADL_Instrument +{ + /* Version of the instrument object */ + int version; + /* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + ADL_SInt16 note_offset1; + /* MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */ + ADL_SInt16 note_offset2; + /* MIDI note velocity offset (taken from Apogee TMB format) */ + ADL_SInt8 midi_velocity_offset; + /* Second voice detune level (taken from DMX OP2) */ + ADL_SInt8 second_voice_detune; + /* Percussion MIDI base tone number at which this drum will be played */ + ADL_UInt8 percussion_key_number; + /* Enum ADL_InstrumentFlags */ + ADL_UInt8 inst_flags; + /* Feedback&Connection register for first and second operators */ + ADL_UInt8 fb_conn1_C0; + /* Feedback&Connection register for third and fourth operators */ + ADL_UInt8 fb_conn2_C0; + /* Operators register data */ + ADL_Operator operators[4]; + /* Millisecond delay of sounding while key is on */ + ADL_UInt16 delay_on_ms; + /* Millisecond delay of sounding after key off */ + ADL_UInt16 delay_off_ms; +} ADL_Instrument; + #ifdef __cplusplus } #endif diff --git a/src/adldata.hh b/src/adldata.hh index 65fd6bd..2ac469f 100644 --- a/src/adldata.hh +++ b/src/adldata.hh @@ -104,4 +104,14 @@ inline adlinsdata2::adlinsdata2(const adlinsdata &d) adl[1] = ::adl[d.adlno2]; } +/** + * @brief Convert external instrument to internal instrument + */ +void cvt_ADLI_to_FMIns(adlinsdata2 &dst, const struct ADL_Instrument &src); + +/** + * @brief Convert internal instrument to external instrument + */ +void cvt_FMIns_to_ADLI(struct ADL_Instrument &dst, const adlinsdata2 &src); + #endif //ADLDATA_H diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp index c6402e1..f9891a8 100644 --- a/src/adlmidi.cpp +++ b/src/adlmidi.cpp @@ -143,6 +143,142 @@ ADLMIDI_EXPORT const char *const *adl_getBankNames() return banknames; } +ADLMIDI_EXPORT int adl_reserveBanks(ADL_MIDIPlayer *device, unsigned banks) +{ + if(!device) + return -1; + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + OPL3::BankMap &map = play->opl.dynamic_banks; + map.reserve(banks); + return (int)map.capacity(); +} + +ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, int flags, ADL_Bank *bank) +{ + if(!device || !idp || !bank) + return -1; + + ADL_BankId id = *idp; + if(id.lsb > 127 || id.msb > 127 || id.percussive > 1) + return -1; + unsigned idnumber = (id.msb << 8) | id.lsb | (id.percussive ? OPL3::PercussionTag : 0); + + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + OPL3::BankMap &map = play->opl.dynamic_banks; + + OPL3::BankMap::iterator it; + if(!(flags & ADLMIDI_Bank_Create)) + { + it = map.find(idnumber); + if(it == map.end()) + return -1; + } + else + { + std::pair value; + value.first = idnumber; + memset(&value.second, 0, sizeof(value.second)); + for (unsigned i = 0; i < 128; ++i) + value.second.ins[i].flags = adlinsdata::Flag_NoSound; + + std::pair ir; + if(flags & ADLMIDI_Bank_CreateRt) + { + ir = map.insert(value, OPL3::BankMap::do_not_expand_t()); + if(ir.first == map.end()) + return -1; + } + else + ir = map.insert(value); + it = ir.first; + } + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getBankId(ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id) +{ + if(!device || !bank) + return -1; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + unsigned idnumber = it->first; + id->msb = (idnumber >> 8) & 127; + id->lsb = idnumber & 127; + id->percussive = (idnumber & OPL3::PercussionTag) ? 1 : 0; + return 0; +} + +ADLMIDI_EXPORT int adl_removeBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device || !bank) + return -1; + + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + OPL3::BankMap &map = play->opl.dynamic_banks; + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + size_t size = map.size(); + map.erase(it); + return (map.size() != size) ? 0 : -1; +} + +ADLMIDI_EXPORT int adl_getFirstBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device) + return -1; + + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + OPL3::BankMap &map = play->opl.dynamic_banks; + + OPL3::BankMap::iterator it = map.begin(); + if(it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getNextBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device) + return -1; + + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + OPL3::BankMap &map = play->opl.dynamic_banks; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + if(++it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getInstrument(ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return 1; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + cvt_FMIns_to_ADLI(*ins, it->second.ins[index]); + ins->version = 0; + return 0; +} + +ADLMIDI_EXPORT int adl_setInstrument(ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return 1; + + if(ins->version != 0) + return 1; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + cvt_ADLI_to_FMIns(it->second.ins[index], *ins); + return 0; +} + ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4) { if(!device) diff --git a/src/adlmidi_bankmap.h b/src/adlmidi_bankmap.h index 9b71d8f..178a1c1 100644 --- a/src/adlmidi_bankmap.h +++ b/src/adlmidi_bankmap.h @@ -85,6 +85,8 @@ public: iterator &operator++(); bool operator==(const iterator &o) const; bool operator!=(const iterator &o) const; + void to_ptrs(void *ptrs[3]); + static iterator from_ptrs(void *const ptrs[3]); private: Slot **buckets; Slot *slot; diff --git a/src/adlmidi_bankmap.tcc b/src/adlmidi_bankmap.tcc index 938192d..9303f0d 100644 --- a/src/adlmidi_bankmap.tcc +++ b/src/adlmidi_bankmap.tcc @@ -136,6 +136,25 @@ inline bool BasicBankMap::iterator::operator!=(const iterator &o) const return !operator==(o); } +template +void BasicBankMap::iterator::to_ptrs(void *ptrs[3]) +{ + ptrs[0] = buckets; + ptrs[1] = slot; + ptrs[2] = (void *)index; +} + +template +typename BasicBankMap::iterator +BasicBankMap::iterator::from_ptrs(void *const ptrs[3]) +{ + iterator it; + it.buckets = (Slot **)ptrs[0]; + it.slot = (Slot *)ptrs[1]; + it.index = (size_t)ptrs[2]; + return it; +} + template std::pair::iterator, bool> BasicBankMap::insert(const value_type &value) diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp index 8deb3b9..ac9d51a 100644 --- a/src/adlmidi_load.cpp +++ b/src/adlmidi_load.cpp @@ -72,7 +72,8 @@ bool MIDIplay::LoadBank(const void *data, size_t size) return LoadBank(file); } -static void cvt_WOPLI_to_FMIns(adlinsdata2 &ins, const WOPLInstrument &in) +template +static void cvt_generic_to_FMIns(adlinsdata2 &ins, const WOPLI &in) { ins.voice2_fine_tune = 0.0; int8_t voice2_fine_tune = in.second_voice_detune; @@ -83,7 +84,7 @@ static void cvt_WOPLI_to_FMIns(adlinsdata2 &ins, const WOPLInstrument &in) else if(voice2_fine_tune == -1) ins.voice2_fine_tune = -0.000025; else - ins.voice2_fine_tune = ((voice2_fine_tune * 15.625) / 1000.0); + ins.voice2_fine_tune = voice2_fine_tune * (15.625 / 1000.0); } ins.tone = in.percussion_key_number; @@ -114,7 +115,8 @@ static void cvt_WOPLI_to_FMIns(adlinsdata2 &ins, const WOPLInstrument &in) ins.adl[0].feedconn = in.fb_conn1_C0; if(!fourOps) ins.adl[1] = ins.adl[0]; - else { + else + { ins.adl[1].finetune = in.note_offset2; ins.adl[1].feedconn = in.fb_conn2_C0; } @@ -123,6 +125,75 @@ static void cvt_WOPLI_to_FMIns(adlinsdata2 &ins, const WOPLInstrument &in) ins.ms_sound_koff = in.delay_off_ms; } +template +static void cvt_FMIns_to_generic(WOPLI &ins, const adlinsdata2 &in) +{ + ins.second_voice_detune = 0; + double voice2_fine_tune = in.voice2_fine_tune; + if(voice2_fine_tune != 0) + { + if(voice2_fine_tune > 0 && voice2_fine_tune <= 0.000025) + ins.second_voice_detune = 1; + else if(voice2_fine_tune < 0 && voice2_fine_tune >= -0.000025) + ins.second_voice_detune = -1; + else + { + long value = lround(voice2_fine_tune * (1000.0 / 15.625)); + value = (value < -128) ? -128 : value; + value = (value > +127) ? +127 : value; + ins.second_voice_detune = static_cast(value); + } + } + + ins.percussion_key_number = in.tone; + bool fourOps = (in.flags & adlinsdata::Flag_Pseudo4op) || in.adl[0] != in.adl[1]; + ins.inst_flags = fourOps ? WOPL_Ins_4op : 0; + ins.inst_flags|= (in.flags & adlinsdata::Flag_Pseudo4op) ? WOPL_Ins_Pseudo4op : 0; + ins.inst_flags|= (in.flags & adlinsdata::Flag_NoSound) ? WOPL_Ins_IsBlank : 0; + + for(size_t op = 0, slt = 0; op < (fourOps ? 4 : 2); op++, slt++) + { + ins.operators[op].waveform_E0 = static_cast(in.adl[slt].carrier_E862 >> 24); + ins.operators[op].susrel_80 = static_cast(in.adl[slt].carrier_E862 >> 16); + ins.operators[op].atdec_60 = static_cast(in.adl[slt].carrier_E862 >> 8); + ins.operators[op].avekf_20 = static_cast(in.adl[slt].carrier_E862 >> 0); + ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40; + + op++; + ins.operators[op].waveform_E0 = static_cast(in.adl[slt].carrier_E862 >> 24); + ins.operators[op].susrel_80 = static_cast(in.adl[slt].carrier_E862 >> 16); + ins.operators[op].atdec_60 = static_cast(in.adl[slt].carrier_E862 >> 8); + ins.operators[op].avekf_20 = static_cast(in.adl[slt].carrier_E862 >> 0); + ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40; + } + + ins.note_offset1 = in.adl[0].finetune; + ins.fb_conn1_C0 = in.adl[0].feedconn; + if(!fourOps) + { + ins.operators[2] = ins.operators[0]; + ins.operators[3] = ins.operators[1]; + } + else + { + ins.note_offset2 = in.adl[1].finetune; + ins.fb_conn2_C0 = in.adl[1].feedconn; + } + + ins.delay_on_ms = in.ms_sound_kon; + ins.delay_off_ms = in.ms_sound_koff; +} + +void cvt_ADLI_to_FMIns(adlinsdata2 &ins, const ADL_Instrument &in) +{ + return cvt_generic_to_FMIns(ins, in); +} + +void cvt_FMIns_to_ADLI(ADL_Instrument &ins, const adlinsdata2 &in) +{ + cvt_FMIns_to_generic(ins, in); +} + bool MIDIplay::LoadBank(MIDIplay::fileReader &fr) { int err = 0; @@ -207,7 +278,7 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr) adlinsdata2 &ins = bank.ins[j]; std::memset(&ins, 0, sizeof(adlinsdata2)); WOPLInstrument &inIns = slots_src_ins[ss][i].ins[j]; - cvt_WOPLI_to_FMIns(ins, inIns); + cvt_generic_to_FMIns(ins, inIns); } } } diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index 8e3b158..63b4854 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -35,6 +35,9 @@ # endif #endif +// Require declarations of unstable API for extern "C" +#define ADLMIDI_UNSTABLE_API + #ifdef _WIN32 #define NOMINMAX #endif @@ -226,7 +229,6 @@ public: adlinsdata2 ins[128]; }; typedef BasicBankMap BankMap; -private: BankMap dynamic_banks; AdlBankSetup dynamic_bank_setup; public: -- cgit v1.2.3