diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/adlmidi.cpp | 156 | ||||
-rw-r--r-- | src/adlmidi_load.cpp | 75 | ||||
-rw-r--r-- | src/adlmidi_midiplay.cpp | 152 | ||||
-rw-r--r-- | src/adlmidi_midiplay.hpp | 1029 | ||||
-rw-r--r-- | src/adlmidi_private.cpp | 12 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 988 | ||||
-rw-r--r-- | src/adlmidi_ptr.hpp | 2 | ||||
-rw-r--r-- | src/adlmidi_sequencer.cpp | 44 |
8 files changed, 1278 insertions, 1180 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp index 12d0e68..7f75b6e 100644 --- a/src/adlmidi.cpp +++ b/src/adlmidi.cpp @@ -21,6 +21,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "adlmidi_midiplay.hpp" #include "adlmidi_private.hpp" /* Unify MIDI player casting and interface between ADLMIDI and OPNMIDI */ @@ -112,13 +113,14 @@ ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numChips) else if(play->m_setup.numFourOps < -1) play->m_setup.numFourOps = -1; - if(!play->m_synth.setupLocked()) + OPL3 &synth = *play->m_synth; + if(!synth.setupLocked()) { - play->m_synth.m_numChips = play->m_setup.numChips; + synth.m_numChips = play->m_setup.numChips; if(play->m_setup.numFourOps < 0) adlCalculateFourOpChannels(play, true); else - play->m_synth.m_numFourOps = static_cast<uint32_t>(play->m_setup.numFourOps); + synth.m_numFourOps = static_cast<uint32_t>(play->m_setup.numFourOps); play->partialReset(); return 0; } @@ -141,7 +143,7 @@ ADLMIDI_EXPORT int adl_getNumChipsObtained(struct ADL_MIDIPlayer *device) return -2; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return (int)play->m_synth.m_numChips; + return (int)play->m_synth->m_numChips; } ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) @@ -171,8 +173,9 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) return -1; } + OPL3 &synth = *play->m_synth; play->m_setup.bankId = static_cast<uint32_t>(bankno); - play->m_synth.setEmbeddedBank(play->m_setup.bankId); + synth.setEmbeddedBank(play->m_setup.bankId); play->applySetup(); return 0; @@ -203,7 +206,7 @@ ADLMIDI_EXPORT int adl_reserveBanks(ADL_MIDIPlayer *device, unsigned banks) return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - OPL3::BankMap &map = play->m_synth.m_insBanks; + OPL3::BankMap &map = play->m_synth->m_insBanks; map.reserve(banks); return (int)map.capacity(); } @@ -220,7 +223,7 @@ ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, in MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - OPL3::BankMap &map = play->m_synth.m_insBanks; + OPL3::BankMap &map = play->m_synth->m_insBanks; OPL3::BankMap::iterator it; if(!(flags & ADLMIDI_Bank_Create)) @@ -273,7 +276,7 @@ ADLMIDI_EXPORT int adl_removeBank(ADL_MIDIPlayer *device, ADL_Bank *bank) MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - OPL3::BankMap &map = play->m_synth.m_insBanks; + 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); @@ -287,7 +290,7 @@ ADLMIDI_EXPORT int adl_getFirstBank(ADL_MIDIPlayer *device, ADL_Bank *bank) MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - OPL3::BankMap &map = play->m_synth.m_insBanks; + OPL3::BankMap &map = play->m_synth->m_insBanks; OPL3::BankMap::iterator it = map.begin(); if(it == map.end()) @@ -304,7 +307,7 @@ ADLMIDI_EXPORT int adl_getNextBank(ADL_MIDIPlayer *device, ADL_Bank *bank) MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - OPL3::BankMap &map = play->m_synth.m_insBanks; + OPL3::BankMap &map = play->m_synth->m_insBanks; OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); if(++it == map.end()) @@ -383,14 +386,15 @@ ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4) return -1; } + OPL3 &synth = *play->m_synth; play->m_setup.numFourOps = ops4; - if(!play->m_synth.setupLocked()) + if(!synth.setupLocked()) { if(play->m_setup.numFourOps < 0) adlCalculateFourOpChannels(play, true); else - play->m_synth.m_numFourOps = static_cast<uint32_t>(play->m_setup.numFourOps); - play->m_synth.updateChannelCategories(); + synth.m_numFourOps = static_cast<uint32_t>(play->m_setup.numFourOps); + synth.updateChannelCategories(); } return 0; @@ -411,7 +415,7 @@ ADLMIDI_EXPORT int adl_getNumFourOpsChnObtained(struct ADL_MIDIPlayer *device) return -2; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return (int)play->m_synth.m_numFourOps; + return (int)play->m_synth->m_numFourOps; } @@ -420,13 +424,14 @@ ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod) if(!device) return; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); + OPL3 &synth = *play->m_synth; play->m_setup.rhythmMode = percmod; - if(!play->m_synth.setupLocked()) + if(!synth.setupLocked()) { - play->m_synth.m_rhythmMode = play->m_setup.rhythmMode < 0 ? - (play->m_synth.m_insBankSetup.adLibPercussions) : + synth.m_rhythmMode = play->m_setup.rhythmMode < 0 ? + (synth.m_insBankSetup.adLibPercussions) : (play->m_setup.rhythmMode != 0); - play->m_synth.updateChannelCategories(); + synth.updateChannelCategories(); } } @@ -435,13 +440,14 @@ ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro) if(!device) return; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); + OPL3 &synth = *play->m_synth; play->m_setup.deepVibratoMode = hvibro; - if(!play->m_synth.setupLocked()) + if(!synth.setupLocked()) { - play->m_synth.m_deepVibratoMode = play->m_setup.deepVibratoMode < 0 ? - play->m_synth.m_insBankSetup.deepVibrato : + synth.m_deepVibratoMode = play->m_setup.deepVibratoMode < 0 ? + synth.m_insBankSetup.deepVibrato : (play->m_setup.deepVibratoMode != 0); - play->m_synth.commitDeepFlags(); + synth.commitDeepFlags(); } } @@ -450,7 +456,7 @@ ADLMIDI_EXPORT int adl_getHVibrato(struct ADL_MIDIPlayer *device) if(!device) return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_synth.m_deepVibratoMode; + return play->m_synth->m_deepVibratoMode; } ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo) @@ -458,13 +464,14 @@ ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo) if(!device) return; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); + OPL3 &synth = *play->m_synth; play->m_setup.deepTremoloMode = htremo; - if(!play->m_synth.setupLocked()) + if(!synth.setupLocked()) { - play->m_synth.m_deepTremoloMode = play->m_setup.deepTremoloMode < 0 ? - play->m_synth.m_insBankSetup.deepTremolo : + synth.m_deepTremoloMode = play->m_setup.deepTremoloMode < 0 ? + synth.m_insBankSetup.deepTremolo : (play->m_setup.deepTremoloMode != 0); - play->m_synth.commitDeepFlags(); + synth.commitDeepFlags(); } } @@ -473,7 +480,7 @@ ADLMIDI_EXPORT int adl_getHTremolo(struct ADL_MIDIPlayer *device) if(!device) return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_synth.m_deepTremoloMode; + return play->m_synth->m_deepTremoloMode; } ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod) @@ -482,11 +489,12 @@ ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod) return; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); + OPL3 &synth = *play->m_synth; play->m_setup.scaleModulators = smod; - if(!play->m_synth.setupLocked()) + if(!synth.setupLocked()) { - play->m_synth.m_scaleModulators = play->m_setup.scaleModulators < 0 ? - play->m_synth.m_insBankSetup.scaleModulators : + synth.m_scaleModulators = play->m_setup.scaleModulators < 0 ? + synth.m_insBankSetup.scaleModulators : (play->m_setup.scaleModulators != 0); } } @@ -507,7 +515,7 @@ ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn) MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - play->m_sequencer.setLoopEnabled(loopEn != 0); + play->m_sequencer->setLoopEnabled(loopEn != 0); #else ADL_UNUSED(loopEn); #endif @@ -519,7 +527,7 @@ ADLMIDI_EXPORT void adl_setSoftPanEnabled(ADL_MIDIPlayer *device, int softPanEn) return; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - play->m_synth.m_softPanning = (softPanEn != 0); + play->m_synth->m_softPanning = (softPanEn != 0); } /* !!!DEPRECATED!!! */ @@ -529,13 +537,14 @@ ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int return; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); + OPL3 &synth = *play->m_synth; play->m_setup.logarithmicVolumes = (logvol != 0); - if(!play->m_synth.setupLocked()) + if(!synth.setupLocked()) { if(play->m_setup.logarithmicVolumes) - play->m_synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); + synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); else - play->m_synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(play->m_synth.m_volumeScale)); + synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(synth.m_volumeScale)); } } @@ -545,13 +554,14 @@ ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int v return; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); + OPL3 &synth = *play->m_synth; play->m_setup.volumeScaleModel = volumeModel; - if(!play->m_synth.setupLocked()) + if(!synth.setupLocked()) { 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; + synth.m_volumeScale = (OPL3::VolumesScale)synth.m_insBankSetup.volumeModel; else - play->m_synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(volumeModel)); + synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(volumeModel)); } } @@ -561,7 +571,7 @@ ADLMIDI_EXPORT int adl_getVolumeRangeModel(struct ADL_MIDIPlayer *device) return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_synth.getVolumeScaleModel(); + return play->m_synth->getVolumeScaleModel(); } ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath) @@ -675,8 +685,9 @@ ADLMIDI_EXPORT const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device) #ifndef ADLMIDI_HW_OPL MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - if(!play->m_synth.m_chips.empty()) - return play->m_synth.m_chips[0]->emulatorName(); + OPL3 &synth = *play->m_synth; + if(!synth.m_chips.empty()) + return synth.m_chips[0]->emulatorName(); #else return "Hardware OPL3 chip on 0x330"; #endif @@ -708,8 +719,9 @@ ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled) { MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); + OPL3 &synth = *play->m_synth; play->m_setup.runAtPcmRate = (enabled != 0); - if(!play->m_synth.setupLocked()) + if(!synth.setupLocked()) play->partialReset(); return 0; } @@ -763,7 +775,7 @@ ADLMIDI_EXPORT double adl_totalTimeLength(struct ADL_MIDIPlayer *device) return -1.0; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_sequencer.timeLength(); + return play->m_sequencer->timeLength(); #else ADL_UNUSED(device); return -1.0; @@ -777,7 +789,7 @@ ADLMIDI_EXPORT double adl_loopStartTime(struct ADL_MIDIPlayer *device) return -1.0; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_sequencer.getLoopStart(); + return play->m_sequencer->getLoopStart(); #else ADL_UNUSED(device); return -1.0; @@ -791,7 +803,7 @@ ADLMIDI_EXPORT double adl_loopEndTime(struct ADL_MIDIPlayer *device) return -1.0; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_sequencer.getLoopEnd(); + return play->m_sequencer->getLoopEnd(); #else ADL_UNUSED(device); return -1.0; @@ -805,7 +817,7 @@ ADLMIDI_EXPORT double adl_positionTell(struct ADL_MIDIPlayer *device) return -1.0; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_sequencer.tell(); + return play->m_sequencer->tell(); #else ADL_UNUSED(device); return -1.0; @@ -822,7 +834,7 @@ ADLMIDI_EXPORT void adl_positionSeek(struct ADL_MIDIPlayer *device, double secon MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); play->realTime_panic(); - play->m_setup.delay = play->m_sequencer.seek(seconds, play->m_setup.mindelay); + play->m_setup.delay = play->m_sequencer->seek(seconds, play->m_setup.mindelay); play->m_setup.carry = 0.0; #else ADL_UNUSED(device); @@ -838,7 +850,7 @@ ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device) MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); play->realTime_panic(); - play->m_sequencer.rewind(); + play->m_sequencer->rewind(); #else ADL_UNUSED(device); #endif @@ -851,7 +863,7 @@ ADLMIDI_EXPORT void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo) return; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - play->m_sequencer.setTempo(tempo); + play->m_sequencer->setTempo(tempo); #else ADL_UNUSED(device); ADL_UNUSED(tempo); @@ -877,7 +889,7 @@ ADLMIDI_EXPORT const char *adl_metaMusicTitle(struct ADL_MIDIPlayer *device) return ""; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_sequencer.getMusicTitle().c_str(); + return play->m_sequencer->getMusicTitle().c_str(); #else ADL_UNUSED(device); return ""; @@ -892,7 +904,7 @@ ADLMIDI_EXPORT const char *adl_metaMusicCopyright(struct ADL_MIDIPlayer *device) return ""; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_sequencer.getMusicCopyright().c_str(); + return play->m_sequencer->getMusicCopyright().c_str(); #else ADL_UNUSED(device); return ""; @@ -906,7 +918,7 @@ ADLMIDI_EXPORT size_t adl_metaTrackTitleCount(struct ADL_MIDIPlayer *device) return 0; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_sequencer.getTrackTitles().size(); + return play->m_sequencer->getTrackTitles().size(); #else ADL_UNUSED(device); return 0; @@ -920,7 +932,7 @@ ADLMIDI_EXPORT const char *adl_metaTrackTitle(struct ADL_MIDIPlayer *device, siz return ""; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - const std::vector<std::string> &titles = play->m_sequencer.getTrackTitles(); + const std::vector<std::string> &titles = play->m_sequencer->getTrackTitles(); if(index >= titles.size()) return "INVALID"; return titles[index].c_str(); @@ -939,7 +951,7 @@ ADLMIDI_EXPORT size_t adl_metaMarkerCount(struct ADL_MIDIPlayer *device) return 0; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_sequencer.getMarkers().size(); + return play->m_sequencer->getMarkers().size(); #else ADL_UNUSED(device); return 0; @@ -962,7 +974,7 @@ ADLMIDI_EXPORT Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, siz MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - const std::vector<MidiSequencer::MIDI_MarkerEntry> &markers = play->m_sequencer.getMarkers(); + const std::vector<MidiSequencer::MIDI_MarkerEntry> &markers = play->m_sequencer->getMarkers(); if(index >= markers.size()) { marker.label = "INVALID"; @@ -992,8 +1004,8 @@ ADLMIDI_EXPORT void adl_setRawEventHook(struct ADL_MIDIPlayer *device, ADL_RawEv return; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - play->m_sequencerInterface.onEvent = rawEventHook; - play->m_sequencerInterface.onEvent_userData = userData; + play->m_sequencerInterface->onEvent = rawEventHook; + play->m_sequencerInterface->onEvent_userData = userData; #else ADL_UNUSED(device); ADL_UNUSED(rawEventHook); @@ -1022,8 +1034,8 @@ ADLMIDI_EXPORT void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_D play->hooks.onDebugMessage = debugMessageHook; play->hooks.onDebugMessage_userData = userData; #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - play->m_sequencerInterface.onDebugMessage = debugMessageHook; - play->m_sequencerInterface.onDebugMessage_userData = userData; + play->m_sequencerInterface->onDebugMessage = debugMessageHook; + play->m_sequencerInterface->onDebugMessage_userData = userData; #endif } @@ -1273,7 +1285,7 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, // setup.SkipForward -= 1; //else { - if((player->m_sequencer.positionAtEnd()) && (setup.delay <= 0.0)) + if((player->m_sequencer->positionAtEnd()) && (setup.delay <= 0.0)) break;//Stop to fetch samples at reaching the song end with disabled loop ssize_t leftSamples = left / 2; @@ -1290,16 +1302,17 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, //fill buffer with zeros 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->m_synth.m_numChips; + OPL3 &synth = *player->m_synth; + unsigned int chips = synth.m_numChips; if(chips == 1) { - player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); + 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->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); } /* Process it */ @@ -1380,14 +1393,15 @@ ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleC //fill buffer with zeros 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->m_synth.m_numChips; + OPL3 &synth = *player->m_synth; + unsigned int chips = synth.m_numChips; if(chips == 1) - player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); + 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->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + 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) @@ -1428,7 +1442,7 @@ ADLMIDI_EXPORT int adl_atEnd(struct ADL_MIDIPlayer *device) return 1; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return (int)play->m_sequencer.positionAtEnd(); + return (int)play->m_sequencer->positionAtEnd(); #else ADL_UNUSED(device); return 1; @@ -1442,7 +1456,7 @@ ADLMIDI_EXPORT size_t adl_trackCount(struct ADL_MIDIPlayer *device) return 0; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - return play->m_sequencer.getTrackCount(); + return play->m_sequencer->getTrackCount(); #else ADL_UNUSED(device); return 0; @@ -1456,7 +1470,7 @@ ADLMIDI_EXPORT int adl_setTrackOptions(struct ADL_MIDIPlayer *device, size_t tra return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - MidiSequencer &seq = play->m_sequencer; + MidiSequencer &seq = *play->m_sequencer; unsigned enableFlag = trackOptions & 3; trackOptions &= ~3u; @@ -1497,7 +1511,7 @@ ADLMIDI_EXPORT int adl_setTriggerHandler(struct ADL_MIDIPlayer *device, ADL_Trig return -1; MidiPlayer *play = GET_MIDI_PLAYER(device); assert(play); - MidiSequencer &seq = play->m_sequencer; + MidiSequencer &seq = *play->m_sequencer; seq.setTriggerHandler(handler, userData); return 0; #else diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp index 07ad875..ff036d4 100644 --- a/src/adlmidi_load.cpp +++ b/src/adlmidi_load.cpp @@ -21,6 +21,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "adlmidi_midiplay.hpp" #include "adlmidi_private.hpp" #include "adlmidi_cvt.hpp" #include "wopl/wopl_file.h" @@ -104,16 +105,17 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr) } } - 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; + OPL3 &synth = *m_synth; + synth.m_insBankSetup.adLibPercussions = false; + synth.m_insBankSetup.scaleModulators = false; + synth.m_insBankSetup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0; + synth.m_insBankSetup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0; + synth.m_insBankSetup.volumeModel = wopl->volume_model; m_setup.deepTremoloMode = -1; m_setup.deepVibratoMode = -1; m_setup.volumeScaleModel = ADLMIDI_VolumeModel_AUTO; - m_synth.setEmbeddedBank(m_setup.bankId); + 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 }; @@ -125,7 +127,7 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr) size_t bankno = (slots_src_ins[ss][i].bank_midi_msb * 256) + (slots_src_ins[ss][i].bank_midi_lsb) + (ss ? size_t(OPL3::PercussionTag) : 0); - OPL3::Bank &bank = m_synth.m_insBanks[bankno]; + OPL3::Bank &bank = synth.m_insBanks[bankno]; for(int j = 0; j < 128; j++) { adlinsdata2 &ins = bank.ins[j]; @@ -136,7 +138,7 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr) } } - m_synth.m_embeddedBank = OPL3::CustomBankTag; // Use dynamic banks! + synth.m_embeddedBank = OPL3::CustomBankTag; // Use dynamic banks! //Percussion offset is count of instruments multipled to count of melodic banks applySetup(); @@ -150,7 +152,8 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr) bool MIDIplay::LoadMIDI_pre() { #ifdef DISABLE_EMBEDDED_BANKS - if((m_synth.m_embeddedBank != OPL3::CustomBankTag) || m_synth.m_insBanks.empty()) + OPL3 &synth = *m_synth; + if((synth.m_embeddedBank != OPL3::CustomBankTag) || 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; @@ -165,11 +168,13 @@ bool MIDIplay::LoadMIDI_pre() bool MIDIplay::LoadMIDI_post() { - MidiSequencer::FileFormat format = m_sequencer.getFormat(); + OPL3 &synth = *m_synth; + MidiSequencer &seq = *m_sequencer; + MidiSequencer::FileFormat format = seq.getFormat(); if(format == MidiSequencer::Format_CMF) { - const std::vector<MidiSequencer::CmfInstrument> &instruments = m_sequencer.getRawCmfInstruments(); - m_synth.m_insBanks.clear();//Clean up old banks + const std::vector<MidiSequencer::CmfInstrument> &instruments = seq.getRawCmfInstruments(); + 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) @@ -184,7 +189,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 = m_synth.m_insBanks[bank].ins[i % 128]; + adlinsdata2 &adlins = synth.m_insBanks[bank].ins[i % 128]; adldata adl; adl.modulator_E862 = ((static_cast<uint32_t>(InsData[8] & 0x07) << 24) & 0xFF000000) //WaveForm @@ -209,45 +214,45 @@ bool MIDIplay::LoadMIDI_post() adlins.voice2_fine_tune = 0.0; } - m_synth.m_embeddedBank = OPL3::CustomBankTag; // Ignore AdlBank number, use dynamic banks instead + synth.m_embeddedBank = OPL3::CustomBankTag; // Ignore AdlBank number, use dynamic banks instead //std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo); - m_synth.m_rhythmMode = true; - m_synth.m_musicMode = OPL3::MODE_CMF; - m_synth.m_volumeScale = OPL3::VOLUME_NATIVE; + synth.m_rhythmMode = true; + synth.m_musicMode = OPL3::MODE_CMF; + synth.m_volumeScale = OPL3::VOLUME_NATIVE; - m_synth.m_numChips = 1; - m_synth.m_numFourOps = 0; + synth.m_numChips = 1; + synth.m_numFourOps = 0; } else if(format == MidiSequencer::Format_RSXX) { //opl.CartoonersVolumes = true; - m_synth.m_musicMode = OPL3::MODE_RSXX; - m_synth.m_volumeScale = OPL3::VOLUME_NATIVE; + synth.m_musicMode = OPL3::MODE_RSXX; + synth.m_volumeScale = OPL3::VOLUME_NATIVE; - m_synth.m_numChips = 1; - m_synth.m_numFourOps = 0; + synth.m_numChips = 1; + synth.m_numFourOps = 0; } else if(format == MidiSequencer::Format_IMF) { //std::fprintf(stderr, "Done reading IMF file\n"); - m_synth.m_numFourOps = 0; //Don't use 4-operator channels for IMF playing! - m_synth.m_musicMode = OPL3::MODE_IMF; + synth.m_numFourOps = 0; //Don't use 4-operator channels for IMF playing! + synth.m_musicMode = OPL3::MODE_IMF; - m_synth.m_numChips = 1; - m_synth.m_numFourOps = 0; + synth.m_numChips = 1; + synth.m_numFourOps = 0; } else { - m_synth.m_numChips = m_setup.numChips; + synth.m_numChips = m_setup.numChips; if(m_setup.numFourOps < 0) adlCalculateFourOpChannels(this, true); } m_setup.tick_skip_samples_delay = 0; - m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip + synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously) m_chipChannels.clear(); - m_chipChannels.resize(m_synth.m_numChannels); + m_chipChannels.resize(synth.m_numChannels); return true; } @@ -258,9 +263,10 @@ bool MIDIplay::LoadMIDI(const std::string &filename) file.openFile(filename.c_str()); if(!LoadMIDI_pre()) return false; - if(!m_sequencer.loadMIDI(file)) + MidiSequencer &seq = *m_sequencer; + if(!seq.loadMIDI(file)) { - errorStringOut = m_sequencer.getErrorString(); + errorStringOut = seq.getErrorString(); return false; } if(!LoadMIDI_post()) @@ -274,9 +280,10 @@ bool MIDIplay::LoadMIDI(const void *data, size_t size) file.openData(data, size); if(!LoadMIDI_pre()) return false; - if(!m_sequencer.loadMIDI(file)) + MidiSequencer &seq = *m_sequencer; + if(!seq.loadMIDI(file)) { - errorStringOut = m_sequencer.getErrorString(); + errorStringOut = seq.getErrorString(); return false; } if(!LoadMIDI_post()) diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 1e1da07..5f1fd87 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -21,7 +21,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "adlmidi_midiplay.hpp" #include "adlmidi_private.hpp" +#include "midi_sequencer.hpp" // Mapping from MIDI volume level to OPL level value. @@ -155,7 +157,10 @@ MIDIplay::MIDIplay(unsigned long sampleRate): m_setup.carry = 0.0; m_setup.tick_skip_samples_delay = 0; + m_synth.reset(new OPL3); + #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + m_sequencer.reset(new MidiSequencer); initSequencerInterface(); #endif resetMIDI(); @@ -163,51 +168,57 @@ MIDIplay::MIDIplay(unsigned long sampleRate): realTime_ResetState(); } +MIDIplay::~MIDIplay() +{ +} + void MIDIplay::applySetup() { - m_synth.m_musicMode = OPL3::MODE_MIDI; + OPL3 &synth = *m_synth; + + synth.m_musicMode = OPL3::MODE_MIDI; m_setup.tick_skip_samples_delay = 0; - m_synth.m_runAtPcmRate = m_setup.runAtPcmRate; + synth.m_runAtPcmRate = m_setup.runAtPcmRate; #ifndef DISABLE_EMBEDDED_BANKS - if(m_synth.m_embeddedBank != OPL3::CustomBankTag) - m_synth.m_insBankSetup = adlbanksetup[m_setup.bankId]; + if(synth.m_embeddedBank != OPL3::CustomBankTag) + 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); + synth.m_deepTremoloMode = m_setup.deepTremoloMode < 0 ? + synth.m_insBankSetup.deepTremolo : + (m_setup.deepTremoloMode != 0); + synth.m_deepVibratoMode = m_setup.deepVibratoMode < 0 ? + synth.m_insBankSetup.deepVibrato : + (m_setup.deepVibratoMode != 0); + synth.m_rhythmMode = m_setup.rhythmMode < 0 ? + synth.m_insBankSetup.adLibPercussions : + (m_setup.rhythmMode != 0); + synth.m_scaleModulators = m_setup.scaleModulators < 0 ? + synth.m_insBankSetup.scaleModulators : + (m_setup.scaleModulators != 0); if(m_setup.logarithmicVolumes) - m_synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); + synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); else - m_synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(m_setup.volumeScaleModel)); + synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(m_setup.volumeScaleModel)); if(m_setup.volumeScaleModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model - m_synth.m_volumeScale = (OPL3::VolumesScale)m_synth.m_insBankSetup.volumeModel; + synth.m_volumeScale = (OPL3::VolumesScale)synth.m_insBankSetup.volumeModel; - m_synth.m_numChips = m_setup.numChips; + synth.m_numChips = m_setup.numChips; m_cmfPercussionMode = false; if(m_setup.numFourOps >= 0) - m_synth.m_numFourOps = m_setup.numFourOps; + synth.m_numFourOps = m_setup.numFourOps; else adlCalculateFourOpChannels(this, true); - m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); + synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); m_chipChannels.clear(); - m_chipChannels.resize(m_synth.m_numChannels); + m_chipChannels.resize(synth.m_numChannels); // Reset the arpeggio counter m_arpeggioCounter = 0; @@ -215,12 +226,13 @@ void MIDIplay::applySetup() void MIDIplay::partialReset() { + OPL3 &synth = *m_synth; realTime_panic(); m_setup.tick_skip_samples_delay = 0; - m_synth.m_runAtPcmRate = m_setup.runAtPcmRate; - m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); + synth.m_runAtPcmRate = m_setup.runAtPcmRate; + synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); m_chipChannels.clear(); - m_chipChannels.resize((size_t)m_synth.m_numChannels); + m_chipChannels.resize((size_t)synth.m_numChannels); } void MIDIplay::resetMIDI() @@ -240,7 +252,8 @@ void MIDIplay::resetMIDI() void MIDIplay::TickIterators(double s) { - for(uint16_t c = 0; c < m_synth.m_numChannels; ++c) + OPL3 &synth = *m_synth; + for(uint16_t c = 0; c < synth.m_numChannels; ++c) m_chipChannels[c].addAge(static_cast<int64_t>(s * 1e6)); updateVibrato(s); updateArpeggio(s); @@ -251,11 +264,12 @@ void MIDIplay::TickIterators(double s) void MIDIplay::realTime_ResetState() { + OPL3 &synth = *m_synth; for(size_t ch = 0; ch < m_midiChannels.size(); ch++) { MIDIchannel &chan = m_midiChannels[ch]; chan.resetAllControllers(); - chan.volume = (m_synth.m_musicMode == OPL3::MODE_RSXX) ? 127 : 100; + chan.volume = (synth.m_musicMode == OPL3::MODE_RSXX) ? 127 : 100; chan.vibpos = 0.0; chan.lastlrpn = 0; chan.lastmrpn = 0; @@ -270,10 +284,12 @@ void MIDIplay::realTime_ResetState() bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { + OPL3 &synth = *m_synth; + if(note >= 128) note = 127; - if((m_synth.m_musicMode == OPL3::MODE_RSXX) && (velocity != 0)) + if((synth.m_musicMode == OPL3::MODE_RSXX) && (velocity != 0)) { // Check if this is just a note after-touch MIDIchannel::activenoteiterator i = m_midiChannels[channel].activenotes_find(note); @@ -337,8 +353,8 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) bool caughtMissingBank = false; if((bank & ~static_cast<uint16_t>(OPL3::PercussionTag)) > 0) { - OPL3::BankMap::iterator b = m_synth.m_insBanks.find(bank); - if(b != m_synth.m_insBanks.end()) + OPL3::BankMap::iterator b = synth.m_insBanks.find(bank); + if(b != synth.m_insBanks.end()) bnk = &b->second; if(bnk) ains = &bnk->ins[midiins]; @@ -352,9 +368,9 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) size_t fallback = bank & ~(size_t)0x7F; if(fallback != bank) { - OPL3::BankMap::iterator b = m_synth.m_insBanks.find(fallback); + OPL3::BankMap::iterator b = synth.m_insBanks.find(fallback); caughtMissingBank = false; - if(b != m_synth.m_insBanks.end()) + if(b != synth.m_insBanks.end()) bnk = &b->second; if(bnk) ains = &bnk->ins[midiins]; @@ -380,8 +396,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) != 0) { - OPL3::BankMap::iterator b = m_synth.m_insBanks.find(bank & OPL3::PercussionTag); - if(b != m_synth.m_insBanks.end()) + OPL3::BankMap::iterator b = synth.m_insBanks.find(bank & OPL3::PercussionTag); + if(b != synth.m_insBanks.end()) bnk = &b->second; if(bnk) ains = &bnk->ins[midiins]; @@ -436,7 +452,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) voices[1].pseudo4op = pseudo_4op; #endif /* __WATCOMC__ */ - if((m_synth.m_rhythmMode == 1) && PercussionMap[midiins & 0xFF]) + if((synth.m_rhythmMode == 1) && PercussionMap[midiins & 0xFF]) voices[1] = voices[0];//i[1] = i[0]; bool isBlankNote = (ains->flags & adlinsdata::Flag_NoSound) != 0; @@ -476,7 +492,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)m_synth.m_numChannels; ++a) + for(size_t a = 0; a < (size_t)synth.m_numChannels; ++a) { if(ccount == 1 && static_cast<int32_t>(a) == adlchannel[0]) continue; // ^ Don't use the same channel for primary&secondary @@ -486,7 +502,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) // Only use regular channels uint32_t expected_mode = 0; - if(m_synth.m_rhythmMode) + if(synth.m_rhythmMode) { if(m_cmfPercussionMode) expected_mode = channel < 11 ? 0 : (3 + channel - 11); // CMF @@ -494,7 +510,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) expected_mode = PercussionMap[midiins & 0xFF]; } - if(m_synth.m_channelCategory[a] != expected_mode) + if(synth.m_channelCategory[a] != expected_mode) continue; } else @@ -502,7 +518,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(ccount == 0) { // Only use four-op master channels - if(m_synth.m_channelCategory[a] != OPL3::ChanCat_4op_Master) + if(synth.m_channelCategory[a] != OPL3::ChanCat_4op_Master) continue; } else @@ -637,6 +653,7 @@ void MIDIplay::realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal) void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) { + OPL3 &synth = *m_synth; if(static_cast<size_t>(channel) > m_midiChannels.size()) channel = channel % 16; switch(type) @@ -773,7 +790,7 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) break; case 103: - if(m_synth.m_musicMode == OPL3::MODE_CMF) + if(synth.m_musicMode == OPL3::MODE_CMF) m_cmfPercussionMode = (value != 0); break; // CMF (ctrl 0x67) rhythm mode @@ -1069,11 +1086,12 @@ size_t MIDIplay::realTime_currentDevice(size_t track) void MIDIplay::realTime_rawOPL(uint8_t reg, uint8_t value) { + OPL3 &synth = *m_synth; if((reg & 0xF0) == 0xC0) value |= 0x30; //std::printf("OPL poke %02X, %02X\n", reg, value); //std::fflush(stdout); - m_synth.writeReg(0, reg, value); + synth.writeReg(0, reg, value); } #if defined(ADLMIDI_AUDIO_TICK_HANDLER) @@ -1100,6 +1118,7 @@ void MIDIplay::noteUpdate(size_t midCh, unsigned props_mask, int32_t select_adlchn) { + OPL3 &synth = *m_synth; MIDIchannel::NoteInfo &info = *i; const int16_t noteTone = info.noteTone; const double currentTone = info.currentTone; @@ -1126,7 +1145,7 @@ void MIDIplay::noteUpdate(size_t midCh, if(props_mask & Upd_Patch) { - m_synth.setPatch(c, ins.ains); + synth.setPatch(c, ins.ains); AdlChannel::LocationData *d = m_chipChannels[c].users_find_or_create(my_loc); if(d) // inserts if necessary { @@ -1162,10 +1181,10 @@ void MIDIplay::noteUpdate(size_t midCh, if(do_erase_user && m_chipChannels[c].users_empty()) { - m_synth.noteOff(c); + synth.noteOff(c); if(props_mask & Upd_Mute) // Mute the note { - m_synth.touchNote(c, 0); + synth.touchNote(c, 0); m_chipChannels[c].koff_time_until_neglible_us = 0; } else @@ -1191,7 +1210,7 @@ void MIDIplay::noteUpdate(size_t midCh, } if(props_mask & Upd_Pan) - m_synth.setPan(c, m_midiChannels[midCh].panning); + synth.setPan(c, m_midiChannels[midCh].panning); if(props_mask & Upd_Volume) { @@ -1208,7 +1227,7 @@ void MIDIplay::noteUpdate(size_t midCh, brightness *= 2; } - switch(m_synth.m_volumeScale) + switch(synth.m_volumeScale) { default: case OPL3::VOLUME_Generic: @@ -1264,7 +1283,7 @@ void MIDIplay::noteUpdate(size_t midCh, break; } - m_synth.touchNote(c, static_cast<uint8_t>(volume), static_cast<uint8_t>(brightness)); + synth.touchNote(c, static_cast<uint8_t>(volume), static_cast<uint8_t>(brightness)); /* DEBUG ONLY!!! static uint32_t max = 0; @@ -1302,7 +1321,7 @@ void MIDIplay::noteUpdate(size_t midCh, bend += static_cast<double>(vibrato) * m_midiChannels[midCh].vibdepth * std::sin(m_midiChannels[midCh].vibpos); #define BEND_COEFFICIENT 172.4387 - m_synth.noteOn(c, c_slave, BEND_COEFFICIENT * std::exp(0.057762265 * (currentTone + bend + phase))); + synth.noteOn(c, c_slave, 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); @@ -1340,6 +1359,7 @@ void MIDIplay::setErrorString(const std::string &err) int64_t MIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const { + OPL3 &synth = *m_synth; const AdlChannel &chan = m_chipChannels[c]; int64_t koff_ms = chan.koff_time_until_neglible_us / 1000; int64_t s = -koff_ms; @@ -1350,7 +1370,7 @@ int64_t MIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::Note s -= 40000; // If it's same instrument, better chance to get it when no free channels if(chan.recent_ins == ins) - s = (m_synth.m_musicMode == OPL3::MODE_CMF) ? 0 : -koff_ms; + s = (synth.m_musicMode == OPL3::MODE_CMF) ? 0 : -koff_ms; return s; } @@ -1395,12 +1415,12 @@ int64_t MIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::Note // increase the score slightly. unsigned n_evacuation_stations = 0; - for(size_t c2 = 0; c2 < static_cast<size_t>(m_synth.m_numChannels); ++c2) + for(size_t c2 = 0; c2 < static_cast<size_t>(synth.m_numChannels); ++c2) { if(c2 == c) continue; - if(m_synth.m_channelCategory[c2] - != m_synth.m_channelCategory[c]) continue; + if(synth.m_channelCategory[c2] + != synth.m_channelCategory[c]) continue; for(AdlChannel::LocationData *m = m_chipChannels[c2].users_first; m; m = m->next) { @@ -1422,6 +1442,8 @@ void MIDIplay::prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInf { if(m_chipChannels[c].users_empty()) return; // Nothing to do + OPL3 &synth = *m_synth; + //bool doing_arpeggio = false; for(AdlChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) { @@ -1458,13 +1480,14 @@ void MIDIplay::prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInf // Keyoff the channel so that it can be retriggered, // unless the new note will be introduced as just an arpeggio. if(m_chipChannels[c].users_empty()) - m_synth.noteOff(c); + synth.noteOff(c); } void MIDIplay::killOrEvacuate(size_t from_channel, AdlChannel::LocationData *j, MIDIplay::MIDIchannel::activenoteiterator i) { + OPL3 &synth = *m_synth; uint32_t maxChannels = ADL_MAX_CHIPS * 18; // Before killing the note, check if it can be @@ -1472,7 +1495,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 < m_synth.m_numChannels; ++c) + for(uint32_t c = 0; c < synth.m_numChannels; ++c) { uint16_t cs = static_cast<uint16_t>(c); @@ -1480,7 +1503,7 @@ void MIDIplay::killOrEvacuate(size_t from_channel, break; if(c == from_channel) continue; - if(m_synth.m_channelCategory[c] != m_synth.m_channelCategory[from_channel]) + if(synth.m_channelCategory[c] != synth.m_channelCategory[from_channel]) continue; AdlChannel &adlch = m_chipChannels[c]; @@ -1540,7 +1563,8 @@ void MIDIplay::panic() void MIDIplay::killSustainingNotes(int32_t midCh, int32_t this_adlchn, uint32_t sustain_type) { - uint32_t first = 0, last = m_synth.m_numChannels; + OPL3 &synth = *m_synth; + uint32_t first = 0, last = synth.m_numChannels; if(this_adlchn >= 0) { @@ -1572,13 +1596,14 @@ void MIDIplay::killSustainingNotes(int32_t midCh, int32_t this_adlchn, uint32_t // Keyoff the channel, if there are no users left. if(m_chipChannels[c].users_empty()) - m_synth.noteOff(c); + synth.noteOff(c); } } void MIDIplay::markSostenutoNotes(int32_t midCh) { - uint32_t first = 0, last = m_synth.m_numChannels; + OPL3 &synth = *m_synth; + uint32_t first = 0, last = synth.m_numChannels; for(uint32_t c = first; c < last; ++c) { if(m_chipChannels[c].users_empty()) @@ -1686,6 +1711,9 @@ 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. + + OPL3 &synth = *m_synth; + #if 0 const unsigned desired_arpeggio_rate = 40; // Hz (upper limit) # if 1 @@ -1708,7 +1736,7 @@ void MIDIplay::updateArpeggio(double) // amount = amount of time passed ++m_arpeggioCounter; - for(uint32_t c = 0; c < m_synth.m_numChannels; ++c) + for(uint32_t c = 0; c < synth.m_numChannels; ++c) { retry_arpeggio: if(c > uint32_t(std::numeric_limits<int32_t>::max())) @@ -1790,7 +1818,7 @@ void MIDIplay::describeChannels(char *str, char *attr, size_t size) if (!str || size <= 0) return; - OPL3 &synth = m_synth; + OPL3 &synth = *m_synth; uint32_t numChannels = synth.m_numChannels; uint32_t index = 0; @@ -1855,7 +1883,7 @@ ADLMIDI_EXPORT AdlInstrumentTester::AdlInstrumentTester(ADL_MIDIPlayer *device) P->cur_gm = 0; P->ins_idx = 0; P->play = play; - P->opl = play ? &play->m_synth : NULL; + P->opl = play ? play->m_synth.get() : NULL; #else ADL_UNUSED(device); #endif diff --git a/src/adlmidi_midiplay.hpp b/src/adlmidi_midiplay.hpp new file mode 100644 index 0000000..e85bacf --- /dev/null +++ b/src/adlmidi_midiplay.hpp @@ -0,0 +1,1029 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi> + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru> + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ADLMIDI_MIDIPLAY_HPP +#define ADLMIDI_MIDIPLAY_HPP + +#include "adldata.hh" +#include "adlmidi_private.hpp" +#include "adlmidi_ptr.hpp" +#include "adlmidi.h" +#include "midi_sequencer.h" +#include <vector> +#include <map> +#include <set> +#include <cstring> +#include <cassert> +#include <math.h> +#include <stdint.h> + +class OPL3; +class BW_MidiSequencer; +class FileAndMemReader; + +/** + * @brief Hooks of the internal events + */ +struct MIDIEventHooks +{ + MIDIEventHooks() : + onNote(NULL), + onNote_userData(NULL), + onDebugMessage(NULL), + onDebugMessage_userData(NULL) + {} + + //! Note on/off hooks + typedef void (*NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); + NoteHook onNote; + void *onNote_userData; + + //! Library internal debug messages + typedef void (*DebugMessageHook)(void *userdata, const char *fmt, ...); + DebugMessageHook onDebugMessage; + void *onDebugMessage_userData; +}; + +class MIDIplay +{ + friend void adl_reset(struct ADL_MIDIPlayer*); +public: + explicit MIDIplay(unsigned long sampleRate = 22050); + ~MIDIplay(); + + void applySetup(); + + void partialReset(); + void resetMIDI(); + + /**********************Internal structures and classes**********************/ + + /** + * @brief Persistent settings for each MIDI channel + */ + struct MIDIchannel + { + //! LSB Bank number + uint8_t bank_lsb, + //! MSB Bank number + bank_msb; + //! Current patch number + uint8_t patch; + //! 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; + //! 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_us; + //! 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 + uint8_t vol; + //! Note vibrato (a part of Note Aftertouch feature) + uint8_t vibrato; + //! Tone selected on noteon: + int16_t noteTone; + //! Current tone (!= noteTone if gliding note) + double currentTone; + //! Gliding rate + double glideRate; + //! Patch selected on noteon; index to bank.ins[] + size_t midiins; + //! Is note the percussion instrument + bool isPercussion; + //! Note that plays missing instrument. Doesn't using any chip channels + bool isBlank; + //! Patch selected + const adlinsdata2 *ains; + enum + { + MaxNumPhysChans = 2, + MaxNumPhysItemCount = MaxNumPhysChans + }; + + /** + * @brief Reference to currently using chip channel + */ + struct Phys + { + //! Destination chip channel + uint16_t chip_chan; + //! ins, inde to adl[] + adldata ains; + //! Is this voice must be detunable? + bool pseudo4op; + + void assign(const Phys &oth) + { + ains = oth.ains; + pseudo4op = oth.pseudo4op; + } + bool operator==(const Phys &oth) const + { + return (ains == oth.ains) && (pseudo4op == oth.pseudo4op); + } + bool operator!=(const Phys &oth) const + { + 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; + for(unsigned i = 0; i < chip_channels_count && !ph; ++i) + if(chip_channels[i].chip_chan == chip_chan) + ph = &chip_channels[i]; + return ph; + } + Phys *phys_find_or_create(uint16_t chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(!ph) { + if(chip_channels_count < MaxNumPhysItemCount) { + ph = &chip_channels[chip_channels_count++]; + ph->chip_chan = chip_chan; + } + } + return ph; + } + Phys *phys_ensure_find_or_create(uint16_t chip_chan) + { + Phys *ph = phys_find_or_create(chip_chan); + assert(ph); + return ph; + } + void phys_erase_at(const Phys *ph) + { + intptr_t pos = ph - chip_channels; + assert(pos < static_cast<intptr_t>(chip_channels_count)); + for(intptr_t i = pos + 1; i < static_cast<intptr_t>(chip_channels_count); ++i) + chip_channels[i - 1] = chip_channels[i]; + --chip_channels_count; + } + void phys_erase(unsigned chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(ph) + 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 + { + explicit activenoteiterator(NoteInfo *info = NULL) + : ptr(info) {} + activenoteiterator &operator++() + { + if(ptr->note == 127) + ptr = NULL; + else + for(++ptr; ptr && !ptr->active;) + ptr = (ptr->note == 127) ? NULL : (ptr + 1); + return *this; + } + activenoteiterator operator++(int) + { + activenoteiterator pos = *this; + ++*this; + return pos; + } + NoteInfo &operator*() const + { return *ptr; } + NoteInfo *operator->() const + { return ptr; } + bool operator==(activenoteiterator other) const + { return ptr == other.ptr; } + bool operator!=(activenoteiterator other) const + { return ptr != other.ptr; } + operator NoteInfo *() const + { return ptr; } + private: + NoteInfo *ptr; + }; + + activenoteiterator activenotes_begin() + { + activenoteiterator it(activenotes); + return (it->active) ? it : ++it; + } + + activenoteiterator activenotes_find(uint8_t note) + { + assert(note < 128); + return activenoteiterator( + activenotes[note].active ? &activenotes[note] : NULL); + } + + activenoteiterator activenotes_ensure_find(uint8_t note) + { + activenoteiterator it = activenotes_find(note); + assert(it); + return it; + } + + std::pair<activenoteiterator, bool> activenotes_insert(uint8_t note) + { + assert(note < 128); + NoteInfo &info = activenotes[note]; + bool inserted = !info.active; + if(inserted) info.active = true; + return std::pair<activenoteiterator, bool>(activenoteiterator(&info), inserted); + } + + void activenotes_erase(activenoteiterator pos) + { + if(pos) + pos->active = false; + } + + bool activenotes_empty() + { + return !activenotes_begin(); + } + + void activenotes_clear() + { + for(uint8_t i = 0; i < 128; ++i) { + activenotes[i].note = i; + activenotes[i].active = false; + } + } + + /** + * @brief Reset channel into initial state + */ + void reset() + { + resetAllControllers(); + patch = 0; + vibpos = 0; + bank_lsb = 0; + bank_msb = 0; + lastlrpn = 0; + lastmrpn = 0; + nrpn = false; + is_xg_percussion = false; + } + + /** + * @brief Reset all MIDI controllers into initial state + */ + void resetAllControllers() + { + bend = 0; + bendsense_msb = 2; + bendsense_lsb = 0; + updateBendSensitivity(); + volume = 100; + expression = 127; + sustain = false; + softPedal = false; + vibrato = 0; + aftertouch = 0; + std::memset(noteAftertouch, 0, 128); + noteAfterTouchInUse = false; + vibspeed = 2 * 3.141592653 * 5.0; + vibdepth = 0.5 / 127; + vibdelay_us = 0; + panning = 64; + portamento = 0; + portamentoEnable = false; + portamentoSource = -1; + 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(); + gliding_note_count = 0; + reset(); + } + }; + + /** + * @brief Additional information about OPL3 channels + */ + struct AdlChannel + { + struct Location + { + uint16_t MidCh; + uint8_t note; + bool operator==(const Location &l) const + { return MidCh == l.MidCh && note == l.note; } + bool operator!=(const Location &l) const + { return !operator==(l); } + }; + struct LocationData + { + LocationData *prev, *next; + Location loc; + enum { + Sustain_None = 0x00, + Sustain_Pedal = 0x01, + Sustain_Sostenuto = 0x02, + Sustain_ANY = Sustain_Pedal | Sustain_Sostenuto + }; + uint32_t sustained; + char _padding[6]; + MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[] + //! Has fixed sustain, don't iterate "on" timeout + bool fixed_sustain; + //! Timeout until note will be allowed to be killed by channel manager while it is on + int64_t kon_time_until_neglible_us; + int64_t vibdelay_us; + }; + + //! Time left until sounding will be muted after key off + int64_t koff_time_until_neglible_us; + + //! Recently passed instrument, improves a goodness of released but busy channel when matching + MIDIchannel::NoteInfo::Phys recent_ins; + + enum { users_max = 128 }; + LocationData *users_first, *users_free_cells; + LocationData users_cells[users_max]; + unsigned users_size; + + bool users_empty() const; + LocationData *users_find(Location loc); + LocationData *users_allocate(); + LocationData *users_find_or_create(Location loc); + LocationData *users_insert(const LocationData &x); + void users_erase(LocationData *user); + void users_clear(); + void users_assign(const LocationData *users, size_t count); + + // For channel allocation: + AdlChannel(): koff_time_until_neglible_us(0) + { + users_clear(); + std::memset(&recent_ins, 0, sizeof(MIDIchannel::NoteInfo::Phys)); + } + + AdlChannel(const AdlChannel &oth): koff_time_until_neglible_us(oth.koff_time_until_neglible_us) + { + if(oth.users_first) + { + users_first = NULL; + users_assign(oth.users_first, oth.users_size); + } + else + users_clear(); + } + + AdlChannel &operator=(const AdlChannel &oth) + { + koff_time_until_neglible_us = oth.koff_time_until_neglible_us; + users_assign(oth.users_first, oth.users_size); + return *this; + } + + /** + * @brief Increases age of active note in microseconds time + * @param us Amount time in microseconds + */ + void addAge(int64_t us); + }; + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + /** + * @brief MIDI files player sequencer + */ + AdlMIDI_UPtr<MidiSequencer> m_sequencer; + + /** + * @brief Interface between MIDI sequencer and this library + */ + AdlMIDI_UPtr<BW_MidiRtInterface> m_sequencerInterface; + + /** + * @brief Initialize MIDI sequencer interface + */ + void initSequencerInterface(); +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER + + struct Setup + { + int emulator; + bool runAtPcmRate; + unsigned int bankId; + int numFourOps; + unsigned int numChips; + int deepTremoloMode; + int deepVibratoMode; + int rhythmMode; + bool logarithmicVolumes; + int volumeScaleModel; + //unsigned int SkipForward; + int scaleModulators; + bool fullRangeBrightnessCC74; + + double delay; + double carry; + + /* The lag between visual content and audio content equals */ + /* the sum of these two buffers. */ + double mindelay; + double maxdelay; + + /* For internal usage */ + ssize_t tick_skip_samples_delay; /* Skip tick processing after samples count. */ + /* For internal usage */ + + 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; + }; + + //! 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, + Mode_GS = 0x01, + Mode_XG = 0x02, + Mode_GM2 = 0x04 + }; + //! MIDI Synthesizer mode + uint32_t m_synthMode; + + //! Installed function hooks + MIDIEventHooks hooks; + +private: + //! Per-track MIDI devices map + std::map<std::string, size_t> m_midiDevices; + //! Current MIDI device per track + std::map<size_t /*track*/, size_t /*channel begin index*/> m_currentMidiDevice; + + //! Padding to fix CLanc code model's warning + char _padding[7]; + + //! Chip channels map + std::vector<AdlChannel> m_chipChannels; + //! Counter of arpeggio processing + size_t m_arpeggioCounter; + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + //! Audio tick counter + uint32_t m_audioTickCounter; +#endif + + //! Local error string + std::string errorStringOut; + + //! Missing instruments catches + std::set<size_t> caugh_missing_instruments; + //! Missing melodic banks catches + std::set<size_t> caugh_missing_banks_melodic; + //! Missing percussion banks catches + std::set<size_t> caugh_missing_banks_percussion; + +public: + + const std::string &getErrorString(); + void setErrorString(const std::string &err); + + //! OPL3 Chip manager + AdlMIDI_UPtr<OPL3> m_synth; + + //! 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 + /** + * @brief MIDI file loading pre-process + * @return true on success, false on failure + */ + bool LoadMIDI_pre(); + + /** + * @brief MIDI file loading post-process + * @return true on success, false on failure + */ + bool LoadMIDI_post(); + + /** + * @brief Load music file from a file + * @param filename Path to music file + * @return true on success, false on failure + */ + + bool LoadMIDI(const std::string &filename); + + /** + * @brief Load music file from the memory block + * @param data pointer to the memory block + * @param size size of memory block + * @return true on success, false on failure + */ + bool LoadMIDI(const void *data, size_t size); + + /** + * @brief Periodic tick handler. + * @param s seconds since last call + * @param granularity don't expect intervals smaller than this, in seconds + * @return desired number of seconds until next call + */ + double Tick(double s, double granularity); +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER + + /** + * @brief Process extra iterators like vibrato or arpeggio + * @param s seconds since last call + */ + void TickIterators(double s); + + + /* RealTime event triggers */ + /** + * @brief Reset state of all channels + */ + void realTime_ResetState(); + + /** + * @brief Note On event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param velocity Velocity level (from 0 to 127) + * @return true if Note On event was accepted + */ + bool realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity); + + /** + * @brief Note Off event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + */ + void realTime_NoteOff(uint8_t channel, uint8_t note); + + /** + * @brief Note aftertouch event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param atVal After-Touch level (from 0 to 127) + */ + void realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal); + + /** + * @brief Channel aftertouch event + * @param channel MIDI channel + * @param atVal After-Touch level (from 0 to 127) + */ + void realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal); + + /** + * @brief Controller Change event + * @param channel MIDI channel + * @param type Type of controller + * @param value Value of the controller (from 0 to 127) + */ + void realTime_Controller(uint8_t channel, uint8_t type, uint8_t value); + + /** + * @brief Patch change + * @param channel MIDI channel + * @param patch Patch Number (from 0 to 127) + */ + void realTime_PatchChange(uint8_t channel, uint8_t patch); + + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param pitch Concoctated raw pitch value + */ + void realTime_PitchBend(uint8_t channel, uint16_t pitch); + + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param msb MSB of pitch value + * @param lsb LSB of pitch value + */ + void realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb); + + /** + * @brief LSB Bank Change CC + * @param channel MIDI channel + * @param lsb LSB value of bank number + */ + void realTime_BankChangeLSB(uint8_t channel, uint8_t lsb); + + /** + * @brief MSB Bank Change CC + * @param channel MIDI channel + * @param msb MSB value of bank number + */ + void realTime_BankChangeMSB(uint8_t channel, uint8_t msb); + + /** + * @brief Bank Change (united value) + * @param channel MIDI channel + * @param bank Bank number value + */ + void realTime_BankChange(uint8_t channel, uint16_t bank); + + /** + * @brief Sets the Device identifier + * @param id 7-bit Device identifier + */ + void setDeviceId(uint8_t id); + + /** + * @brief System Exclusive message + * @param msg Raw SysEx Message + * @param size Length of SysEx message + * @return true if message was passed successfully. False on any errors + */ + bool realTime_SysEx(const uint8_t *msg, size_t size); + + /** + * @brief Turn off all notes and mute the sound of releasing notes + */ + void realTime_panic(); + + /** + * @brief Device switch (to extend 16-channels limit of MIDI standard) + * @param track MIDI track index + * @param data Device name + * @param length Length of device name string + */ + void realTime_deviceSwitch(size_t track, const char *data, size_t length); + + /** + * @brief Currently selected device index + * @param track MIDI track index + * @return Multiple 16 value + */ + size_t realTime_currentDevice(size_t track); + + /** + * @brief Send raw OPL chip command + * @param reg OPL Register + * @param value Value to write + */ + void realTime_rawOPL(uint8_t reg, uint8_t value); + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + // Audio rate tick handler + void AudioTick(uint32_t chipId, uint32_t rate); +#endif + +private: + /** + * @brief Hardware manufacturer (Used for SysEx) + */ + enum + { + Manufacturer_Roland = 0x41, + Manufacturer_Yamaha = 0x43, + 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, + RolandModel_SC55 = 0x45, + 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, + Upd_Pan = 0x2, + Upd_Volume = 0x4, + Upd_Pitch = 0x8, + Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch, + Upd_Off = 0x20, + Upd_Mute = 0x40, + Upd_OffMute = Upd_Off + Upd_Mute + }; + + /** + * @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(size_t midCh, + MIDIchannel::activenoteiterator i, + unsigned props_mask, + int32_t select_adlchn = -1); + + /** + * @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); + + /** + * @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 calculateChipChannelGoodness(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 prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); + + /** + * @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); + + /** + * @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, + uint32_t sustain_type = AdlChannel::LocationData::Sustain_ANY); + /** + * @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(size_t 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(size_t midCh, uint8_t note); + + /** + * @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: + /** + * @brief Checks was device name used or not + * @param name Name of MIDI device + * @return Offset of the MIDI Channels, multiple to 16 + */ + size_t chooseDevice(const std::string &name); + + /** + * @brief Gets a textual description of the state of chip channels + * @param text character pointer for text + * @param attr character pointer for text attributes + * @param size number of characters available to write + */ + void describeChannels(char *text, char *attr, size_t size); +}; + +#endif // ADLMIDI_MIDIPLAY_HPP diff --git a/src/adlmidi_private.cpp b/src/adlmidi_private.cpp index 4e8e488..00507ff 100644 --- a/src/adlmidi_private.cpp +++ b/src/adlmidi_private.cpp @@ -21,6 +21,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "adlmidi_midiplay.hpp" #include "adlmidi_private.hpp" std::string ADLMIDI_ErrorString; @@ -36,16 +37,17 @@ void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate) int adlCalculateFourOpChannels(MIDIplay *play, bool silent) { + OPL3 &synth = *play->m_synth; size_t n_fourop[2] = {0, 0}, n_total[2] = {0, 0}; //Automatically calculate how much 4-operator channels is necessary #ifndef DISABLE_EMBEDDED_BANKS - if(play->m_synth.m_embeddedBank == OPL3::CustomBankTag) + if(synth.m_embeddedBank == OPL3::CustomBankTag) #endif { //For custom bank - OPL3::BankMap::iterator it = play->m_synth.m_insBanks.begin(); - OPL3::BankMap::iterator end = play->m_synth.m_insBanks.end(); + OPL3::BankMap::iterator it = synth.m_insBanks.begin(); + OPL3::BankMap::iterator end = synth.m_insBanks.end(); for(; it != end; ++it) { size_t bank = it->first; @@ -99,10 +101,10 @@ int adlCalculateFourOpChannels(MIDIplay *play, bool silent) : (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4); */ - play->m_synth.m_numFourOps = static_cast<unsigned>(numFourOps * play->m_synth.m_numChips); + synth.m_numFourOps = static_cast<unsigned>(numFourOps * synth.m_numChips); // Update channel categories and set up four-operator channels if(!silent) - play->m_synth.updateChannelCategories(); + synth.updateChannelCategories(); return 0; } diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index 95e6ea9..8e95ea5 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -215,994 +215,6 @@ inline int32_t adl_cvtU32(int32_t x) } -/** - * @brief Hooks of the internal events - */ -struct MIDIEventHooks -{ - MIDIEventHooks() : - onNote(NULL), - onNote_userData(NULL), - onDebugMessage(NULL), - onDebugMessage_userData(NULL) - {} - - //! Note on/off hooks - typedef void (*NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); - NoteHook onNote; - void *onNote_userData; - - //! Library internal debug messages - typedef void (*DebugMessageHook)(void *userdata, const char *fmt, ...); - DebugMessageHook onDebugMessage; - void *onDebugMessage_userData; -}; - - -class MIDIplay -{ - friend void adl_reset(struct ADL_MIDIPlayer*); -public: - explicit MIDIplay(unsigned long sampleRate = 22050); - - ~MIDIplay() - {} - - void applySetup(); - - void partialReset(); - void resetMIDI(); - - /**********************Internal structures and classes**********************/ - - /** - * @brief Persistent settings for each MIDI channel - */ - struct MIDIchannel - { - //! LSB Bank number - uint8_t bank_lsb, - //! MSB Bank number - bank_msb; - //! Current patch number - uint8_t patch; - //! 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; - //! 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_us; - //! 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 - uint8_t vol; - //! Note vibrato (a part of Note Aftertouch feature) - uint8_t vibrato; - //! Tone selected on noteon: - int16_t noteTone; - //! Current tone (!= noteTone if gliding note) - double currentTone; - //! Gliding rate - double glideRate; - //! Patch selected on noteon; index to bank.ins[] - size_t midiins; - //! Is note the percussion instrument - bool isPercussion; - //! Note that plays missing instrument. Doesn't using any chip channels - bool isBlank; - //! Patch selected - const adlinsdata2 *ains; - enum - { - MaxNumPhysChans = 2, - MaxNumPhysItemCount = MaxNumPhysChans - }; - - /** - * @brief Reference to currently using chip channel - */ - struct Phys - { - //! Destination chip channel - uint16_t chip_chan; - //! ins, inde to adl[] - adldata ains; - //! Is this voice must be detunable? - bool pseudo4op; - - void assign(const Phys &oth) - { - ains = oth.ains; - pseudo4op = oth.pseudo4op; - } - bool operator==(const Phys &oth) const - { - return (ains == oth.ains) && (pseudo4op == oth.pseudo4op); - } - bool operator!=(const Phys &oth) const - { - 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; - for(unsigned i = 0; i < chip_channels_count && !ph; ++i) - if(chip_channels[i].chip_chan == chip_chan) - ph = &chip_channels[i]; - return ph; - } - Phys *phys_find_or_create(uint16_t chip_chan) - { - Phys *ph = phys_find(chip_chan); - if(!ph) { - if(chip_channels_count < MaxNumPhysItemCount) { - ph = &chip_channels[chip_channels_count++]; - ph->chip_chan = chip_chan; - } - } - return ph; - } - Phys *phys_ensure_find_or_create(uint16_t chip_chan) - { - Phys *ph = phys_find_or_create(chip_chan); - assert(ph); - return ph; - } - void phys_erase_at(const Phys *ph) - { - intptr_t pos = ph - chip_channels; - assert(pos < static_cast<intptr_t>(chip_channels_count)); - for(intptr_t i = pos + 1; i < static_cast<intptr_t>(chip_channels_count); ++i) - chip_channels[i - 1] = chip_channels[i]; - --chip_channels_count; - } - void phys_erase(unsigned chip_chan) - { - Phys *ph = phys_find(chip_chan); - if(ph) - 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 - { - explicit activenoteiterator(NoteInfo *info = NULL) - : ptr(info) {} - activenoteiterator &operator++() - { - if(ptr->note == 127) - ptr = NULL; - else - for(++ptr; ptr && !ptr->active;) - ptr = (ptr->note == 127) ? NULL : (ptr + 1); - return *this; - } - activenoteiterator operator++(int) - { - activenoteiterator pos = *this; - ++*this; - return pos; - } - NoteInfo &operator*() const - { return *ptr; } - NoteInfo *operator->() const - { return ptr; } - bool operator==(activenoteiterator other) const - { return ptr == other.ptr; } - bool operator!=(activenoteiterator other) const - { return ptr != other.ptr; } - operator NoteInfo *() const - { return ptr; } - private: - NoteInfo *ptr; - }; - - activenoteiterator activenotes_begin() - { - activenoteiterator it(activenotes); - return (it->active) ? it : ++it; - } - - activenoteiterator activenotes_find(uint8_t note) - { - assert(note < 128); - return activenoteiterator( - activenotes[note].active ? &activenotes[note] : NULL); - } - - activenoteiterator activenotes_ensure_find(uint8_t note) - { - activenoteiterator it = activenotes_find(note); - assert(it); - return it; - } - - std::pair<activenoteiterator, bool> activenotes_insert(uint8_t note) - { - assert(note < 128); - NoteInfo &info = activenotes[note]; - bool inserted = !info.active; - if(inserted) info.active = true; - return std::pair<activenoteiterator, bool>(activenoteiterator(&info), inserted); - } - - void activenotes_erase(activenoteiterator pos) - { - if(pos) - pos->active = false; - } - - bool activenotes_empty() - { - return !activenotes_begin(); - } - - void activenotes_clear() - { - for(uint8_t i = 0; i < 128; ++i) { - activenotes[i].note = i; - activenotes[i].active = false; - } - } - - /** - * @brief Reset channel into initial state - */ - void reset() - { - resetAllControllers(); - patch = 0; - vibpos = 0; - bank_lsb = 0; - bank_msb = 0; - lastlrpn = 0; - lastmrpn = 0; - nrpn = false; - is_xg_percussion = false; - } - - /** - * @brief Reset all MIDI controllers into initial state - */ - void resetAllControllers() - { - bend = 0; - bendsense_msb = 2; - bendsense_lsb = 0; - updateBendSensitivity(); - volume = 100; - expression = 127; - sustain = false; - softPedal = false; - vibrato = 0; - aftertouch = 0; - std::memset(noteAftertouch, 0, 128); - noteAfterTouchInUse = false; - vibspeed = 2 * 3.141592653 * 5.0; - vibdepth = 0.5 / 127; - vibdelay_us = 0; - panning = 64; - portamento = 0; - portamentoEnable = false; - portamentoSource = -1; - 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(); - gliding_note_count = 0; - reset(); - } - }; - - /** - * @brief Additional information about OPL3 channels - */ - struct AdlChannel - { - struct Location - { - uint16_t MidCh; - uint8_t note; - bool operator==(const Location &l) const - { return MidCh == l.MidCh && note == l.note; } - bool operator!=(const Location &l) const - { return !operator==(l); } - }; - struct LocationData - { - LocationData *prev, *next; - Location loc; - enum { - Sustain_None = 0x00, - Sustain_Pedal = 0x01, - Sustain_Sostenuto = 0x02, - Sustain_ANY = Sustain_Pedal | Sustain_Sostenuto - }; - uint32_t sustained; - char _padding[6]; - MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[] - //! Has fixed sustain, don't iterate "on" timeout - bool fixed_sustain; - //! Timeout until note will be allowed to be killed by channel manager while it is on - int64_t kon_time_until_neglible_us; - int64_t vibdelay_us; - }; - - //! Time left until sounding will be muted after key off - int64_t koff_time_until_neglible_us; - - //! Recently passed instrument, improves a goodness of released but busy channel when matching - MIDIchannel::NoteInfo::Phys recent_ins; - - enum { users_max = 128 }; - LocationData *users_first, *users_free_cells; - LocationData users_cells[users_max]; - unsigned users_size; - - bool users_empty() const; - LocationData *users_find(Location loc); - LocationData *users_allocate(); - LocationData *users_find_or_create(Location loc); - LocationData *users_insert(const LocationData &x); - void users_erase(LocationData *user); - void users_clear(); - void users_assign(const LocationData *users, size_t count); - - // For channel allocation: - AdlChannel(): koff_time_until_neglible_us(0) - { - users_clear(); - std::memset(&recent_ins, 0, sizeof(MIDIchannel::NoteInfo::Phys)); - } - - AdlChannel(const AdlChannel &oth): koff_time_until_neglible_us(oth.koff_time_until_neglible_us) - { - if(oth.users_first) - { - users_first = NULL; - users_assign(oth.users_first, oth.users_size); - } - else - users_clear(); - } - - AdlChannel &operator=(const AdlChannel &oth) - { - koff_time_until_neglible_us = oth.koff_time_until_neglible_us; - users_assign(oth.users_first, oth.users_size); - return *this; - } - - /** - * @brief Increases age of active note in microseconds time - * @param us Amount time in microseconds - */ - void addAge(int64_t us); - }; - -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - /** - * @brief MIDI files player sequencer - */ - MidiSequencer m_sequencer; - - /** - * @brief Interface between MIDI sequencer and this library - */ - BW_MidiRtInterface m_sequencerInterface; - - /** - * @brief Initialize MIDI sequencer interface - */ - void initSequencerInterface(); -#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER - - struct Setup - { - int emulator; - bool runAtPcmRate; - unsigned int bankId; - int numFourOps; - unsigned int numChips; - int deepTremoloMode; - int deepVibratoMode; - int rhythmMode; - bool logarithmicVolumes; - int volumeScaleModel; - //unsigned int SkipForward; - int scaleModulators; - bool fullRangeBrightnessCC74; - - double delay; - double carry; - - /* The lag between visual content and audio content equals */ - /* the sum of these two buffers. */ - double mindelay; - double maxdelay; - - /* For internal usage */ - ssize_t tick_skip_samples_delay; /* Skip tick processing after samples count. */ - /* For internal usage */ - - 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; - }; - - //! 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, - Mode_GS = 0x01, - Mode_XG = 0x02, - Mode_GM2 = 0x04 - }; - //! MIDI Synthesizer mode - uint32_t m_synthMode; - - //! Installed function hooks - MIDIEventHooks hooks; - -private: - //! Per-track MIDI devices map - std::map<std::string, size_t> m_midiDevices; - //! Current MIDI device per track - std::map<size_t /*track*/, size_t /*channel begin index*/> m_currentMidiDevice; - - //! Padding to fix CLanc code model's warning - char _padding[7]; - - //! Chip channels map - std::vector<AdlChannel> m_chipChannels; - //! Counter of arpeggio processing - size_t m_arpeggioCounter; - -#if defined(ADLMIDI_AUDIO_TICK_HANDLER) - //! Audio tick counter - uint32_t m_audioTickCounter; -#endif - - //! Local error string - std::string errorStringOut; - - //! Missing instruments catches - std::set<size_t> caugh_missing_instruments; - //! Missing melodic banks catches - std::set<size_t> caugh_missing_banks_melodic; - //! Missing percussion banks catches - std::set<size_t> caugh_missing_banks_percussion; - -public: - - const std::string &getErrorString(); - void setErrorString(const std::string &err); - - //! OPL3 Chip manager - OPL3 m_synth; - - //! 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 - /** - * @brief MIDI file loading pre-process - * @return true on success, false on failure - */ - bool LoadMIDI_pre(); - - /** - * @brief MIDI file loading post-process - * @return true on success, false on failure - */ - bool LoadMIDI_post(); - - /** - * @brief Load music file from a file - * @param filename Path to music file - * @return true on success, false on failure - */ - - bool LoadMIDI(const std::string &filename); - - /** - * @brief Load music file from the memory block - * @param data pointer to the memory block - * @param size size of memory block - * @return true on success, false on failure - */ - bool LoadMIDI(const void *data, size_t size); - - /** - * @brief Periodic tick handler. - * @param s seconds since last call - * @param granularity don't expect intervals smaller than this, in seconds - * @return desired number of seconds until next call - */ - double Tick(double s, double granularity); -#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER - - /** - * @brief Process extra iterators like vibrato or arpeggio - * @param s seconds since last call - */ - void TickIterators(double s); - - - /* RealTime event triggers */ - /** - * @brief Reset state of all channels - */ - void realTime_ResetState(); - - /** - * @brief Note On event - * @param channel MIDI channel - * @param note Note key (from 0 to 127) - * @param velocity Velocity level (from 0 to 127) - * @return true if Note On event was accepted - */ - bool realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity); - - /** - * @brief Note Off event - * @param channel MIDI channel - * @param note Note key (from 0 to 127) - */ - void realTime_NoteOff(uint8_t channel, uint8_t note); - - /** - * @brief Note aftertouch event - * @param channel MIDI channel - * @param note Note key (from 0 to 127) - * @param atVal After-Touch level (from 0 to 127) - */ - void realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal); - - /** - * @brief Channel aftertouch event - * @param channel MIDI channel - * @param atVal After-Touch level (from 0 to 127) - */ - void realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal); - - /** - * @brief Controller Change event - * @param channel MIDI channel - * @param type Type of controller - * @param value Value of the controller (from 0 to 127) - */ - void realTime_Controller(uint8_t channel, uint8_t type, uint8_t value); - - /** - * @brief Patch change - * @param channel MIDI channel - * @param patch Patch Number (from 0 to 127) - */ - void realTime_PatchChange(uint8_t channel, uint8_t patch); - - /** - * @brief Pitch bend change - * @param channel MIDI channel - * @param pitch Concoctated raw pitch value - */ - void realTime_PitchBend(uint8_t channel, uint16_t pitch); - - /** - * @brief Pitch bend change - * @param channel MIDI channel - * @param msb MSB of pitch value - * @param lsb LSB of pitch value - */ - void realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb); - - /** - * @brief LSB Bank Change CC - * @param channel MIDI channel - * @param lsb LSB value of bank number - */ - void realTime_BankChangeLSB(uint8_t channel, uint8_t lsb); - - /** - * @brief MSB Bank Change CC - * @param channel MIDI channel - * @param msb MSB value of bank number - */ - void realTime_BankChangeMSB(uint8_t channel, uint8_t msb); - - /** - * @brief Bank Change (united value) - * @param channel MIDI channel - * @param bank Bank number value - */ - void realTime_BankChange(uint8_t channel, uint16_t bank); - - /** - * @brief Sets the Device identifier - * @param id 7-bit Device identifier - */ - void setDeviceId(uint8_t id); - - /** - * @brief System Exclusive message - * @param msg Raw SysEx Message - * @param size Length of SysEx message - * @return true if message was passed successfully. False on any errors - */ - bool realTime_SysEx(const uint8_t *msg, size_t size); - - /** - * @brief Turn off all notes and mute the sound of releasing notes - */ - void realTime_panic(); - - /** - * @brief Device switch (to extend 16-channels limit of MIDI standard) - * @param track MIDI track index - * @param data Device name - * @param length Length of device name string - */ - void realTime_deviceSwitch(size_t track, const char *data, size_t length); - - /** - * @brief Currently selected device index - * @param track MIDI track index - * @return Multiple 16 value - */ - size_t realTime_currentDevice(size_t track); - - /** - * @brief Send raw OPL chip command - * @param reg OPL Register - * @param value Value to write - */ - void realTime_rawOPL(uint8_t reg, uint8_t value); - -#if defined(ADLMIDI_AUDIO_TICK_HANDLER) - // Audio rate tick handler - void AudioTick(uint32_t chipId, uint32_t rate); -#endif - -private: - /** - * @brief Hardware manufacturer (Used for SysEx) - */ - enum - { - Manufacturer_Roland = 0x41, - Manufacturer_Yamaha = 0x43, - 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, - RolandModel_SC55 = 0x45, - 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, - Upd_Pan = 0x2, - Upd_Volume = 0x4, - Upd_Pitch = 0x8, - Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch, - Upd_Off = 0x20, - Upd_Mute = 0x40, - Upd_OffMute = Upd_Off + Upd_Mute - }; - - /** - * @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(size_t midCh, - MIDIchannel::activenoteiterator i, - unsigned props_mask, - int32_t select_adlchn = -1); - - /** - * @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); - - /** - * @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 calculateChipChannelGoodness(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 prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); - - /** - * @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); - - /** - * @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, - uint32_t sustain_type = AdlChannel::LocationData::Sustain_ANY); - /** - * @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(size_t 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(size_t midCh, uint8_t note); - - /** - * @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: - /** - * @brief Checks was device name used or not - * @param name Name of MIDI device - * @return Offset of the MIDI Channels, multiple to 16 - */ - size_t chooseDevice(const std::string &name); - - /** - * @brief Gets a textual description of the state of chip channels - * @param text character pointer for text - * @param attr character pointer for text attributes - * @param size number of characters available to write - */ - void describeChannels(char *text, char *attr, size_t size); -}; - // I think, this is useless inside of Library /* struct FourChars diff --git a/src/adlmidi_ptr.hpp b/src/adlmidi_ptr.hpp index 7d1086b..aaaed78 100644 --- a/src/adlmidi_ptr.hpp +++ b/src/adlmidi_ptr.hpp @@ -54,7 +54,7 @@ class AdlMIDI_UPtr { T *m_p; public: - explicit AdlMIDI_UPtr(T *p) + explicit AdlMIDI_UPtr(T *p = NULL) : m_p(p) {} ~AdlMIDI_UPtr() { diff --git a/src/adlmidi_sequencer.cpp b/src/adlmidi_sequencer.cpp index 66b9b9a..c315ac3 100644 --- a/src/adlmidi_sequencer.cpp +++ b/src/adlmidi_sequencer.cpp @@ -28,6 +28,7 @@ // Inlucde MIDI sequencer class implementation #include "midi_sequencer_impl.hpp" +#include "adlmidi_midiplay.hpp" #include "adlmidi_private.hpp" /**************************************************** @@ -106,37 +107,42 @@ static size_t rtCurrentDevice(void *userdata, size_t track) void MIDIplay::initSequencerInterface() { - std::memset(&m_sequencerInterface, 0, sizeof(BW_MidiRtInterface)); + BW_MidiRtInterface *seq = new BW_MidiRtInterface; + m_sequencerInterface.reset(seq); - m_sequencerInterface.onDebugMessage = hooks.onDebugMessage; - m_sequencerInterface.onDebugMessage_userData = hooks.onDebugMessage_userData; + std::memset(seq, 0, sizeof(BW_MidiRtInterface)); + + seq->onDebugMessage = hooks.onDebugMessage; + seq->onDebugMessage_userData = hooks.onDebugMessage_userData; /* MIDI Real-Time calls */ - m_sequencerInterface.rtUserData = this; - m_sequencerInterface.rt_noteOn = rtNoteOn; - m_sequencerInterface.rt_noteOff = rtNoteOff; - m_sequencerInterface.rt_noteAfterTouch = rtNoteAfterTouch; - m_sequencerInterface.rt_channelAfterTouch = rtChannelAfterTouch; - m_sequencerInterface.rt_controllerChange = rtControllerChange; - m_sequencerInterface.rt_patchChange = rtPatchChange; - m_sequencerInterface.rt_pitchBend = rtPitchBend; - m_sequencerInterface.rt_systemExclusive = rtSysEx; + seq->rtUserData = this; + seq->rt_noteOn = rtNoteOn; + seq->rt_noteOff = rtNoteOff; + seq->rt_noteAfterTouch = rtNoteAfterTouch; + seq->rt_channelAfterTouch = rtChannelAfterTouch; + seq->rt_controllerChange = rtControllerChange; + seq->rt_patchChange = rtPatchChange; + seq->rt_pitchBend = rtPitchBend; + seq->rt_systemExclusive = rtSysEx; /* NonStandard calls */ - m_sequencerInterface.rt_rawOPL = rtRawOPL; - m_sequencerInterface.rt_deviceSwitch = rtDeviceSwitch; - m_sequencerInterface.rt_currentDevice = rtCurrentDevice; + seq->rt_rawOPL = rtRawOPL; + seq->rt_deviceSwitch = rtDeviceSwitch; + seq->rt_currentDevice = rtCurrentDevice; /* NonStandard calls End */ - m_sequencer.setInterface(&m_sequencerInterface); + m_sequencer->setInterface(seq); } double MIDIplay::Tick(double s, double granularity) { - double ret = m_sequencer.Tick(s, granularity); + MidiSequencer &seqr = *m_sequencer; + double ret = seqr.Tick(s, granularity); - s *= m_sequencer.getTempoMultiplier(); - for(uint16_t c = 0; c < m_synth.m_numChannels; ++c) + OPL3 &synth = *m_synth; + s *= seqr.getTempoMultiplier(); + for(uint16_t c = 0; c < synth.m_numChannels; ++c) m_chipChannels[c].addAge(static_cast<int64_t>(s * 1e6)); updateVibrato(s); |