aboutsummaryrefslogtreecommitdiff
path: root/src/adlmidi_midiplay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/adlmidi_midiplay.cpp')
-rw-r--r--src/adlmidi_midiplay.cpp123
1 files changed, 97 insertions, 26 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