aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/adlmidi.cpp193
-rw-r--r--src/adlmidi_load.cpp82
-rw-r--r--src/adlmidi_midiplay.cpp189
-rw-r--r--src/adlmidi_midiplay.hpp1016
-rw-r--r--src/adlmidi_opl3.cpp12
-rw-r--r--src/adlmidi_opl3.hpp321
-rw-r--r--src/adlmidi_private.cpp15
-rw-r--r--src/adlmidi_private.hpp1313
-rw-r--r--src/adlmidi_ptr.hpp2
-rw-r--r--src/adlmidi_sequencer.cpp45
-rw-r--r--src/midi_sequencer.h2
11 files changed, 1657 insertions, 1533 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp
index 12d0e68..541dbb4 100644
--- a/src/adlmidi.cpp
+++ b/src/adlmidi.cpp
@@ -21,7 +21,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "adlmidi_midiplay.hpp"
+#include "adlmidi_opl3.hpp"
#include "adlmidi_private.hpp"
+#ifndef ADLMIDI_HW_OPL
+#include "chips/opl_chip_base.h"
+#endif
+#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
+#include "midi_sequencer.hpp"
+#endif
/* Unify MIDI player casting and interface between ADLMIDI and OPNMIDI */
#define GET_MIDI_PLAYER(device) reinterpret_cast<MIDIplay *>((device)->adl_midiPlayer)
@@ -112,13 +120,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())
+ Synth &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 +150,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 +180,9 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank)
return -1;
}
+ Synth &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 +213,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;
+ Synth::BankMap &map = play->m_synth->m_insBanks;
map.reserve(banks);
return (int)map.capacity();
}
@@ -216,13 +226,13 @@ ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, in
ADL_BankId id = *idp;
if(id.lsb > 127 || id.msb > 127 || id.percussive > 1)
return -1;
- size_t idnumber = ((id.msb << 8) | id.lsb | (id.percussive ? size_t(OPL3::PercussionTag) : 0));
+ size_t idnumber = ((id.msb << 8) | id.lsb | (id.percussive ? size_t(Synth::PercussionTag) : 0));
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
- OPL3::BankMap &map = play->m_synth.m_insBanks;
+ Synth::BankMap &map = play->m_synth->m_insBanks;
- OPL3::BankMap::iterator it;
+ Synth::BankMap::iterator it;
if(!(flags & ADLMIDI_Bank_Create))
{
it = map.find(idnumber);
@@ -231,16 +241,16 @@ ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, in
}
else
{
- std::pair<size_t, OPL3::Bank> value;
+ std::pair<size_t, Synth::Bank> value;
value.first = idnumber;
memset(&value.second, 0, sizeof(value.second));
for (unsigned i = 0; i < 128; ++i)
value.second.ins[i].flags = adlinsdata::Flag_NoSound;
- std::pair<OPL3::BankMap::iterator, bool> ir;
+ std::pair<Synth::BankMap::iterator, bool> ir;
if(flags & ADLMIDI_Bank_CreateRt)
{
- ir = map.insert(value, OPL3::BankMap::do_not_expand_t());
+ ir = map.insert(value, Synth::BankMap::do_not_expand_t());
if(ir.first == map.end())
return -1;
}
@@ -258,11 +268,11 @@ ADLMIDI_EXPORT int adl_getBankId(ADL_MIDIPlayer *device, const ADL_Bank *bank, A
if(!device || !bank)
return -1;
- OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
- OPL3::BankMap::key_type idnumber = it->first;
+ Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
+ Synth::BankMap::key_type idnumber = it->first;
id->msb = (idnumber >> 8) & 127;
id->lsb = idnumber & 127;
- id->percussive = (idnumber & OPL3::PercussionTag) ? 1 : 0;
+ id->percussive = (idnumber & Synth::PercussionTag) ? 1 : 0;
return 0;
}
@@ -273,8 +283,8 @@ 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::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
+ Synth::BankMap &map = play->m_synth->m_insBanks;
+ Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
size_t size = map.size();
map.erase(it);
return (map.size() != size) ? 0 : -1;
@@ -287,9 +297,9 @@ 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;
+ Synth::BankMap &map = play->m_synth->m_insBanks;
- OPL3::BankMap::iterator it = map.begin();
+ Synth::BankMap::iterator it = map.begin();
if(it == map.end())
return -1;
@@ -304,9 +314,9 @@ 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;
+ Synth::BankMap &map = play->m_synth->m_insBanks;
- OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
+ Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
if(++it == map.end())
return -1;
@@ -319,7 +329,7 @@ ADLMIDI_EXPORT int adl_getInstrument(ADL_MIDIPlayer *device, const ADL_Bank *ban
if(!device || !bank || index > 127 || !ins)
return -1;
- OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
+ Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
cvt_FMIns_to_ADLI(*ins, it->second.ins[index]);
ins->version = 0;
return 0;
@@ -333,7 +343,7 @@ ADLMIDI_EXPORT int adl_setInstrument(ADL_MIDIPlayer *device, ADL_Bank *bank, uns
if(ins->version != 0)
return -1;
- OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
+ Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
cvt_ADLI_to_FMIns(it->second.ins[index], *ins);
return 0;
}
@@ -356,11 +366,11 @@ ADLMIDI_EXPORT int adl_loadEmbeddedBank(struct ADL_MIDIPlayer *device, ADL_Bank
if(num < 0 || num >= maxAdlBanks())
return -1;
- OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer);
+ Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
size_t id = it->first;
for (unsigned i = 0; i < 128; ++i) {
- size_t insno = i + ((id & OPL3::PercussionTag) ? 128 : 0);
+ size_t insno = i + ((id & Synth::PercussionTag) ? 128 : 0);
size_t adlmeta = ::banks[num][insno];
it->second.ins[i] = adlinsdata2::from_adldata(::adlins[adlmeta]);
}
@@ -383,14 +393,15 @@ ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4)
return -1;
}
+ Synth &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 +422,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 +431,14 @@ ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod)
if(!device) return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
+ Synth &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 +447,14 @@ ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro)
if(!device) return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
+ Synth &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 +463,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 +471,14 @@ ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo)
if(!device) return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
+ Synth &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 +487,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 +496,12 @@ ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
+ Synth &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 +522,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 +534,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 +544,14 @@ ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
+ Synth &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 +561,14 @@ ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int v
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
+ Synth &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 = (Synth::VolumesScale)synth.m_insBankSetup.volumeModel;
else
- play->m_synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(volumeModel));
+ synth.setVolumeScaleModel(static_cast<ADLMIDI_VolumeModels>(volumeModel));
}
}
@@ -561,7 +578,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 +692,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();
+ Synth &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 +726,9 @@ ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled)
{
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
+ Synth &synth = *play->m_synth;
play->m_setup.runAtPcmRate = (enabled != 0);
- if(!play->m_synth.setupLocked())
+ if(!synth.setupLocked())
play->partialReset();
return 0;
}
@@ -763,7 +782,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 +796,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 +810,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 +824,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 +841,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 +857,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 +870,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 +896,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 +911,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 +925,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 +939,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 +958,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 +981,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 +1011,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 +1041,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 +1292,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 +1309,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;
+ Synth &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 +1400,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;
+ Synth &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 +1449,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 +1463,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 +1477,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 +1518,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..e270f3b 100644
--- a/src/adlmidi_load.cpp
+++ b/src/adlmidi_load.cpp
@@ -21,8 +21,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "adlmidi_midiplay.hpp"
+#include "adlmidi_opl3.hpp"
#include "adlmidi_private.hpp"
#include "adlmidi_cvt.hpp"
+#include "file_reader.hpp"
+#include "midi_sequencer.hpp"
#include "wopl/wopl_file.h"
bool MIDIplay::LoadBank(const std::string &filename)
@@ -104,16 +108,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;
+ Synth &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 };
@@ -124,8 +129,8 @@ 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];
+ (ss ? size_t(Synth::PercussionTag) : 0);
+ Synth::Bank &bank = synth.m_insBanks[bankno];
for(int j = 0; j < 128; j++)
{
adlinsdata2 &ins = bank.ins[j];
@@ -136,7 +141,7 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr)
}
}
- m_synth.m_embeddedBank = OPL3::CustomBankTag; // Use dynamic banks!
+ synth.m_embeddedBank = Synth::CustomBankTag; // Use dynamic banks!
//Percussion offset is count of instruments multipled to count of melodic banks
applySetup();
@@ -150,7 +155,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())
+ Synth &synth = *m_synth;
+ if((synth.m_embeddedBank != Synth::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 +171,13 @@ bool MIDIplay::LoadMIDI_pre()
bool MIDIplay::LoadMIDI_post()
{
- MidiSequencer::FileFormat format = m_sequencer.getFormat();
+ Synth &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)
@@ -179,12 +187,12 @@ bool MIDIplay::LoadMIDI_post()
bank = ((bank & 127) + ((bank >> 7) << 8));
if(bank > 127 + (127 << 8))
break;
- bank += (i % 256 < 128) ? 0 : size_t(OPL3::PercussionTag);
+ bank += (i % 256 < 128) ? 0 : size_t(Synth::PercussionTag);
/*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 +217,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 = Synth::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 = Synth::MODE_CMF;
+ synth.m_volumeScale = Synth::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 = Synth::MODE_RSXX;
+ synth.m_volumeScale = Synth::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 = Synth::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 +266,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 +283,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..1a20e9e 100644
--- a/src/adlmidi_midiplay.cpp
+++ b/src/adlmidi_midiplay.cpp
@@ -21,7 +21,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "adlmidi_midiplay.hpp"
+#include "adlmidi_opl3.hpp"
#include "adlmidi_private.hpp"
+#include "midi_sequencer.hpp"
// Mapping from MIDI volume level to OPL level value.
@@ -155,7 +158,10 @@ MIDIplay::MIDIplay(unsigned long sampleRate):
m_setup.carry = 0.0;
m_setup.tick_skip_samples_delay = 0;
+ m_synth.reset(new Synth);
+
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
+ m_sequencer.reset(new MidiSequencer);
initSequencerInterface();
#endif
resetMIDI();
@@ -163,51 +169,57 @@ MIDIplay::MIDIplay(unsigned long sampleRate):
realTime_ResetState();
}
+MIDIplay::~MIDIplay()
+{
+}
+
void MIDIplay::applySetup()
{
- m_synth.m_musicMode = OPL3::MODE_MIDI;
+ Synth &synth = *m_synth;
+
+ synth.m_musicMode = Synth::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 != Synth::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 = (Synth::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 +227,13 @@ void MIDIplay::applySetup()
void MIDIplay::partialReset()
{
+ Synth &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 +253,8 @@ void MIDIplay::resetMIDI()
void MIDIplay::TickIterators(double s)
{
- for(uint16_t c = 0; c < m_synth.m_numChannels; ++c)
+ Synth &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 +265,12 @@ void MIDIplay::TickIterators(double s)
void MIDIplay::realTime_ResetState()
{
+ Synth &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 == Synth::MODE_RSXX) ? 127 : 100;
chan.vibpos = 0.0;
chan.lastlrpn = 0;
chan.lastmrpn = 0;
@@ -270,10 +285,12 @@ void MIDIplay::realTime_ResetState()
bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
{
+ Synth &synth = *m_synth;
+
if(note >= 128)
note = 127;
- if((m_synth.m_musicMode == OPL3::MODE_RSXX) && (velocity != 0))
+ if((synth.m_musicMode == Synth::MODE_RSXX) && (velocity != 0))
{
// Check if this is just a note after-touch
MIDIchannel::activenoteiterator i = m_midiChannels[channel].activenotes_find(note);
@@ -328,17 +345,17 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
}
if(isPercussion)
- bank += OPL3::PercussionTag;
+ bank += Synth::PercussionTag;
- const adlinsdata2 *ains = &OPL3::m_emptyInstrument;
+ const adlinsdata2 *ains = &Synth::m_emptyInstrument;
//Set bank bank
- const OPL3::Bank *bnk = NULL;
+ const Synth::Bank *bnk = NULL;
bool caughtMissingBank = false;
- if((bank & ~static_cast<uint16_t>(OPL3::PercussionTag)) > 0)
+ if((bank & ~static_cast<uint16_t>(Synth::PercussionTag)) > 0)
{
- OPL3::BankMap::iterator b = m_synth.m_insBanks.find(bank);
- if(b != m_synth.m_insBanks.end())
+ Synth::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 +369,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);
+ Synth::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];
@@ -373,15 +390,15 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
{
hooks.onDebugMessage(hooks.onDebugMessage_userData,
"[%i] Playing missing %s MIDI bank %i (patch %i)",
- channel, text, (bank & ~static_cast<uint16_t>(OPL3::PercussionTag)), midiins);
+ channel, text, (bank & ~static_cast<uint16_t>(Synth::PercussionTag)), midiins);
}
}
//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())
+ Synth::BankMap::iterator b = synth.m_insBanks.find(bank & Synth::PercussionTag);
+ if(b != synth.m_insBanks.end())
bnk = &b->second;
if(bnk)
ains = &bnk->ins[midiins];
@@ -436,7 +453,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 +493,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 +503,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 +511,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 +519,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] != Synth::ChanCat_4op_Master)
continue;
}
else
@@ -637,6 +654,7 @@ void MIDIplay::realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal)
void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value)
{
+ Synth &synth = *m_synth;
if(static_cast<size_t>(channel) > m_midiChannels.size())
channel = channel % 16;
switch(type)
@@ -773,7 +791,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 == Synth::MODE_CMF)
m_cmfPercussionMode = (value != 0);
break; // CMF (ctrl 0x67) rhythm mode
@@ -1069,11 +1087,12 @@ size_t MIDIplay::realTime_currentDevice(size_t track)
void MIDIplay::realTime_rawOPL(uint8_t reg, uint8_t value)
{
+ Synth &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 +1119,7 @@ void MIDIplay::noteUpdate(size_t midCh,
unsigned props_mask,
int32_t select_adlchn)
{
+ Synth &synth = *m_synth;
MIDIchannel::NoteInfo &info = *i;
const int16_t noteTone = info.noteTone;
const double currentTone = info.currentTone;
@@ -1126,7 +1146,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 +1182,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 +1211,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,10 +1228,10 @@ void MIDIplay::noteUpdate(size_t midCh,
brightness *= 2;
}
- switch(m_synth.m_volumeScale)
+ switch(synth.m_volumeScale)
{
default:
- case OPL3::VOLUME_Generic:
+ case Synth::VOLUME_Generic:
{
volume = vol * m_masterVolume * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression;
@@ -1231,7 +1251,7 @@ void MIDIplay::noteUpdate(size_t midCh,
}
break;
- case OPL3::VOLUME_NATIVE:
+ case Synth::VOLUME_NATIVE:
{
volume = vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression;
// volume = volume * m_masterVolume / (127 * 127 * 127) / 2;
@@ -1239,7 +1259,7 @@ void MIDIplay::noteUpdate(size_t midCh,
}
break;
- case OPL3::VOLUME_DMX:
+ case Synth::VOLUME_DMX:
{
volume = 2 * (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 16129) + 1;
//volume = 2 * (Ch[MidCh].volume) + 1;
@@ -1247,7 +1267,7 @@ void MIDIplay::noteUpdate(size_t midCh,
}
break;
- case OPL3::VOLUME_APOGEE:
+ case Synth::VOLUME_APOGEE:
{
volume = (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 16129);
volume = ((64 * (vol + 0x80)) * volume) >> 15;
@@ -1255,7 +1275,7 @@ void MIDIplay::noteUpdate(size_t midCh,
}
break;
- case OPL3::VOLUME_9X:
+ case Synth::VOLUME_9X:
{
//volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * m_masterVolume / 16129 /*2048383*/) >> 2)];
volume = 63 - W9X_volume_mapping_table[((vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 2048383) >> 2)];
@@ -1264,7 +1284,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 +1322,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 +1360,7 @@ void MIDIplay::setErrorString(const std::string &err)
int64_t MIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const
{
+ Synth &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 +1371,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 == Synth::MODE_CMF) ? 0 : -koff_ms;
return s;
}
@@ -1395,12 +1416,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 +1443,8 @@ void MIDIplay::prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInf
{
if(m_chipChannels[c].users_empty()) return; // Nothing to do
+ Synth &synth = *m_synth;
+
//bool doing_arpeggio = false;
for(AdlChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;)
{
@@ -1458,13 +1481,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)
{
+ Synth &synth = *m_synth;
uint32_t maxChannels = ADL_MAX_CHIPS * 18;
// Before killing the note, check if it can be
@@ -1472,7 +1496,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 +1504,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 +1564,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;
+ Synth &synth = *m_synth;
+ uint32_t first = 0, last = synth.m_numChannels;
if(this_adlchn >= 0)
{
@@ -1572,13 +1597,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;
+ Synth &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 +1712,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.
+
+ Synth &synth = *m_synth;
+
#if 0
const unsigned desired_arpeggio_rate = 40; // Hz (upper limit)
# if 1
@@ -1708,7 +1737,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 +1819,7 @@ void MIDIplay::describeChannels(char *str, char *attr, size_t size)
if (!str || size <= 0)
return;
- OPL3 &synth = m_synth;
+ Synth &synth = *m_synth;
uint32_t numChannels = synth.m_numChannels;
uint32_t index = 0;
@@ -1811,11 +1840,11 @@ void MIDIplay::describeChannels(char *str, char *attr, size_t size)
{
switch(synth.m_channelCategory[index])
{
- case OPL3::ChanCat_Regular:
+ case Synth::ChanCat_Regular:
str[index] = '+';
break;
- case OPL3::ChanCat_4op_Master:
- case OPL3::ChanCat_4op_Slave:
+ case Synth::ChanCat_4op_Master:
+ case Synth::ChanCat_4op_Slave:
str[index] = '#';
break;
default: // rhythm-mode percussion
@@ -1843,7 +1872,7 @@ struct AdlInstrumentTester::Impl
uint32_t cur_gm;
uint32_t ins_idx;
std::vector<uint32_t> adl_ins_list;
- OPL3 *opl;
+ Synth *opl;
MIDIplay *play;
};
@@ -1855,7 +1884,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
@@ -1885,8 +1914,8 @@ ADLMIDI_EXPORT void AdlInstrumentTester::FindAdlList()
ADLMIDI_EXPORT void AdlInstrumentTester::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127
{
#ifndef DISABLE_EMBEDDED_BANKS
- OPL3 *opl = P->opl;
- if(opl->m_volumeScale == OPL3::VOLUME_NATIVE)
+ Synth *opl = P->opl;
+ if(opl->m_volumeScale == Synth::VOLUME_NATIVE)
opl->touchNote(c, static_cast<uint8_t>(volume * 127 / (127 * 127 * 127) / 2));
else
{
@@ -1905,7 +1934,7 @@ ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note)
{
#ifndef DISABLE_EMBEDDED_BANKS
MIDIplay *play = P->play;
- OPL3 *opl = P->opl;
+ Synth *opl = P->opl;
if(P->adl_ins_list.empty()) FindAdlList();
const unsigned meta = P->adl_ins_list[P->ins_idx];
const adlinsdata2 ains = adlinsdata2::from_adldata(::adlins[meta]);
@@ -1971,7 +2000,7 @@ ADLMIDI_EXPORT void AdlInstrumentTester::NextGM(int offset)
ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset)
{
#ifndef DISABLE_EMBEDDED_BANKS
- //OPL3 *opl = P->opl;
+ //Synth *opl = P->opl;
if(P->adl_ins_list.empty()) FindAdlList();
const unsigned NumBanks = (unsigned)adl_getBanksCount();
P->ins_idx = (uint32_t)((int32_t)P->ins_idx + (int32_t)P->adl_ins_list.size() + offset) % (int32_t)P->adl_ins_list.size();
diff --git a/src/adlmidi_midiplay.hpp b/src/adlmidi_midiplay.hpp
new file mode 100644
index 0000000..02a45a9
--- /dev/null
+++ b/src/adlmidi_midiplay.hpp
@@ -0,0 +1,1016 @@
+/*
+ * 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"
+
+/**
+ * @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<Synth> 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_opl3.cpp b/src/adlmidi_opl3.cpp
index f3672d3..0f977e2 100644
--- a/src/adlmidi_opl3.cpp
+++ b/src/adlmidi_opl3.cpp
@@ -21,6 +21,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "adlmidi_opl3.hpp"
#include "adlmidi_private.hpp"
#include <stdlib.h>
#include <cassert>
@@ -148,6 +149,13 @@ static const uint16_t g_channelsMap[23] =
Ports: ???
*/
+enum
+{
+ OPL_PANNING_LEFT = 0x10,
+ OPL_PANNING_RIGHT = 0x20,
+ OPL_PANNING_BOTH = 0x30
+};
+
static adlinsdata2 makeEmptyInstrument()
{
adlinsdata2 ins;
@@ -181,6 +189,10 @@ OPL3::OPL3() :
#endif
}
+OPL3::~OPL3()
+{
+}
+
bool OPL3::setupLocked()
{
return (m_musicMode == MODE_CMF ||
diff --git a/src/adlmidi_opl3.hpp b/src/adlmidi_opl3.hpp
new file mode 100644
index 0000000..c5b7fcd
--- /dev/null
+++ b/src/adlmidi_opl3.hpp
@@ -0,0 +1,321 @@
+/*
+ * 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_OPL3_HPP
+#define ADLMIDI_OPL3_HPP
+
+#include "adldata.hh"
+#include "adlmidi_ptr.hpp"
+#include "adlmidi_private.hpp"
+#include "adlmidi_bankmap.h"
+
+/**
+ * @brief OPL3 Chip management class
+ */
+class OPL3
+{
+ friend class MIDIplay;
+ friend class AdlInstrumentTester;
+ friend int adlCalculateFourOpChannels(MIDIplay *play, bool silent);
+public:
+ enum
+ {
+ PercussionTag = 1 << 15,
+ CustomBankTag = 0xFFFFFFFF
+ };
+
+ //! Total number of chip channels between all running emulators
+ uint32_t m_numChannels;
+ //! Just a padding. Reserved.
+ char _padding[4];
+#ifndef ADLMIDI_HW_OPL
+ //! Running chip emulators
+ std::vector<AdlMIDI_SPtr<OPLChipBase > > m_chips;
+#endif
+
+private:
+ //! Cached patch data, needed by Touch()
+ std::vector<adldata> m_insCache;
+ //! Value written to B0, cached, needed by NoteOff.
+ /*! Contains Key on/off state, octave block and frequency number values
+ */
+ std::vector<uint32_t> m_keyBlockFNumCache;
+ //! Cached BD registry value (flags register: DeepTremolo, DeepVibrato, and RhythmMode)
+ std::vector<uint32_t> m_regBD;
+
+public:
+ /**
+ * @brief MIDI bank entry
+ */
+ struct Bank
+ {
+ //! MIDI Bank instruments
+ adlinsdata2 ins[128];
+ };
+ typedef BasicBankMap<Bank> BankMap;
+ //! MIDI bank instruments data
+ BankMap m_insBanks;
+ //! MIDI bank-wide setup
+ AdlBankSetup m_insBankSetup;
+
+public:
+ //! Blank instrument template
+ static const adlinsdata2 m_emptyInstrument;
+ //! Total number of running concurrent emulated chips
+ uint32_t m_numChips;
+ //! Currently running embedded bank number. "CustomBankTag" means usign of the custom bank.
+ uint32_t m_embeddedBank;
+ //! Total number of needed four-operator channels in all running chips
+ uint32_t m_numFourOps;
+ //! Turn global Deep Tremolo mode on
+ bool m_deepTremoloMode;
+ //! Turn global Deep Vibrato mode on
+ bool m_deepVibratoMode;
+ //! Use Rhythm Mode percussions
+ bool m_rhythmMode;
+ //! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too.
+ bool m_scaleModulators;
+ //! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates.
+ bool m_runAtPcmRate;
+ //! Enable soft panning
+ bool m_softPanning;
+
+ //! Just a padding. Reserved.
+ char _padding2[3];
+
+ /**
+ * @brief Music playing mode
+ */
+ enum MusicMode
+ {
+ //! MIDI mode
+ MODE_MIDI,
+ //! Id-Software Music mode
+ MODE_IMF,
+ //! Creative Music Files mode
+ MODE_CMF,
+ //! EA-MUS (a.k.a. RSXX) mode
+ MODE_RSXX
+ } m_musicMode;
+
+ /**
+ * @brief Volume models enum
+ */
+ enum VolumesScale
+ {
+ //! Generic volume model (linearization of logarithmic scale)
+ VOLUME_Generic,
+ //! OPL3 native logarithmic scale
+ VOLUME_NATIVE,
+ //! DMX volume scale logarithmic table
+ VOLUME_DMX,
+ //! Apoge Sound System volume scaling model
+ VOLUME_APOGEE,
+ //! Windows 9x driver volume scale table
+ VOLUME_9X
+ } m_volumeScale;
+
+ //! Reserved
+ char _padding3[8];
+
+ /**
+ * @brief Channel categiry enumeration
+ */
+ enum ChanCat
+ {
+ //! Regular melodic/percussion channel
+ ChanCat_Regular = 0,
+ //! Four-op master
+ ChanCat_4op_Master = 1,
+ //! Four-op slave
+ ChanCat_4op_Slave = 2,
+ //! Rhythm-mode Bass drum
+ ChanCat_Rhythm_Bass = 3,
+ //! Rhythm-mode Snare drum
+ ChanCat_Rhythm_Snare = 4,
+ //! Rhythm-mode Tom-Tom
+ ChanCat_Rhythm_Tom = 5,
+ //! Rhythm-mode Cymbal
+ ChanCat_Rhythm_Cymbal = 6,
+ //! Rhythm-mode Hi-Hat
+ ChanCat_Rhythm_HiHat = 7,
+ //! Rhythm-mode Slave channel
+ ChanCat_Rhythm_Slave = 8
+ };
+
+ //! Category of the channel
+ /*! 1 = quad-master, 2 = quad-slave, 0 = regular
+ 3 = percussion BassDrum
+ 4 = percussion Snare
+ 5 = percussion Tom
+ 6 = percussion Crash cymbal
+ 7 = percussion Hihat
+ 8 = percussion slave
+ */
+ std::vector<uint32_t> m_channelCategory;
+
+
+ /**
+ * @brief C.O. Constructor
+ */
+ OPL3();
+
+ /**
+ * @brief C.O. Destructor
+ */
+ ~OPL3();
+
+ /**
+ * @brief Checks are setup locked to be changed on the fly or not
+ * @return true when setup on the fly is locked
+ */
+ bool setupLocked();
+
+ /**
+ * @brief Choose one of embedded banks
+ * @param bank ID of the bank
+ */
+ void setEmbeddedBank(uint32_t bank);
+
+ /**
+ * @brief Write data to OPL3 chip register
+ * @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored
+ * @param address Register address to write
+ * @param value Value to write
+ */
+ void writeReg(size_t chip, uint16_t address, uint8_t value);
+
+ /**
+ * @brief Write data to OPL3 chip register
+ * @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored
+ * @param address Register address to write
+ * @param value Value to write
+ */
+ void writeRegI(size_t chip, uint32_t address, uint32_t value);
+
+ /**
+ * @brief Write to soft panning control of OPL3 chip emulator
+ * @param chip Index of emulated chip.
+ * @param address Register of channel to write
+ * @param value Value to write
+ */
+ void writePan(size_t chip, uint32_t address, uint32_t value);
+
+ /**
+ * @brief Off the note in specified chip channel
+ * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
+ */
+ void noteOff(size_t c);
+
+ /**
+ * @brief On the note in specified chip channel with specified frequency of the tone
+ * @param c1 Channel of chip [or master 4-op channel] (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
+ * @param c2 Slave 4-op channel of chip, unused for 2op (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
+ * @param hertz Frequency of the tone in hertzes
+ */
+ void noteOn(size_t c1, size_t c2, double hertz);
+
+ /**
+ * @brief Change setup of instrument in specified chip channel
+ * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
+ * @param volume Volume level (from 0 to 63)
+ * @param brightness CC74 Brightness level (from 0 to 127)
+ */
+ void touchNote(size_t c, uint8_t volume, uint8_t brightness = 127);
+
+ /**
+ * @brief Set the instrument into specified chip channel
+ * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
+ * @param instrument Instrument data to set into the chip channel
+ */
+ void setPatch(size_t c, const adldata &instrument);
+
+ /**
+ * @brief Set panpot position
+ * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
+ * @param value 3-bit panpot value
+ */
+ void setPan(size_t c, uint8_t value);
+
+ /**
+ * @brief Shut up all chip channels
+ */
+ void silenceAll();
+
+ /**
+ * @brief Commit updated flag states to chip registers
+ */
+ void updateChannelCategories();
+
+ /**
+ * @brief commit deepTremolo and deepVibrato flags
+ */
+ void commitDeepFlags();
+
+ /**
+ * @brief Set the volume scaling model
+ * @param volumeModel Type of volume scale model scale
+ */
+ void setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel);
+
+ /**
+ * @brief Get the volume scaling model
+ */
+ ADLMIDI_VolumeModels getVolumeScaleModel();
+
+ #ifndef ADLMIDI_HW_OPL
+ /**
+ * @brief Clean up all running emulated chip instances
+ */
+ void clearChips();
+ #endif
+
+ /**
+ * @brief Reset chip properties and initialize them
+ * @param emulator Type of chip emulator
+ * @param PCM_RATE Output sample rate to generate on output
+ * @param audioTickHandler PCM-accurate clock hook
+ */
+ void reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler);
+};
+
+/**
+ * @brief Check emulator availability
+ * @param emulator Emulator ID (ADL_Emulator)
+ * @return true when emulator is available
+ */
+extern bool adl_isEmulatorAvailable(int emulator);
+
+/**
+ * @brief Find highest emulator
+ * @return The ADL_Emulator enum value which contains ID of highest emulator
+ */
+extern int adl_getHighestEmulator();
+
+/**
+ * @brief Find lowest emulator
+ * @return The ADL_Emulator enum value which contains ID of lowest emulator
+ */
+extern int adl_getLowestEmulator();
+
+#endif // ADLMIDI_OPL3_HPP
diff --git a/src/adlmidi_private.cpp b/src/adlmidi_private.cpp
index 4e8e488..b2c9b8d 100644
--- a/src/adlmidi_private.cpp
+++ b/src/adlmidi_private.cpp
@@ -21,6 +21,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "adlmidi_midiplay.hpp"
+#include "adlmidi_opl3.hpp"
#include "adlmidi_private.hpp"
std::string ADLMIDI_ErrorString;
@@ -36,20 +38,21 @@ void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate)
int adlCalculateFourOpChannels(MIDIplay *play, bool silent)
{
+ Synth &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 == Synth::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();
+ Synth::BankMap::iterator it = synth.m_insBanks.begin();
+ Synth::BankMap::iterator end = synth.m_insBanks.end();
for(; it != end; ++it)
{
size_t bank = it->first;
- size_t div = (bank & OPL3::PercussionTag) ? 1 : 0;
+ size_t div = (bank & Synth::PercussionTag) ? 1 : 0;
for(size_t i = 0; i < 128; ++i)
{
adlinsdata2 &ins = it->second.ins[i];
@@ -99,10 +102,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 59ba555..62b6223 100644
--- a/src/adlmidi_private.hpp
+++ b/src/adlmidi_private.hpp
@@ -49,7 +49,6 @@ typedef __int64 ssize_t;
# else
typedef __int32 ssize_t;
# endif
-# define NOMINMAX 1 //Don't override std::min and std::max
# else
# ifdef _WIN64
typedef int64_t ssize_t;
@@ -130,18 +129,20 @@ typedef int32_t ssize_t;
#define INT32_MAX 0x7fffffff
#endif
-#include "file_reader.hpp"
+class FileAndMemReader;
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
// Rename class to avoid ABI collisions
#define BW_MidiSequencer AdlMidiSequencer
-#include "midi_sequencer.hpp"
+class BW_MidiSequencer;
typedef BW_MidiSequencer MidiSequencer;
+typedef struct BW_MidiRtInterface BW_MidiRtInterface;
#endif//ADLMIDI_DISABLE_MIDI_SEQUENCER
-#ifndef ADLMIDI_HW_OPL
-#include "chips/opl_chip_base.h"
-#endif
+class OPL3;
+class OPLChipBase;
+
+typedef class OPL3 Synth;
#include "adldata.hh"
@@ -153,13 +154,10 @@ typedef BW_MidiSequencer MidiSequencer;
#endif
#include "adlmidi_ptr.hpp"
-#include "adlmidi_bankmap.h"
-#define ADL_UNUSED(x) (void)x
+class MIDIplay;
-#define OPL_PANNING_LEFT 0x10
-#define OPL_PANNING_RIGHT 0x20
-#define OPL_PANNING_BOTH 0x30
+#define ADL_UNUSED(x) (void)x
#ifdef ADLMIDI_HW_OPL
#define ADL_MAX_CHIPS 1
@@ -218,1280 +216,6 @@ inline int32_t adl_cvtU32(int32_t x)
return (uint32_t)adl_cvtS32(x) - (uint32_t)INT32_MIN;
}
-struct ADL_MIDIPlayer;
-class MIDIplay;
-/**
- * @brief OPL3 Chip management class
- */
-class OPL3
-{
- friend class MIDIplay;
- friend class AdlInstrumentTester;
- friend int adlCalculateFourOpChannels(MIDIplay *play, bool silent);
-public:
- enum
- {
- PercussionTag = 1 << 15,
- CustomBankTag = 0xFFFFFFFF
- };
-
- //! Total number of chip channels between all running emulators
- uint32_t m_numChannels;
- //! Just a padding. Reserved.
- char _padding[4];
-#ifndef ADLMIDI_HW_OPL
- //! Running chip emulators
- std::vector<AdlMIDI_SPtr<OPLChipBase > > m_chips;
-#endif
-
-private:
- //! Cached patch data, needed by Touch()
- std::vector<adldata> m_insCache;
- //! Value written to B0, cached, needed by NoteOff.
- /*! Contains Key on/off state, octave block and frequency number values
- */
- std::vector<uint32_t> m_keyBlockFNumCache;
- //! Cached BD registry value (flags register: DeepTremolo, DeepVibrato, and RhythmMode)
- std::vector<uint32_t> m_regBD;
-
-public:
- /**
- * @brief MIDI bank entry
- */
- struct Bank
- {
- //! MIDI Bank instruments
- adlinsdata2 ins[128];
- };
- typedef BasicBankMap<Bank> BankMap;
- //! MIDI bank instruments data
- BankMap m_insBanks;
- //! MIDI bank-wide setup
- AdlBankSetup m_insBankSetup;
-
-public:
- //! Blank instrument template
- static const adlinsdata2 m_emptyInstrument;
- //! Total number of running concurrent emulated chips
- uint32_t m_numChips;
- //! Currently running embedded bank number. "CustomBankTag" means usign of the custom bank.
- uint32_t m_embeddedBank;
- //! Total number of needed four-operator channels in all running chips
- uint32_t m_numFourOps;
- //! Turn global Deep Tremolo mode on
- bool m_deepTremoloMode;
- //! Turn global Deep Vibrato mode on
- bool m_deepVibratoMode;
- //! Use Rhythm Mode percussions
- bool m_rhythmMode;
- //! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too.
- bool m_scaleModulators;
- //! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates.
- bool m_runAtPcmRate;
- //! Enable soft panning
- bool m_softPanning;
-
- //! Just a padding. Reserved.
- char _padding2[3];
-
- /**
- * @brief Music playing mode
- */
- enum MusicMode
- {
- //! MIDI mode
- MODE_MIDI,
- //! Id-Software Music mode
- MODE_IMF,
- //! Creative Music Files mode
- MODE_CMF,
- //! EA-MUS (a.k.a. RSXX) mode
- MODE_RSXX
- } m_musicMode;
-
- /**
- * @brief Volume models enum
- */
- enum VolumesScale
- {
- //! Generic volume model (linearization of logarithmic scale)
- VOLUME_Generic,
- //! OPL3 native logarithmic scale
- VOLUME_NATIVE,
- //! DMX volume scale logarithmic table
- VOLUME_DMX,
- //! Apoge Sound System volume scaling model
- VOLUME_APOGEE,
- //! Windows 9x driver volume scale table
- VOLUME_9X
- } m_volumeScale;
-
- //! Reserved
- char _padding3[8];
-
- /**
- * @brief Channel categiry enumeration
- */
- enum ChanCat
- {
- //! Regular melodic/percussion channel
- ChanCat_Regular = 0,
- //! Four-op master
- ChanCat_4op_Master = 1,
- //! Four-op slave
- ChanCat_4op_Slave = 2,
- //! Rhythm-mode Bass drum
- ChanCat_Rhythm_Bass = 3,
- //! Rhythm-mode Snare drum
- ChanCat_Rhythm_Snare = 4,
- //! Rhythm-mode Tom-Tom
- ChanCat_Rhythm_Tom = 5,
- //! Rhythm-mode Cymbal
- ChanCat_Rhythm_Cymbal = 6,
- //! Rhythm-mode Hi-Hat
- ChanCat_Rhythm_HiHat = 7,
- //! Rhythm-mode Slave channel
- ChanCat_Rhythm_Slave = 8
- };
-
- //! Category of the channel
- /*! 1 = quad-master, 2 = quad-slave, 0 = regular
- 3 = percussion BassDrum
- 4 = percussion Snare
- 5 = percussion Tom
- 6 = percussion Crash cymbal
- 7 = percussion Hihat
- 8 = percussion slave
- */
- std::vector<uint32_t> m_channelCategory;
-
-
- /**
- * @brief C.O. Constructor
- */
- OPL3();
-
- /**
- * @brief Checks are setup locked to be changed on the fly or not
- * @return true when setup on the fly is locked
- */
- bool setupLocked();
-
- /**
- * @brief Choose one of embedded banks
- * @param bank ID of the bank
- */
- void setEmbeddedBank(uint32_t bank);
-
- /**
- * @brief Write data to OPL3 chip register
- * @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored
- * @param address Register address to write
- * @param value Value to write
- */
- void writeReg(size_t chip, uint16_t address, uint8_t value);
-
- /**
- * @brief Write data to OPL3 chip register
- * @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored
- * @param address Register address to write
- * @param value Value to write
- */
- void writeRegI(size_t chip, uint32_t address, uint32_t value);
-
- /**
- * @brief Write to soft panning control of OPL3 chip emulator
- * @param chip Index of emulated chip.
- * @param address Register of channel to write
- * @param value Value to write
- */
- void writePan(size_t chip, uint32_t address, uint32_t value);
-
- /**
- * @brief Off the note in specified chip channel
- * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
- */
- void noteOff(size_t c);
-
- /**
- * @brief On the note in specified chip channel with specified frequency of the tone
- * @param c1 Channel of chip [or master 4-op channel] (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
- * @param c2 Slave 4-op channel of chip, unused for 2op (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
- * @param hertz Frequency of the tone in hertzes
- */
- void noteOn(size_t c1, size_t c2, double hertz);
-
- /**
- * @brief Change setup of instrument in specified chip channel
- * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
- * @param volume Volume level (from 0 to 63)
- * @param brightness CC74 Brightness level (from 0 to 127)
- */
- void touchNote(size_t c, uint8_t volume, uint8_t brightness = 127);
-
- /**
- * @brief Set the instrument into specified chip channel
- * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
- * @param instrument Instrument data to set into the chip channel
- */
- void setPatch(size_t c, const adldata &instrument);
-
- /**
- * @brief Set panpot position
- * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
- * @param value 3-bit panpot value
- */
- void setPan(size_t c, uint8_t value);
-
- /**
- * @brief Shut up all chip channels
- */
- void silenceAll();
-
- /**
- * @brief Commit updated flag states to chip registers
- */
- void updateChannelCategories();
-
- /**
- * @brief commit deepTremolo and deepVibrato flags
- */
- void commitDeepFlags();
-
- /**
- * @brief Set the volume scaling model
- * @param volumeModel Type of volume scale model scale
- */
- void setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel);
-
- /**
- * @brief Get the volume scaling model
- */
- ADLMIDI_VolumeModels getVolumeScaleModel();
-
- #ifndef ADLMIDI_HW_OPL
- /**
- * @brief Clean up all running emulated chip instances
- */
- void clearChips();
- #endif
-
- /**
- * @brief Reset chip properties and initialize them
- * @param emulator Type of chip emulator
- * @param PCM_RATE Output sample rate to generate on output
- * @param audioTickHandler PCM-accurate clock hook
- */
- void reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler);
-};
-
-
-/**
- * @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
-{
- char ret[4];
-
- FourChars(const char *s)
- {
- for(unsigned c = 0; c < 4; ++c)
- ret[c] = s[c];
- }
- FourChars(unsigned w) // Little-endian
- {
- for(unsigned c = 0; c < 4; ++c)
- ret[c] = static_cast<int8_t>((w >>(c * 8)) & 0xFF);
- }
-};
-*/
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate);
@@ -1505,23 +229,4 @@ extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate)
*/
extern int adlCalculateFourOpChannels(MIDIplay *play, bool silent = false);
-/**
- * @brief Check emulator availability
- * @param emulator Emulator ID (ADL_Emulator)
- * @return true when emulator is available
- */
-extern bool adl_isEmulatorAvailable(int emulator);
-
-/**
- * @brief Find highest emulator
- * @return The ADL_Emulator enum value which contains ID of highest emulator
- */
-extern int adl_getHighestEmulator();
-
-/**
- * @brief Find lowest emulator
- * @return The ADL_Emulator enum value which contains ID of lowest emulator
- */
-extern int adl_getLowestEmulator();
-
#endif // ADLMIDI_PRIVATE_HPP
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..a1fe5ab 100644
--- a/src/adlmidi_sequencer.cpp
+++ b/src/adlmidi_sequencer.cpp
@@ -28,6 +28,8 @@
// Inlucde MIDI sequencer class implementation
#include "midi_sequencer_impl.hpp"
+#include "adlmidi_midiplay.hpp"
+#include "adlmidi_opl3.hpp"
#include "adlmidi_private.hpp"
/****************************************************
@@ -106,37 +108,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)
+ Synth &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);
diff --git a/src/midi_sequencer.h b/src/midi_sequencer.h
index c6069d7..50e7109 100644
--- a/src/midi_sequencer.h
+++ b/src/midi_sequencer.h
@@ -36,7 +36,7 @@ extern "C" {
/**
\brief Real-Time MIDI interface between Sequencer and the Synthesizer
*/
-typedef struct
+typedef struct BW_MidiRtInterface
{
/*! Raw MIDI event hook */
typedef void (*RawEventHook)(void *userdata, uint8_t type, uint8_t subtype, uint8_t channel, const uint8_t *data, size_t len);