diff options
-rw-r--r-- | include/adlmidi.h | 4 | ||||
-rw-r--r-- | src/adldata.hh | 30 | ||||
-rw-r--r-- | src/adlmidi.cpp | 156 | ||||
-rw-r--r-- | src/adlmidi_bankmap.tcc | 2 | ||||
-rw-r--r-- | src/adlmidi_load.cpp | 61 | ||||
-rw-r--r-- | src/adlmidi_midiplay.cpp | 572 | ||||
-rw-r--r-- | src/adlmidi_opl3.cpp | 402 | ||||
-rw-r--r-- | src/adlmidi_private.cpp | 14 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 581 | ||||
-rw-r--r-- | src/adlmidi_sequencer.cpp | 10 | ||||
-rw-r--r-- | src/chips/opl_chip_base.tcc | 4 |
11 files changed, 1102 insertions, 734 deletions
diff --git a/include/adlmidi.h b/include/adlmidi.h index 8bb0bf1..9b7a6d9 100644 --- a/include/adlmidi.h +++ b/include/adlmidi.h @@ -97,7 +97,7 @@ struct ADL_MIDIPlayer #define adl_setNumCards adl_setNumChips /* Sets number of emulated chips (from 1 to 100). Emulation of multiple chips exchanges polyphony limits*/ -extern int adl_setNumChips(struct ADL_MIDIPlayer *device, int numCards); +extern int adl_setNumChips(struct ADL_MIDIPlayer *device, int numChips); /* Get current number of emulated chips */ extern int adl_getNumChips(struct ADL_MIDIPlayer *device); @@ -238,7 +238,7 @@ extern const char *adl_errorInfo(struct ADL_MIDIPlayer *device); extern struct ADL_MIDIPlayer *adl_init(long sample_rate); /*Set 4-bit device identifier*/ -extern int adl_setDeviceIdentifier(struct ADL_MIDIPlayer *device, unsigned id); +extern int adl_setDeviceIdentifier(struct ADL_MIDIPlayer *device, ADL_UInt8 id); /*Load MIDI file from File System*/ extern int adl_openFile(struct ADL_MIDIPlayer *device, const char *filePath); diff --git a/src/adldata.hh b/src/adldata.hh index 104db82..73c3d94 100644 --- a/src/adldata.hh +++ b/src/adldata.hh @@ -26,6 +26,7 @@ #include <string.h> #include <stdint.h> +#include <cstring> #pragma pack(push, 1) #define ADLDATA_BYTE_COMPARABLE(T) \ @@ -34,18 +35,17 @@ inline bool operator!=(const T &a, const T &b) \ { return !operator==(a, b); } -extern const struct adldata +struct adldata { uint32_t modulator_E862, carrier_E862; // See below uint8_t modulator_40, carrier_40; // KSL/attenuation settings uint8_t feedconn; // Feedback/connection bits for the channel int8_t finetune; -} adl[]; +}; ADLDATA_BYTE_COMPARABLE(struct adldata) -enum { adlDefaultNumber = 189 }; -extern const struct adlinsdata +struct adlinsdata { enum { Flag_Pseudo4op = 0x01, Flag_NoSound = 0x02, Flag_Real4op = 0x04 }; @@ -55,11 +55,8 @@ extern const struct adlinsdata uint16_t ms_sound_kon; // Number of milliseconds it produces sound; uint16_t ms_sound_koff; double voice2_fine_tune; -} adlins[]; +}; ADLDATA_BYTE_COMPARABLE(struct adlinsdata) -int maxAdlBanks(); -extern const unsigned short banks[][256]; -extern const char* const banknames[]; enum { adlNoteOnMaxTime = 40000 }; @@ -85,14 +82,23 @@ ADLDATA_BYTE_COMPARABLE(struct adlinsdata2) /** * @brief Bank global setup */ -extern const struct AdlBankSetup +struct AdlBankSetup { int volumeModel; bool deepTremolo; bool deepVibrato; bool adLibPercussions; bool scaleModulators; -} adlbanksetup[]; +}; + +#ifndef DISABLE_EMBEDDED_BANKS +int maxAdlBanks(); +extern const adldata adl[]; +extern const adlinsdata adlins[]; +extern const unsigned short banks[][256]; +extern const char* const banknames[]; +extern const AdlBankSetup adlbanksetup[]; +#endif /** * @brief Conversion of storage formats @@ -102,8 +108,12 @@ inline adlinsdata2::adlinsdata2(const adlinsdata &d) ms_sound_kon(d.ms_sound_kon), ms_sound_koff(d.ms_sound_koff), voice2_fine_tune(d.voice2_fine_tune) { +#ifdef DISABLE_EMBEDDED_BANKS + std::memset(adl, 0, sizeof(adldata) * 2); +#else adl[0] = ::adl[d.adlno1]; adl[1] = ::adl[d.adlno2]; +#endif } /** diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp index f627ce3..1027aef 100644 --- a/src/adlmidi.cpp +++ b/src/adlmidi.cpp @@ -24,11 +24,11 @@ #include "adlmidi_private.hpp" #ifdef ADLMIDI_HW_OPL -#define MaxCards 1 -#define MaxCards_STR "1" //Why not just "#MaxCards" ? Watcom fails to pass this with "syntax error" :-P +#define MaxChips 1 +#define MaxChips_STR "1" //Why not just "#MaxCards" ? Watcom fails to pass this with "syntax error" :-P #else -#define MaxCards 100 -#define MaxCards_STR "100" +#define MaxChips 100 +#define MaxChips_STR "100" #endif /* Unify MIDI player casting and interface between ADLMIDI and OPNMIDI */ @@ -72,7 +72,7 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate) return midi_device; } -ADLMIDI_EXPORT int adl_setDeviceIdentifier(ADL_MIDIPlayer *device, unsigned id) +ADLMIDI_EXPORT int adl_setDeviceIdentifier(ADL_MIDIPlayer *device, ADL_UInt8 id) { if(!device || id > 0x0f) return -1; @@ -83,25 +83,25 @@ ADLMIDI_EXPORT int adl_setDeviceIdentifier(ADL_MIDIPlayer *device, unsigned id) return 0; } -ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numCards) +ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numChips) { if(device == NULL) return -2; MidiPlayer *play = GET_MIDI_PLAYER(device); #ifdef ADLMIDI_HW_OPL - (void)numCards; - play->m_setup.NumCards = 1; + ADL_UNUSED(numChips); + play->m_setup.numChips = 1; #else - play->m_setup.NumCards = static_cast<unsigned int>(numCards); + play->m_setup.numChips = static_cast<unsigned int>(numChips); #endif - if(play->m_setup.NumCards < 1 || play->m_setup.NumCards > MaxCards) + if(play->m_setup.numChips < 1 || play->m_setup.numChips > MaxChips) { - play->setErrorString("number of chips may only be 1.." MaxCards_STR ".\n"); + play->setErrorString("number of chips may only be 1.." MaxChips_STR ".\n"); return -1; } - play->opl.NumCards = play->m_setup.NumCards; + play->m_synth.m_numChips = play->m_setup.numChips; adl_reset(device); return adlRefreshNumCards(device); @@ -113,16 +113,18 @@ ADLMIDI_EXPORT int adl_getNumChips(struct ADL_MIDIPlayer *device) return -2; MidiPlayer *play = GET_MIDI_PLAYER(device); if(play) - return (int)play->m_setup.NumCards; + return (int)play->m_setup.numChips; return -2; } ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) { #ifdef DISABLE_EMBEDDED_BANKS - ADL_UNUSED(device); ADL_UNUSED(bank); - ADLMIDI_ErrorString = "This build of libADLMIDI has no embedded banks. Please load bank by using of adl_openBankFile() or adl_openBankData() functions instead of adl_setBank()"; + MidiPlayer *play = GET_MIDI_PLAYER(device); + play->setErrorString("This build of libADLMIDI has no embedded banks. " + "Please load bank by using of adl_openBankFile() or " + "adl_openBankData() functions instead of adl_setBank()"); return -1; #else const uint32_t NumBanks = static_cast<uint32_t>(maxAdlBanks()); @@ -140,8 +142,8 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) return -1; } - play->m_setup.AdlBank = static_cast<uint32_t>(bankno); - play->opl.setEmbeddedBank(play->m_setup.AdlBank); + play->m_setup.bankId = static_cast<uint32_t>(bankno); + play->m_synth.setEmbeddedBank(play->m_setup.bankId); play->applySetup(); return adlRefreshNumCards(device); @@ -150,12 +152,20 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) ADLMIDI_EXPORT int adl_getBanksCount() { +#ifndef DISABLE_EMBEDDED_BANKS return maxAdlBanks(); +#else + return 0; +#endif } ADLMIDI_EXPORT const char *const *adl_getBankNames() { +#ifndef DISABLE_EMBEDDED_BANKS return banknames; +#else + return NULL; +#endif } ADLMIDI_EXPORT int adl_reserveBanks(ADL_MIDIPlayer *device, unsigned banks) @@ -163,7 +173,7 @@ ADLMIDI_EXPORT int adl_reserveBanks(ADL_MIDIPlayer *device, unsigned banks) if(!device) return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); - OPL3::BankMap &map = play->opl.dynamic_banks; + OPL3::BankMap &map = play->m_synth.m_insBanks; map.reserve(banks); return (int)map.capacity(); } @@ -176,10 +186,10 @@ ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, in 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); + uint16_t idnumber = uint16_t((id.msb << 8) | id.lsb | (id.percussive ? OPL3::PercussionTag : 0)); MidiPlayer *play = GET_MIDI_PLAYER(device); - OPL3::BankMap &map = play->opl.dynamic_banks; + OPL3::BankMap &map = play->m_synth.m_insBanks; OPL3::BankMap::iterator it; if(!(flags & ADLMIDI_Bank_Create)) @@ -231,7 +241,7 @@ ADLMIDI_EXPORT int adl_removeBank(ADL_MIDIPlayer *device, ADL_Bank *bank) return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); - OPL3::BankMap &map = play->opl.dynamic_banks; + OPL3::BankMap &map = play->m_synth.m_insBanks; OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); size_t size = map.size(); map.erase(it); @@ -244,7 +254,7 @@ ADLMIDI_EXPORT int adl_getFirstBank(ADL_MIDIPlayer *device, ADL_Bank *bank) return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); - OPL3::BankMap &map = play->opl.dynamic_banks; + OPL3::BankMap &map = play->m_synth.m_insBanks; OPL3::BankMap::iterator it = map.begin(); if(it == map.end()) @@ -260,7 +270,7 @@ ADLMIDI_EXPORT int adl_getNextBank(ADL_MIDIPlayer *device, ADL_Bank *bank) return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); - OPL3::BankMap &map = play->opl.dynamic_banks; + OPL3::BankMap &map = play->m_synth.m_insBanks; OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); if(++it == map.end()) @@ -299,16 +309,16 @@ ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4) if(!device) return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); - if((unsigned int)ops4 > 6 * play->m_setup.NumCards) + if((unsigned int)ops4 > 6 * play->m_setup.numChips) { char errBuff[250]; - snprintf(errBuff, 250, "number of four-op channels may only be 0..%u when %u OPL3 cards are used.\n", (6 * (play->m_setup.NumCards)), play->m_setup.NumCards); + snprintf(errBuff, 250, "number of four-op channels may only be 0..%u when %u OPL3 cards are used.\n", (6 * (play->m_setup.numChips)), play->m_setup.numChips); play->setErrorString(errBuff); return -1; } - play->m_setup.NumFourOps = static_cast<unsigned int>(ops4); - play->opl.NumFourOps = play->m_setup.NumFourOps; + play->m_setup.numFourOps = static_cast<unsigned int>(ops4); + play->m_synth.m_numFourOps = play->m_setup.numFourOps; return 0; //adlRefreshNumCards(device); } @@ -319,7 +329,7 @@ ADLMIDI_EXPORT int adl_getNumFourOpsChn(struct ADL_MIDIPlayer *device) return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); if(play) - return (int)play->m_setup.NumFourOps; + return (int)play->m_setup.numFourOps; return -1; } @@ -327,33 +337,33 @@ ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod) { if(!device) return; MidiPlayer *play = GET_MIDI_PLAYER(device); - play->m_setup.AdlPercussionMode = percmod; - play->opl.AdlPercussionMode = play->m_setup.AdlPercussionMode < 0 ? - play->opl.dynamic_bank_setup.adLibPercussions : - (play->m_setup.AdlPercussionMode != 0); - play->opl.updateFlags(); + play->m_setup.rhythmMode = percmod; + play->m_synth.m_rhythmMode = play->m_setup.rhythmMode < 0 ? + (play->m_synth.m_insBankSetup.adLibPercussions) : + (play->m_setup.rhythmMode != 0); + play->m_synth.updateChannelCategories(); } ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro) { if(!device) return; MidiPlayer *play = GET_MIDI_PLAYER(device); - play->m_setup.HighVibratoMode = hvibro; - play->opl.HighVibratoMode = play->m_setup.HighVibratoMode < 0 ? - play->opl.dynamic_bank_setup.deepVibrato : - (play->m_setup.HighVibratoMode != 0); - play->opl.updateDeepFlags(); + play->m_setup.deepVibratoMode = hvibro; + play->m_synth.m_deepVibratoMode = play->m_setup.deepVibratoMode < 0 ? + play->m_synth.m_insBankSetup.deepVibrato : + (play->m_setup.deepVibratoMode != 0); + play->m_synth.commitDeepFlags(); } ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo) { if(!device) return; MidiPlayer *play = GET_MIDI_PLAYER(device); - play->m_setup.HighTremoloMode = htremo; - play->opl.HighTremoloMode = play->m_setup.HighTremoloMode < 0 ? - play->opl.dynamic_bank_setup.deepTremolo : - (play->m_setup.HighTremoloMode != 0); - play->opl.updateDeepFlags(); + play->m_setup.deepTremoloMode = htremo; + play->m_synth.m_deepTremoloMode = play->m_setup.deepTremoloMode < 0 ? + play->m_synth.m_insBankSetup.deepTremolo : + (play->m_setup.deepTremoloMode != 0); + play->m_synth.commitDeepFlags(); } ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod) @@ -363,10 +373,10 @@ ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod) MidiPlayer *play = GET_MIDI_PLAYER(device); if(!play) return; - play->m_setup.ScaleModulators = smod; - play->opl.ScaleModulators = play->m_setup.ScaleModulators < 0 ? - play->opl.dynamic_bank_setup.scaleModulators : - (play->m_setup.ScaleModulators != 0); + play->m_setup.scaleModulators = smod; + play->m_synth.m_scaleModulators = play->m_setup.scaleModulators < 0 ? + play->m_synth.m_insBankSetup.scaleModulators : + (play->m_setup.scaleModulators != 0); } ADLMIDI_EXPORT void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness) @@ -401,11 +411,11 @@ ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int MidiPlayer *play = GET_MIDI_PLAYER(device); if(!play) return; - play->m_setup.LogarithmicVolumes = (logvol != 0); - if(play->m_setup.LogarithmicVolumes) - play->opl.ChangeVolumeRangesModel(ADLMIDI_VolumeModel_NativeOPL3); + play->m_setup.logarithmicVolumes = (logvol != 0); + if(play->m_setup.logarithmicVolumes) + play->m_synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); else - play->opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(play->opl.m_volumeScale)); + play->m_synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(play->m_synth.m_volumeScale)); } ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel) @@ -415,11 +425,11 @@ ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int v MidiPlayer *play = GET_MIDI_PLAYER(device); if(!play) return; - play->m_setup.VolumeModel = volumeModel; - if(play->m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model - play->opl.m_volumeScale = (OPL3::VolumesScale)play->opl.dynamic_bank_setup.volumeModel; + play->m_setup.volumeScaleModel = volumeModel; + if(play->m_setup.volumeScaleModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model + play->m_synth.m_volumeScale = (OPL3::VolumesScale)play->m_synth.m_insBankSetup.volumeModel; else - play->opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(volumeModel)); + play->m_synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(volumeModel)); } ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath) @@ -532,8 +542,8 @@ ADLMIDI_EXPORT const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device) { #ifndef ADLMIDI_HW_OPL MidiPlayer *play = GET_MIDI_PLAYER(device); - if(play && !play->opl.cardsOP2.empty()) - return play->opl.cardsOP2[0]->emulatorName(); + if(play && !play->m_synth.m_chips.empty()) + return play->m_synth.m_chips[0]->emulatorName(); #else return "Hardware OPL3 chip on 0x330"; #endif @@ -629,10 +639,10 @@ ADLMIDI_EXPORT void adl_reset(struct ADL_MIDIPlayer *device) return; MidiPlayer *play = GET_MIDI_PLAYER(device); play->m_setup.tick_skip_samples_delay = 0; - play->opl.runAtPcmRate = play->m_setup.runAtPcmRate; - play->opl.Reset(play->m_setup.emulator, play->m_setup.PCM_RATE, play); - play->ch.clear(); - play->ch.resize((size_t)play->opl.NumChannels); + play->m_synth.m_runAtPcmRate = play->m_setup.runAtPcmRate; + play->m_synth.reset(play->m_setup.emulator, play->m_setup.PCM_RATE, play); + play->m_chipChannels.clear(); + play->m_chipChannels.resize((size_t)play->m_synth.m_numChannels); } ADLMIDI_EXPORT double adl_totalTimeLength(struct ADL_MIDIPlayer *device) @@ -1129,9 +1139,9 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, else { setup.delay -= eat_delay; - setup.carry += setup.PCM_RATE * eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; n_periodCountStereo = static_cast<ssize_t>(setup.carry); - setup.carry -= n_periodCountStereo; + setup.carry -= double(n_periodCountStereo); } //if(setup.SkipForward > 0) @@ -1153,18 +1163,18 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int32_t *out_buf = player->outBuf; + int32_t *out_buf = player->m_outBuf; std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0])); - unsigned int chips = player->opl.NumCards; + unsigned int chips = player->m_synth.m_numChips; if(chips == 1) { - player->opl.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); } else if(n_periodCountStereo > 0) { /* Generate data from every chip and mix result */ for(size_t card = 0; card < chips; ++card) - player->opl.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); } /* Process it */ @@ -1228,9 +1238,9 @@ ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleC {//... const double eat_delay = delay < setup.maxdelay ? delay : setup.maxdelay; delay -= eat_delay; - setup.carry += setup.PCM_RATE * eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; n_periodCountStereo = static_cast<ssize_t>(setup.carry); - setup.carry -= n_periodCountStereo; + setup.carry -= double(n_periodCountStereo); { ssize_t leftSamples = left / 2; @@ -1242,16 +1252,16 @@ ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleC ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int32_t *out_buf = player->outBuf; + int32_t *out_buf = player->m_outBuf; std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0])); - unsigned int chips = player->opl.NumCards; + unsigned int chips = player->m_synth.m_numChips; if(chips == 1) - player->opl.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); else if(n_periodCountStereo > 0) { /* Generate data from every chip and mix result */ for(unsigned card = 0; card < chips; ++card) - player->opl.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); } /* Process it */ if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) diff --git a/src/adlmidi_bankmap.tcc b/src/adlmidi_bankmap.tcc index 76e7001..90d8894 100644 --- a/src/adlmidi_bankmap.tcc +++ b/src/adlmidi_bankmap.tcc @@ -37,7 +37,7 @@ template <class T> inline size_t BasicBankMap<T>::hash(key_type key) { // disregard the 0 high bit in LSB - key = (key & 127) | ((key >> 8) << 7); + key = key_type(key & 127) | key_type((key >> 8) << 7); // take low part as hash value return key & (hash_buckets - 1); } diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp index 9487188..43b74f2 100644 --- a/src/adlmidi_load.cpp +++ b/src/adlmidi_load.cpp @@ -215,29 +215,30 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr) } } - opl.dynamic_bank_setup.adLibPercussions = false; - opl.dynamic_bank_setup.scaleModulators = false; - opl.dynamic_bank_setup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0; - opl.dynamic_bank_setup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0; - opl.dynamic_bank_setup.volumeModel = wopl->volume_model; - m_setup.HighTremoloMode = -1; - m_setup.HighVibratoMode = -1; - m_setup.VolumeModel = ADLMIDI_VolumeModel_AUTO; + m_synth.m_insBankSetup.adLibPercussions = false; + m_synth.m_insBankSetup.scaleModulators = false; + m_synth.m_insBankSetup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0; + m_synth.m_insBankSetup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0; + m_synth.m_insBankSetup.volumeModel = wopl->volume_model; + m_setup.deepTremoloMode = -1; + m_setup.deepVibratoMode = -1; + m_setup.volumeScaleModel = ADLMIDI_VolumeModel_AUTO; - opl.setEmbeddedBank(m_setup.AdlBank); + m_synth.setEmbeddedBank(m_setup.bankId); uint16_t slots_counts[2] = {wopl->banks_count_melodic, wopl->banks_count_percussion}; WOPLBank *slots_src_ins[2] = { wopl->banks_melodic, wopl->banks_percussive }; - for(unsigned ss = 0; ss < 2; ss++) + for(size_t ss = 0; ss < 2; ss++) { - for(unsigned i = 0; i < slots_counts[ss]; i++) + for(size_t i = 0; i < slots_counts[ss]; i++) { - unsigned bankno = + uint16_t bankno = uint16_t( (slots_src_ins[ss][i].bank_midi_msb * 256) + slots_src_ins[ss][i].bank_midi_lsb + - (ss ? OPL3::PercussionTag : 0); - OPL3::Bank &bank = opl.dynamic_banks[bankno]; + (ss ? OPL3::PercussionTag : 0) + ); + OPL3::Bank &bank = m_synth.m_insBanks[bankno]; for(int j = 0; j < 128; j++) { adlinsdata2 &ins = bank.ins[j]; @@ -248,7 +249,7 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr) } } - opl.AdlBank = ~0u; // Use dynamic banks! + m_synth.m_embeddedBank = ~0u; // Use dynamic banks! //Percussion offset is count of instruments multipled to count of melodic banks applySetup(); @@ -262,7 +263,7 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr) bool MIDIplay::LoadMIDI_pre() { #ifdef DISABLE_EMBEDDED_BANKS - if((opl.AdlBank != ~0u) || opl.dynamic_banks.empty()) + if((m_synth.m_embeddedBank != ~0u) || m_synth.m_insBanks.empty()) { errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!"; return false; @@ -284,14 +285,14 @@ bool MIDIplay::LoadMIDI_post() if(format == MidiSequencer::Format_CMF) { const std::vector<MidiSequencer::CmfInstrument> &instruments = m_sequencer.getRawCmfInstruments(); - opl.dynamic_banks.clear();//Clean up old banks + m_synth.m_insBanks.clear();//Clean up old banks uint16_t ins_count = static_cast<uint16_t>(instruments.size()); for(uint16_t i = 0; i < ins_count; ++i) { const uint8_t *InsData = instruments[i].data; uint16_t bank = i / 256; - bank = (bank & 127) + ((bank >> 7) << 8); + bank = uint16_t((bank & 127) + ((bank >> 7) << 8)); if(bank > 127 + (127 << 8)) break; bank += (i % 256 < 128) ? 0 : OPL3::PercussionTag; @@ -299,7 +300,7 @@ bool MIDIplay::LoadMIDI_post() /*std::printf("Ins %3u: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", i, InsData[0],InsData[1],InsData[2],InsData[3], InsData[4],InsData[5],InsData[6],InsData[7], InsData[8],InsData[9],InsData[10],InsData[11], InsData[12],InsData[13],InsData[14],InsData[15]);*/ - adlinsdata2 &adlins = opl.dynamic_banks[bank].ins[i % 128]; + adlinsdata2 &adlins = m_synth.m_insBanks[bank].ins[i % 128]; adldata adl; adl.modulator_E862 = ((static_cast<uint32_t>(InsData[8] & 0x07) << 24) & 0xFF000000) //WaveForm @@ -324,29 +325,29 @@ bool MIDIplay::LoadMIDI_post() adlins.voice2_fine_tune = 0.0; } - opl.AdlBank = ~0u; // Ignore AdlBank number, use dynamic banks instead + m_synth.m_embeddedBank = ~0u; // Ignore AdlBank number, use dynamic banks instead //std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo); - opl.AdlPercussionMode = true; - opl.m_musicMode = OPL3::MODE_CMF; - opl.m_volumeScale = OPL3::VOLUME_NATIVE; + m_synth.m_rhythmMode = true; + m_synth.m_musicMode = OPL3::MODE_CMF; + m_synth.m_volumeScale = OPL3::VOLUME_NATIVE; } else if(format == MidiSequencer::Format_RSXX) { //opl.CartoonersVolumes = true; - opl.m_musicMode = OPL3::MODE_RSXX; - opl.m_volumeScale = OPL3::VOLUME_NATIVE; + m_synth.m_musicMode = OPL3::MODE_RSXX; + m_synth.m_volumeScale = OPL3::VOLUME_NATIVE; } else if(format == MidiSequencer::Format_IMF) { //std::fprintf(stderr, "Done reading IMF file\n"); - opl.NumFourOps = 0; //Don't use 4-operator channels for IMF playing! - opl.m_musicMode = OPL3::MODE_IMF; + m_synth.m_numFourOps = 0; //Don't use 4-operator channels for IMF playing! + m_synth.m_musicMode = OPL3::MODE_IMF; } - opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip + m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously) - ch.clear(); - ch.resize(opl.NumChannels); + m_chipChannels.clear(); + m_chipChannels.resize(m_synth.m_numChannels); return true; } diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 190a521..ecf2732 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -100,7 +100,7 @@ inline bool isXgPercChannel(uint8_t msb, uint8_t lsb) return (msb == 0x7E || msb == 0x7F) && (lsb == 0); } -void MIDIplay::AdlChannel::AddAge(int64_t ms) +void MIDIplay::AdlChannel::addAge(int64_t ms) { const int64_t neg = static_cast<int64_t>(-0x1FFFFFFFl); if(users_empty()) @@ -118,7 +118,7 @@ void MIDIplay::AdlChannel::AddAge(int64_t ms) } MIDIplay::MIDIplay(unsigned long sampleRate): - cmf_percussion_mode(false), + m_cmfPercussionMode(false), m_masterVolume(MasterVolumeDefault), m_sysExDeviceId(0), m_synthMode(Mode_XG), @@ -127,7 +127,7 @@ MIDIplay::MIDIplay(unsigned long sampleRate): , m_audioTickCounter(0) #endif { - devices.clear(); + m_midiDevices.clear(); m_setup.emulator = ADLMIDI_EMU_NUKED; m_setup.runAtPcmRate = false; @@ -136,16 +136,16 @@ MIDIplay::MIDIplay(unsigned long sampleRate): m_setup.mindelay = 1.0 / (double)m_setup.PCM_RATE; m_setup.maxdelay = 512.0 / (double)m_setup.PCM_RATE; - m_setup.AdlBank = 0; - m_setup.NumFourOps = 7; - m_setup.NumCards = 2; - m_setup.HighTremoloMode = -1; - m_setup.HighVibratoMode = -1; - m_setup.AdlPercussionMode = -1; - m_setup.LogarithmicVolumes = false; - m_setup.VolumeModel = ADLMIDI_VolumeModel_AUTO; + m_setup.bankId = 0; + m_setup.numFourOps = 7; + m_setup.numChips = 2; + m_setup.deepTremoloMode = -1; + m_setup.deepVibratoMode = -1; + m_setup.rhythmMode = -1; + m_setup.logarithmicVolumes = false; + m_setup.volumeScaleModel = ADLMIDI_VolumeModel_AUTO; //m_setup.SkipForward = 0; - m_setup.ScaleModulators = -1; + m_setup.scaleModulators = -1; m_setup.fullRangeBrightnessCC74 = false; m_setup.delay = 0.0; m_setup.carry = 0.0; @@ -155,49 +155,51 @@ MIDIplay::MIDIplay(unsigned long sampleRate): initSequencerInterface(); #endif applySetup(); - ChooseDevice("none"); + chooseDevice("none"); realTime_ResetState(); } void MIDIplay::applySetup() { - opl.m_musicMode = OPL3::MODE_MIDI; + m_synth.m_musicMode = OPL3::MODE_MIDI; m_setup.tick_skip_samples_delay = 0; - opl.runAtPcmRate = m_setup.runAtPcmRate; - - if(opl.AdlBank != ~0u) - opl.dynamic_bank_setup = adlbanksetup[m_setup.AdlBank]; - - opl.HighTremoloMode = m_setup.HighTremoloMode < 0 ? - opl.dynamic_bank_setup.deepTremolo : - (m_setup.HighTremoloMode != 0); - opl.HighVibratoMode = m_setup.HighVibratoMode < 0 ? - opl.dynamic_bank_setup.deepVibrato : - (m_setup.HighVibratoMode != 0); - opl.AdlPercussionMode = m_setup.AdlPercussionMode < 0 ? - opl.dynamic_bank_setup.adLibPercussions : - (m_setup.AdlPercussionMode != 0); - opl.ScaleModulators = m_setup.ScaleModulators < 0 ? - opl.dynamic_bank_setup.scaleModulators : - (m_setup.ScaleModulators != 0); - - if(m_setup.LogarithmicVolumes) - opl.ChangeVolumeRangesModel(ADLMIDI_VolumeModel_NativeOPL3); + m_synth.m_runAtPcmRate = m_setup.runAtPcmRate; + +#ifndef DISABLE_EMBEDDED_BANKS + if(m_synth.m_embeddedBank != ~0u) + m_synth.m_insBankSetup = adlbanksetup[m_setup.bankId]; +#endif + + m_synth.m_deepTremoloMode = m_setup.deepTremoloMode < 0 ? + m_synth.m_insBankSetup.deepTremolo : + (m_setup.deepTremoloMode != 0); + m_synth.m_deepVibratoMode = m_setup.deepVibratoMode < 0 ? + m_synth.m_insBankSetup.deepVibrato : + (m_setup.deepVibratoMode != 0); + m_synth.m_rhythmMode = m_setup.rhythmMode < 0 ? + m_synth.m_insBankSetup.adLibPercussions : + (m_setup.rhythmMode != 0); + m_synth.m_scaleModulators = m_setup.scaleModulators < 0 ? + m_synth.m_insBankSetup.scaleModulators : + (m_setup.scaleModulators != 0); + + if(m_setup.logarithmicVolumes) + m_synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); else - opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(m_setup.VolumeModel)); + m_synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(m_setup.volumeScaleModel)); - if(m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model - opl.m_volumeScale = (OPL3::VolumesScale)opl.dynamic_bank_setup.volumeModel; + if(m_setup.volumeScaleModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model + m_synth.m_volumeScale = (OPL3::VolumesScale)m_synth.m_insBankSetup.volumeModel; - opl.NumCards = m_setup.NumCards; - opl.NumFourOps = m_setup.NumFourOps; - cmf_percussion_mode = false; + m_synth.m_numChips = m_setup.numChips; + m_synth.m_numFourOps = m_setup.numFourOps; + m_cmfPercussionMode = false; - opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this); - ch.clear(); - ch.resize(opl.NumChannels); + m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); + m_chipChannels.clear(); + m_chipChannels.resize(m_synth.m_numChannels); // Reset the arpeggio counter m_arpeggioCounter = 0; @@ -205,30 +207,30 @@ void MIDIplay::applySetup() void MIDIplay::TickIterators(double s) { - for(uint16_t c = 0; c < opl.NumChannels; ++c) - ch[c].AddAge(static_cast<int64_t>(s * 1000.0)); - UpdateVibrato(s); - UpdateArpeggio(s); + for(uint16_t c = 0; c < m_synth.m_numChannels; ++c) + m_chipChannels[c].addAge(static_cast<int64_t>(s * 1000.0)); + updateVibrato(s); + updateArpeggio(s); #if !defined(ADLMIDI_AUDIO_TICK_HANDLER) - UpdateGlide(s); + updateGlide(s); #endif } void MIDIplay::realTime_ResetState() { - for(size_t ch = 0; ch < Ch.size(); ch++) + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) { - MIDIchannel &chan = Ch[ch]; + MIDIchannel &chan = m_midiChannels[ch]; chan.resetAllControllers(); - chan.volume = (opl.m_musicMode == OPL3::MODE_RSXX) ? 127 : 100; + chan.volume = (m_synth.m_musicMode == OPL3::MODE_RSXX) ? 127 : 100; chan.vibpos = 0.0; chan.lastlrpn = 0; chan.lastmrpn = 0; chan.nrpn = false; if((m_synthMode & Mode_GS) != 0)// Reset custom drum channels on GS chan.is_xg_percussion = false; - NoteUpdate_All(uint16_t(ch), Upd_All); - NoteUpdate_All(uint16_t(ch), Upd_Off); + noteUpdateAll(uint16_t(ch), Upd_All); + noteUpdateAll(uint16_t(ch), Upd_Off); } m_masterVolume = MasterVolumeDefault; } @@ -238,20 +240,20 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(note >= 128) note = 127; - if((opl.m_musicMode == OPL3::MODE_RSXX) && (velocity != 0)) + if((m_synth.m_musicMode == OPL3::MODE_RSXX) && (velocity != 0)) { // Check if this is just a note after-touch - MIDIchannel::activenoteiterator i = Ch[channel].activenotes_find(note); + MIDIchannel::activenoteiterator i = m_midiChannels[channel].activenotes_find(note); if(i) { i->vol = velocity; - NoteUpdate(channel, i, Upd_Volume); + noteUpdate(channel, i, Upd_Volume); return false; } } channel = channel % 16; - NoteOff(channel, note); + noteOff(channel, note); // On Note on, Keyoff the note first, just in case keyoff // was omitted; this fixes Dance of sugar-plum fairy // by Microsoft. Now that we've done a Keyoff, @@ -260,7 +262,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(velocity == 0) return false; - MIDIchannel &midiChan = Ch[channel]; + MIDIchannel &midiChan = m_midiChannels[channel]; size_t midiins = midiChan.patch; bool isPercussion = (channel % 16 == 9) || midiChan.is_xg_percussion; @@ -299,14 +301,14 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(isPercussion) bank += OPL3::PercussionTag; - const adlinsdata2 *ains = &OPL3::emptyInstrument; + const adlinsdata2 *ains = &OPL3::m_emptyInstrument; //Set bank bank const OPL3::Bank *bnk = NULL; if((bank & ~(uint16_t)OPL3::PercussionTag) > 0) { - OPL3::BankMap::iterator b = opl.dynamic_banks.find(bank); - if(b != opl.dynamic_banks.end()) + OPL3::BankMap::iterator b = m_synth.m_insBanks.find(bank); + if(b != m_synth.m_insBanks.end()) bnk = &b->second; if(bnk) @@ -324,8 +326,8 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) //Or fall back to first bank if(ains->flags & adlinsdata::Flag_NoSound) { - OPL3::BankMap::iterator b = opl.dynamic_banks.find(bank & OPL3::PercussionTag); - if(b != opl.dynamic_banks.end()) + OPL3::BankMap::iterator b = m_synth.m_insBanks.find(bank & OPL3::PercussionTag); + if(b != m_synth.m_insBanks.end()) bnk = &b->second; if(bnk) @@ -340,7 +342,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(hooks.onDebugMessage) { if(caugh_missing_instruments.insert(static_cast<uint8_t>(midiins)).second) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caught a blank instrument %i (offset %i) in the MIDI bank %u", channel, Ch[channel].patch, midiins, bank); + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caught a blank instrument %i (offset %i) in the MIDI bank %u", channel, m_midiChannels[channel].patch, midiins, bank); } bank = 0; midiins = midiChan.patch; @@ -376,7 +378,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) voices[1].pseudo4op = pseudo_4op; #endif /* __WATCOMC__ */ - if((opl.AdlPercussionMode == 1) && PercussionMap[midiins & 0xFF]) + if((m_synth.m_rhythmMode == 1) && PercussionMap[midiins & 0xFF]) voices[1] = voices[0];//i[1] = i[0]; if(hooks.onDebugMessage) @@ -402,7 +404,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) int32_t c = -1; int32_t bs = -0x7FFFFFFFl; - for(size_t a = 0; a < (size_t)opl.NumChannels; ++a) + for(size_t a = 0; a < (size_t)m_synth.m_numChannels; ++a) { if(ccount == 1 && static_cast<int32_t>(a) == adlchannel[0]) continue; // ^ Don't use the same channel for primary&secondary @@ -412,15 +414,15 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) // Only use regular channels uint8_t expected_mode = 0; - if(opl.AdlPercussionMode == 1) + if(m_synth.m_rhythmMode == 1) { - if(cmf_percussion_mode) + if(m_cmfPercussionMode) expected_mode = channel < 11 ? 0 : (3 + channel - 11); // CMF else expected_mode = PercussionMap[midiins & 0xFF]; } - if(opl.four_op_category[a] != expected_mode) + if(m_synth.m_channelCategory[a] != expected_mode) continue; } else @@ -428,7 +430,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(ccount == 0) { // Only use four-op master channels - if(opl.four_op_category[a] != 1) + if(m_synth.m_channelCategory[a] != 1) continue; } else @@ -439,7 +441,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) } } - int64_t s = CalculateAdlChannelGoodness(a, voices[ccount], channel); + int64_t s = calculateAdlChannelGoodness(a, voices[ccount]); if(s > bs) { bs = (int32_t)s; // Best candidate wins @@ -456,7 +458,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) continue; // Could not play this note. Ignore it. } - PrepareAdlChannelForNewNote(static_cast<size_t>(c), voices[ccount]); + prepareAdlChannelForNewNote(static_cast<size_t>(c), voices[ccount]); adlchannel[ccount] = c; } @@ -509,21 +511,21 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) ir.first->phys_ensure_find_or_create(chipChan)->assign(voices[ccount]); } - NoteUpdate(channel, ir.first, Upd_All | Upd_Patch); + noteUpdate(channel, ir.first, Upd_All | Upd_Patch); return true; } void MIDIplay::realTime_NoteOff(uint8_t channel, uint8_t note) { channel = channel % 16; - NoteOff(channel, note); + noteOff(channel, note); } void MIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal) { channel = channel % 16; - MIDIchannel &chan = Ch[channel]; - MIDIchannel::activenoteiterator i = Ch[channel].activenotes_find(note); + MIDIchannel &chan = m_midiChannels[channel]; + MIDIchannel::activenoteiterator i = m_midiChannels[channel].activenotes_find(note); if(i) { i->vibrato = atVal; @@ -543,7 +545,7 @@ void MIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t at void MIDIplay::realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal) { channel = channel % 16; - Ch[channel].aftertouch = atVal; + m_midiChannels[channel].aftertouch = atVal; } void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) @@ -553,89 +555,89 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) { case 1: // Adjust vibrato //UI.PrintLn("%u:vibrato %d", MidCh,value); - Ch[channel].vibrato = value; + m_midiChannels[channel].vibrato = value; break; case 0: // Set bank msb (GM bank) - Ch[channel].bank_msb = value; + m_midiChannels[channel].bank_msb = value; if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode - Ch[channel].is_xg_percussion = isXgPercChannel(Ch[channel].bank_msb, Ch[channel].bank_lsb); + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); break; case 32: // Set bank lsb (XG bank) - Ch[channel].bank_lsb = value; + m_midiChannels[channel].bank_lsb = value; if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode - Ch[channel].is_xg_percussion = isXgPercChannel(Ch[channel].bank_msb, Ch[channel].bank_lsb); + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); break; case 5: // Set portamento msb - Ch[channel].portamento = static_cast<uint16_t>((Ch[channel].portamento & 0x7F) | (value << 7)); - UpdatePortamento(channel); + m_midiChannels[channel].portamento = static_cast<uint16_t>((m_midiChannels[channel].portamento & 0x7F) | (value << 7)); + updatePortamento(channel); break; case 37: // Set portamento lsb - Ch[channel].portamento = (Ch[channel].portamento & 0x3F80) | (value); - UpdatePortamento(channel); + m_midiChannels[channel].portamento = (m_midiChannels[channel].portamento & 0x3F80) | (value); + updatePortamento(channel); break; case 65: // Enable/disable portamento - Ch[channel].portamentoEnable = value >= 64; - UpdatePortamento(channel); + m_midiChannels[channel].portamentoEnable = value >= 64; + updatePortamento(channel); break; case 7: // Change volume - Ch[channel].volume = value; - NoteUpdate_All(channel, Upd_Volume); + m_midiChannels[channel].volume = value; + noteUpdateAll(channel, Upd_Volume); break; case 74: // Change brightness - Ch[channel].brightness = value; - NoteUpdate_All(channel, Upd_Volume); + m_midiChannels[channel].brightness = value; + noteUpdateAll(channel, Upd_Volume); break; case 64: // Enable/disable sustain - Ch[channel].sustain = (value >= 64); - if(!Ch[channel].sustain) - KillSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Pedal); + m_midiChannels[channel].sustain = (value >= 64); + if(!m_midiChannels[channel].sustain) + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Pedal); break; case 66: // Enable/disable sostenuto if(value >= 64) //Find notes and mark them as sostenutoed - MarkSostenutoNotes(channel); + markSostenutoNotes(channel); else - KillSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Sostenuto); + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Sostenuto); break; case 67: // Enable/disable soft-pedal - Ch[channel].softPedal = (value >= 64); + m_midiChannels[channel].softPedal = (value >= 64); break; case 11: // Change expression (another volume factor) - Ch[channel].expression = value; - NoteUpdate_All(channel, Upd_Volume); + m_midiChannels[channel].expression = value; + noteUpdateAll(channel, Upd_Volume); break; case 10: // Change panning - Ch[channel].panning = 0x00; - if(value < 64 + 32) Ch[channel].panning |= OPL_PANNING_LEFT; - if(value >= 64 - 32) Ch[channel].panning |= OPL_PANNING_RIGHT; + m_midiChannels[channel].panning = 0x00; + if(value < 64 + 32) m_midiChannels[channel].panning |= OPL_PANNING_LEFT; + if(value >= 64 - 32) m_midiChannels[channel].panning |= OPL_PANNING_RIGHT; - NoteUpdate_All(channel, Upd_Pan); + noteUpdateAll(channel, Upd_Pan); break; case 121: // Reset all controllers - Ch[channel].resetAllControllers(); - NoteUpdate_All(channel, Upd_Pan + Upd_Volume + Upd_Pitch); + m_midiChannels[channel].resetAllControllers(); + noteUpdateAll(channel, Upd_Pan + Upd_Volume + Upd_Pitch); // Kill all sustained notes - KillSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_ANY); + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_ANY); break; case 120: // All sounds off - NoteUpdate_All(channel, Upd_OffMute); + noteUpdateAll(channel, Upd_OffMute); break; case 123: // All notes off - NoteUpdate_All(channel, Upd_Off); + noteUpdateAll(channel, Upd_Off); break; case 91: @@ -654,38 +656,39 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) break; // Phaser effect depth. We don't do. case 98: - Ch[channel].lastlrpn = value; - Ch[channel].nrpn = true; + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = true; break; case 99: - Ch[channel].lastmrpn = value; - Ch[channel].nrpn = true; + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = true; break; case 100: - Ch[channel].lastlrpn = value; - Ch[channel].nrpn = false; + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = false; break; case 101: - Ch[channel].lastmrpn = value; - Ch[channel].nrpn = false; + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = false; break; case 113: break; // Related to pitch-bender, used by missimp.mid in Duke3D case 6: - SetRPN(channel, value, true); + setRPN(channel, value, true); break; case 38: - SetRPN(channel, value, false); + setRPN(channel, value, false); break; case 103: - cmf_percussion_mode = (value != 0); + if(m_synth.m_musicMode == OPL3::MODE_CMF) + m_cmfPercussionMode = (value != 0); break; // CMF (ctrl 0x67) rhythm mode default: @@ -697,40 +700,40 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) void MIDIplay::realTime_PatchChange(uint8_t channel, uint8_t patch) { channel = channel % 16; - Ch[channel].patch = patch; + m_midiChannels[channel].patch = patch; } void MIDIplay::realTime_PitchBend(uint8_t channel, uint16_t pitch) { channel = channel % 16; - Ch[channel].bend = int(pitch) - 8192; - NoteUpdate_All(channel, Upd_Pitch); + m_midiChannels[channel].bend = int(pitch) - 8192; + noteUpdateAll(channel, Upd_Pitch); } void MIDIplay::realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb) { channel = channel % 16; - Ch[channel].bend = int(lsb) + int(msb) * 128 - 8192; - NoteUpdate_All(channel, Upd_Pitch); + m_midiChannels[channel].bend = int(lsb) + int(msb) * 128 - 8192; + noteUpdateAll(channel, Upd_Pitch); } void MIDIplay::realTime_BankChangeLSB(uint8_t channel, uint8_t lsb) { channel = channel % 16; - Ch[channel].bank_lsb = lsb; + m_midiChannels[channel].bank_lsb = lsb; } void MIDIplay::realTime_BankChangeMSB(uint8_t channel, uint8_t msb) { channel = channel % 16; - Ch[channel].bank_msb = msb; + m_midiChannels[channel].bank_msb = msb; } void MIDIplay::realTime_BankChange(uint8_t channel, uint16_t bank) { channel = channel % 16; - Ch[channel].bank_lsb = uint8_t(bank & 0xFF); - Ch[channel].bank_msb = uint8_t((bank >> 8) & 0xFF); + m_midiChannels[channel].bank_lsb = uint8_t(bank & 0xFF); + m_midiChannels[channel].bank_msb = uint8_t((bank >> 8) & 0xFF); } void MIDIplay::setDeviceId(uint8_t id) @@ -798,8 +801,8 @@ bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data (((unsigned)data[0] & 0x7F)) | (((unsigned)data[1] & 0x7F) << 7); m_masterVolume = volume >> 7; - for(size_t ch = 0; ch < Ch.size(); ch++) - NoteUpdate_All(uint16_t(ch), Upd_Volume); + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) + noteUpdateAll(uint16_t(ch), Upd_Volume); return true; } @@ -889,7 +892,7 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) { if(size != 1 || (dev & 0xF0) != 0x10) break; - if(Ch.size() < 16) + if(m_midiChannels.size() < 16) break; unsigned value = data[0] & 0x7F; const uint8_t channels_map[16] = @@ -900,7 +903,7 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland Percussion set: %02X on channel %u (from %X)", value, channels_map[target_channel], target_channel); - Ch[channels_map[target_channel]].is_xg_percussion = ((value == 0x01)) || ((value == 0x02)); + m_midiChannels[channels_map[target_channel]].is_xg_percussion = ((value == 0x01)) || ((value == 0x02)); return true; } } @@ -955,19 +958,19 @@ bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) void MIDIplay::realTime_panic() { - Panic(); - KillSustainingNotes(-1, -1, AdlChannel::LocationData::Sustain_ANY); + panic(); + killSustainingNotes(-1, -1, AdlChannel::LocationData::Sustain_ANY); } void MIDIplay::realTime_deviceSwitch(size_t track, const char *data, size_t length) { const std::string indata(data, length); - current_device[track] = ChooseDevice(indata); + m_currentMidiDevice[track] = chooseDevice(indata); } uint64_t MIDIplay::realTime_currentDevice(size_t track) { - return current_device[track]; + return m_currentMidiDevice[track]; } void MIDIplay::realTime_rawOPL(uint8_t reg, uint8_t value) @@ -976,7 +979,7 @@ void MIDIplay::realTime_rawOPL(uint8_t reg, uint8_t value) value |= 0x30; //std::printf("OPL poke %02X, %02X\n", reg, value); //std::fflush(stdout); - opl.Poke(0, reg, value); + m_synth.writeReg(0, reg, value); } #if defined(ADLMIDI_AUDIO_TICK_HANDLER) @@ -993,12 +996,12 @@ void MIDIplay::AudioTick(uint32_t chipId, uint32_t rate) if(tickNumber % portamentoInterval == 0) { double portamentoDelta = timeDelta * portamentoInterval; - UpdateGlide(portamentoDelta); + updateGlide(portamentoDelta); } } #endif -void MIDIplay::NoteUpdate(uint16_t MidCh, +void MIDIplay::noteUpdate(uint16_t midCh, MIDIplay::MIDIchannel::activenoteiterator i, unsigned props_mask, int32_t select_adlchn) @@ -1010,7 +1013,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, const int midiins = static_cast<int>(info.midiins); const adlinsdata2 &ains = *info.ains; AdlChannel::Location my_loc; - my_loc.MidCh = MidCh; + my_loc.MidCh = midCh; my_loc.note = info.note; for(unsigned ccount = 0, ctotal = info.chip_channels_count; ccount < ctotal; ccount++) @@ -1022,8 +1025,8 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, if(props_mask & Upd_Patch) { - opl.Patch(c, ins.ains); - AdlChannel::LocationData *d = ch[c].users_find_or_create(my_loc); + m_synth.setPatch(c, ins.ains); + AdlChannel::LocationData *d = m_chipChannels[c].users_find_or_create(my_loc); if(d) // inserts if necessary { d->sustained = AdlChannel::LocationData::Sustain_None; @@ -1045,27 +1048,27 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, if(props_mask & Upd_Off) // note off { - if(!Ch[MidCh].sustain) + if(!m_midiChannels[midCh].sustain) { - AdlChannel::LocationData *k = ch[c].users_find(my_loc); + AdlChannel::LocationData *k = m_chipChannels[c].users_find(my_loc); bool do_erase_user = (k && ((k->sustained & AdlChannel::LocationData::Sustain_Sostenuto) == 0)); if(do_erase_user) - ch[c].users_erase(k); + m_chipChannels[c].users_erase(k); if(hooks.onNote) hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, 0, 0.0); - if(do_erase_user && ch[c].users_empty()) + if(do_erase_user && m_chipChannels[c].users_empty()) { - opl.NoteOff(c); + m_synth.noteOff(c); if(props_mask & Upd_Mute) // Mute the note { - opl.Touch_Real(c, 0); - ch[c].koff_time_until_neglible = 0; + m_synth.touchReal(c, 0); + m_chipChannels[c].koff_time_until_neglible = 0; } else { - ch[c].koff_time_until_neglible = ains.ms_sound_koff; + m_chipChannels[c].koff_time_until_neglible = ains.ms_sound_koff; } } } @@ -1073,7 +1076,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, { // Sustain: Forget about the note, but don't key it off. // Also will avoid overwriting it very soon. - AdlChannel::LocationData *d = ch[c].users_find_or_create(my_loc); + AdlChannel::LocationData *d = m_chipChannels[c].users_find_or_create(my_loc); if(d) d->sustained |= AdlChannel::LocationData::Sustain_Pedal; // note: not erased! if(hooks.onNote) @@ -1086,13 +1089,13 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, } if(props_mask & Upd_Pan) - opl.Pan(c, Ch[MidCh].panning); + m_synth.setPan(c, m_midiChannels[midCh].panning); if(props_mask & Upd_Volume) { uint32_t volume; - bool is_percussion = (MidCh == 9) || Ch[MidCh].is_xg_percussion; - uint8_t brightness = is_percussion ? 127 : Ch[MidCh].brightness; + bool is_percussion = (midCh == 9) || m_midiChannels[midCh].is_xg_percussion; + uint8_t brightness = is_percussion ? 127 : m_midiChannels[midCh].brightness; if(!m_setup.fullRangeBrightnessCC74) { @@ -1103,12 +1106,12 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, brightness *= 2; } - switch(opl.m_volumeScale) + switch(m_synth.m_volumeScale) { default: case OPL3::VOLUME_Generic: { - volume = vol * m_masterVolume * Ch[MidCh].volume * Ch[MidCh].expression; + volume = vol * m_masterVolume * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression; /* If the channel has arpeggio, the effective volume of * *this* instrument is actually lower due to timesharing. @@ -1128,7 +1131,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, case OPL3::VOLUME_NATIVE: { - volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; + volume = vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression; // volume = volume * m_masterVolume / (127 * 127 * 127) / 2; volume = (volume * m_masterVolume) / 4096766; } @@ -1136,7 +1139,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, case OPL3::VOLUME_DMX: { - volume = 2 * (Ch[MidCh].volume * Ch[MidCh].expression * m_masterVolume / 16129) + 1; + volume = 2 * (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 16129) + 1; //volume = 2 * (Ch[MidCh].volume) + 1; volume = (DMX_volume_mapping_table[(vol < 128) ? vol : 127] * volume) >> 9; } @@ -1144,7 +1147,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, case OPL3::VOLUME_APOGEE: { - volume = (Ch[MidCh].volume * Ch[MidCh].expression * m_masterVolume / 16129); + volume = (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 16129); volume = ((64 * (vol + 0x80)) * volume) >> 15; //volume = ((63 * (vol + 0x80)) * Ch[MidCh].volume) >> 15; } @@ -1153,13 +1156,13 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, case OPL3::VOLUME_9X: { //volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * m_masterVolume / 16129 /*2048383*/) >> 2)]; - volume = 63 - W9X_volume_mapping_table[((vol * Ch[MidCh].volume * Ch[MidCh].expression * m_masterVolume / 2048383) >> 2)]; + volume = 63 - W9X_volume_mapping_table[((vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 2048383) >> 2)]; //volume = W9X_volume_mapping_table[vol >> 2] + volume; } break; } - opl.Touch_Real(c, volume, brightness); + m_synth.touchReal(c, volume, brightness); /* DEBUG ONLY!!! static uint32_t max = 0; @@ -1177,15 +1180,15 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, if(props_mask & Upd_Pitch) { - AdlChannel::LocationData *d = ch[c].users_find(my_loc); + AdlChannel::LocationData *d = m_chipChannels[c].users_find(my_loc); // Don't bend a sustained note if(!d || (d->sustained == AdlChannel::LocationData::Sustain_None)) { - double midibend = Ch[MidCh].bend * Ch[MidCh].bendsense; + double midibend = m_midiChannels[midCh].bend * m_midiChannels[midCh].bendsense; double bend = midibend + ins.ains.finetune; double phase = 0.0; - uint8_t vibrato = std::max(Ch[MidCh].vibrato, Ch[MidCh].aftertouch); + uint8_t vibrato = std::max(m_midiChannels[midCh].vibrato, m_midiChannels[midCh].aftertouch); vibrato = std::max(vibrato, i->vibrato); if((ains.flags & adlinsdata::Flag_Pseudo4op) && ins.pseudo4op) @@ -1193,11 +1196,11 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, phase = ains.voice2_fine_tune;//0.125; // Detune the note slightly (this is what Doom does) } - if(vibrato && (!d || d->vibdelay >= Ch[MidCh].vibdelay)) - bend += static_cast<double>(vibrato) * Ch[MidCh].vibdepth * std::sin(Ch[MidCh].vibpos); + if(vibrato && (!d || d->vibdelay >= m_midiChannels[midCh].vibdelay)) + bend += static_cast<double>(vibrato) * m_midiChannels[midCh].vibdepth * std::sin(m_midiChannels[midCh].vibpos); #define BEND_COEFFICIENT 172.4387 - opl.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (currentTone + bend + phase))); + m_synth.noteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (currentTone + bend + phase))); #undef BEND_COEFFICIENT if(hooks.onNote) hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, vol, midibend); @@ -1208,8 +1211,18 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, if(info.chip_channels_count == 0) { if(i->glideRate != HUGE_VAL) - --Ch[MidCh].gliding_note_count; - Ch[MidCh].activenotes_erase(i); + --m_midiChannels[midCh].gliding_note_count; + m_midiChannels[midCh].activenotes_erase(i); + } +} + +void MIDIplay::noteUpdateAll(size_t midCh, unsigned props_mask) +{ + for(MIDIchannel::activenoteiterator + i = m_midiChannels[midCh].activenotes_begin(); i;) + { + MIDIchannel::activenoteiterator j(i++); + noteUpdate(midCh, j, props_mask); } } @@ -1223,13 +1236,13 @@ void MIDIplay::setErrorString(const std::string &err) errorStringOut = err; } -int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t) const +int64_t MIDIplay::calculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const { - int64_t s = (opl.m_musicMode != OPL3::MODE_CMF) ? -ch[c].koff_time_until_neglible : 0; + int64_t s = (m_synth.m_musicMode != OPL3::MODE_CMF) ? -m_chipChannels[c].koff_time_until_neglible : 0; // Same midi-instrument = some stability //if(c == MidCh) s += 4; - for(AdlChannel::LocationData *j = ch[c].users_first; j; j = j->next) + for(AdlChannel::LocationData *j = m_chipChannels[c].users_first; j; j = j->next) { s -= 4000; @@ -1239,7 +1252,7 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteI s -= (j->kon_time_until_neglible / 2); MIDIchannel::activenoteiterator - k = const_cast<MIDIchannel &>(Ch[j->loc.MidCh]).activenotes_find(j->loc.note); + k = const_cast<MIDIchannel &>(m_midiChannels[j->loc.MidCh]).activenotes_find(j->loc.note); if(k) { @@ -1270,14 +1283,14 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteI // increase the score slightly. unsigned n_evacuation_stations = 0; - for(size_t c2 = 0; c2 < static_cast<size_t>(opl.NumChannels); ++c2) + for(size_t c2 = 0; c2 < static_cast<size_t>(m_synth.m_numChannels); ++c2) { if(c2 == c) continue; - if(opl.four_op_category[c2] - != opl.four_op_category[c]) continue; + if(m_synth.m_channelCategory[c2] + != m_synth.m_channelCategory[c]) continue; - for(AdlChannel::LocationData *m = ch[c2].users_first; m; m = m->next) + for(AdlChannel::LocationData *m = m_chipChannels[c2].users_first; m; m = m->next) { if(m->sustained != AdlChannel::LocationData::Sustain_None) continue; if(m->vibdelay >= 200) continue; @@ -1293,12 +1306,12 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteI } -void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins) +void MIDIplay::prepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins) { - if(ch[c].users_empty()) return; // Nothing to do + if(m_chipChannels[c].users_empty()) return; // Nothing to do //bool doing_arpeggio = false; - for(AdlChannel::LocationData *jnext = ch[c].users_first; jnext;) + for(AdlChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) { AdlChannel::LocationData *j = jnext; jnext = jnext->next; @@ -1308,7 +1321,7 @@ void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo // Collision: Kill old note, // UNLESS we're going to do arpeggio MIDIchannel::activenoteiterator i - (Ch[j->loc.MidCh].activenotes_ensure_find(j->loc.note)); + (m_midiChannels[j->loc.MidCh].activenotes_ensure_find(j->loc.note)); // Check if we can do arpeggio. if((j->vibdelay < 70 @@ -1320,7 +1333,7 @@ void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo continue; } - KillOrEvacuate(c, j, i); + killOrEvacuate(c, j, i); // ^ will also erase j from ch[c].users. } } @@ -1328,15 +1341,15 @@ void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo // Kill all sustained notes on this channel // Don't keep them for arpeggio, because arpeggio requires // an intact "activenotes" record. This is a design flaw. - KillSustainingNotes(-1, static_cast<int32_t>(c), AdlChannel::LocationData::Sustain_ANY); + killSustainingNotes(-1, static_cast<int32_t>(c), AdlChannel::LocationData::Sustain_ANY); // Keyoff the channel so that it can be retriggered, // unless the new note will be introduced as just an arpeggio. - if(ch[c].users_empty()) - opl.NoteOff(c); + if(m_chipChannels[c].users_empty()) + m_synth.noteOff(c); } -void MIDIplay::KillOrEvacuate(size_t from_channel, +void MIDIplay::killOrEvacuate(size_t from_channel, AdlChannel::LocationData *j, MIDIplay::MIDIchannel::activenoteiterator i) { @@ -1345,7 +1358,7 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, // instrument. This helps if e.g. all channels // are full of strings and we want to do percussion. // FIXME: This does not care about four-op entanglements. - for(uint32_t c = 0; c < opl.NumChannels; ++c) + for(uint32_t c = 0; c < m_synth.m_numChannels; ++c) { uint16_t cs = static_cast<uint16_t>(c); @@ -1353,10 +1366,10 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, break; if(c == from_channel) continue; - if(opl.four_op_category[c] != opl.four_op_category[from_channel]) + if(m_synth.m_channelCategory[c] != m_synth.m_channelCategory[from_channel]) continue; - AdlChannel &adlch = ch[c]; + AdlChannel &adlch = m_chipChannels[c]; if(adlch.users_size == AdlChannel::users_max) continue; // no room for more arpeggio on channel @@ -1381,9 +1394,9 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, i->phys_erase(static_cast<uint16_t>(from_channel)); i->phys_ensure_find_or_create(cs)->assign(j->ins); - if(!ch[cs].users_insert(*j)) + if(!m_chipChannels[cs].users_insert(*j)) assert(false); - ch[from_channel].users_erase(j); + m_chipChannels[from_channel].users_erase(j); return; } } @@ -1396,24 +1409,24 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, ins );*/ // Kill it - NoteUpdate(j->loc.MidCh, + noteUpdate(j->loc.MidCh, i, Upd_Off, static_cast<int32_t>(from_channel)); } -void MIDIplay::Panic() +void MIDIplay::panic() { - for(uint8_t chan = 0; chan < Ch.size(); chan++) + for(uint8_t chan = 0; chan < m_midiChannels.size(); chan++) { for(uint8_t note = 0; note < 128; note++) realTime_NoteOff(chan, note); } } -void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn, uint8_t sustain_type) +void MIDIplay::killSustainingNotes(int32_t MidCh, int32_t this_adlchn, uint8_t sustain_type) { - uint32_t first = 0, last = opl.NumChannels; + uint32_t first = 0, last = m_synth.m_numChannels; if(this_adlchn >= 0) { @@ -1423,10 +1436,10 @@ void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn, uint8_t s for(uint32_t c = first; c < last; ++c) { - if(ch[c].users_empty()) + if(m_chipChannels[c].users_empty()) continue; // Nothing to do - for(AdlChannel::LocationData *jnext = ch[c].users_first; jnext;) + for(AdlChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) { AdlChannel::LocationData *j = jnext; jnext = jnext->next; @@ -1439,25 +1452,25 @@ void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn, uint8_t s hooks.onNote(hooks.onNote_userData, (int)c, j->loc.note, midiins, 0, 0.0); j->sustained &= ~sustain_type; if((j->sustained == AdlChannel::LocationData::Sustain_None)) - ch[c].users_erase(j);//Remove only when note is clean from any holders + m_chipChannels[c].users_erase(j);//Remove only when note is clean from any holders } } // Keyoff the channel, if there are no users left. - if(ch[c].users_empty()) - opl.NoteOff(c); + if(m_chipChannels[c].users_empty()) + m_synth.noteOff(c); } } -void MIDIplay::MarkSostenutoNotes(int32_t MidCh) +void MIDIplay::markSostenutoNotes(int32_t MidCh) { - uint32_t first = 0, last = opl.NumChannels; + uint32_t first = 0, last = m_synth.m_numChannels; for(uint32_t c = first; c < last; ++c) { - if(ch[c].users_empty()) + if(m_chipChannels[c].users_empty()) continue; // Nothing to do - for(AdlChannel::LocationData *jnext = ch[c].users_first; jnext;) + for(AdlChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) { AdlChannel::LocationData *j = jnext; jnext = jnext->next; @@ -1467,40 +1480,40 @@ void MIDIplay::MarkSostenutoNotes(int32_t MidCh) } } -void MIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) +void MIDIplay::setRPN(unsigned MidCh, unsigned value, bool MSB) { - bool nrpn = Ch[MidCh].nrpn; - unsigned addr = Ch[MidCh].lastmrpn * 0x100 + Ch[MidCh].lastlrpn; + bool nrpn = m_midiChannels[MidCh].nrpn; + unsigned addr = m_midiChannels[MidCh].lastmrpn * 0x100 + m_midiChannels[MidCh].lastlrpn; switch(addr + nrpn * 0x10000 + MSB * 0x20000) { case 0x0000 + 0*0x10000 + 1*0x20000: // Pitch-bender sensitivity - Ch[MidCh].bendsense_msb = value; - Ch[MidCh].updateBendSensitivity(); + m_midiChannels[MidCh].bendsense_msb = value; + m_midiChannels[MidCh].updateBendSensitivity(); break; case 0x0000 + 0*0x10000 + 0*0x20000: // Pitch-bender sensitivity LSB - Ch[MidCh].bendsense_lsb = value; - Ch[MidCh].updateBendSensitivity(); + m_midiChannels[MidCh].bendsense_lsb = value; + m_midiChannels[MidCh].updateBendSensitivity(); break; case 0x0108 + 1*0x10000 + 1*0x20000: if((m_synthMode & Mode_XG) != 0) // Vibrato speed { - if(value == 64) Ch[MidCh].vibspeed = 1.0; - else if(value < 100) Ch[MidCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1)); - else Ch[MidCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385); - Ch[MidCh].vibspeed *= 2 * 3.141592653 * 5.0; + if(value == 64) m_midiChannels[MidCh].vibspeed = 1.0; + else if(value < 100) m_midiChannels[MidCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1)); + else m_midiChannels[MidCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385); + m_midiChannels[MidCh].vibspeed *= 2 * 3.141592653 * 5.0; } break; case 0x0109 + 1*0x10000 + 1*0x20000: if((m_synthMode & Mode_XG) != 0) // Vibrato depth { - Ch[MidCh].vibdepth = ((value - 64) * 0.15) * 0.01; + m_midiChannels[MidCh].vibdepth = ((value - 64) * 0.15) * 0.01; } break; case 0x010A + 1*0x10000 + 1*0x20000: if((m_synthMode & Mode_XG) != 0) // Vibrato delay in millisecons { - Ch[MidCh].vibdelay = value ? int64_t(0.2092 * std::exp(0.0795 * (double)value)) : 0; + m_midiChannels[MidCh].vibdelay = value ? int64_t(0.2092 * std::exp(0.0795 * (double)value)) : 0; } break; default:/* UI.PrintLn("%s %04X <- %d (%cSB) (ch %u)", @@ -1509,63 +1522,53 @@ void MIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) } } -void MIDIplay::UpdatePortamento(unsigned MidCh) +void MIDIplay::updatePortamento(size_t midCh) { double rate = HUGE_VAL; - uint16_t midival = Ch[MidCh].portamento; - if(Ch[MidCh].portamentoEnable && midival > 0) + uint16_t midival = m_midiChannels[midCh].portamento; + if(m_midiChannels[midCh].portamentoEnable && midival > 0) rate = 350.0 * std::pow(2.0, -0.062 * (1.0 / 128) * midival); - Ch[MidCh].portamentoRate = rate; + m_midiChannels[midCh].portamentoRate = rate; } -void MIDIplay::NoteUpdate_All(uint16_t MidCh, unsigned props_mask) -{ - for(MIDIchannel::activenoteiterator - i = Ch[MidCh].activenotes_begin(); i;) - { - MIDIchannel::activenoteiterator j(i++); - NoteUpdate(MidCh, j, props_mask); - } -} -void MIDIplay::NoteOff(uint16_t MidCh, uint8_t note) +void MIDIplay::noteOff(uint16_t midCh, uint8_t note) { MIDIchannel::activenoteiterator - i = Ch[MidCh].activenotes_find(note); - + i = m_midiChannels[midCh].activenotes_find(note); if(i) - NoteUpdate(MidCh, i, Upd_Off); + noteUpdate(midCh, i, Upd_Off); } -void MIDIplay::UpdateVibrato(double amount) +void MIDIplay::updateVibrato(double amount) { - for(size_t a = 0, b = Ch.size(); a < b; ++a) + for(size_t a = 0, b = m_midiChannels.size(); a < b; ++a) { - if(Ch[a].hasVibrato() && !Ch[a].activenotes_empty()) + if(m_midiChannels[a].hasVibrato() && !m_midiChannels[a].activenotes_empty()) { - NoteUpdate_All(static_cast<uint16_t>(a), Upd_Pitch); - Ch[a].vibpos += amount * Ch[a].vibspeed; + noteUpdateAll(static_cast<uint16_t>(a), Upd_Pitch); + m_midiChannels[a].vibpos += amount * m_midiChannels[a].vibspeed; } else - Ch[a].vibpos = 0.0; + m_midiChannels[a].vibpos = 0.0; } } -uint64_t MIDIplay::ChooseDevice(const std::string &name) +uint64_t MIDIplay::chooseDevice(const std::string &name) { - std::map<std::string, uint64_t>::iterator i = devices.find(name); + std::map<std::string, uint64_t>::iterator i = m_midiDevices.find(name); - if(i != devices.end()) + if(i != m_midiDevices.end()) return i->second; - size_t n = devices.size() * 16; - devices.insert(std::make_pair(name, n)); - Ch.resize(n + 16); + size_t n = m_midiDevices.size() * 16; + m_midiDevices.insert(std::make_pair(name, n)); + m_midiChannels.resize(n + 16); return n; } -void MIDIplay::UpdateArpeggio(double) // amount = amount of time passed +void MIDIplay::updateArpeggio(double) // amount = amount of time passed { // If there is an adlib channel that has multiple notes // simulated on the same channel, arpeggio them. @@ -1591,17 +1594,17 @@ void MIDIplay::UpdateArpeggio(double) // amount = amount of time passed ++m_arpeggioCounter; - for(uint32_t c = 0; c < opl.NumChannels; ++c) + for(uint32_t c = 0; c < m_synth.m_numChannels; ++c) { retry_arpeggio: if(c > uint32_t(std::numeric_limits<int32_t>::max())) break; - size_t n_users = ch[c].users_size; + size_t n_users = m_chipChannels[c].users_size; if(n_users > 1) { - AdlChannel::LocationData *i = ch[c].users_first; + AdlChannel::LocationData *i = m_chipChannels[c].users_first; size_t rate_reduction = 3; if(n_users >= 3) @@ -1618,17 +1621,17 @@ retry_arpeggio: { if(i->kon_time_until_neglible <= 0l) { - NoteUpdate( + noteUpdate( i->loc.MidCh, - Ch[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), + m_midiChannels[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), Upd_Off, static_cast<int32_t>(c)); goto retry_arpeggio; } - NoteUpdate( + noteUpdate( i->loc.MidCh, - Ch[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), + m_midiChannels[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), Upd_Pitch | Upd_Volume | Upd_Pan, static_cast<int32_t>(c)); } @@ -1636,13 +1639,13 @@ retry_arpeggio: } } -void MIDIplay::UpdateGlide(double amount) +void MIDIplay::updateGlide(double amount) { - size_t num_channels = Ch.size(); + size_t num_channels = m_midiChannels.size(); for(size_t channel = 0; channel < num_channels; ++channel) { - MIDIchannel &midiChan = Ch[channel]; + MIDIchannel &midiChan = m_midiChannels[channel]; if(midiChan.gliding_note_count == 0) continue; @@ -1662,7 +1665,7 @@ void MIDIplay::UpdateGlide(double amount) if(currentTone != previousTone) { it->currentTone = currentTone; - NoteUpdate(static_cast<uint16_t>(channel), it, Upd_Pitch); + noteUpdate(static_cast<uint16_t>(channel), it, Upd_Pitch); } } } @@ -1683,11 +1686,15 @@ struct AdlInstrumentTester::Impl ADLMIDI_EXPORT AdlInstrumentTester::AdlInstrumentTester(ADL_MIDIPlayer *device) : P(new Impl) { +#ifndef DISABLE_EMBEDDED_BANKS MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); P->cur_gm = 0; P->ins_idx = 0; P->play = play; - P->opl = play ? &play->opl : NULL; + P->opl = play ? &play->m_synth : NULL; +#else + ADL_UNUSED(device); +#endif } ADLMIDI_EXPORT AdlInstrumentTester::~AdlInstrumentTester() @@ -1697,6 +1704,7 @@ ADLMIDI_EXPORT AdlInstrumentTester::~AdlInstrumentTester() ADLMIDI_EXPORT void AdlInstrumentTester::FindAdlList() { +#ifndef DISABLE_EMBEDDED_BANKS const unsigned NumBanks = (unsigned)adl_getBanksCount(); std::set<unsigned> adl_ins_set; for(unsigned bankno = 0; bankno < NumBanks; ++bankno) @@ -1704,27 +1712,34 @@ ADLMIDI_EXPORT void AdlInstrumentTester::FindAdlList() P->adl_ins_list.assign(adl_ins_set.begin(), adl_ins_set.end()); P->ins_idx = 0; NextAdl(0); - P->opl->Silence(); + P->opl->silenceAll(); +#endif } ADLMIDI_EXPORT void AdlInstrumentTester::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127 { +#ifndef DISABLE_EMBEDDED_BANKS OPL3 *opl = P->opl; if(opl->m_volumeScale == OPL3::VOLUME_NATIVE) - opl->Touch_Real(c, volume * 127 / (127 * 127 * 127) / 2); + opl->touchReal(c, volume * 127 / (127 * 127 * 127) / 2); else { // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) - opl->Touch_Real(c, volume > 8725 ? static_cast<unsigned int>(std::log((double)volume) * 11.541561 + (0.5 - 104.22845)) : 0); + opl->touchReal(c, volume > 8725 ? static_cast<unsigned int>(std::log((double)volume) * 11.541561 + (0.5 - 104.22845)) : 0); // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); } +#else + ADL_UNUSED(c); + ADL_UNUSED(volume); +#endif } ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) { +#ifndef DISABLE_EMBEDDED_BANKS MIDIplay *play = P->play; OPL3 *opl = P->opl; if(P->adl_ins_list.empty()) FindAdlList(); @@ -1763,27 +1778,35 @@ ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) } } - opl->NoteOff(0); - opl->NoteOff(3); - opl->NoteOff(6); + opl->noteOff(0); + opl->noteOff(3); + opl->noteOff(6); for(unsigned c = 0; c < 2; ++c) { if(adlchannel[c] < 0) continue; - opl->Patch((uint16_t)adlchannel[c], ains.adl[c]); - opl->Touch_Real((uint16_t)adlchannel[c], 127 * 127 * 100); - opl->Pan((uint16_t)adlchannel[c], 0x30); - opl->NoteOn((uint16_t)adlchannel[c], hertz); + opl->setPatch(static_cast<size_t>(adlchannel[c]), ains.adl[c]); + opl->touchReal(static_cast<size_t>(adlchannel[c]), 63); + opl->setPan(static_cast<size_t>(adlchannel[c]), 0x30); + opl->noteOn(static_cast<size_t>(adlchannel[c]), hertz); } +#else + ADL_UNUSED(note); +#endif } ADLMIDI_EXPORT void AdlInstrumentTester::NextGM(int offset) { +#ifndef DISABLE_EMBEDDED_BANKS P->cur_gm = (P->cur_gm + 256 + (uint32_t)offset) & 0xFF; FindAdlList(); +#else + ADL_UNUSED(offset); +#endif } ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) { +#ifndef DISABLE_EMBEDDED_BANKS //OPL3 *opl = P->opl; if(P->adl_ins_list.empty()) FindAdlList(); const unsigned NumBanks = (unsigned)adl_getBanksCount(); @@ -1829,10 +1852,14 @@ ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) std::printf("\n"); } +#else + ADL_UNUSED(offset); +#endif } ADLMIDI_EXPORT bool AdlInstrumentTester::HandleInputChar(char ch) { +#ifndef DISABLE_EMBEDDED_BANKS static const char notes[] = "zsxdcvgbhnjmq2w3er5t6y7ui9o0p"; // c'd'ef'g'a'bC'D'EF'G'A'Bc'd'e switch(ch) @@ -1867,6 +1894,9 @@ ADLMIDI_EXPORT bool AdlInstrumentTester::HandleInputChar(char ch) if(p && *p) DoNote((int)(p - notes) - 12); } +#else + ADL_UNUSED(ch); +#endif return true; } diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp index 5d15d00..2d2bc78 100644 --- a/src/adlmidi_opl3.cpp +++ b/src/adlmidi_opl3.cpp @@ -42,32 +42,8 @@ static const unsigned OPLBase = 0x388; # endif #endif -#ifdef DISABLE_EMBEDDED_BANKS -/* - Dummy data which replaces adldata.cpp banks database -*/ - -const struct adldata adl[] = -{ - {0, 0, (unsigned char)'\0', (unsigned char)'\0', (unsigned char)'\0', 0} -}; - -const struct adlinsdata adlins[] = -{ - {0, 0, 0, 0, 0, 0, 0.0} -}; - -int maxAdlBanks() -{ - return 0; -} - -const unsigned short banks[][256] = {{0}}; -const char *const banknames[] = {"<Embedded banks are disabled>"}; -const AdlBankSetup adlbanksetup[] = {{0, 1, 1, 0, 0}}; -#endif - -static const unsigned short Operators[23 * 2] = +//! Per-channel and per-operator registers map +static const uint16_t g_operatorsMap[23 * 2] = { // Channels 0-2 0x000, 0x003, 0x001, 0x004, 0x002, 0x005, // operators 0, 3, 1, 4, 2, 5 @@ -91,7 +67,8 @@ static const unsigned short Operators[23 * 2] = 0x011, 0xFFF }; // operator 13 -static const unsigned short Channels[23] = +//! Channel map to regoster offsets +static const uint16_t g_channelsMap[23] = { 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, // 0..8 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, // 9..17 (secondary set) @@ -129,19 +106,46 @@ static const unsigned short Channels[23] = Ports: ??? */ -void OPL3::setEmbeddedBank(unsigned int bank) +static adlinsdata2 makeEmptyInstrument() +{ + adlinsdata2 ins; + memset(&ins, 0, sizeof(adlinsdata2)); + ins.flags = adlinsdata::Flag_NoSound; + return ins; +} + +const adlinsdata2 OPL3::m_emptyInstrument = makeEmptyInstrument(); + +OPL3::OPL3() : + m_numChips(1), + m_numFourOps(0), + m_deepTremoloMode(false), + m_deepVibratoMode(false), + m_rhythmMode(false), + m_musicMode(MODE_MIDI), + m_volumeScale(VOLUME_Generic) { - AdlBank = bank; +#ifdef DISABLE_EMBEDDED_BANKS + m_embeddedBank = ~0u; +#else + setEmbeddedBank(0); +#endif +} + +void OPL3::setEmbeddedBank(uint32_t bank) +{ +#ifndef DISABLE_EMBEDDED_BANKS + m_embeddedBank = bank; //Embedded banks are supports 128:128 GM set only - dynamic_banks.clear(); + m_insBanks.clear(); if(bank >= static_cast<unsigned int>(maxAdlBanks())) return; Bank *bank_pair[2] = { - &dynamic_banks[0], - &dynamic_banks[PercussionTag] + &m_insBanks[0], + &m_insBanks[PercussionTag] }; for(unsigned i = 0; i < 256; ++i) @@ -150,79 +154,56 @@ void OPL3::setEmbeddedBank(unsigned int bank) adlinsdata2 &ins = bank_pair[i / 128]->ins[i % 128]; ins = adlinsdata2(adlins[meta]); } -} - -static adlinsdata2 makeEmptyInstrument() -{ - adlinsdata2 ins; - memset(&ins, 0, sizeof(adlinsdata2)); - ins.flags = adlinsdata::Flag_NoSound; - return ins; -} - -const adlinsdata2 OPL3::emptyInstrument = makeEmptyInstrument(); - -OPL3::OPL3() : - NumCards(1), - NumFourOps(0), - HighTremoloMode(false), - HighVibratoMode(false), - AdlPercussionMode(false), - m_musicMode(MODE_MIDI), - m_volumeScale(VOLUME_Generic) -{ -#ifdef DISABLE_EMBEDDED_BANKS - AdlBank = ~0u; #else - setEmbeddedBank(0); + ADL_UNUSED(bank); #endif } -void OPL3::Poke(size_t card, uint16_t index, uint8_t value) +void OPL3::writeReg(size_t chip, uint16_t address, uint8_t value) { #ifdef ADLMIDI_HW_OPL - (void)card; - unsigned o = index >> 8; + ADL_UNUSED(chip); + unsigned o = address >> 8; unsigned port = OPLBase + o * 2; #ifdef __DJGPP__ - outportb(port, index); + outportb(port, address); for(unsigned c = 0; c < 6; ++c) inportb(port); outportb(port + 1, value); for(unsigned c = 0; c < 35; ++c) inportb(port); #endif #ifdef __WATCOMC__ - outp(port, index); + outp(port, address); for(uint16_t c = 0; c < 6; ++c) inp(port); outp(port + 1, value); for(uint16_t c = 0; c < 35; ++c) inp(port); #endif//__WATCOMC__ - #else - cardsOP2[card]->writeReg(index, value); + #else//ADLMIDI_HW_OPL + m_chips[chip]->writeReg(address, value); #endif } -void OPL3::NoteOff(size_t c) +void OPL3::noteOff(size_t c) { size_t card = c / 23, cc = c % 23; if(cc >= 18) { - regBD[card] &= ~(0x10 >> (cc - 18)); - Poke(card, 0xBD, regBD[card]); + m_regBD[card] &= ~(0x10 >> (cc - 18)); + writeReg(card, 0xBD, m_regBD[card]); return; } - Poke(card, 0xB0 + Channels[cc], pit[c] & 0xDF); + writeReg(card, 0xB0 + g_channelsMap[cc], m_pit[c] & 0xDF); } -void OPL3::NoteOn(unsigned c, double hertz) // Hertz range: 0..131071 +void OPL3::noteOn(size_t c, double hertz) // Hertz range: 0..131071 { - unsigned card = c / 23, cc = c % 23; - unsigned x = 0x2000; + size_t card = c / 23, cc = c % 23; + uint32_t x = 0x2000; if(hertz < 0 || hertz > 131071) // Avoid infinite loop return; @@ -233,53 +214,54 @@ void OPL3::NoteOn(unsigned c, double hertz) // Hertz range: 0..131071 x += 0x400; } - x += static_cast<unsigned int>(hertz + 0.5); - unsigned chn = Channels[cc]; + x += static_cast<uint32_t>(hertz + 0.5); + uint32_t chn = g_channelsMap[cc]; if(cc >= 18) { - regBD[card] |= (0x10 >> (cc - 18)); - Poke(card, 0x0BD, regBD[card]); + m_regBD[card] |= (0x10 >> (cc - 18)); + writeReg(card, 0x0BD, m_regBD[card]); x &= ~0x2000u; //x |= 0x800; // for test } if(chn != 0xFFF) { - Poke(card, 0xA0 + chn, x & 0xFF); - Poke(card, 0xB0 + chn, pit[c] = static_cast<uint8_t>(x >> 8)); + writeReg(card, 0xA0 + chn, static_cast<uint8_t>(x & 0xFF)); + writeReg(card, 0xB0 + chn, static_cast<uint8_t>(x >> 8)); + m_pit[c] = static_cast<uint8_t>(x >> 8); } } -void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) +void OPL3::touchReal(uint32_t c, uint8_t volume, uint8_t brightness) { if(volume > 63) volume = 63; size_t card = c / 23, cc = c % 23; - const adldata &adli = ins[c]; - uint16_t o1 = Operators[cc * 2 + 0]; - uint16_t o2 = Operators[cc * 2 + 1]; + const adldata &adli = m_ins[c]; + uint16_t o1 = g_operatorsMap[cc * 2 + 0]; + uint16_t o2 = g_operatorsMap[cc * 2 + 1]; uint8_t x = adli.modulator_40, y = adli.carrier_40; uint16_t mode = 1; // 2-op AM - if(four_op_category[c] == 0 || four_op_category[c] == 3) + if(m_channelCategory[c] == 0 || m_channelCategory[c] == 3) { mode = adli.feedconn & 1; // 2-op FM or 2-op AM } - else if(four_op_category[c] == 1 || four_op_category[c] == 2) + else if(m_channelCategory[c] == 1 || m_channelCategory[c] == 2) { const adldata *i0, *i1; - if(four_op_category[c] == 1) + if(m_channelCategory[c] == 1) { i0 = &adli; - i1 = &ins[c + 3]; + i1 = &m_ins[c + 3]; mode = 2; // 4-op xx-xx ops 1&2 } else { - i0 = &ins[c - 3]; + i0 = &m_ins[c - 3]; i1 = &adli; mode = 6; // 4-op xx-xx ops 3&4 } @@ -303,14 +285,14 @@ void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) if(m_musicMode == MODE_RSXX) { - Poke(card, 0x40 + o1, x); + writeReg(card, 0x40 + o1, x); if(o2 != 0xFFF) - Poke(card, 0x40 + o2, y - volume / 2); + writeReg(card, 0x40 + o2, y - volume / 2); } else { - bool do_modulator = do_ops[ mode ][ 0 ] || ScaleModulators; - bool do_carrier = do_ops[ mode ][ 1 ] || ScaleModulators; + bool do_modulator = do_ops[ mode ][ 0 ] || m_scaleModulators; + bool do_carrier = do_ops[ mode ][ 1 ] || m_scaleModulators; uint32_t modulator = do_modulator ? (x | 63) - volume + volume * (x & 63) / 63 : x; uint32_t carrier = do_carrier ? (y | 63) - volume + volume * (y & 63) / 63 : y; @@ -324,9 +306,9 @@ void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) carrier = (carrier | 63) - brightness + brightness * (carrier & 63) / 63; } - Poke(card, 0x40 + o1, modulator); + writeReg(card, 0x40 + o1, modulator); if(o2 != 0xFFF) - Poke(card, 0x40 + o2, carrier); + writeReg(card, 0x40 + o2, carrier); } // Correct formula (ST3, AdPlug): @@ -351,70 +333,69 @@ void OPL3::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127 } }*/ -void OPL3::Patch(uint16_t c, const adldata &adli) +void OPL3::setPatch(uint16_t c, const adldata &instrument) { uint16_t card = c / 23, cc = c % 23; static const uint8_t data[4] = {0x20, 0x60, 0x80, 0xE0}; - ins[c] = adli; - uint16_t o1 = Operators[cc * 2 + 0]; - uint16_t o2 = Operators[cc * 2 + 1]; - unsigned x = adli.modulator_E862, y = adli.carrier_E862; + m_ins[c] = instrument; + uint16_t o1 = g_operatorsMap[cc * 2 + 0]; + uint16_t o2 = g_operatorsMap[cc * 2 + 1]; + unsigned x = instrument.modulator_E862, y = instrument.carrier_E862; for(unsigned a = 0; a < 4; ++a, x >>= 8, y >>= 8) { - Poke(card, data[a] + o1, x & 0xFF); + writeReg(card, data[a] + o1, x & 0xFF); if(o2 != 0xFFF) - Poke(card, data[a] + o2, y & 0xFF); + writeReg(card, data[a] + o2, y & 0xFF); } } -void OPL3::Pan(unsigned c, unsigned value) +void OPL3::setPan(size_t c, uint8_t value) { - unsigned card = c / 23, cc = c % 23; - - if(Channels[cc] != 0xFFF) - Poke(card, 0xC0 + Channels[cc], ins[c].feedconn | value); + size_t card = c / 23, cc = c % 23; + if(g_channelsMap[cc] != 0xFFF) + writeReg(card, 0xC0 + g_channelsMap[cc], m_ins[c].feedconn | value); } -void OPL3::Silence() // Silence all OPL channels. +void OPL3::silenceAll() // Silence all OPL channels. { - for(unsigned c = 0; c < NumChannels; ++c) + for(size_t c = 0; c < m_numChannels; ++c) { - NoteOff(c); - Touch_Real(c, 0); + noteOff(c); + touchReal(c, 0); } } -void OPL3::updateFlags() +void OPL3::updateChannelCategories() { - unsigned fours = NumFourOps; + uint32_t fours = m_numFourOps; - for(unsigned card = 0; card < NumCards; ++card) + for(size_t chip = 0; chip < m_numChips; ++chip) { - Poke(card, 0x0BD, regBD[card] = (HighTremoloMode * 0x80 - + HighVibratoMode * 0x40 - + AdlPercussionMode * 0x20)); - unsigned fours_this_card = std::min(fours, 6u); - Poke(card, 0x104, (1 << fours_this_card) - 1); - fours -= fours_this_card; + m_regBD[chip] = (m_deepTremoloMode * 0x80 + m_deepVibratoMode * 0x40 + m_rhythmMode * 0x20); + writeReg(chip, 0x0BD, m_regBD[chip]); + uint8_t fours_this_chip = std::min(fours, static_cast<uint32_t>(6u)); + writeReg(chip, 0x104, (1 << fours_this_chip) - 1); + fours -= fours_this_chip; } // Mark all channels that are reserved for four-operator function - if(AdlPercussionMode == 1) - for(unsigned a = 0; a < NumCards; ++a) + if(m_rhythmMode == 1) + { + for(size_t a = 0; a < m_numChips; ++a) { - for(unsigned b = 0; b < 5; ++b) - four_op_category[a * 23 + 18 + b] = static_cast<char>(b + 3); - for(unsigned b = 0; b < 3; ++b) - four_op_category[a * 23 + 6 + b] = 8; + for(size_t b = 0; b < 5; ++b) + m_channelCategory[a * 23 + 18 + b] = static_cast<char>(b + 3); + for(size_t b = 0; b < 3; ++b) + m_channelCategory[a * 23 + 6 + b] = ChanCat_Rhythm_Slave; } + } - unsigned nextfour = 0; - - for(unsigned a = 0; a < NumFourOps; ++a) + size_t nextfour = 0; + for(size_t a = 0; a < m_numFourOps; ++a) { - four_op_category[nextfour ] = 1; - four_op_category[nextfour + 3] = 2; + m_channelCategory[nextfour] = ChanCat_4op_Master; + m_channelCategory[nextfour + 3] = ChanCat_4op_Slave; switch(a % 6) { @@ -434,19 +415,45 @@ void OPL3::updateFlags() break; } } + +/**/ +/* + In two-op mode, channels 0..8 go as follows: + Op1[port] Op2[port] + Channel 0: 00 00 03 03 + Channel 1: 01 01 04 04 + Channel 2: 02 02 05 05 + Channel 3: 06 08 09 0B + Channel 4: 07 09 10 0C + Channel 5: 08 0A 11 0D + Channel 6: 12 10 15 13 + Channel 7: 13 11 16 14 + Channel 8: 14 12 17 15 + In four-op mode, channels 0..8 go as follows: + Op1[port] Op2[port] Op3[port] Op4[port] + Channel 0: 00 00 03 03 06 08 09 0B + Channel 1: 01 01 04 04 07 09 10 0C + Channel 2: 02 02 05 05 08 0A 11 0D + Channel 3: CHANNEL 0 SLAVE + Channel 4: CHANNEL 1 SLAVE + Channel 5: CHANNEL 2 SLAVE + Channel 6: 12 10 15 13 + Channel 7: 13 11 16 14 + Channel 8: 14 12 17 15 + Same goes principally for channels 9-17 respectively. + */ } -void OPL3::updateDeepFlags() +void OPL3::commitDeepFlags() { - for(unsigned card = 0; card < NumCards; ++card) + for(size_t chip = 0; chip < m_numChips; ++chip) { - Poke(card, 0x0BD, regBD[card] = (HighTremoloMode * 0x80 - + HighVibratoMode * 0x40 - + AdlPercussionMode * 0x20)); + m_regBD[chip] = (m_deepTremoloMode * 0x80 + m_deepVibratoMode * 0x40 + m_rhythmMode * 0x20); + writeReg(chip, 0x0BD, m_regBD[chip]); } } -void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel) +void OPL3::setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel) { switch(volumeModel) { @@ -476,18 +483,18 @@ void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel) } #ifndef ADLMIDI_HW_OPL -void OPL3::ClearChips() +void OPL3::clearChips() { - for(size_t i = 0; i < cardsOP2.size(); i++) - cardsOP2[i].reset(NULL); - cardsOP2.clear(); + for(size_t i = 0; i < m_chips.size(); i++) + m_chips[i].reset(NULL); + m_chips.clear(); } #endif -void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) +void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) { #ifndef ADLMIDI_HW_OPL - ClearChips(); + clearChips(); #else (void)emulator; (void)PCM_RATE; @@ -495,24 +502,27 @@ void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) #if !defined(ADLMIDI_AUDIO_TICK_HANDLER) (void)audioTickHandler; #endif - ins.clear(); - pit.clear(); - regBD.clear(); + m_ins.clear(); + m_pit.clear(); + m_regBD.clear(); #ifndef ADLMIDI_HW_OPL - cardsOP2.resize(NumCards, AdlMIDI_SPtr<OPLChipBase>()); + m_chips.resize(m_numChips, AdlMIDI_SPtr<OPLChipBase>()); #endif - NumChannels = NumCards * 23; - ins.resize(NumChannels, adl[adlDefaultNumber]); - pit.resize(NumChannels, 0); - regBD.resize(NumCards, 0); - four_op_category.resize(NumChannels, 0); + const struct adldata defaultInsCache = { 0x1557403,0x005B381, 0x49,0x80, 0x4, +0 }; + m_numChannels = m_numChips * 23; + m_ins.resize(m_numChannels, defaultInsCache); + m_pit.resize(m_numChannels, 0); + m_regBD.resize(m_numChips, 0); + m_channelCategory.resize(m_numChannels, 0); - for(unsigned p = 0, a = 0; a < NumCards; ++a) + for(size_t p = 0, a = 0; a < m_numChips; ++a) { - for(unsigned b = 0; b < 18; ++b) four_op_category[p++] = 0; - for(unsigned b = 0; b < 5; ++b) four_op_category[p++] = 8; + for(size_t b = 0; b < 18; ++b) + m_channelCategory[p++] = 0; + for(size_t b = 0; b < 5; ++b) + m_channelCategory[p++] = ChanCat_Rhythm_Slave; } static const uint16_t data[] = @@ -521,9 +531,9 @@ void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable 0x001, 32, 0x105, 1 // Enable wave, OPL3 extensions }; - unsigned fours = NumFourOps; +// size_t fours = m_numFourOps; - for(size_t i = 0; i < NumCards; ++i) + for(size_t i = 0; i < m_numChips; ++i) { #ifndef ADLMIDI_HW_OPL OPLChipBase *chip; @@ -544,91 +554,23 @@ void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) break; #endif } - cardsOP2[i].reset(chip); + m_chips[i].reset(chip); chip->setChipId((uint32_t)i); chip->setRate((uint32_t)PCM_RATE); - if(runAtPcmRate) + if(m_runAtPcmRate) chip->setRunningAtPcmRate(true); # if defined(ADLMIDI_AUDIO_TICK_HANDLER) chip->setAudioTickHandlerInstance(audioTickHandler); # endif #endif // ADLMIDI_HW_OPL - for(unsigned a = 0; a < 18; ++a) Poke(i, 0xB0 + Channels[a], 0x00); - for(unsigned a = 0; a < sizeof(data) / sizeof(*data); a += 2) - Poke(i, data[a], static_cast<uint8_t>(data[a + 1])); - Poke(i, 0x0BD, regBD[i] = (HighTremoloMode * 0x80 - + HighVibratoMode * 0x40 - + AdlPercussionMode * 0x20)); - unsigned fours_this_card = std::min(fours, 6u); - Poke(i, 0x104, (1 << fours_this_card) - 1); - //fprintf(stderr, "Card %u: %u four-ops.\n", card, fours_this_card); - fours -= fours_this_card; - } - - // Mark all channels that are reserved for four-operator function - if(AdlPercussionMode == 1) - { - for(unsigned a = 0; a < NumCards; ++a) - { - for(unsigned b = 0; b < 5; ++b) four_op_category[a * 23 + 18 + b] = static_cast<char>(b + 3); - for(unsigned b = 0; b < 3; ++b) four_op_category[a * 23 + 6 + b] = 8; - } - } - unsigned nextfour = 0; - - for(unsigned a = 0; a < NumFourOps; ++a) - { - four_op_category[nextfour ] = 1; - four_op_category[nextfour + 3] = 2; - - switch(a % 6) - { - case 0: - case 1: - nextfour += 1; - break; - - case 2: - nextfour += 9 - 2; - break; - - case 3: - case 4: - nextfour += 1; - break; - - case 5: - nextfour += 23 - 9 - 2; - break; - } + /* Clean-up channels from any playing junk sounds */ + for(size_t a = 0; a < 18; ++a) + writeReg(i, 0xB0 + g_channelsMap[a], 0x00); + for(size_t a = 0; a < sizeof(data) / sizeof(*data); a += 2) + writeReg(i, data[a], static_cast<uint8_t>(data[a + 1])); } - /**/ - /* - In two-op mode, channels 0..8 go as follows: - Op1[port] Op2[port] - Channel 0: 00 00 03 03 - Channel 1: 01 01 04 04 - Channel 2: 02 02 05 05 - Channel 3: 06 08 09 0B - Channel 4: 07 09 10 0C - Channel 5: 08 0A 11 0D - Channel 6: 12 10 15 13 - Channel 7: 13 11 16 14 - Channel 8: 14 12 17 15 - In four-op mode, channels 0..8 go as follows: - Op1[port] Op2[port] Op3[port] Op4[port] - Channel 0: 00 00 03 03 06 08 09 0B - Channel 1: 01 01 04 04 07 09 10 0C - Channel 2: 02 02 05 05 08 0A 11 0D - Channel 3: CHANNEL 0 SLAVE - Channel 4: CHANNEL 1 SLAVE - Channel 5: CHANNEL 2 SLAVE - Channel 6: 12 10 15 13 - Channel 7: 13 11 16 14 - Channel 8: 14 12 17 15 - Same goes principally for channels 9-17 respectively. - */ - Silence(); + updateChannelCategories(); + silenceAll(); } diff --git a/src/adlmidi_private.cpp b/src/adlmidi_private.cpp index 3bc73a4..8d7b9a7 100644 --- a/src/adlmidi_private.cpp +++ b/src/adlmidi_private.cpp @@ -40,11 +40,13 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device) MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); //Automatically calculate how much 4-operator channels is necessary - if(play->opl.AdlBank == ~0u) +#ifndef DISABLE_EMBEDDED_BANKS + if(play->m_synth.m_embeddedBank == ~0u) +#endif { //For custom bank - OPL3::BankMap::iterator it = play->opl.dynamic_banks.begin(); - OPL3::BankMap::iterator end = play->opl.dynamic_banks.end(); + OPL3::BankMap::iterator it = play->m_synth.m_insBanks.begin(); + OPL3::BankMap::iterator end = play->m_synth.m_insBanks.end(); for(; it != end; ++it) { uint16_t bank = it->first; @@ -60,12 +62,13 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device) } } } +#ifndef DISABLE_EMBEDDED_BANKS else { //For embedded bank for(unsigned a = 0; a < 256; ++a) { - unsigned insno = banks[play->m_setup.AdlBank][a]; + unsigned insno = banks[play->m_setup.bankId][a]; if(insno == 198) continue; ++n_total[a / 128]; @@ -74,6 +77,7 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device) ++n_fourop[a / 128]; } } +#endif unsigned numFourOps = 0; @@ -96,7 +100,7 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device) : (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4); */ - play->opl.NumFourOps = play->m_setup.NumFourOps = (numFourOps * play->m_setup.NumCards); + play->m_synth.m_numFourOps = play->m_setup.numFourOps = (numFourOps * play->m_setup.numChips); return 0; } diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index f34404d..c2771fc 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -145,10 +145,13 @@ typedef BW_MidiSequencer MidiSequencer; #endif #include "adldata.hh" + #include "adlmidi.h" //Main API + #ifndef ADLMIDI_DISABLE_CPP_EXTRAS #include "adlmidi.hpp" //Extra C++ API #endif + #include "adlmidi_ptr.hpp" #include "adlmidi_bankmap.h" @@ -208,101 +211,231 @@ inline int32_t adl_cvtU32(int32_t x) } struct ADL_MIDIPlayer; +/** + * @brief OPL3 Chip management class + */ class OPL3 { -public: friend class MIDIplay; friend class AdlInstrumentTester; - uint32_t NumChannels; + friend int adlRefreshNumCards(ADL_MIDIPlayer *device); +public: + enum { PercussionTag = 1 << 15 }; + + //! Total number of chip channels between all running emulators + uint32_t m_numChannels; + //! Just a padding. Reserved. char ____padding[4]; #ifndef ADLMIDI_HW_OPL - std::vector<AdlMIDI_SPtr<OPLChipBase > > cardsOP2; + std::vector<AdlMIDI_SPtr<OPLChipBase > > m_chips; #endif + private: - std::vector<adldata> ins; // patch data, cached, needed by Touch() - std::vector<uint8_t> pit; // value poked to B0, cached, needed by NoteOff)( - std::vector<uint8_t> regBD; + //! Cached patch data, needed by Touch() + std::vector<adldata> m_ins; + //! Value poked to B0, cached, needed by NoteOff) + std::vector<uint8_t> m_pit; + //! Cached BD registry value (flags register: DeepTremolo, DeepVibrato, and RhythmMode) + std::vector<uint8_t> m_regBD; - friend int adlRefreshNumCards(ADL_MIDIPlayer *device); public: + /** + * @brief MIDI bank entry + */ struct Bank { + //! MIDI Bank instruments adlinsdata2 ins[128]; }; typedef BasicBankMap<Bank> BankMap; - BankMap dynamic_banks; - AdlBankSetup dynamic_bank_setup; + //! MIDI bank instruments data + BankMap m_insBanks; + //! MIDI bank-wide setup + AdlBankSetup m_insBankSetup; public: - void setEmbeddedBank(unsigned int bank); - static const adlinsdata2 emptyInstrument; - enum { PercussionTag = 1 << 15 }; - + //! Blank instrument template + static const adlinsdata2 m_emptyInstrument; //! Total number of running concurrent emulated chips - unsigned int NumCards; + uint32_t m_numChips; //! Currently running embedded bank number. "~0" means usign of the custom bank. - unsigned int AdlBank; + uint32_t m_embeddedBank; //! Total number of needed four-operator channels in all running chips - unsigned int NumFourOps; + uint32_t m_numFourOps; //! Turn global Deep Tremolo mode on - bool HighTremoloMode; + bool m_deepTremoloMode; //! Turn global Deep Vibrato mode on - bool HighVibratoMode; - //! Use AdLib percussion mode - bool AdlPercussionMode; + bool m_deepVibratoMode; + //! Use Rhythm Mode percussions + bool m_rhythmMode; //! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too. - bool ScaleModulators; + bool m_scaleModulators; //! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates. - bool runAtPcmRate; - // ! Required to play CMF files. Can be turned on by using of "CMF" volume model - //bool LogarithmicVolumes; //[REPLACED WITH "m_volumeScale == VOLUME_NATIVE", DEPRECATED!!!] - // ! Required to play EA-MUS files [REPLACED WITH "m_musicMode", DEPRECATED!!!] - //bool CartoonersVolumes; + bool m_runAtPcmRate; + + /** + * @brief Music playing mode + */ enum MusicMode { + //! MIDI mode MODE_MIDI, + //! Id-Software Music mode MODE_IMF, + //! Creative Music Files mode MODE_CMF, + //! EA-MUS (a.k.a. RSXX) mode MODE_RSXX } m_musicMode; + //! Just a padding. Reserved. char ___padding2[3]; - //! Volume models enum + + /** + * @brief Volume models enum + */ enum VolumesScale { + //! Generic volume model (linearization of logarithmic scale) VOLUME_Generic, + //! OPL3 native logarithmic scale VOLUME_NATIVE, + //! DMX volume scale logarithmic table VOLUME_DMX, + //! Apoge Sound System volume scaling model VOLUME_APOGEE, + //! Windows 9x driver volume scale table VOLUME_9X } m_volumeScale; - OPL3(); + //! Reserved char ____padding3[8]; - std::vector<char> four_op_category; // 1 = quad-master, 2 = quad-slave, 0 = regular - // 3 = percussion BassDrum - // 4 = percussion Snare - // 5 = percussion Tom - // 6 = percussion Crash cymbal - // 7 = percussion Hihat - // 8 = percussion slave - - void Poke(size_t card, uint16_t index, uint8_t value); - - void NoteOff(size_t c); - void NoteOn(unsigned c, double hertz); - void Touch_Real(unsigned c, unsigned volume, uint8_t brightness = 127); - //void Touch(unsigned c, unsigned volume) - - void Patch(uint16_t c, const adldata &adli); - void Pan(unsigned c, unsigned value); - void Silence(); - void updateFlags(); - void updateDeepFlags(); - void ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel); + + /** + * @brief Channel categiry enumeration + */ + enum ChanCat + { + //! Regular melodic/percussion channel + ChanCat_Regular = 0, + //! Four-op master + ChanCat_4op_Master = 1, + //! Four-op slave + ChanCat_4op_Slave = 2, + //! Rhythm-mode Bass drum + ChanCat_Rhythm_Bass = 3, + //! Rhythm-mode Snare drum + ChanCat_Rhythm_Snare = 4, + //! Rhythm-mode Tom-Tom + ChanCat_Rhythm_Tom = 5, + //! Rhythm-mode Cymbal + ChanCat_Rhythm_Cymbal = 6, + //! Rhythm-mode Hi-Hat + ChanCat_Rhythm_HiHat = 7, + //! Rhythm-mode Slave channel + ChanCat_Rhythm_Slave = 8 + }; + + //! Category of the channel + /*! 1 = quad-master, 2 = quad-slave, 0 = regular + 3 = percussion BassDrum + 4 = percussion Snare + 5 = percussion Tom + 6 = percussion Crash cymbal + 7 = percussion Hihat + 8 = percussion slave + */ + std::vector<char> m_channelCategory; + + + /** + * @brief C.O. Constructor + */ + OPL3(); + + /** + * @brief Choose one of embedded banks + * @param bank ID of the bank + */ + void setEmbeddedBank(uint32_t bank); + + /** + * @brief Write data to OPL3 chip register + * @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored + * @param index Register address to write + * @param value Value to write + */ + void writeReg(size_t chip, uint16_t address, uint8_t value); + + /** + * @brief Off the note in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + */ + void noteOff(size_t c); + + /** + * @brief On the note in specified chip channel with specified frequency of the tone + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param hertz Frequency of the tone in hertzes + */ + void noteOn(size_t c, double hertz); + + /** + * @brief Change setup of instrument in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param volume Volume level (from 0 to 63) + * @param brightness CC74 Brightness level (from 0 to 127) + */ + void touchReal(uint32_t c, uint8_t volume, uint8_t brightness = 127); + + /** + * @brief Set the instrument into specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param instrument Instrument data to set into the chip channel + */ + void setPatch(uint16_t c, const adldata &instrument); + + /** + * @brief Set panpot position + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param value 3-bit panpot value + */ + void setPan(size_t c, uint8_t value); + + /** + * @brief Shut up all chip channels + */ + void silenceAll(); + + /** + * @brief Commit updated flag states to chip registers + */ + void updateChannelCategories(); + + /** + * @brief commit deepTremolo and deepVibrato flags + */ + void commitDeepFlags(); + + /** + * @brief Set the volume scaling model + * @param volumeModel Type of volume scale model scale + */ + void setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel); + #ifndef ADLMIDI_HW_OPL - void ClearChips(); + /** + * @brief Clean up all running emulated chip instances + */ + void clearChips(); #endif - void Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler); + + /** + * @brief Reset chip properties and initialize them + * @param emulator Type of chip emulator + * @param PCM_RATE Output sample rate to generate on output + * @param audioTickHandler PCM-accurate clock hook + */ + void reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler); }; @@ -343,56 +476,105 @@ public: /**********************Internal structures and classes**********************/ - // Persistent settings for each MIDI channel + /** + * @brief Persistent settings for each MIDI channel + */ struct MIDIchannel { - uint8_t bank_lsb, bank_msb; + //! LSB Bank number + uint8_t bank_lsb, + //! MSB Bank number + bank_msb; + //! Current patch number uint8_t patch; - uint8_t volume, expression; - uint8_t panning, vibrato, aftertouch; + //! Volume level + uint8_t volume, + //! Expression level + expression; + //! Panning level + uint8_t panning, + //! Vibrato level + vibrato, + //! Channel aftertouch level + aftertouch; + //! Portamento time uint16_t portamento; + //! Is Pedal sustain active bool sustain; + //! Is Soft pedal active bool softPedal; + //! Is portamento enabled bool portamentoEnable; + //! Source note number used by portamento int8_t portamentoSource; // note number or -1 + //! Portamento rate double portamentoRate; //! Per note Aftertouch values uint8_t noteAftertouch[128]; //! Is note aftertouch has any non-zero value bool noteAfterTouchInUse; + //! Reserved char ____padding[6]; + //! Pitch bend value int bend; + //! Pitch bend sensitivity double bendsense; - int bendsense_lsb, bendsense_msb; - double vibpos, vibspeed, vibdepth; + //! Pitch bend sensitivity LSB value + int bendsense_lsb, + //! Pitch bend sensitivity MSB value + bendsense_msb; + //! Vibrato position value + double vibpos, + //! Vibrato speed value + vibspeed, + //! Vibrato depth value + vibdepth; + //! Vibrato delay time int64_t vibdelay; - uint8_t lastlrpn, lastmrpn; + //! Last LSB part of RPN value received + uint8_t lastlrpn, + //! Last MSB poart of RPN value received + lastmrpn; + //! Interpret RPN value as NRPN bool nrpn; + //! Brightness level uint8_t brightness; + + //! Is melodic channel turned into percussion bool is_xg_percussion; + + /** + * @brief Per-Note information + */ struct NoteInfo { + //! Note number uint8_t note; + //! Is note active bool active; - // Current pressure + //! Current pressure uint8_t vol; - // Note vibrato (a part of Note Aftertouch feature) + //! Note vibrato (a part of Note Aftertouch feature) uint8_t vibrato; - // Tone selected on noteon: + //! Tone selected on noteon: int16_t noteTone; - // Current tone (!= noteTone if gliding note) + //! Current tone (!= noteTone if gliding note) double currentTone; - // Gliding rate + //! Gliding rate double glideRate; - // Patch selected on noteon; index to bank.ins[] + //! Patch selected on noteon; index to bank.ins[] size_t midiins; - // Patch selected + //! Patch selected const adlinsdata2 *ains; enum { MaxNumPhysChans = 2, MaxNumPhysItemCount = MaxNumPhysChans, }; + + /** + * @brief Reference to currently using chip channel + */ struct Phys { //! Destination chip channel @@ -416,11 +598,12 @@ public: return !operator==(oth); } }; + //! List of OPL3 channels it is currently occupying. Phys chip_channels[MaxNumPhysItemCount]; //! Count of used channels. unsigned chip_channels_count; - // + Phys *phys_find(unsigned chip_chan) { Phys *ph = NULL; @@ -461,8 +644,13 @@ public: phys_erase_at(ph); } }; + + //! Reserved char ____padding2[5]; + //! Count of gliding notes in this channel unsigned gliding_note_count; + + //! Active notes in the channel NoteInfo activenotes[128]; struct activenoteiterator @@ -546,6 +734,9 @@ public: } } + /** + * @brief Reset channel into initial state + */ void reset() { resetAllControllers(); @@ -558,6 +749,10 @@ public: nrpn = false; is_xg_percussion = false; } + + /** + * @brief Reset all MIDI controllers into initial state + */ void resetAllControllers() { bend = 0; @@ -582,15 +777,25 @@ public: portamentoRate = HUGE_VAL; brightness = 127; } + + /** + * @brief Has channel vibrato to process + * @return + */ bool hasVibrato() { return (vibrato > 0) || (aftertouch > 0) || noteAfterTouchInUse; } + + /** + * @brief Commit pitch bend sensitivity value from MSB and LSB + */ void updateBendSensitivity() { int cent = bendsense_msb * 128 + bendsense_lsb; bendsense = cent * (1.0 / (128 * 8192)); } + MIDIchannel() { activenotes_clear(); @@ -599,7 +804,9 @@ public: } }; - // Additional information about OPL3 channels + /** + * @brief Additional information about OPL3 channels + */ struct AdlChannel { struct Location @@ -631,7 +838,7 @@ public: int64_t vibdelay; }; - // If the channel is keyoff'd + //! Time left until sounding will be muted after key off int64_t koff_time_until_neglible; enum { users_max = 128 }; @@ -672,7 +879,11 @@ public: return *this; } - void AddAge(int64_t ms); + /** + * @brief Increases age of active note in milliseconds time + * @param ms Amount time in milliseconds + */ + void addAge(int64_t ms); }; #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER @@ -696,16 +907,16 @@ public: { int emulator; bool runAtPcmRate; - unsigned int AdlBank; - unsigned int NumFourOps; - unsigned int NumCards; - int HighTremoloMode; - int HighVibratoMode; - int AdlPercussionMode; - bool LogarithmicVolumes; - int VolumeModel; + unsigned int bankId; + unsigned int numFourOps; + unsigned int numChips; + int deepTremoloMode; + int deepVibratoMode; + int rhythmMode; + bool logarithmicVolumes; + int volumeScaleModel; //unsigned int SkipForward; - int ScaleModulators; + int scaleModulators; bool fullRangeBrightnessCC74; double delay; @@ -723,18 +934,34 @@ public: unsigned long PCM_RATE; }; + /** + * @brief MIDI Marker entry + */ struct MIDI_MarkerEntry { + //! Label of marker std::string label; + //! Absolute position in seconds double pos_time; + //! Absolute position in ticks in the track uint64_t pos_ticks; }; - std::vector<MIDIchannel> Ch; - bool cmf_percussion_mode; + //! Available MIDI Channels + std::vector<MIDIchannel> m_midiChannels; + + //! CMF Rhythm mode + bool m_cmfPercussionMode; + + //! Master volume, controlled via SysEx uint8_t m_masterVolume; + + //! SysEx device ID uint8_t m_sysExDeviceId; + /** + * @brief MIDI Synthesizer mode + */ enum SynthMode { Mode_GM = 0x00, @@ -742,18 +969,23 @@ public: Mode_XG = 0x02, Mode_GM2 = 0x04, }; + //! MIDI Synthesizer mode uint32_t m_synthMode; + //! Installed function hooks MIDIEventHooks hooks; private: - std::map<std::string, uint64_t> devices; - std::map<uint64_t /*track*/, uint64_t /*channel begin index*/> current_device; + //! Per-track MIDI devices map + std::map<std::string, uint64_t> m_midiDevices; + //! Current MIDI device per track + std::map<uint64_t /*track*/, uint64_t /*channel begin index*/> m_currentMidiDevice; - //Padding to fix CLanc code model's warning + //! Padding to fix CLanc code model's warning char ____padding[7]; - std::vector<AdlChannel> ch; + //! Chip channels map + std::vector<AdlChannel> m_chipChannels; //! Counter of arpeggio processing size_t m_arpeggioCounter; @@ -777,14 +1009,35 @@ public: const std::string &getErrorString(); void setErrorString(const std::string &err); - OPL3 opl; + //! OPL3 Chip manager + OPL3 m_synth; - int32_t outBuf[1024]; + //! Generator output buffer + int32_t m_outBuf[1024]; + //! Synthesizer setup Setup m_setup; + /** + * @brief Load custom bank from file + * @param filename Path to bank file + * @return true on succes + */ bool LoadBank(const std::string &filename); + + /** + * @brief Load custom bank from memory block + * @param data Pointer to memory block where raw bank file is stored + * @param size Size of given memory block + * @return true on succes + */ bool LoadBank(const void *data, size_t size); + + /** + * @brief Load custom bank from opened FileAndMemReader class + * @param fr Instance with opened file + * @return true on succes + */ bool LoadBank(FileAndMemReader &fr); #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER @@ -967,6 +1220,9 @@ public: #endif private: + /** + * @brief Hardware manufacturer (Used for SysEx) + */ enum { Manufacturer_Roland = 0x41, @@ -974,11 +1230,19 @@ private: Manufacturer_UniversalNonRealtime = 0x7E, Manufacturer_UniversalRealtime = 0x7F }; + + /** + * @brief Roland Mode (Used for SysEx) + */ enum { RolandMode_Request = 0x11, RolandMode_Send = 0x12 }; + + /** + * @brief Device model (Used for SysEx) + */ enum { RolandModel_GS = 0x42, @@ -986,11 +1250,38 @@ private: YamahaModel_XG = 0x4C }; + /** + * @brief Process generic SysEx events + * @param dev Device ID + * @param realtime Is real-time event + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ bool doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Roland devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ bool doRolandSysEx(unsigned dev, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Yamaha devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ bool doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size); private: + /** + * @brief Note Update properties + */ enum { Upd_Patch = 0x1, @@ -1003,39 +1294,119 @@ private: Upd_OffMute = Upd_Off + Upd_Mute }; - void NoteUpdate(uint16_t MidCh, + /** + * @brief Update active note + * @param MidCh MIDI Channel where note is processing + * @param i Iterator that points to active note in the MIDI channel + * @param props_mask Properties to update + * @param select_adlchn Specify chip channel, or -1 - all chip channels used by the note + */ + void noteUpdate(uint16_t midCh, MIDIchannel::activenoteiterator i, unsigned props_mask, int32_t select_adlchn = -1); - // Determine how good a candidate this adlchannel - // would be for playing a note from this instrument. - int64_t CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t /*MidCh*/) const; + /** + * @brief Update all notes in specified MIDI channel + * @param midCh MIDI channel to update all notes in it + * @param props_mask Properties to update + */ + void noteUpdateAll(size_t midCh, unsigned props_mask); + - // A new note will be played on this channel using this instrument. - // Kill existing notes on this channel (or don't, if we do arpeggio) - void PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); + /** + * @brief Determine how good a candidate this adlchannel would be for playing a note from this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * @return Calculated coodness points + */ + int64_t calculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const; + + /** + * @brief A new note will be played on this channel using this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * Kill existing notes on this channel (or don't, if we do arpeggio) + */ + void prepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); - void KillOrEvacuate( + /** + * @brief Kills note that uses wanted channel. When arpeggio is possible, note is evaluating to another channel + * @param from_channel Wanted chip channel + * @param j Chip channel instance + * @param i MIDI Channel active note instance + */ + void killOrEvacuate( size_t from_channel, AdlChannel::LocationData *j, MIDIchannel::activenoteiterator i); - void Panic(); - void KillSustainingNotes(int32_t MidCh = -1, + + /** + * @brief Off all notes and silence sound + */ + void panic(); + + /** + * @brief Kill note, sustaining by pedal or sostenuto + * @param MidCh MIDI channel, -1 - all MIDI channels + * @param this_adlchn Chip channel, -1 - all chip channels + * @param sustain_type Type of systain to process + */ + void killSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1, uint8_t sustain_type = AdlChannel::LocationData::Sustain_ANY); - void MarkSostenutoNotes(int32_t MidCh = -1); - void SetRPN(unsigned MidCh, unsigned value, bool MSB); - void UpdatePortamento(unsigned MidCh); - void NoteUpdate_All(uint16_t MidCh, unsigned props_mask); - void NoteOff(uint16_t MidCh, uint8_t note); + /** + * @brief Find active notes and mark them as sostenuto-sustained + * @param MidCh MIDI channel, -1 - all MIDI channels + */ + void markSostenutoNotes(int32_t MidCh = -1); + + /** + * @brief Set RPN event value + * @param MidCh MIDI channel + * @param value 1 byte part of RPN value + * @param MSB is MSB or LSB part of value + */ + void setRPN(unsigned MidCh, unsigned value, bool MSB); + + /** + * @brief Update portamento setup in MIDI channel + * @param midCh MIDI channel where portamento needed to be updated + */ + void updatePortamento(size_t midCh); + + /** + * @brief Off the note + * @param midCh MIDI channel + * @param note Note to off + */ + void noteOff(uint16_t midCh, uint8_t note); - void UpdateVibrato(double amount); - void UpdateArpeggio(double /*amount*/); - void UpdateGlide(double amount); + /** + * @brief Update processing of vibrato to amount of seconds + * @param amount Amount value in seconds + */ + void updateVibrato(double amount); + + /** + * @brief Update auto-arpeggio + * @param amount Amount value in seconds [UNUSED] + */ + void updateArpeggio(double /*amount*/); + + /** + * @brief Update Portamento gliding to amount of seconds + * @param amount Amount value in seconds + */ + void updateGlide(double amount); public: - uint64_t ChooseDevice(const std::string &name); + /** + * @brief Checks was device name used or not + * @param name Name of MIDI device + * @return Offset of the MIDI Channels, multiple to 16 + */ + uint64_t chooseDevice(const std::string &name); }; // I think, this is useless inside of Library diff --git a/src/adlmidi_sequencer.cpp b/src/adlmidi_sequencer.cpp index b8bf3dd..516f80d 100644 --- a/src/adlmidi_sequencer.cpp +++ b/src/adlmidi_sequencer.cpp @@ -136,13 +136,13 @@ double MIDIplay::Tick(double s, double granularity) double ret = m_sequencer.Tick(s, granularity); s *= m_sequencer.getTempoMultiplier(); - for(uint16_t c = 0; c < opl.NumChannels; ++c) - ch[c].AddAge(static_cast<int64_t>(s * 1000.0)); + for(uint16_t c = 0; c < m_synth.m_numChannels; ++c) + m_chipChannels[c].addAge(static_cast<int64_t>(s * 1000.0)); - UpdateVibrato(s); - UpdateArpeggio(s); + updateVibrato(s); + updateArpeggio(s); #if !defined(ADLMIDI_AUDIO_TICK_HANDLER) - UpdateGlide(s); + updateGlide(s); #endif return ret; diff --git a/src/chips/opl_chip_base.tcc b/src/chips/opl_chip_base.tcc index 48ad103..a64aa7c 100644 --- a/src/chips/opl_chip_base.tcc +++ b/src/chips/opl_chip_base.tcc @@ -236,8 +236,8 @@ void OPLChipBaseT<T>::resampledGenerate(int32_t *output) rsm->out_count = 1; rsm->out_data = f_out; } - output[0] = std::lround(f_out[0]); - output[1] = std::lround(f_out[1]); + output[0] = static_cast<int32_t>(std::lround(f_out[0])); + output[1] = static_cast<int32_t>(std::lround(f_out[1])); } #else template <class T> |