diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/adlmidi_midiplay.cpp | 123 | ||||
-rw-r--r-- | src/adlmidi_private.cpp | 2 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 28 |
3 files changed, 122 insertions, 31 deletions
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 89c1a73..2f186a6 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -680,8 +680,10 @@ bool MIDIplay::buildTrackData() MIDIplay::MIDIplay(unsigned long sampleRate): cmf_percussion_mode(false), - m_arpeggioCounter(0), - m_audioTickCounter(0) + m_arpeggioCounter(0) +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + , m_audioTickCounter(0) +#endif #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER , fullSongTimeLength(0.0), postSongWaitDelay(1.0), @@ -825,6 +827,9 @@ double MIDIplay::Tick(double s, double granularity) UpdateVibrato(s); UpdateArpeggio(s); +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + UpdateGlide(s); +#endif if(CurrentPositionNew.wait < 0.0)//Avoid negative delay value! return 0.0; @@ -839,6 +844,9 @@ void MIDIplay::TickIteratos(double s) ch[c].AddAge(static_cast<int64_t>(s * 1000.0)); UpdateVibrato(s); UpdateArpeggio(s); +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + UpdateGlide(s); +#endif } #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER @@ -1209,11 +1217,29 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) ir = midiChan.activenotes_insert(note); ir.first->vol = velocity; ir.first->vibrato = midiChan.noteAftertouch[note]; - ir.first->tone = tone; + ir.first->noteTone = tone; + ir.first->currentTone = tone; + ir.first->glideRate = HUGE_VAL; ir.first->midiins = midiins; ir.first->ains = ains; ir.first->chip_channels_count = 0; + int8_t currentPortamentoSource = midiChan.portamentoSource; + double currentPortamentoRate = midiChan.portamentoRate; + bool portamentoEnable = + midiChan.portamentoEnable && currentPortamentoRate != HUGE_VAL && + !isPercussion && !isXgPercussion; + // Record the last note on MIDI channel as source of portamento + midiChan.portamentoSource = portamentoEnable ? (int8_t)note : (int8_t)-1; + + // Enable gliding on portamento note + if (portamentoEnable && currentPortamentoSource >= 0) + { + ir.first->currentTone = currentPortamentoSource; + ir.first->glideRate = currentPortamentoRate; + ++midiChan.gliding_note_count; + } + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) { int32_t c = adlchannel[ccount]; @@ -1222,6 +1248,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) uint16_t chipChan = static_cast<uint16_t>(adlchannel[ccount]); ir.first->phys_ensure_find_or_create(chipChan)->assign(voices[ccount]); } + NoteUpdate(channel, ir.first, Upd_All | Upd_Patch); return true; } @@ -1281,17 +1308,17 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) case 5: // Set portamento msb Ch[channel].portamento = static_cast<uint16_t>((Ch[channel].portamento & 0x7F) | (value << 7)); - //UpdatePortamento(MidCh); + UpdatePortamento(channel); break; case 37: // Set portamento lsb Ch[channel].portamento = (Ch[channel].portamento & 0x3F80) | (value); - //UpdatePortamento(MidCh); + UpdatePortamento(channel); break; case 65: // Enable/disable portamento - // value >= 64 ? enabled : disabled - //UpdatePortamento(MidCh); + Ch[channel].portamentoEnable = value >= 64; + UpdatePortamento(channel); break; case 7: // Change volume @@ -1324,7 +1351,6 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) case 121: // Reset all controllers Ch[channel].resetAllControllers(); - //UpdatePortamento(MidCh); NoteUpdate_All(channel, Upd_Pan + Upd_Volume + Upd_Pitch); // Kill all sustained notes KillSustainingNotes(channel); @@ -1439,13 +1465,24 @@ void MIDIplay::realTime_panic() KillSustainingNotes(-1, -1); } -void MIDIplay::AudioTick(uint32_t chipId, uint32_t /*rate*/) +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +void MIDIplay::AudioTick(uint32_t chipId, uint32_t rate) { if(chipId != 0) // do first chip ticks only return; - /*uint32_t tickNumber = */m_audioTickCounter++; + uint32_t tickNumber = m_audioTickCounter++; + double timeDelta = 1.0 / rate; + + enum { portamentoInterval = 32 }; // for efficiency, set rate limit on pitch updates + + if(tickNumber % portamentoInterval == 0) + { + double portamentoDelta = timeDelta * portamentoInterval; + UpdateGlide(portamentoDelta); + } } +#endif void MIDIplay::NoteUpdate(uint16_t MidCh, MIDIplay::MIDIchannel::activenoteiterator i, @@ -1453,7 +1490,8 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, int32_t select_adlchn) { MIDIchannel::NoteInfo &info = *i; - const int16_t tone = info.tone; + const int16_t noteTone = info.noteTone; + const double currentTone = info.currentTone; const uint8_t vol = info.vol; const int midiins = static_cast<int>(info.midiins); const adlinsdata2 &ains = *info.ains; @@ -1501,7 +1539,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, ch[c].users_erase(k); if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, c, tone, midiins, 0, 0.0); + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, 0, 0.0); if(ch[c].users_empty()) { @@ -1525,7 +1563,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, if(d) d->sustained = true; // note: not erased! if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, c, tone, midiins, -1, 0.0); + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, -1, 0.0); } info.phys_erase_at(&ins); // decrements channel count @@ -1653,16 +1691,20 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, #else # define BEND_COEFFICIENT 172.4387 #endif - opl.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (static_cast<double>(tone) + bend + phase))); + opl.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (currentTone + bend + phase))); #undef BEND_COEFFICIENT if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, c, tone, midiins, vol, midibend); + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, vol, midibend); } } } if(info.chip_channels_count == 0) + { + if(i->glideRate != HUGE_VAL) + --Ch[MidCh].gliding_note_count; Ch[MidCh].activenotes_erase(i); + } } #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER @@ -2314,11 +2356,11 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, { hooks.onNote(hooks.onNote_userData, (int)from_channel, - i->tone, + i->noteTone, static_cast<int>(i->midiins), 0, 0.0); hooks.onNote(hooks.onNote_userData, (int)c, - i->tone, + i->noteTone, static_cast<int>(i->midiins), i->vol, 0.0); } @@ -2423,15 +2465,14 @@ void MIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) } } -//void MIDIplay::UpdatePortamento(unsigned MidCh) -//{ -// // mt = 2^(portamento/2048) * (1.0 / 5000.0) -// /* -// double mt = std::exp(0.00033845077 * Ch[MidCh].portamento); -// NoteUpdate_All(MidCh, Upd_Pitch); -// */ -// //UI.PrintLn("Portamento %u: %u (unimplemented)", MidCh, Ch[MidCh].portamento); -//} +void MIDIplay::UpdatePortamento(unsigned MidCh) +{ + double rate = HUGE_VAL; + uint16_t midival = Ch[MidCh].portamento; + if(Ch[MidCh].portamentoEnable && midival > 0) + rate = 350.0 * std::pow(2.0, -0.062 * (1.0 / 128) * midival); + Ch[MidCh].portamentoRate = rate; +} void MIDIplay::NoteUpdate_All(uint16_t MidCh, unsigned props_mask) { @@ -2554,6 +2595,36 @@ retry_arpeggio: } } +void MIDIplay::UpdateGlide(double amount) +{ + for(unsigned channel = 0; channel < 16; ++channel) + { + MIDIchannel &midiChan = Ch[channel]; + if(midiChan.gliding_note_count == 0) + continue; + + for(MIDIchannel::activenoteiterator it = midiChan.activenotes_begin(); + it; ++it) + { + double finalTone = it->noteTone; + double previousTone = it->currentTone; + + bool directionUp = previousTone < finalTone; + double toneIncr = amount * (directionUp ? +it->glideRate : -it->glideRate); + + double currentTone = previousTone + toneIncr; + bool glideFinished = !(directionUp ? (currentTone < finalTone) : (currentTone > finalTone)); + currentTone = glideFinished ? finalTone : currentTone; + + if(currentTone != previousTone) + { + it->currentTone = currentTone; + NoteUpdate(channel, it, Upd_Pitch); + } + } + } +} + #ifndef ADLMIDI_DISABLE_CPP_EXTRAS diff --git a/src/adlmidi_private.cpp b/src/adlmidi_private.cpp index 1ee7c4a..3bc73a4 100644 --- a/src/adlmidi_private.cpp +++ b/src/adlmidi_private.cpp @@ -27,10 +27,12 @@ std::string ADLMIDI_ErrorString; // Generator callback on audio rate ticks +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate) { reinterpret_cast<MIDIplay *>(instance)->AudioTick(chipId, rate); } +#endif int adlRefreshNumCards(ADL_MIDIPlayer *device) { diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index 0e63b5a..fda629d 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -334,7 +334,7 @@ class MIDIplay { friend void adl_reset(struct ADL_MIDIPlayer*); public: - MIDIplay(unsigned long sampleRate = 22050); + explicit MIDIplay(unsigned long sampleRate = 22050); ~MIDIplay() {} @@ -496,11 +496,14 @@ public: // Persistent settings for each MIDI channel struct MIDIchannel { - uint16_t portamento; uint8_t bank_lsb, bank_msb; uint8_t patch; uint8_t volume, expression; uint8_t panning, vibrato, aftertouch, sustain; + uint16_t portamento; + bool portamentoEnable; + int8_t portamentoSource; // note number or -1 + double portamentoRate; //! Per note Aftertouch values uint8_t noteAftertouch[128]; //! Is note aftertouch has any non-zero value @@ -524,8 +527,11 @@ public: // Note vibrato (a part of Note Aftertouch feature) uint8_t vibrato; // Tone selected on noteon: - int16_t tone; - char ____padding2[4]; + 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; // Patch selected @@ -604,6 +610,7 @@ public: } }; char ____padding2[5]; + unsigned gliding_note_count; NoteInfo activenotes[128]; struct activenoteiterator @@ -717,6 +724,9 @@ public: vibdelay = 0; panning = OPL_PANNING_BOTH; portamento = 0; + portamentoEnable = false; + portamentoSource = -1; + portamentoRate = HUGE_VAL; brightness = 127; } bool hasVibrato() @@ -731,6 +741,7 @@ public: MIDIchannel() { activenotes_clear(); + gliding_note_count = 0; reset(); } }; @@ -987,8 +998,10 @@ private: //! Counter of arpeggio processing size_t m_arpeggioCounter; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) //! Audio tick counter uint32_t m_audioTickCounter; +#endif #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER std::vector<std::vector<uint8_t> > TrackData; @@ -1187,8 +1200,10 @@ public: void realTime_panic(); +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) // Audio rate tick handler void AudioTick(uint32_t chipId, uint32_t rate); +#endif private: enum @@ -1228,12 +1243,13 @@ private: void Panic(); void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1); void SetRPN(unsigned MidCh, unsigned value, bool MSB); - //void UpdatePortamento(unsigned MidCh) + 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*/); + void UpdateGlide(double amount); public: uint64_t ChooseDevice(const std::string &name); @@ -1258,7 +1274,9 @@ struct FourChars }; */ +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); +#endif extern int adlRefreshNumCards(ADL_MIDIPlayer *device); |