aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/adlmidi_midiplay.cpp806
-rw-r--r--src/adlmidi_private.hpp56
2 files changed, 489 insertions, 373 deletions
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp
index d070981..6ced7ba 100644
--- a/src/adlmidi_midiplay.cpp
+++ b/src/adlmidi_midiplay.cpp
@@ -165,17 +165,17 @@ double MIDIplay::Tick(double s, double granularity)
int AntiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing
- while((CurrentPosition.wait <= granularity * 0.5l) && (AntiFreezeCounter > 0))
+ while((CurrentPosition.wait <= granularity * 0.5) && (AntiFreezeCounter > 0))
{
//std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait);
ProcessEvents();
- if(CurrentPosition.wait <= 0.0l)
+ if(CurrentPosition.wait <= 0.0)
AntiFreezeCounter--;
}
if(AntiFreezeCounter <= 0)
- CurrentPosition.wait += 1.0l;/* Add extra 1 second when over 10000 events
+ CurrentPosition.wait += 1.0;/* Add extra 1 second when over 10000 events
with zero delay are been detected */
@@ -187,6 +187,435 @@ double MIDIplay::Tick(double s, double granularity)
return CurrentPosition.wait;
}
+void MIDIplay::realTime_ResetState()
+{
+ for(size_t ch = 0; ch < Ch.size(); ch++)
+ {
+ MIDIchannel &chan = Ch[ch];
+ chan.volume = 100;
+ chan.expression = 127;
+ chan.panning = 0x30;
+ chan.vibrato = 0;
+ chan.sustain = 0;
+ chan.bend = 0.0;
+ chan.bendsense = 2 / 8192.0;
+ chan.vibpos = 0.0;
+ chan.vibdepth = 0.5 / 127.0;
+ chan.vibdelay = 0.0;
+ chan.lastlrpn = 0;
+ chan.lastmrpn = 0;
+ chan.nrpn = false;
+ NoteUpdate_All(uint16_t(ch), Upd_All);
+ NoteUpdate_All(uint16_t(ch), Upd_Off);
+ }
+}
+
+bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
+{
+ channel = channel % 16;
+ NoteOff(channel, note);
+ // On Note on, Keyoff the note first, just in case keyoff
+ // was omitted; this fixes Dance of sugar-plum fairy
+ // by Microsoft. Now that we've done a Keyoff,
+ // check if we still need to do a Keyon.
+ // vol=0 and event 8x are both Keyoff-only.
+ if(velocity == 0)
+ return false;
+
+ uint8_t midiins = Ch[channel].patch;
+
+ if(channel % 16 == 9)
+ midiins = 128 + note; // Percussion instrument
+
+ /*
+ if(MidCh%16 == 9 || (midiins != 32 && midiins != 46 && midiins != 48 && midiins != 50))
+ break; // HACK
+ if(midiins == 46) vol = (vol*7)/10; // HACK
+ if(midiins == 48 || midiins == 50) vol /= 4; // HACK
+ */
+ //if(midiins == 56) vol = vol*6/10; // HACK
+
+ /* ====================================================================================
+ * TODO: Instead of this shit implement support of multiple banks by using WOPL format
+ * (which will allow to implement GS or XG compatible banks!)
+ * ====================================================================================
+
+ static std::set<uint32_t> bank_warnings;
+
+ if(Ch[channel].bank_msb)
+ {
+ uint32_t bankid = midiins + 256 * Ch[channel].bank_msb;
+ std::set<uint32_t>::iterator
+ i = bank_warnings.lower_bound(bankid);
+
+ if(i == bank_warnings.end() || *i != bankid)
+ {
+ ADLMIDI_ErrorString.clear();
+ std::stringstream s;
+ s << "[" << channel << "]Bank " << Ch[channel].bank_msb <<
+ " undefined, patch=" << ((midiins & 128) ? 'P' : 'M') << (midiins & 127);
+ ADLMIDI_ErrorString = s.str();
+ bank_warnings.insert(i, bankid);
+ }
+ }
+
+ if(Ch[channel].bank_lsb)
+ {
+ unsigned bankid = Ch[channel].bank_lsb * 65536;
+ std::set<unsigned>::iterator
+ i = bank_warnings.lower_bound(bankid);
+
+ if(i == bank_warnings.end() || *i != bankid)
+ {
+ ADLMIDI_ErrorString.clear();
+ std::stringstream s;
+ s << "[" << channel << "]Bank lsb " << Ch[channel].bank_lsb << " undefined";
+ ADLMIDI_ErrorString = s.str();
+ bank_warnings.insert(i, bankid);
+ }
+ }
+ */
+
+ //int meta = banks[opl.AdlBank][midiins];
+ const uint32_t meta = opl.GetAdlMetaNumber(midiins);
+ const adlinsdata &ains = opl.GetAdlMetaIns(meta);
+ int16_t tone = note;
+
+ if(ains.tone)
+ {
+ /*if(ains.tone < 20)
+ tone += ains.tone;
+ else*/
+ if(ains.tone < 128)
+ tone = ains.tone;
+ else
+ tone -= ains.tone - 128;
+ }
+
+ uint16_t i[2] = { ains.adlno1, ains.adlno2 };
+ bool pseudo_4op = ains.flags & adlinsdata::Flag_Pseudo4op;
+
+ if((opl.AdlPercussionMode == 1) && PercussionMap[midiins & 0xFF]) i[1] = i[0];
+
+ static std::set<uint8_t> missing_warnings;
+
+ if(!missing_warnings.count(static_cast<uint8_t>(midiins)) && (ains.flags & adlinsdata::Flag_NoSound))
+ {
+ //UI.PrintLn("[%i]Playing missing instrument %i", MidCh, midiins);
+ missing_warnings.insert(static_cast<uint8_t>(midiins));
+ }
+
+ // Allocate AdLib channel (the physical sound channel for the note)
+ int32_t adlchannel[2] = { -1, -1 };
+
+ for(uint32_t ccount = 0; ccount < 2; ++ccount)
+ {
+ if(ccount == 1)
+ {
+ if(i[0] == i[1]) break; // No secondary channel
+
+ if(adlchannel[0] == -1)
+ break; // No secondary if primary failed
+ }
+
+ int32_t c = -1;
+ long bs = -0x7FFFFFFFl;
+
+ for(uint32_t a = 0; a < opl.NumChannels; ++a)
+ {
+ if(ccount == 1 && static_cast<int32_t>(a) == adlchannel[0]) continue;
+
+ // ^ Don't use the same channel for primary&secondary
+
+ if(i[0] == i[1] || pseudo_4op)
+ {
+ // Only use regular channels
+ uint8_t expected_mode = 0;
+
+ if(opl.AdlPercussionMode == 1)
+ {
+ if(cmf_percussion_mode)
+ expected_mode = channel < 11 ? 0 : (3 + channel - 11); // CMF
+ else
+ expected_mode = PercussionMap[midiins & 0xFF];
+ }
+
+ if(opl.four_op_category[a] != expected_mode)
+ continue;
+ }
+ else
+ {
+ if(ccount == 0)
+ {
+ // Only use four-op master channels
+ if(opl.four_op_category[a] != 1)
+ continue;
+ }
+ else
+ {
+ // The secondary must be played on a specific channel.
+ if(a != static_cast<uint32_t>(adlchannel[0]) + 3)
+ continue;
+ }
+ }
+
+ long s = CalculateAdlChannelGoodness(a, i[ccount], channel );
+
+ if(s > bs)
+ {
+ bs = s; // Best candidate wins
+ c = static_cast<int32_t>(a);
+ }
+ }
+
+ if(c < 0)
+ {
+ //UI.PrintLn("ignored unplaceable note");
+ continue; // Could not play this note. Ignore it.
+ }
+
+ PrepareAdlChannelForNewNote(static_cast<size_t>(c), i[ccount]);
+ adlchannel[ccount] = c;
+ }
+
+ if(adlchannel[0] < 0 && adlchannel[1] < 0)
+ {
+ // The note could not be played, at all.
+ return false;
+ }
+
+ //UI.PrintLn("i1=%d:%d, i2=%d:%d", i[0],adlchannel[0], i[1],adlchannel[1]);
+ // Allocate active note for MIDI channel
+ std::pair<MIDIchannel::activenoteiterator, bool>
+ ir = Ch[channel].activenotes.insert(std::make_pair(note, MIDIchannel::NoteInfo()));
+ ir.first->second.vol = velocity;
+ ir.first->second.tone = tone;
+ ir.first->second.midiins = midiins;
+ ir.first->second.insmeta = meta;
+
+ for(unsigned ccount = 0; ccount < 2; ++ccount)
+ {
+ int32_t c = adlchannel[ccount];
+
+ if(c < 0)
+ continue;
+
+ ir.first->second.phys[ static_cast<uint16_t>(adlchannel[ccount]) ] = i[ccount];
+ }
+ NoteUpdate(channel, ir.first, Upd_All | Upd_Patch);
+ return true;
+}
+
+void MIDIplay::realTime_NoteOff(uint8_t channel, uint8_t note)
+{
+ channel = channel % 16;
+ NoteOff(channel, note);
+}
+
+void MIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal)
+{
+ channel = channel % 16;
+ MIDIchannel::activenoteiterator
+ i = Ch[channel].activenotes.find(note);
+ if(i == Ch[channel].activenotes.end())
+ {
+ // Ignore touch if note is not active
+ return;
+ }
+ i->second.vol = 127 - atVal;
+ NoteUpdate(channel, i, Upd_Volume);
+}
+
+void MIDIplay::realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal)
+{
+ // TODO: Verify, is this correct action?
+ channel = channel % 16;
+ for(MIDIchannel::activenoteiterator
+ i = Ch[channel].activenotes.begin();
+ i != Ch[channel].activenotes.end();
+ ++i)
+ {
+ // Set this pressure to all active notes on the channel
+ i->second.vol = 127 - atVal;
+ }
+
+ NoteUpdate_All(channel, Upd_Volume);
+}
+
+void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value)
+{
+ channel = channel % 16;
+ switch(type)
+ {
+ case 1: // Adjust vibrato
+ //UI.PrintLn("%u:vibrato %d", MidCh,value);
+ Ch[channel].vibrato = value;
+ break;
+
+ case 0: // Set bank msb (GM bank)
+ Ch[channel].bank_msb = value;
+ break;
+
+ case 32: // Set bank lsb (XG bank)
+ Ch[channel].bank_lsb = value;
+ break;
+
+ case 5: // Set portamento msb
+ Ch[channel].portamento = static_cast<uint16_t>((Ch[channel].portamento & 0x7F) | (value << 7));
+ //UpdatePortamento(MidCh);
+ break;
+
+ case 37: // Set portamento lsb
+ Ch[channel].portamento = (Ch[channel].portamento & 0x3F80) | (value);
+ //UpdatePortamento(MidCh);
+ break;
+
+ case 65: // Enable/disable portamento
+ // value >= 64 ? enabled : disabled
+ //UpdatePortamento(MidCh);
+ break;
+
+ case 7: // Change volume
+ Ch[channel].volume = value;
+ NoteUpdate_All(channel, Upd_Volume);
+ break;
+
+ case 64: // Enable/disable sustain
+ Ch[channel].sustain = value;
+
+ if(!value) KillSustainingNotes(channel);
+
+ break;
+
+ case 11: // Change expression (another volume factor)
+ Ch[channel].expression = value;
+ NoteUpdate_All(channel, Upd_Volume);
+ break;
+
+ case 10: // Change panning
+ Ch[channel].panning = 0x00;
+ if(value < 64 + 32) Ch[channel].panning |= 0x10;
+ if(value >= 64 - 32) Ch[channel].panning |= 0x20;
+
+ NoteUpdate_All(channel, Upd_Pan);
+ break;
+
+ case 121: // Reset all controllers
+ Ch[channel].bend = 0;
+ Ch[channel].volume = 100;
+ Ch[channel].expression = 100;
+ Ch[channel].sustain = 0;
+ Ch[channel].vibrato = 0;
+ Ch[channel].vibspeed = 2 * 3.141592653 * 5.0;
+ Ch[channel].vibdepth = 0.5 / 127;
+ Ch[channel].vibdelay = 0;
+ Ch[channel].panning = 0x30;
+ Ch[channel].portamento = 0;
+ //UpdatePortamento(MidCh);
+ NoteUpdate_All(channel, Upd_Pan + Upd_Volume + Upd_Pitch);
+ // Kill all sustained notes
+ KillSustainingNotes(channel);
+ break;
+
+ case 123: // All notes off
+ NoteUpdate_All(channel, Upd_Off);
+ break;
+
+ case 91:
+ break; // Reverb effect depth. We don't do per-channel reverb.
+
+ case 92:
+ break; // Tremolo effect depth. We don't do...
+
+ case 93:
+ break; // Chorus effect depth. We don't do.
+
+ case 94:
+ break; // Celeste effect depth. We don't do.
+
+ case 95:
+ break; // Phaser effect depth. We don't do.
+
+ case 98:
+ Ch[channel].lastlrpn = value;
+ Ch[channel].nrpn = true;
+ break;
+
+ case 99:
+ Ch[channel].lastmrpn = value;
+ Ch[channel].nrpn = true;
+ break;
+
+ case 100:
+ Ch[channel].lastlrpn = value;
+ Ch[channel].nrpn = false;
+ break;
+
+ case 101:
+ Ch[channel].lastmrpn = value;
+ Ch[channel].nrpn = false;
+ break;
+
+ case 113:
+ break; // Related to pitch-bender, used by missimp.mid in Duke3D
+
+ case 6:
+ SetRPN(channel, value, true);
+ break;
+
+ case 38:
+ SetRPN(channel, value, false);
+ break;
+
+ case 103:
+ cmf_percussion_mode = value;
+ break; // CMF (ctrl 0x67) rhythm mode
+
+ default:
+ break;
+ //UI.PrintLn("Ctrl %d <- %d (ch %u)", ctrlno, value, MidCh);
+ }
+}
+
+void MIDIplay::realTime_PatchChange(uint8_t channel, uint8_t patch)
+{
+ channel = channel % 16;
+ Ch[channel].patch = patch;
+}
+
+void MIDIplay::realTime_PitchBend(uint8_t channel, uint16_t pitch)
+{
+ channel = channel % 16;
+ Ch[channel].bend = (uint32_t(pitch) - 8192) * Ch[channel].bendsense;
+ NoteUpdate_All(channel, Upd_Pitch);
+}
+
+void MIDIplay::realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb)
+{
+ channel = channel % 16;
+ Ch[channel].bend = (int(lsb) + int(msb) * 128 - 8192) * Ch[channel].bendsense;
+ NoteUpdate_All(channel, Upd_Pitch);
+}
+
+void MIDIplay::realTime_BankChangeLSB(uint8_t channel, uint8_t lsb)
+{
+ channel = channel % 16;
+ Ch[channel].bank_lsb = lsb;
+}
+
+void MIDIplay::realTime_BankChangeMSB(uint8_t channel, uint8_t msb)
+{
+ channel = channel % 16;
+ Ch[channel].bank_msb = msb;
+}
+
+void MIDIplay::realTime_BankChange(uint8_t channel, uint16_t bank)
+{
+ channel = channel % 16;
+ Ch[channel].bank_lsb = uint8_t(bank & 0xFFFF);
+ Ch[channel].bank_msb = uint8_t((bank >> 16) & 0xFFFF);
+}
+
void MIDIplay::NoteUpdate(uint16_t MidCh,
MIDIplay::MIDIchannel::activenoteiterator i,
@@ -504,6 +933,7 @@ void MIDIplay::HandleEvent(size_t tk)
if(evtype == 6)
{
+ /* Move this away from events handler */
for(size_t i = 0; i < data.size(); i++)
{
if(data[i] <= 'Z' && data[i] >= 'A')
@@ -575,197 +1005,19 @@ void MIDIplay::HandleEvent(size_t tk)
switch(EvType)
{
case 0x8: // Note off
+ {
+ uint8_t note = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ //if(MidCh != 9) note -= 12; // HACK
+ realTime_NoteOff(MidCh, note);
+ break;
+ }
case 0x9: // Note on
{
uint8_t note = TrackData[tk][CurrentPosition.track[tk].ptr++];
uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
//if(MidCh != 9) note -= 12; // HACK
- NoteOff(MidCh, note);
-
- // On Note on, Keyoff the note first, just in case keyoff
- // was omitted; this fixes Dance of sugar-plum fairy
- // by Microsoft. Now that we've done a Keyoff,
- // check if we still need to do a Keyon.
- // vol=0 and event 8x are both Keyoff-only.
- if(vol == 0 || EvType == 0x8) break;
-
- uint8_t midiins = Ch[MidCh].patch;
-
- if(MidCh % 16 == 9)
- midiins = 128 + note; // Percussion instrument
-
- /*
- if(MidCh%16 == 9 || (midiins != 32 && midiins != 46 && midiins != 48 && midiins != 50))
- break; // HACK
- if(midiins == 46) vol = (vol*7)/10; // HACK
- if(midiins == 48 || midiins == 50) vol /= 4; // HACK
- */
- //if(midiins == 56) vol = vol*6/10; // HACK
- static std::set<uint32_t> bank_warnings;
-
- if(Ch[MidCh].bank_msb)
- {
- uint32_t bankid = midiins + 256 * Ch[MidCh].bank_msb;
- std::set<uint32_t>::iterator
- i = bank_warnings.lower_bound(bankid);
-
- if(i == bank_warnings.end() || *i != bankid)
- {
- ADLMIDI_ErrorString.clear();
- std::stringstream s;
- s << "[" << MidCh << "]Bank " << Ch[MidCh].bank_msb <<
- " undefined, patch=" << ((midiins & 128) ? 'P' : 'M') << (midiins & 127);
- ADLMIDI_ErrorString = s.str();
- bank_warnings.insert(i, bankid);
- }
- }
-
- if(Ch[MidCh].bank_lsb)
- {
- unsigned bankid = Ch[MidCh].bank_lsb * 65536;
- std::set<unsigned>::iterator
- i = bank_warnings.lower_bound(bankid);
-
- if(i == bank_warnings.end() || *i != bankid)
- {
- ADLMIDI_ErrorString.clear();
- std::stringstream s;
- s << "[" << MidCh << "]Bank lsb " << Ch[MidCh].bank_lsb << " undefined";
- ADLMIDI_ErrorString = s.str();
- bank_warnings.insert(i, bankid);
- }
- }
-
- //int meta = banks[opl.AdlBank][midiins];
- const uint32_t meta = opl.GetAdlMetaNumber(midiins);
- const adlinsdata &ains = opl.GetAdlMetaIns(meta);
- int16_t tone = note;
-
- if(ains.tone)
- {
- /*if(ains.tone < 20)
- tone += ains.tone;
- else*/
- if(ains.tone < 128)
- tone = ains.tone;
- else
- tone -= ains.tone - 128;
- }
-
- uint16_t i[2] = { ains.adlno1, ains.adlno2 };
- bool pseudo_4op = ains.flags & adlinsdata::Flag_Pseudo4op;
-
- if((opl.AdlPercussionMode == 1) && PercussionMap[midiins & 0xFF]) i[1] = i[0];
-
- static std::set<uint8_t> missing_warnings;
-
- if(!missing_warnings.count(static_cast<uint8_t>(midiins)) && (ains.flags & adlinsdata::Flag_NoSound))
- {
- //UI.PrintLn("[%i]Playing missing instrument %i", MidCh, midiins);
- missing_warnings.insert(static_cast<uint8_t>(midiins));
- }
-
- // Allocate AdLib channel (the physical sound channel for the note)
- int32_t adlchannel[2] = { -1, -1 };
-
- for(uint32_t ccount = 0; ccount < 2; ++ccount)
- {
- if(ccount == 1)
- {
- if(i[0] == i[1]) break; // No secondary channel
-
- if(adlchannel[0] == -1)
- break; // No secondary if primary failed
- }
-
- int32_t c = -1;
- long bs = -0x7FFFFFFFl;
-
- for(uint32_t a = 0; a < opl.NumChannels; ++a)
- {
- if(ccount == 1 && static_cast<int32_t>(a) == adlchannel[0]) continue;
-
- // ^ Don't use the same channel for primary&secondary
-
- if(i[0] == i[1] || pseudo_4op)
- {
- // Only use regular channels
- uint8_t expected_mode = 0;
-
- if(opl.AdlPercussionMode == 1)
- {
- if(cmf_percussion_mode)
- expected_mode = MidCh < 11 ? 0 : (3 + MidCh - 11); // CMF
- else
- expected_mode = PercussionMap[midiins & 0xFF];
- }
-
- if(opl.four_op_category[a] != expected_mode)
- continue;
- }
- else
- {
- if(ccount == 0)
- {
- // Only use four-op master channels
- if(opl.four_op_category[a] != 1)
- continue;
- }
- else
- {
- // The secondary must be played on a specific channel.
- if(a != static_cast<uint32_t>(adlchannel[0]) + 3)
- continue;
- }
- }
-
- long s = CalculateAdlChannelGoodness(a, i[ccount], MidCh);
-
- if(s > bs)
- {
- bs = s; // Best candidate wins
- c = static_cast<int32_t>(a);
- }
- }
-
- if(c < 0)
- {
- //UI.PrintLn("ignored unplaceable note");
- continue; // Could not play this note. Ignore it.
- }
-
- PrepareAdlChannelForNewNote(static_cast<size_t>(c), i[ccount]);
- adlchannel[ccount] = c;
- }
-
- if(adlchannel[0] < 0 && adlchannel[1] < 0)
- {
- // The note could not be played, at all.
- break;
- }
-
- //UI.PrintLn("i1=%d:%d, i2=%d:%d", i[0],adlchannel[0], i[1],adlchannel[1]);
- // Allocate active note for MIDI channel
- std::pair<MIDIchannel::activenoteiterator, bool>
- ir = Ch[MidCh].activenotes.insert(
- std::make_pair(note, MIDIchannel::NoteInfo()));
- ir.first->second.vol = vol;
- ir.first->second.tone = tone;
- ir.first->second.midiins = midiins;
- ir.first->second.insmeta = meta;
-
- for(unsigned ccount = 0; ccount < 2; ++ccount)
- {
- int32_t c = adlchannel[ccount];
-
- if(c < 0)
- continue;
-
- ir.first->second.phys[ static_cast<uint16_t>(adlchannel[ccount]) ] = i[ccount];
- }
-
- CurrentPosition.began = true;
- NoteUpdate(MidCh, ir.first, Upd_All | Upd_Patch);
+ if(realTime_NoteOn(MidCh, note, vol))
+ CurrentPosition.began = true;
break;
}
@@ -773,17 +1025,7 @@ void MIDIplay::HandleEvent(size_t tk)
{
uint8_t note = TrackData[tk][CurrentPosition.track[tk].ptr++];
uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
- MIDIchannel::activenoteiterator
- i = Ch[MidCh].activenotes.find(note);
-
- if(i == Ch[MidCh].activenotes.end())
- {
- // Ignore touch if note is not active
- break;
- }
-
- i->second.vol = vol;
- NoteUpdate(MidCh, i, Upd_Volume);
+ realTime_NoteAfterTouch(MidCh, note, vol);
break;
}
@@ -792,179 +1034,33 @@ void MIDIplay::HandleEvent(size_t tk)
uint8_t ctrlno = TrackData[tk][CurrentPosition.track[tk].ptr++];
uint8_t value = TrackData[tk][CurrentPosition.track[tk].ptr++];
- switch(ctrlno)
+ if((ctrlno == 111) && !invalidLoop)
{
- case 1: // Adjust vibrato
- //UI.PrintLn("%u:vibrato %d", MidCh,value);
- Ch[MidCh].vibrato = value;
- break;
-
- case 0: // Set bank msb (GM bank)
- Ch[MidCh].bank_msb = value;
- break;
-
- case 32: // Set bank lsb (XG bank)
- Ch[MidCh].bank_lsb = value;
- break;
-
- case 5: // Set portamento msb
- Ch[MidCh].portamento = static_cast<uint16_t>((Ch[MidCh].portamento & 0x7F) | (value << 7));
- //UpdatePortamento(MidCh);
- break;
-
- case 37: // Set portamento lsb
- Ch[MidCh].portamento = (Ch[MidCh].portamento & 0x3F80) | (value);
- //UpdatePortamento(MidCh);
- break;
-
- case 65: // Enable/disable portamento
- // value >= 64 ? enabled : disabled
- //UpdatePortamento(MidCh);
- break;
-
- case 7: // Change volume
- Ch[MidCh].volume = value;
- NoteUpdate_All(MidCh, Upd_Volume);
- break;
-
- case 64: // Enable/disable sustain
- Ch[MidCh].sustain = value;
-
- if(!value) KillSustainingNotes(MidCh);
-
- break;
-
- case 11: // Change expression (another volume factor)
- Ch[MidCh].expression = value;
- NoteUpdate_All(MidCh, Upd_Volume);
- break;
-
- case 10: // Change panning
- Ch[MidCh].panning = 0x00;
-
- if(value < 64 + 32) Ch[MidCh].panning |= 0x10;
-
- if(value >= 64 - 32) Ch[MidCh].panning |= 0x20;
-
- NoteUpdate_All(MidCh, Upd_Pan);
- break;
-
- case 121: // Reset all controllers
- Ch[MidCh].bend = 0;
- Ch[MidCh].volume = 100;
- Ch[MidCh].expression = 100;
- Ch[MidCh].sustain = 0;
- Ch[MidCh].vibrato = 0;
- Ch[MidCh].vibspeed = 2 * 3.141592653 * 5.0;
- Ch[MidCh].vibdepth = 0.5 / 127;
- Ch[MidCh].vibdelay = 0;
- Ch[MidCh].panning = 0x30;
- Ch[MidCh].portamento = 0;
- //UpdatePortamento(MidCh);
- NoteUpdate_All(MidCh, Upd_Pan + Upd_Volume + Upd_Pitch);
- // Kill all sustained notes
- KillSustainingNotes(MidCh);
- break;
-
- case 123: // All notes off
- NoteUpdate_All(MidCh, Upd_Off);
- break;
-
- case 91:
- break; // Reverb effect depth. We don't do per-channel reverb.
-
- case 92:
- break; // Tremolo effect depth. We don't do...
-
- case 93:
- break; // Chorus effect depth. We don't do.
-
- case 94:
- break; // Celeste effect depth. We don't do.
-
- case 95:
- break; // Phaser effect depth. We don't do.
-
- case 98:
- Ch[MidCh].lastlrpn = value;
- Ch[MidCh].nrpn = true;
- break;
-
- case 99:
- Ch[MidCh].lastmrpn = value;
- Ch[MidCh].nrpn = true;
+ loopStart = true;
+ loopStart_passed = true;
break;
-
- case 100:
- Ch[MidCh].lastlrpn = value;
- Ch[MidCh].nrpn = false;
- break;
-
- case 101:
- Ch[MidCh].lastmrpn = value;
- Ch[MidCh].nrpn = false;
- break;
-
- case 113:
- break; // Related to pitch-bender, used by missimp.mid in Duke3D
-
- case 6:
- SetRPN(MidCh, value, true);
- break;
-
- case 38:
- SetRPN(MidCh, value, false);
- break;
-
- case 103:
- cmf_percussion_mode = value;
- break; // CMF (ctrl 0x67) rhythm mode
-
- case 111://LoopStart unofficial controller
- if(!invalidLoop)
- {
- loopStart = true;
- loopStart_passed = true;
- }
-
- break;
-
- default:
- break;
- //UI.PrintLn("Ctrl %d <- %d (ch %u)", ctrlno, value, MidCh);
}
-
+ realTime_Controller(MidCh, ctrlno, value);
break;
}
case 0xC: // Patch change
- Ch[MidCh].patch = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ realTime_PatchChange(MidCh, TrackData[tk][CurrentPosition.track[tk].ptr++]);
break;
case 0xD: // Channel after-touch
{
// TODO: Verify, is this correct action?
uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
-
- for(MIDIchannel::activenoteiterator
- i = Ch[MidCh].activenotes.begin();
- i != Ch[MidCh].activenotes.end();
- ++i)
- {
- // Set this pressure to all active notes on the channel
- i->second.vol = vol;
- }
-
- NoteUpdate_All(MidCh, Upd_Volume);
+ realTime_ChannelAfterTouch(MidCh, vol);
break;
}
case 0xE: // Wheel/pitch bend
{
- int a = TrackData[tk][CurrentPosition.track[tk].ptr++];
- int b = TrackData[tk][CurrentPosition.track[tk].ptr++];
- Ch[MidCh].bend = (a + b * 128 - 8192) * Ch[MidCh].bendsense;
- NoteUpdate_All(MidCh, Upd_Pitch);
+ uint8_t a = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ uint8_t b = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ realTime_PitchBend(MidCh, b, a);
break;
}
}
diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp
index 46e6e2e..5b28ea6 100644
--- a/src/adlmidi_private.hpp
+++ b/src/adlmidi_private.hpp
@@ -26,27 +26,27 @@
// Setup compiler defines useful for exporting required public API symbols in gme.cpp
#ifndef ADLMIDI_EXPORT
-#if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL)
-#define ADLMIDI_EXPORT __declspec(dllexport)
-#elif defined (LIBADLMIDI_VISIBILITY)
-#define ADLMIDI_EXPORT __attribute__((visibility ("default")))
-#else
-#define ADLMIDI_EXPORT
-#endif
+ #if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL)
+ #define ADLMIDI_EXPORT __declspec(dllexport)
+ #elif defined (LIBADLMIDI_VISIBILITY)
+ #define ADLMIDI_EXPORT __attribute__((visibility ("default")))
+ #else
+ #define ADLMIDI_EXPORT
+ #endif
#endif
#ifdef _WIN32
-#undef NO_OLDNAMES
-
-#ifdef _MSC_VER
-#ifdef _WIN64
- typedef __int64 ssize_t;
-#else
- typedef __int32 ssize_t;
-#endif
+ #undef NO_OLDNAMES
+
+ #ifdef _MSC_VER
+ #ifdef _WIN64
+ typedef __int64 ssize_t;
+ #else
+ typedef __int32 ssize_t;
+ #endif
+ #endif
#endif
-#endif
#include <vector>
#include <string>
#include <sstream>
@@ -67,9 +67,9 @@
#include "fraction.h"
#ifdef ADLMIDI_USE_DOSBOX_OPL
-#include "dbopl.h"
+ #include "dbopl.h"
#else
-#include "nukedopl3.h"
+ #include "nukedopl3.h"
#endif
#include "adldata.hh"
@@ -465,6 +465,26 @@ public:
*/
double Tick(double s, double granularity);
+ /* RealTime event triggers */
+ void realTime_ResetState();
+
+ bool realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity);
+ void realTime_NoteOff(uint8_t channel, uint8_t note);
+
+ void realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal);
+ void realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal);
+
+ void realTime_Controller(uint8_t channel, uint8_t type, uint8_t value);
+
+ void realTime_PatchChange(uint8_t channel, uint8_t patch);
+
+ void realTime_PitchBend(uint8_t channel, uint16_t pitch);
+ void realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb);
+
+ void realTime_BankChangeLSB(uint8_t channel, uint8_t lsb);
+ void realTime_BankChangeMSB(uint8_t channel, uint8_t msb);
+ void realTime_BankChange(uint8_t channel, uint16_t bank);
+
private:
enum
{