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