diff options
-rw-r--r-- | include/adlmidi.h | 53 | ||||
-rw-r--r-- | src/adlmidi.cpp | 237 | ||||
-rw-r--r-- | src/adlmidi_load.cpp | 17 | ||||
-rw-r--r-- | src/adlmidi_midiplay.cpp | 32 | ||||
-rw-r--r-- | src/adlmidi_opl3.cpp | 10 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 12 |
6 files changed, 298 insertions, 63 deletions
diff --git a/include/adlmidi.h b/include/adlmidi.h index c67952f..bc1b2d3 100644 --- a/include/adlmidi.h +++ b/include/adlmidi.h @@ -33,14 +33,14 @@ extern "C" { #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) #include <stdint.h> typedef uint8_t ADL_UInt8; -typedef uint16_t ADL_Uint16; -typedef int8_t ADL_Sint8; +typedef uint16_t ADL_UInt16; +typedef int8_t ADL_SInt8; typedef int16_t ADL_Sint16; #else typedef unsigned char ADL_UInt8; -typedef unsigned short ADL_Uint16; -typedef char ADL_Sint8; -typedef short ADL_Sint16; +typedef unsigned short ADL_UInt16; +typedef char ADL_SInt8; +typedef short ADL_SInt16; #endif enum ADLMIDI_VolumeModels @@ -67,7 +67,7 @@ extern int adl_setNumChips(struct ADL_MIDIPlayer *device, int numCards); /* Get current number of emulated chips */ extern int adl_getNumChips(struct ADL_MIDIPlayer *device); -/* Sets a number of the patches bank from 0 to N banks */ +/* Sets a number of the patches bank from 0 to N banks. Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. */ extern int adl_setBank(struct ADL_MIDIPlayer *device, int bank); /* Returns total number of available banks */ @@ -107,7 +107,7 @@ extern void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol) /*Set different volume range model */ extern void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel); -/*Load WOPL bank file from File System*/ +/*Load WOPL bank file from File System. Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time.*/ extern int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath); /*Load WOPL bank file from memory data*/ @@ -192,12 +192,10 @@ extern size_t adl_metaMarkerCount(struct ADL_MIDIPlayer *device); extern const struct Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size_t index); - - /*Take a sample buffer and iterate MIDI timers */ extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short out[]); -/*Generate audio output from chip emulators without iteration of MIDI timers. 512 samples per channel is a maximum*/ +/*Generate audio output from chip emulators without iteration of MIDI timers.*/ extern int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out); /** @@ -215,9 +213,44 @@ extern double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, doub /*Returns 1 if music position has reached end*/ extern int adl_atEnd(struct ADL_MIDIPlayer *device); +/**RealTime**/ + /*Force Off all notes on all channels*/ extern void adl_panic(struct ADL_MIDIPlayer *device); +/*Reset states of all controllers on all MIDI channels*/ +extern void adl_rt_resetState(struct ADL_MIDIPlayer *device); + +/*Turn specific MIDI note ON*/ +extern bool adl_rt_noteOn(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 velocity); + +/*Turn specific MIDI note OFF*/ +extern void adl_rt_noteOff(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note); + +/*Set note after-touch*/ +extern void adl_rt_noteAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 atVal); +/*Set channel after-touch*/ +extern void adl_rt_channelAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 atVal); + +/*Apply controller change*/ +extern void adl_rt_controllerChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 type, ADL_UInt8 value); + +/*Apply patch change*/ +extern void adl_rt_patchChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 patch); + +/*Apply pitch bend change*/ +extern void adl_rt_pitchBend(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 pitch); +/*Apply pitch bend change*/ +extern void adl_rt_pitchBendML(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb, ADL_UInt8 lsb); + +/*Change LSB of the bank*/ +extern void adl_rt_bankChangeLSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 lsb); +/*Change MSB of the bank*/ +extern void adl_rt_bankChangeMSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb); +/*Change bank by absolute signed value*/ +extern void adl_rt_bankChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_SInt16 bank); + + /**Hooks**/ typedef void (*ADL_RawEventHook)(void *userdata, ADL_UInt8 type, ADL_UInt8 subtype, ADL_UInt8 channel, const ADL_UInt8 *data, size_t len); diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp index 380630a..dd4a3e1 100644 --- a/src/adlmidi.cpp +++ b/src/adlmidi.cpp @@ -75,6 +75,7 @@ ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numCards) } play->opl.NumCards = play->m_setup.NumCards; + adl_reset(device); return adlRefreshNumCards(device); } @@ -173,6 +174,7 @@ ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod) MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); play->m_setup.AdlPercussionMode = percmod; play->opl.AdlPercussionMode = play->m_setup.AdlPercussionMode; + play->opl.updateFlags(); } ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro) @@ -181,6 +183,7 @@ ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro) MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); play->m_setup.HighVibratoMode = hvibro; play->opl.HighVibratoMode = play->m_setup.HighVibratoMode; + play->opl.updateDeepFlags(); } ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo) @@ -189,6 +192,7 @@ ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo) MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); play->m_setup.HighTremoloMode = htremo; play->opl.HighTremoloMode = play->m_setup.HighTremoloMode; + play->opl.updateDeepFlags(); } ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod) @@ -358,6 +362,8 @@ ADLMIDI_EXPORT void adl_reset(struct ADL_MIDIPlayer *device) MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); play->m_setup.tick_skip_samples_delay = 0; play->opl.Reset(play->m_setup.PCM_RATE); + play->ch.clear(); + play->ch.resize(play->opl.NumChannels); } ADLMIDI_EXPORT double adl_totalTimeLength(struct ADL_MIDIPlayer *device) @@ -640,45 +646,90 @@ ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, if(!device) return 0; - /* - * TODO: Implement the processing of vibrato and arpeggio here. - * Otherwise, in realtime playing there are will not be processed - * and chip channel management will work incorrectly! - */ - MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); - sampleCount = (sampleCount > 1024) ? 1024 : sampleCount; + MIDIplay::Setup &setup = player->m_setup; - //! Count of stereo samples - ssize_t in_generatedStereo = sampleCount / 2; - //! Unsigned total sample count - //fill buffer with zeros - std::memset(out, 0, static_cast<size_t>(sampleCount) * sizeof(int16_t)); + ssize_t gotten_len = 0; + ssize_t n_periodCountStereo = 512; - if(player->m_setup.NumCards == 1) - { - #ifdef ADLMIDI_USE_DOSBOX_OPL - player->opl.cards[0].GenerateArr(out, &in_generatedStereo); - sampleCount = in_generatedStereo * 2; - #else - OPL3_GenerateStream(&player->opl.cards[0], out, static_cast<Bit32u>(in_generatedStereo)); - #endif - } - else + int left = sampleCount; + bool hasSkipped = setup.tick_skip_samples_delay > 0; + + double delay = double(sampleCount) / double(setup.PCM_RATE); + + while(left > 0) { - /* Generate data from every chip and mix result */ - for(unsigned card = 0; card < player->m_setup.NumCards; ++card) - { - #ifdef ADLMIDI_USE_DOSBOX_OPL - player->opl.cards[card].GenerateArrMix(out, &in_generatedStereo); - sampleCount = in_generatedStereo * 2; - #else - OPL3_GenerateStreamMix(&player->opl.cards[card], out, static_cast<Bit32u>(in_generatedStereo)); - #endif - } + {//... + const double eat_delay = delay < setup.maxdelay ? delay : setup.maxdelay; + if(hasSkipped) + { + size_t samples = setup.tick_skip_samples_delay > sampleCount ? sampleCount : setup.tick_skip_samples_delay; + n_periodCountStereo = samples / 2; + } + else + { + delay -= eat_delay; + setup.carry += setup.PCM_RATE * eat_delay; + n_periodCountStereo = static_cast<ssize_t>(setup.carry); + setup.carry -= n_periodCountStereo; + } + + { + ssize_t leftSamples = left / 2; + if(n_periodCountStereo > leftSamples) + { + setup.tick_skip_samples_delay = (n_periodCountStereo - leftSamples) * 2; + n_periodCountStereo = leftSamples; + } + //! Count of stereo samples + ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo; + //! Total count of samples + ssize_t in_generatedPhys = in_generatedStereo * 2; + //! Unsigned total sample count + //fill buffer with zeros + int16_t *out_buf = player->outBuf; + std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(int16_t)); + unsigned int chips = player->opl.NumCards; + if(chips == 1) + { + #ifdef ADLMIDI_USE_DOSBOX_OPL + player->opl.cards[0].GenerateArr(out_buf, &in_generatedStereo); + in_generatedPhys = in_generatedStereo * 2; + #else + OPL3_GenerateStream(&player->opl.cards[0], out_buf, static_cast<Bit32u>(in_generatedStereo)); + #endif + } + else if(n_periodCountStereo > 0) + { + /* Generate data from every chip and mix result */ + for(unsigned card = 0; card < chips; ++card) + { + #ifdef ADLMIDI_USE_DOSBOX_OPL + player->opl.cards[card].GenerateArrMix(out_buf, &in_generatedStereo); + in_generatedPhys = in_generatedStereo * 2; + #else + OPL3_GenerateStreamMix(&player->opl.cards[card], out_buf, static_cast<Bit32u>(in_generatedStereo)); + #endif + } + } + /* Process it */ + SendStereoAudio(setup, sampleCount, in_generatedStereo, out_buf, gotten_len, out); + + left -= (int)in_generatedPhys; + gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; + } + + if(hasSkipped) + { + setup.tick_skip_samples_delay -= n_periodCountStereo * 2; + hasSkipped = setup.tick_skip_samples_delay > 0; + } + else + player->TickIteratos(eat_delay); + }//... } - return sampleCount; + return static_cast<int>(gotten_len); #endif } @@ -711,3 +762,123 @@ ADLMIDI_EXPORT void adl_panic(struct ADL_MIDIPlayer *device) return; player->realTime_panic(); } + +ADLMIDI_EXPORT void adl_rt_resetState(struct ADL_MIDIPlayer *device) +{ + if(!device) + return; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return; + player->realTime_ResetState(); +} + +bool adl_rt_noteOn(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 velocity) +{ + if(!device) + return false; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return false; + return player->realTime_NoteOn(channel, note, velocity); +} + +void adl_rt_noteOff(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note) +{ + if(!device) + return; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return; + player->realTime_NoteOff(channel, note); +} + +void adl_rt_noteAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 atVal) +{ + if(!device) + return; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return; + player->realTime_NoteAfterTouch(channel, note, atVal); +} + +void adl_rt_channelAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 atVal) +{ + if(!device) + return; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return; + player->realTime_ChannelAfterTouch(channel, atVal); +} + +void adl_rt_controllerChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 type, ADL_UInt8 value) +{ + if(!device) + return; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return; + player->realTime_Controller(channel, type, value); +} + +void adl_rt_patchChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 patch) +{ + if(!device) + return; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return; + player->realTime_PatchChange(channel, patch); +} + +void adl_rt_pitchBend(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 pitch) +{ + if(!device) + return; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return; + player->realTime_PitchBend(channel, pitch); +} + +void adl_rt_pitchBendML(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb, ADL_UInt8 lsb) +{ + if(!device) + return; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return; + player->realTime_PitchBend(channel, msb, lsb); +} + +void adl_rt_bankChangeLSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 lsb) +{ + if(!device) + return; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return; + player->realTime_BankChangeLSB(channel, lsb); +} + +void adl_rt_bankChangeMSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb) +{ + if(!device) + return; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return; + player->realTime_BankChangeMSB(channel, msb); +} + +void adl_rt_bankChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_SInt16 bank) +{ + if(!device) + return; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return; + player->realTime_BankChange(channel, (uint16_t)bank); +} diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp index 89428c9..f33c091 100644 --- a/src/adlmidi_load.cpp +++ b/src/adlmidi_load.cpp @@ -379,22 +379,7 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr) } /**** Set all properties BEFORE starting of actial file reading! ****/ - m_setup.tick_skip_samples_delay = 0; - opl.HighTremoloMode = m_setup.HighTremoloMode == -1 ? adlbanksetup[m_setup.AdlBank].deepTremolo : (bool)m_setup.HighTremoloMode; - opl.HighVibratoMode = m_setup.HighVibratoMode == -1 ? adlbanksetup[m_setup.AdlBank].deepVibrato : (bool)m_setup.HighVibratoMode; - opl.AdlPercussionMode = m_setup.AdlPercussionMode == -1 ? adlbanksetup[m_setup.AdlBank].adLibPercussions : (bool)m_setup.AdlPercussionMode; - opl.ScaleModulators = m_setup.ScaleModulators == -1 ? adlbanksetup[m_setup.AdlBank].scaleModulators : (bool)m_setup.ScaleModulators; - opl.LogarithmicVolumes = m_setup.LogarithmicVolumes; - //opl.CartoonersVolumes = false; - opl.m_musicMode = OPL3::MODE_MIDI; - opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(m_setup.VolumeModel)); - if(m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model - opl.m_volumeScale = (OPL3::VolumesScale)adlbanksetup[m_setup.AdlBank].volumeModel; - - opl.NumCards = m_setup.NumCards; - opl.NumFourOps = m_setup.NumFourOps; - cmf_percussion_mode = false; - opl.Reset(m_setup.PCM_RATE); + applySetup(); atEnd = false; loopStart = true; diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index dacb2b1..2470903 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -717,14 +717,30 @@ MIDIplay::MIDIplay(): m_setup.carry = 0.0; m_setup.tick_skip_samples_delay = 0; - opl.NumCards = m_setup.NumCards; - opl.AdlBank = m_setup.AdlBank; - opl.NumFourOps = m_setup.NumFourOps; - opl.LogarithmicVolumes = m_setup.LogarithmicVolumes; + applySetup(); +} + +void MIDIplay::applySetup() +{ + m_setup.tick_skip_samples_delay = 0; opl.HighTremoloMode = m_setup.HighTremoloMode == -1 ? adlbanksetup[m_setup.AdlBank].deepTremolo : (bool)m_setup.HighTremoloMode; opl.HighVibratoMode = m_setup.HighVibratoMode == -1 ? adlbanksetup[m_setup.AdlBank].deepVibrato : (bool)m_setup.HighVibratoMode; opl.AdlPercussionMode = m_setup.AdlPercussionMode == -1 ? adlbanksetup[m_setup.AdlBank].adLibPercussions : (bool)m_setup.AdlPercussionMode; opl.ScaleModulators = m_setup.ScaleModulators == -1 ? adlbanksetup[m_setup.AdlBank].scaleModulators : (bool)m_setup.ScaleModulators; + opl.LogarithmicVolumes = m_setup.LogarithmicVolumes; + //opl.CartoonersVolumes = false; + opl.m_musicMode = OPL3::MODE_MIDI; + opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(m_setup.VolumeModel)); + if(m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model + opl.m_volumeScale = (OPL3::VolumesScale)adlbanksetup[m_setup.AdlBank].volumeModel; + + opl.NumCards = m_setup.NumCards; + opl.NumFourOps = m_setup.NumFourOps; + cmf_percussion_mode = false; + + opl.Reset(m_setup.PCM_RATE); + ch.clear(); + ch.resize(opl.NumChannels); } uint64_t MIDIplay::ReadVarLen(uint8_t **ptr) @@ -795,6 +811,14 @@ double MIDIplay::Tick(double s, double granularity) return CurrentPositionNew.wait; } +void MIDIplay::TickIteratos(double s) +{ + for(uint16_t c = 0; c < opl.NumChannels; ++c) + ch[c].AddAge(static_cast<int64_t>(s * 1000.0)); + UpdateVibrato(s); + UpdateArpeggio(s); +} + void MIDIplay::seek(double seconds) { if(seconds < 0.0) diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp index 273f548..9813621 100644 --- a/src/adlmidi_opl3.cpp +++ b/src/adlmidi_opl3.cpp @@ -458,6 +458,16 @@ void OPL3::updateFlags() } } +void OPL3::updateDeepFlags() +{ + for(unsigned card = 0; card < NumCards; ++card) + { + Poke(card, 0x0BD, regBD[card] = (HighTremoloMode * 0x80 + + HighVibratoMode * 0x40 + + AdlPercussionMode * 0x20)); + } +} + void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel) { switch(volumeModel) diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index 46187bc..cb100dc 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -166,6 +166,7 @@ public: }; class MIDIplay; +struct ADL_MIDIPlayer; class OPL3 { public: @@ -263,6 +264,7 @@ public: void Pan(unsigned c, unsigned value); void Silence(); void updateFlags(); + void updateDeepFlags(); void ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel); void Reset(unsigned long PCM_RATE); }; @@ -300,12 +302,15 @@ struct MIDIEventHooks class MIDIplay { + friend void adl_reset(struct ADL_MIDIPlayer*); public: MIDIplay(); ~MIDIplay() {} + void applySetup(); + /**********************Internal structures and classes**********************/ /** @@ -852,6 +857,12 @@ public: double Tick(double s, double granularity); /** + * @brief Process extra iterators like vibrato or arpeggio + * @param s seconds since last call + */ + void TickIteratos(double s); + + /** * @brief Change current position to specified time position in seconds * @param seconds Absolute time position in seconds */ @@ -950,6 +961,7 @@ private: //void UpdatePortamento(unsigned MidCh) void NoteUpdate_All(uint16_t MidCh, unsigned props_mask); void NoteOff(uint16_t MidCh, uint8_t note); + void UpdateVibrato(double amount); void UpdateArpeggio(double /*amount*/); |