aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/adlmidi_midiplay.cpp123
-rw-r--r--src/adlmidi_private.cpp2
-rw-r--r--src/adlmidi_private.hpp28
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);