From dff692fff4a9908094312cda6e4c16d98431babf Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Tue, 19 Jun 2018 16:34:52 +0200 Subject: polyphonic portamento --- src/adlmidi_midiplay.cpp | 95 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 24 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 89c1a73..6dad2a9 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -1209,11 +1209,26 @@ 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; + bool portamentoEnable = midiChan.portamentoEnable && + !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 = midiChan.portamentoRate; + } + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) { int32_t c = adlchannel[ccount]; @@ -1222,6 +1237,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) uint16_t chipChan = static_cast(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 +1297,17 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) case 5: // Set portamento msb Ch[channel].portamento = static_cast((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 +1340,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,12 +1454,44 @@ void MIDIplay::realTime_panic() KillSustainingNotes(-1, -1); } -void MIDIplay::AudioTick(uint32_t chipId, uint32_t /*rate*/) +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; + + for(unsigned channel = 0; channel < 16; ++channel) + { + MIDIchannel &midiChan = Ch[channel]; + for(MIDIchannel::activenoteiterator it = midiChan.activenotes_begin(); + it; ++it) + { + double finalTone = it->noteTone; + double previousTone = it->currentTone; + + bool directionUp = previousTone < finalTone; + double toneIncr = portamentoDelta * (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); + } + } + } + } } void MIDIplay::NoteUpdate(uint16_t MidCh, @@ -1453,7 +1500,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(info.midiins); const adlinsdata2 &ains = *info.ains; @@ -1501,7 +1549,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 +1573,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,10 +1701,10 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, #else # define BEND_COEFFICIENT 172.4387 #endif - opl.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (static_cast(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); } } } @@ -2314,11 +2362,11 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, { hooks.onNote(hooks.onNote_userData, (int)from_channel, - i->tone, + i->noteTone, static_cast(i->midiins), 0, 0.0); hooks.onNote(hooks.onNote_userData, (int)c, - i->tone, + i->noteTone, static_cast(i->midiins), i->vol, 0.0); } @@ -2423,15 +2471,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::exp2(-0.062 * (1.0 / 128) * midival); + Ch[MidCh].portamentoRate = rate; +} void MIDIplay::NoteUpdate_All(uint16_t MidCh, unsigned props_mask) { -- cgit v1.2.3 From 2f26855c27aaffee0472edf43dbc5ac5ca1a5162 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Tue, 19 Jun 2018 21:15:45 +0200 Subject: allow portamento to be updated by MIDI::Tick --- src/adlmidi_midiplay.cpp | 66 +++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 26 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 6dad2a9..f52d936 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(s * 1000.0)); UpdateVibrato(s); UpdateArpeggio(s); +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + UpdateGlide(s); +#endif } #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER @@ -1454,6 +1462,7 @@ void MIDIplay::realTime_panic() KillSustainingNotes(-1, -1); } +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) void MIDIplay::AudioTick(uint32_t chipId, uint32_t rate) { if(chipId != 0) // do first chip ticks only @@ -1467,32 +1476,10 @@ void MIDIplay::AudioTick(uint32_t chipId, uint32_t rate) if(tickNumber % portamentoInterval == 0) { double portamentoDelta = timeDelta * portamentoInterval; - - for(unsigned channel = 0; channel < 16; ++channel) - { - MIDIchannel &midiChan = Ch[channel]; - for(MIDIchannel::activenoteiterator it = midiChan.activenotes_begin(); - it; ++it) - { - double finalTone = it->noteTone; - double previousTone = it->currentTone; - - bool directionUp = previousTone < finalTone; - double toneIncr = portamentoDelta * (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); - } - } - } + UpdateGlide(portamentoDelta); } } +#endif void MIDIplay::NoteUpdate(uint16_t MidCh, MIDIplay::MIDIchannel::activenoteiterator i, @@ -2601,6 +2588,33 @@ retry_arpeggio: } } +void MIDIplay::UpdateGlide(double amount) +{ + for(unsigned channel = 0; channel < 16; ++channel) + { + MIDIchannel &midiChan = Ch[channel]; + 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 -- cgit v1.2.3 From 94470c7e544a8086b02aa12e7361b64bacb44ee6 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Tue, 19 Jun 2018 22:42:42 +0300 Subject: Fixed DJGPP build --- src/adlmidi_midiplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index f52d936..0a4639a 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -2463,7 +2463,7 @@ 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::exp2(-0.062 * (1.0 / 128) * midival); + rate = 350.0 * std::pow(2.0, -0.062 * (1.0 / 128) * midival); Ch[MidCh].portamentoRate = rate; } -- cgit v1.2.3 From 6586740caacfb17210a26e9618c31c54ec0da703 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Tue, 19 Jun 2018 22:13:40 +0200 Subject: avoid portamento work when no notes have it on channel --- src/adlmidi_midiplay.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 0a4639a..2f186a6 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -1225,7 +1225,9 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) ir.first->chip_channels_count = 0; int8_t currentPortamentoSource = midiChan.portamentoSource; - bool portamentoEnable = midiChan.portamentoEnable && + 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; @@ -1234,7 +1236,8 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if (portamentoEnable && currentPortamentoSource >= 0) { ir.first->currentTone = currentPortamentoSource; - ir.first->glideRate = midiChan.portamentoRate; + ir.first->glideRate = currentPortamentoRate; + ++midiChan.gliding_note_count; } for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) @@ -1697,7 +1700,11 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, } 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 @@ -2593,6 +2600,9 @@ 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) { -- cgit v1.2.3 From 252e65097e9b8a815485fc03e4358698803f1a73 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Wed, 20 Jun 2018 21:45:54 +0200 Subject: basic framework of sysex handling and sequencer support --- src/adlmidi_midiplay.cpp | 178 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 2f186a6..e0cacfa 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -680,6 +680,7 @@ bool MIDIplay::buildTrackData() MIDIplay::MIDIplay(unsigned long sampleRate): cmf_percussion_mode(false), + m_sysExDeviceId(0), m_arpeggioCounter(0) #if defined(ADLMIDI_AUDIO_TICK_HANDLER) , m_audioTickCounter(0) @@ -1459,6 +1460,172 @@ void MIDIplay::realTime_BankChange(uint8_t channel, uint16_t bank) Ch[channel].bank_msb = uint8_t((bank >> 8) & 0xFF); } +void MIDIplay::setDeviceId(uint8_t id) +{ + m_sysExDeviceId = id; +} + +bool MIDIplay::realTime_SysEx(const uint8_t *msg, unsigned size) +{ + if(size < 4 || msg[0] != 0xF0 || msg[size - 1] != 0xF7) + return false; + + unsigned manufacturer = msg[1]; + unsigned dev = msg[2]; + msg += 3; + size -= 4; + + switch(manufacturer) + { + default: + break; + case Manufacturer_UniversalNonRealtime: + case Manufacturer_UniversalRealtime: + return doUniversalSysEx( + dev, manufacturer == Manufacturer_UniversalRealtime, msg, size); + case Manufacturer_Roland: + return doRolandSysEx(dev, msg, size); + case Manufacturer_Yamaha: + return doYamahaSysEx(dev, msg, size); + } + + return false; +} + +bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, unsigned size) +{ + bool devicematch = dev == 0x7F || dev == m_sysExDeviceId; + if(size < 2 || !devicematch) + return false; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 8) | + (((unsigned)data[1] & 0x7F)); + data += 2; + size -= 2; + + switch(((unsigned)realtime << 16) | address) + { + case (0 << 16) | 0x0901: // GM System On + /*TODO*/ + return true; + case (0 << 16) | 0x0902: // GM System Off + /*TODO*/ + return true; + case (1 << 16) | 0x0401: // MIDI Master Volume + if(size != 2) + break; + unsigned volume = + (((unsigned)data[0] & 0x7F)) | + (((unsigned)data[1] & 0x7F) << 7); + /*TODO*/ + (void)volume; + return true; + } + + return false; +} + +bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, unsigned size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 6 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + unsigned mode = data[1] & 0x7F; + unsigned checksum = data[size - 1] & 0x7F; + data += 2; + size -= 3; + +#if !defined(ADLMIDI_SKIP_ROLAND_CHECKSUM) + { + unsigned checkvalue = 0; + for(unsigned i = 0; i < size; ++i) + checkvalue += data[i] & 0x7F; + checkvalue = (128 - (checkvalue & 127)) & 127; + if(checkvalue != checksum) + return false; + } +#endif + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + data += 3; + size -= 3; + + if(mode != RolandMode_Send) // don't have MIDI-Out reply ability + return false; + + switch((model << 24) | address) + { + case (RolandModel_GS << 24) | 0x00007F: // System Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned mode = data[0] & 0x7F; + /*TODO*/ + (void)mode; + return true; + } + case (RolandModel_GS << 24) | 0x40007F: // Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned value = data[0] & 0x7F; + /*TODO*/ + (void)value; + return true; + } + } + + return false; +} + +bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, unsigned size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 1 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + ++data; + --size; + + switch((model << 8) | (dev & 0xF0)) + { + case (YamahaModel_XG << 8) | 0x10: // parameter change + { + if(size < 3) + break; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + data += 3; + size -= 3; + + switch(address) + { + case 0x00007E: // XG System On + if(size != 1) + break; + unsigned value = data[0] & 0x7F; + /*TODO*/ + (void)value; + return true; + } + + break; + } + } + + return false; +} + void MIDIplay::realTime_panic() { Panic(); @@ -1845,6 +2012,10 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t **pptr, uint8_t *end, int &stat evt.isValid = 0; return evt; } + evt.type = MidiEvent::T_SYSEX; + evt.data.clear(); + evt.data.push_back(byte); + std::copy(ptr, ptr + length, std::back_inserter(evt.data)); ptr += (size_t)length; return evt; } @@ -2067,6 +2238,13 @@ void MIDIplay::HandleEvent(size_t tk, const MIDIplay::MidiEvent &evt, int &statu { //std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length ); //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/); +#if 0 + fputs("SysEx:", stderr); + for(size_t i = 0; i < evt.data.size(); ++i) + fprintf(stderr, " %02X", evt.data[i]); + fputc('\n', stderr); +#endif + realTime_SysEx(evt.data.data(), (unsigned)evt.data.size()); return; } -- cgit v1.2.3 From 9f0f9e4e374798851da39335406ac4bafad81297 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Thu, 21 Jun 2018 04:23:14 +0300 Subject: Move MIDI sequencer into completely separated class TODO: - implement C bindings for most of class functions - test it in work on any different synthesizer --- src/adlmidi_midiplay.cpp | 1292 ++-------------------------------------------- 1 file changed, 32 insertions(+), 1260 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 2f186a6..5b7f0d9 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -115,586 +115,12 @@ void MIDIplay::AdlChannel::AddAge(int64_t ms) } } -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - -MIDIplay::MidiEvent::MidiEvent() : - type(T_UNKNOWN), - subtype(T_UNKNOWN), - channel(0), - isValid(1), - absPosition(0) -{} - - -MIDIplay::MidiTrackRow::MidiTrackRow() : - time(0.0), - delay(0), - absPos(0), - timeDelay(0.0) -{} - -void MIDIplay::MidiTrackRow::reset() -{ - time = 0.0; - delay = 0; - absPos = 0; - timeDelay = 0.0; - events.clear(); -} - -void MIDIplay::MidiTrackRow::sortEvents(bool *noteStates) -{ - typedef std::vector EvtArr; - EvtArr metas; - EvtArr noteOffs; - EvtArr controllers; - EvtArr anyOther; - - metas.reserve(events.size()); - noteOffs.reserve(events.size()); - controllers.reserve(events.size()); - anyOther.reserve(events.size()); - - for(size_t i = 0; i < events.size(); i++) - { - if(events[i].type == MidiEvent::T_NOTEOFF) - noteOffs.push_back(events[i]); - else if((events[i].type == MidiEvent::T_CTRLCHANGE) - || (events[i].type == MidiEvent::T_PATCHCHANGE) - || (events[i].type == MidiEvent::T_WHEEL) - || (events[i].type == MidiEvent::T_CHANAFTTOUCH)) - { - controllers.push_back(events[i]); - } - else if((events[i].type == MidiEvent::T_SPECIAL) && (events[i].subtype == MidiEvent::ST_MARKER)) - metas.push_back(events[i]); - else - anyOther.push_back(events[i]); - } - - /* - * If Note-Off and it's Note-On is on the same row - move this damned note off down! - */ - if(noteStates) - { - std::set markAsOn; - for(size_t i = 0; i < anyOther.size(); i++) - { - const MidiEvent e = anyOther[i]; - if(e.type == MidiEvent::T_NOTEON) - { - const size_t note_i = (e.channel * 255) + (e.data[0] & 0x7F); - //Check, was previously note is on or off - bool wasOn = noteStates[note_i]; - markAsOn.insert(note_i); - // Detect zero-length notes are following previously pressed note - int noteOffsOnSameNote = 0; - for(EvtArr::iterator j = noteOffs.begin(); j != noteOffs.end();) - { - //If note was off, and note-off on same row with note-on - move it down! - if( - ((*j).channel == e.channel) && - ((*j).data[0] == e.data[0]) - ) - { - //If note is already off OR more than one note-off on same row and same note - if(!wasOn || (noteOffsOnSameNote != 0)) - { - anyOther.push_back(*j); - j = noteOffs.erase(j); - markAsOn.erase(note_i); - continue; - } - else - { - //When same row has many note-offs on same row - //that means a zero-length note follows previous note - //it must be shuted down - noteOffsOnSameNote++; - } - } - j++; - } - } - } - - //Mark other notes as released - for(EvtArr::iterator j = noteOffs.begin(); j != noteOffs.end(); j++) - { - size_t note_i = (j->channel * 255) + (j->data[0] & 0x7F); - noteStates[note_i] = false; - } - - for(std::set::iterator j = markAsOn.begin(); j != markAsOn.end(); j++) - noteStates[*j] = true; - } - /***********************************************************************************/ - - events.clear(); - events.insert(events.end(), noteOffs.begin(), noteOffs.end()); - events.insert(events.end(), metas.begin(), metas.end()); - events.insert(events.end(), controllers.begin(), controllers.end()); - events.insert(events.end(), anyOther.begin(), anyOther.end()); -} -#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER - -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER -bool MIDIplay::buildTrackData() -{ - fullSongTimeLength = 0.0; - loopStartTime = -1.0; - loopEndTime = -1.0; - musTitle.clear(); - musCopyright.clear(); - musTrackTitles.clear(); - musMarkers.clear(); - caugh_missing_instruments.clear(); - caugh_missing_banks_melodic.clear(); - caugh_missing_banks_percussion.clear(); - trackDataNew.clear(); - const size_t trackCount = TrackData.size(); - trackDataNew.resize(trackCount, MidiTrackQueue()); - - invalidLoop = false; - bool gotLoopStart = false, gotLoopEnd = false, gotLoopEventInThisRow = false; - //! Tick position of loop start tag - uint64_t loopStartTicks = 0; - //! Tick position of loop end tag - uint64_t loopEndTicks = 0; - //! Full length of song in ticks - uint64_t ticksSongLength = 0; - //! Cache for error message strign - char error[150]; - - CurrentPositionNew.track.clear(); - CurrentPositionNew.track.resize(trackCount); - - //! Caches note on/off states. - bool noteStates[16 * 255]; - /* This is required to carefully detect zero-length notes * - * and avoid a move of "note-off" event over "note-on" while sort. * - * Otherwise, after sort those notes will play infinite sound */ - - //Tempo change events - std::vector tempos; - - /* - * TODO: Make this be safer for memory in case of broken input data - * which may cause going away of available track data (and then give a crash!) - * - * POST: Check this more carefully for possible vulnuabilities are can crash this - */ - for(size_t tk = 0; tk < trackCount; ++tk) - { - uint64_t abs_position = 0; - int status = 0; - MidiEvent event; - bool ok = false; - uint8_t *end = TrackData[tk].data() + TrackData[tk].size(); - uint8_t *trackPtr = TrackData[tk].data(); - std::memset(noteStates, 0, sizeof(noteStates)); - - //Time delay that follows the first event in the track - { - MidiTrackRow evtPos; - if(opl.m_musicMode == OPL3::MODE_RSXX) - ok = true; - else - evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); - if(!ok) - { - int len = snprintf(error, 150, "buildTrackData: Can't read variable-length value at begin of track %d.\n", (int)tk); - if((len > 0) && (len < 150)) - errorString += std::string(error, (size_t)len); - return false; - } - - //HACK: Begin every track with "Reset all controllers" event to avoid controllers state break came from end of song - for(uint8_t chan = 0; chan < 16; chan++) - { - MidiEvent event; - event.type = MidiEvent::T_CTRLCHANGE; - event.channel = chan; - event.data.push_back(121); - event.data.push_back(0); - evtPos.events.push_back(event); - } - - evtPos.absPos = abs_position; - abs_position += evtPos.delay; - trackDataNew[tk].push_back(evtPos); - } - - MidiTrackRow evtPos; - do - { - event = parseEvent(&trackPtr, end, status); - if(!event.isValid) - { - int len = snprintf(error, 150, "buildTrackData: Fail to parse event in the track %d.\n", (int)tk); - if((len > 0) && (len < 150)) - errorString += std::string(error, (size_t)len); - return false; - } - - evtPos.events.push_back(event); - if(event.type == MidiEvent::T_SPECIAL) - { - if(event.subtype == MidiEvent::ST_TEMPOCHANGE) - { - event.absPosition = abs_position; - tempos.push_back(event); - } - else if(!invalidLoop && (event.subtype == MidiEvent::ST_LOOPSTART)) - { - /* - * loopStart is invalid when: - * - starts together with loopEnd - * - appears more than one time in same MIDI file - */ - if(gotLoopStart || gotLoopEventInThisRow) - invalidLoop = true; - else - { - gotLoopStart = true; - loopStartTicks = abs_position; - } - //In this row we got loop event, register this! - gotLoopEventInThisRow = true; - } - else if(!invalidLoop && (event.subtype == MidiEvent::ST_LOOPEND)) - { - /* - * loopEnd is invalid when: - * - starts before loopStart - * - starts together with loopStart - * - appars more than one time in same MIDI file - */ - if(gotLoopEnd || gotLoopEventInThisRow) - invalidLoop = true; - else - { - gotLoopEnd = true; - loopEndTicks = abs_position; - } - //In this row we got loop event, register this! - gotLoopEventInThisRow = true; - } - } - - if(event.subtype != MidiEvent::ST_ENDTRACK)//Don't try to read delta after EndOfTrack event! - { - evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); - if(!ok) - { - /* End of track has been reached! However, there is no EOT event presented */ - event.type = MidiEvent::T_SPECIAL; - event.subtype = MidiEvent::ST_ENDTRACK; - } - } - - if((evtPos.delay > 0) || (event.subtype == MidiEvent::ST_ENDTRACK)) - { - evtPos.absPos = abs_position; - abs_position += evtPos.delay; - evtPos.sortEvents(noteStates); - trackDataNew[tk].push_back(evtPos); - evtPos.reset(); - gotLoopEventInThisRow = false; - } - } - while((trackPtr <= end) && (event.subtype != MidiEvent::ST_ENDTRACK)); - - if(ticksSongLength < abs_position) - ticksSongLength = abs_position; - //Set the chain of events begin - if(trackDataNew[tk].size() > 0) - CurrentPositionNew.track[tk].pos = trackDataNew[tk].begin(); - } - - if(gotLoopStart && !gotLoopEnd) - { - gotLoopEnd = true; - loopEndTicks = ticksSongLength; - } - - //loopStart must be located before loopEnd! - if(loopStartTicks >= loopEndTicks) - invalidLoop = true; - - /********************************************************************************/ - //Calculate time basing on collected tempo events - /********************************************************************************/ - for(size_t tk = 0; tk < trackCount; ++tk) - { - fraction currentTempo = Tempo; - double time = 0.0; - uint64_t abs_position = 0; - size_t tempo_change_index = 0; - MidiTrackQueue &track = trackDataNew[tk]; - if(track.empty()) - continue;//Empty track is useless! - -#ifdef DEBUG_TIME_CALCULATION - std::fprintf(stdout, "\n============Track %" PRIuPTR "=============\n", tk); - std::fflush(stdout); -#endif - - MidiTrackRow *posPrev = &(*(track.begin()));//First element - for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++) - { -#ifdef DEBUG_TIME_CALCULATION - bool tempoChanged = false; -#endif - MidiTrackRow &pos = *it; - if((posPrev != &pos) && //Skip first event - (!tempos.empty()) && //Only when in-track tempo events are available - (tempo_change_index < tempos.size()) - ) - { - // If tempo event is going between of current and previous event - if(tempos[tempo_change_index].absPosition <= pos.absPos) - { - //Stop points: begin point and tempo change points are before end point - std::vector points; - fraction t; - TempoChangePoint firstPoint = {posPrev->absPos, currentTempo}; - points.push_back(firstPoint); - - //Collect tempo change points between previous and current events - do - { - TempoChangePoint tempoMarker; - MidiEvent &tempoPoint = tempos[tempo_change_index]; - tempoMarker.absPos = tempoPoint.absPosition; - tempoMarker.tempo = InvDeltaTicks * fraction(ReadBEint(tempoPoint.data.data(), tempoPoint.data.size())); - points.push_back(tempoMarker); - tempo_change_index++; - } - while((tempo_change_index < tempos.size()) && - (tempos[tempo_change_index].absPosition <= pos.absPos)); - - // Re-calculate time delay of previous event - time -= posPrev->timeDelay; - posPrev->timeDelay = 0.0; - - for(size_t i = 0, j = 1; j < points.size(); i++, j++) - { - /* If one or more tempo events are appears between of two events, - * calculate delays between each tempo point, begin and end */ - uint64_t midDelay = 0; - //Delay between points - midDelay = points[j].absPos - points[i].absPos; - //Time delay between points - t = midDelay * currentTempo; - posPrev->timeDelay += t.value(); - - //Apply next tempo - currentTempo = points[j].tempo; -#ifdef DEBUG_TIME_CALCULATION - tempoChanged = true; -#endif - } - //Then calculate time between last tempo change point and end point - TempoChangePoint tailTempo = points.back(); - uint64_t postDelay = pos.absPos - tailTempo.absPos; - t = postDelay * currentTempo; - posPrev->timeDelay += t.value(); - - //Store Common time delay - posPrev->time = time; - time += posPrev->timeDelay; - } - } - - fraction t = pos.delay * currentTempo; - pos.timeDelay = t.value(); - pos.time = time; - time += pos.timeDelay; - - //Capture markers after time value calculation - for(size_t i = 0; i < pos.events.size(); i++) - { - MidiEvent &e = pos.events[i]; - if((e.type == MidiEvent::T_SPECIAL) && (e.subtype == MidiEvent::ST_MARKER)) - { - MIDI_MarkerEntry marker; - marker.label = std::string((char *)e.data.data(), e.data.size()); - marker.pos_ticks = pos.absPos; - marker.pos_time = pos.time; - musMarkers.push_back(marker); - } - } - - //Capture loop points time positions - if(!invalidLoop) - { - // Set loop points times - if(loopStartTicks == pos.absPos) - loopStartTime = pos.time; - else if(loopEndTicks == pos.absPos) - loopEndTime = pos.time; - } - -#ifdef DEBUG_TIME_CALCULATION - std::fprintf(stdout, "= %10" PRId64 " = %10f%s\n", pos.absPos, pos.time, tempoChanged ? " <----TEMPO CHANGED" : ""); - std::fflush(stdout); -#endif - - abs_position += pos.delay; - posPrev = &pos; - } - - if(time > fullSongTimeLength) - fullSongTimeLength = time; - } - - fullSongTimeLength += postSongWaitDelay; - //Set begin of the music - trackBeginPositionNew = CurrentPositionNew; - //Initial loop position will begin at begin of track until passing of the loop point - LoopBeginPositionNew = CurrentPositionNew; - - /********************************************************************************/ - //Resolve "hell of all times" of too short drum notes: - //move too short percussion note-offs far far away as possible - /********************************************************************************/ -#if 1 //Use this to record WAVEs for comparison before/after implementing of this - if(opl.m_musicMode == OPL3::MODE_MIDI)//Percussion fix is needed for MIDI only, not for IMF/RSXX or CMF - { - //! Minimal real time in seconds -#define DRUM_NOTE_MIN_TIME 0.03 - //! Minimal ticks count -#define DRUM_NOTE_MIN_TICKS 15 - struct NoteState - { - double delay; - uint64_t delayTicks; - bool isOn; - char ___pad[7]; - } drNotes[255]; - uint16_t banks[16]; - - for(size_t tk = 0; tk < trackCount; ++tk) - { - std::memset(drNotes, 0, sizeof(drNotes)); - std::memset(banks, 0, sizeof(banks)); - MidiTrackQueue &track = trackDataNew[tk]; - if(track.empty()) - continue;//Empty track is useless! - - for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++) - { - MidiTrackRow &pos = *it; - - for(ssize_t e = 0; e < (ssize_t)pos.events.size(); e++) - { - MidiEvent *et = &pos.events[(size_t)e]; - - /* Set MSB/LSB bank */ - if(et->type == MidiEvent::T_CTRLCHANGE) - { - uint8_t ctrlno = et->data[0]; - uint8_t value = et->data[1]; - switch(ctrlno) - { - case 0: // Set bank msb (GM bank) - banks[et->channel] = uint16_t(uint16_t(value) << 8) | uint16_t(banks[et->channel] & 0x00FF); - break; - case 32: // Set bank lsb (XG bank) - banks[et->channel] = (banks[et->channel] & 0xFF00) | (uint16_t(value) & 0x00FF); - break; - } - continue; - } - - bool percussion = (et->channel == 9) || - banks[et->channel] == 0x7E00 || //XG SFX1/SFX2 channel (16128 signed decimal) - banks[et->channel] == 0x7F00; //XG Percussion channel (16256 signed decimal) - if(!percussion) - continue; - - if(et->type == MidiEvent::T_NOTEON) - { - uint8_t note = et->data[0] & 0x7F; - NoteState &ns = drNotes[note]; - ns.isOn = true; - ns.delay = 0.0; - ns.delayTicks = 0; - } - else if(et->type == MidiEvent::T_NOTEOFF) - { - uint8_t note = et->data[0] & 0x7F; - NoteState &ns = drNotes[note]; - if(ns.isOn) - { - ns.isOn = false; - if(ns.delayTicks < DRUM_NOTE_MIN_TICKS || ns.delay < DRUM_NOTE_MIN_TIME)//If note is too short - { - //Move it into next event position if that possible - for(MidiTrackQueue::iterator itNext = it; - itNext != track.end(); - itNext++) - { - MidiTrackRow &posN = *itNext; - if(ns.delayTicks > DRUM_NOTE_MIN_TICKS && ns.delay > DRUM_NOTE_MIN_TIME) - { - //Put note-off into begin of next event list - posN.events.insert(posN.events.begin(), pos.events[(size_t)e]); - //Renive this event from a current row - pos.events.erase(pos.events.begin() + (int)e); - e--; - break; - } - ns.delay += posN.timeDelay; - ns.delayTicks += posN.delay; - } - } - ns.delay = 0.0; - ns.delayTicks = 0; - } - } - } - - //Append time delays to sustaining notes - for(size_t no = 0; no < 128; no++) - { - NoteState &ns = drNotes[no]; - if(ns.isOn) - { - ns.delay += pos.timeDelay; - ns.delayTicks += pos.delay; - } - } - } - } -#undef DRUM_NOTE_MIN_TIME -#undef DRUM_NOTE_MIN_TICKS - } -#endif - - return true; -} -#endif - - MIDIplay::MIDIplay(unsigned long sampleRate): cmf_percussion_mode(false), m_arpeggioCounter(0) #if defined(ADLMIDI_AUDIO_TICK_HANDLER) , m_audioTickCounter(0) #endif -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - , fullSongTimeLength(0.0), - postSongWaitDelay(1.0), - loopStartTime(-1.0), - loopEndTime(-1.0), - tempoMultiplier(1.0), - atEnd(false), - loopStart(false), - loopEnd(false), - invalidLoop(false) -#endif { devices.clear(); @@ -714,13 +140,15 @@ MIDIplay::MIDIplay(unsigned long sampleRate): m_setup.LogarithmicVolumes = false; m_setup.VolumeModel = ADLMIDI_VolumeModel_AUTO; //m_setup.SkipForward = 0; - m_setup.loopingIsEnabled = false; m_setup.ScaleModulators = -1; m_setup.fullRangeBrightnessCC74 = false; m_setup.delay = 0.0; m_setup.carry = 0.0; m_setup.tick_skip_samples_delay = 0; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + initSequencerInterface(); +#endif applySetup(); ChooseDevice("none"); realTime_ResetState(); @@ -766,79 +194,7 @@ void MIDIplay::applySetup() m_arpeggioCounter = 0; } -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER -uint64_t MIDIplay::ReadVarLen(uint8_t **ptr) -{ - uint64_t result = 0; - for(;;) - { - uint8_t byte = *((*ptr)++); - result = (result << 7) + (byte & 0x7F); - if(!(byte & 0x80)) - break; - } - return result; -} - -uint64_t MIDIplay::ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok) -{ - uint64_t result = 0; - ok = false; - - for(;;) - { - if(*ptr >= end) - return 2; - unsigned char byte = *((*ptr)++); - result = (result << 7) + (byte & 0x7F); - if(!(byte & 0x80)) - break; - } - - ok = true; - return result; -} - -double MIDIplay::Tick(double s, double granularity) -{ - s *= tempoMultiplier; -#ifdef ENABLE_BEGIN_SILENCE_SKIPPING - if(CurrentPositionNew.began) -#endif - CurrentPositionNew.wait -= s; - CurrentPositionNew.absTimePosition += s; - - int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing - while((CurrentPositionNew.wait <= granularity * 0.5) && (antiFreezeCounter > 0)) - { - //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); - if(!ProcessEventsNew()) - break; - if(CurrentPositionNew.wait <= 0.0) - antiFreezeCounter--; - } - - if(antiFreezeCounter <= 0) - CurrentPositionNew.wait += 1.0;/* Add extra 1 second when over 10000 events - with zero delay are been detected */ - - for(uint16_t c = 0; c < opl.NumChannels; ++c) - ch[c].AddAge(static_cast(s * 1000.0)); - - 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; - - return CurrentPositionNew.wait; -} -#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ - -void MIDIplay::TickIteratos(double s) +void MIDIplay::TickIterators(double s) { for(uint16_t c = 0; c < opl.NumChannels; ++c) ch[c].AddAge(static_cast(s * 1000.0)); @@ -849,115 +205,6 @@ void MIDIplay::TickIteratos(double s) #endif } -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - -void MIDIplay::seek(double seconds) -{ - if(seconds < 0.0) - return;//Seeking negative position is forbidden! :-P - const double granularity = m_setup.mindelay, - granualityHalf = granularity * 0.5, - s = seconds;//m_setup.delay < m_setup.maxdelay ? m_setup.delay : m_setup.maxdelay; - - /* Attempt to go away out of song end must rewind position to begin */ - if(seconds > fullSongTimeLength) - { - rewind(); - return; - } - - bool loopFlagState = m_setup.loopingIsEnabled; - // Turn loop pooints off because it causes wrong position rememberin on a quick seek - m_setup.loopingIsEnabled = false; - - /* - * Seeking search is similar to regular ticking, except of next things: - * - We don't processsing arpeggio and vibrato - * - To keep correctness of the state after seek, begin every search from begin - * - All sustaining notes must be killed - * - Ignore Note-On events - */ - rewind(); - - /* - * Set "loop Start" to false to prevent overwrite of loopStart position with - * seek destinition position - * - * TODO: Detect & set loopStart position on load time to don't break loop while seeking - */ - loopStart = false; - - while((CurrentPositionNew.absTimePosition < seconds) && - (CurrentPositionNew.absTimePosition < fullSongTimeLength)) - { - CurrentPositionNew.wait -= s; - CurrentPositionNew.absTimePosition += s; - int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing - double dstWait = CurrentPositionNew.wait + granualityHalf; - while((CurrentPositionNew.wait <= granualityHalf)/*&& (antiFreezeCounter > 0)*/) - { - //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); - if(!ProcessEventsNew(true)) - break; - //Avoid freeze because of no waiting increasing in more than 10000 cycles - if(CurrentPositionNew.wait <= dstWait) - antiFreezeCounter--; - else - { - dstWait = CurrentPositionNew.wait + granualityHalf; - antiFreezeCounter = 10000; - } - } - if(antiFreezeCounter <= 0) - CurrentPositionNew.wait += 1.0;/* Add extra 1 second when over 10000 events - with zero delay are been detected */ - } - - if(CurrentPositionNew.wait < 0.0) - CurrentPositionNew.wait = 0.0; - - m_setup.loopingIsEnabled = loopFlagState; - m_setup.delay = CurrentPositionNew.wait; - m_setup.carry = 0.0; -} - -double MIDIplay::tell() -{ - return CurrentPositionNew.absTimePosition; -} - -double MIDIplay::timeLength() -{ - return fullSongTimeLength; -} - -double MIDIplay::getLoopStart() -{ - return loopStartTime; -} - -double MIDIplay::getLoopEnd() -{ - return loopEndTime; -} - -void MIDIplay::rewind() -{ - Panic(); - KillSustainingNotes(-1, -1); - CurrentPositionNew = trackBeginPositionNew; - atEnd = false; - loopStart = true; - loopEnd = false; - //invalidLoop = false;//No more needed here as this flag is set on load time -} - -void MIDIplay::setTempo(double tempo) -{ - tempoMultiplier = tempo; -} -#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ - void MIDIplay::realTime_ResetState() { for(size_t ch = 0; ch < Ch.size(); ch++) @@ -1465,6 +712,26 @@ void MIDIplay::realTime_panic() KillSustainingNotes(-1, -1); } +void MIDIplay::realTime_deviceSwitch(size_t track, const char *data, size_t length) +{ + const std::string indata(data, length); + current_device[track] = ChooseDevice(indata); +} + +uint64_t MIDIplay::realTime_currentDevice(size_t track) +{ + return current_device[track]; +} + +void MIDIplay::realTime_rawOPL(uint8_t reg, uint8_t value) +{ + if((reg & 0xF0) == 0xC0) + value |= 0x30; + //std::printf("OPL poke %02X, %02X\n", reg, value); + //std::fflush(stdout); + opl.Poke(0, reg, value); +} + #if defined(ADLMIDI_AUDIO_TICK_HANDLER) void MIDIplay::AudioTick(uint32_t chipId, uint32_t rate) { @@ -1652,17 +919,17 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, } /* DEBUG ONLY!!! - static uint32_t max = 0; + static uint32_t max = 0; - if(volume == 0) - max = 0; + if(volume == 0) + max = 0; - if(volume > max) - max = volume; + if(volume > max) + max = volume; - printf("%d\n", max); - fflush(stdout); - */ + printf("%d\n", max); + fflush(stdout); + */ } if(props_mask & Upd_Pitch) @@ -1707,339 +974,6 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, } } -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER -bool MIDIplay::ProcessEventsNew(bool isSeek) -{ - if(CurrentPositionNew.track.size() == 0) - atEnd = true;//No MIDI track data to play - if(atEnd) - return false;//No more events in the queue - - loopEnd = false; - const size_t TrackCount = CurrentPositionNew.track.size(); - const PositionNew RowBeginPosition(CurrentPositionNew); - -#ifdef DEBUG_TIME_CALCULATION - double maxTime = 0.0; -#endif - - for(size_t tk = 0; tk < TrackCount; ++tk) - { - PositionNew::TrackInfo &track = CurrentPositionNew.track[tk]; - if((track.status >= 0) && (track.delay <= 0)) - { - //Check is an end of track has been reached - if(track.pos == trackDataNew[tk].end()) - { - track.status = -1; - break; - } - - // Handle event - for(size_t i = 0; i < track.pos->events.size(); i++) - { - const MidiEvent &evt = track.pos->events[i]; -#ifdef ENABLE_BEGIN_SILENCE_SKIPPING - if(!CurrentPositionNew.began && (evt.type == MidiEvent::T_NOTEON)) - CurrentPositionNew.began = true; -#endif - if(isSeek && (evt.type == MidiEvent::T_NOTEON)) - continue; - HandleEvent(tk, evt, track.status); - if(loopEnd) - break;//Stop event handling on catching loopEnd event! - } - -#ifdef DEBUG_TIME_CALCULATION - if(maxTime < track.pos->time) - maxTime = track.pos->time; -#endif - // Read next event time (unless the track just ended) - if(track.status >= 0) - { - track.delay += track.pos->delay; - track.pos++; - } - } - } - -#ifdef DEBUG_TIME_CALCULATION - std::fprintf(stdout, " \r"); - std::fprintf(stdout, "Time: %10f; Audio: %10f\r", maxTime, CurrentPositionNew.absTimePosition); - std::fflush(stdout); -#endif - - // Find shortest delay from all track - uint64_t shortest = 0; - bool shortest_no = true; - - for(size_t tk = 0; tk < TrackCount; ++tk) - { - PositionNew::TrackInfo &track = CurrentPositionNew.track[tk]; - if((track.status >= 0) && (shortest_no || track.delay < shortest)) - { - shortest = track.delay; - shortest_no = false; - } - } - - //if(shortest > 0) UI.PrintLn("shortest: %ld", shortest); - - // Schedule the next playevent to be processed after that delay - for(size_t tk = 0; tk < TrackCount; ++tk) - CurrentPositionNew.track[tk].delay -= shortest; - - fraction t = shortest * Tempo; - -#ifdef ENABLE_BEGIN_SILENCE_SKIPPING - if(CurrentPositionNew.began) -#endif - CurrentPositionNew.wait += t.value(); - - //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel()); - if(loopStart) - { - LoopBeginPositionNew = RowBeginPosition; - loopStart = false; - } - - if(shortest_no || loopEnd) - { - //Loop if song end or loop end point has reached - loopEnd = false; - shortest = 0; - if(!m_setup.loopingIsEnabled) - { - atEnd = true; //Don't handle events anymore - CurrentPositionNew.wait += postSongWaitDelay;//One second delay until stop playing - return true;//We have caugh end here! - } - CurrentPositionNew = LoopBeginPositionNew; - } - - return true;//Has events in queue -} - -MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t **pptr, uint8_t *end, int &status) -{ - uint8_t *&ptr = *pptr; - MIDIplay::MidiEvent evt; - - if(ptr + 1 > end) - { - //When track doesn't ends on the middle of event data, it's must be fine - evt.type = MidiEvent::T_SPECIAL; - evt.subtype = MidiEvent::ST_ENDTRACK; - return evt; - } - - unsigned char byte = *(ptr++); - bool ok = false; - - if(byte == MidiEvent::T_SYSEX || byte == MidiEvent::T_SYSEX2)// Ignore SysEx - { - uint64_t length = ReadVarLenEx(pptr, end, ok); - if(!ok || (ptr + length > end)) - { - errorString += "parseEvent: Can't read SysEx event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - ptr += (size_t)length; - return evt; - } - - if(byte == MidiEvent::T_SPECIAL) - { - // Special event FF - uint8_t evtype = *(ptr++); - uint64_t length = ReadVarLenEx(pptr, end, ok); - if(!ok || (ptr + length > end)) - { - errorString += "parseEvent: Can't read Special event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - std::string data(length ? (const char *)ptr : 0, (size_t)length); - ptr += (size_t)length; - - evt.type = byte; - evt.subtype = evtype; - evt.data.insert(evt.data.begin(), data.begin(), data.end()); - -#if 0 /* Print all tempo events */ - if(evt.subtype == MidiEvent::ST_TEMPOCHANGE) - { - if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Temp Change: %02X%02X%02X", evt.data[0], evt.data[1], evt.data[2]); - } -#endif - - /* TODO: Store those meta-strings separately and give ability to read them - * by external functions (to display song title and copyright in the player) */ - if(evt.subtype == MidiEvent::ST_COPYRIGHT) - { - if(musCopyright.empty()) - { - musCopyright = std::string((const char *)evt.data.data(), evt.data.size()); - if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Music copyright: %s", musCopyright.c_str()); - } - else if(hooks.onDebugMessage) - { - std::string str((const char *)evt.data.data(), evt.data.size()); - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Extra copyright event: %s", str.c_str()); - } - } - else if(evt.subtype == MidiEvent::ST_SQTRKTITLE) - { - if(musTitle.empty()) - { - musTitle = std::string((const char *)evt.data.data(), evt.data.size()); - if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Music title: %s", musTitle.c_str()); - } - else if(hooks.onDebugMessage) - { - //TODO: Store track titles and associate them with each track and make API to retreive them - std::string str((const char *)evt.data.data(), evt.data.size()); - musTrackTitles.push_back(str); - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Track title: %s", str.c_str()); - } - } - else if(evt.subtype == MidiEvent::ST_INSTRTITLE) - { - if(hooks.onDebugMessage) - { - std::string str((const char *)evt.data.data(), evt.data.size()); - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Instrument: %s", str.c_str()); - } - } - else if(evt.subtype == MidiEvent::ST_MARKER) - { - //To lower - for(size_t i = 0; i < data.size(); i++) - { - if(data[i] <= 'Z' && data[i] >= 'A') - data[i] = data[i] - ('Z' - 'z'); - } - - if(data == "loopstart") - { - //Return a custom Loop Start event instead of Marker - evt.subtype = MidiEvent::ST_LOOPSTART; - evt.data.clear();//Data is not needed - return evt; - } - - if(data == "loopend") - { - //Return a custom Loop End event instead of Marker - evt.subtype = MidiEvent::ST_LOOPEND; - evt.data.clear();//Data is not needed - return evt; - } - } - - if(evtype == MidiEvent::ST_ENDTRACK) - status = -1;//Finalize track - - return evt; - } - - // Any normal event (80..EF) - if(byte < 0x80) - { - byte = static_cast(status | 0x80); - ptr--; - } - - //Sys Com Song Select(Song #) [0-127] - if(byte == MidiEvent::T_SYSCOMSNGSEL) - { - if(ptr + 1 > end) - { - errorString += "parseEvent: Can't read System Command Song Select event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - evt.type = byte; - evt.data.push_back(*(ptr++)); - return evt; - } - - //Sys Com Song Position Pntr [LSB, MSB] - if(byte == MidiEvent::T_SYSCOMSPOSPTR) - { - if(ptr + 2 > end) - { - errorString += "parseEvent: Can't read System Command Position Pointer event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - evt.type = byte; - evt.data.push_back(*(ptr++)); - evt.data.push_back(*(ptr++)); - return evt; - } - - uint8_t midCh = byte & 0x0F, evType = (byte >> 4) & 0x0F; - status = byte; - evt.channel = midCh; - evt.type = evType; - - switch(evType) - { - case MidiEvent::T_NOTEOFF://2 byte length - case MidiEvent::T_NOTEON: - case MidiEvent::T_NOTETOUCH: - case MidiEvent::T_CTRLCHANGE: - case MidiEvent::T_WHEEL: - if(ptr + 2 > end) - { - errorString += "parseEvent: Can't read regular 2-byte event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - - evt.data.push_back(*(ptr++)); - evt.data.push_back(*(ptr++)); - - /* TODO: Implement conversion of RSXX's note volumes out of synthesizer */ - /*if((opl.m_musicMode == OPL3::MODE_RSXX) && (evType == MidiEvent::T_NOTEON) && (evt.data[1] != 0)) - { - //NOT WORKING YET - evt.type = MidiEvent::T_NOTETOUCH; - } - else */if((evType == MidiEvent::T_NOTEON) && (evt.data[1] == 0)) - { - evt.type = MidiEvent::T_NOTEOFF; // Note ON with zero velocity is Note OFF! - } //111'th loopStart controller (RPG Maker and others) - else if((evType == MidiEvent::T_CTRLCHANGE) && (evt.data[0] == 111)) - { - //Change event type to custom Loop Start event and clear data - evt.type = MidiEvent::T_SPECIAL; - evt.subtype = MidiEvent::ST_LOOPSTART; - evt.data.clear(); - } - - return evt; - case MidiEvent::T_PATCHCHANGE://1 byte length - case MidiEvent::T_CHANAFTTOUCH: - if(ptr + 1 > end) - { - errorString += "parseEvent: Can't read regular 1-byte event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - evt.data.push_back(*(ptr++)); - return evt; - } - - return evt; -} -#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ - const std::string &MIDIplay::getErrorString() { return errorStringOut; @@ -2050,165 +984,6 @@ void MIDIplay::setErrorString(const std::string &err) errorStringOut = err; } -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER -void MIDIplay::HandleEvent(size_t tk, const MIDIplay::MidiEvent &evt, int &status) -{ - if(hooks.onEvent) - { - hooks.onEvent(hooks.onEvent_userData, - evt.type, - evt.subtype, - evt.channel, - evt.data.data(), - evt.data.size()); - } - - if(evt.type == MidiEvent::T_SYSEX || evt.type == MidiEvent::T_SYSEX2) // Ignore SysEx - { - //std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length ); - //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/); - return; - } - - if(evt.type == MidiEvent::T_SPECIAL) - { - // Special event FF - uint8_t evtype = evt.subtype; - uint64_t length = (uint64_t)evt.data.size(); - std::string data(length ? (const char *)evt.data.data() : 0, (size_t)length); - - if(evtype == MidiEvent::ST_ENDTRACK)//End Of Track - { - status = -1; - return; - } - - if(evtype == MidiEvent::ST_TEMPOCHANGE)//Tempo change - { - Tempo = InvDeltaTicks * fraction(ReadBEint(evt.data.data(), evt.data.size())); - return; - } - - if(evtype == MidiEvent::ST_MARKER)//Meta event - { - //Do nothing! :-P - return; - } - - if(evtype == MidiEvent::ST_DEVICESWITCH) - { - current_device[tk] = ChooseDevice(data); - return; - } - - //if(evtype >= 1 && evtype <= 6) - // UI.PrintLn("Meta %d: %s", evtype, data.c_str()); - - //Turn on Loop handling when loop is enabled - if(m_setup.loopingIsEnabled && !invalidLoop) - { - if(evtype == MidiEvent::ST_LOOPSTART) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib - { - loopStart = true; - return; - } - - if(evtype == MidiEvent::ST_LOOPEND) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib - { - loopEnd = true; - return; - } - } - - if(evtype == MidiEvent::ST_RAWOPL) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib - { - uint8_t i = static_cast(data[0]), v = static_cast(data[1]); - if((i & 0xF0) == 0xC0) - v |= 0x30; - //std::printf("OPL poke %02X, %02X\n", i, v); - //std::fflush(stdout); - opl.Poke(0, i, v); - return; - } - - return; - } - - // Any normal event (80..EF) - // if(evt.type < 0x80) - // { - // byte = static_cast(CurrentPosition.track[tk].status | 0x80); - // CurrentPosition.track[tk].ptr--; - // } - - if(evt.type == MidiEvent::T_SYSCOMSNGSEL || - evt.type == MidiEvent::T_SYSCOMSPOSPTR) - return; - - /*UI.PrintLn("@%X Track %u: %02X %02X", - CurrentPosition.track[tk].ptr-1, (unsigned)tk, byte, - TrackData[tk][CurrentPosition.track[tk].ptr]);*/ - uint8_t midCh = evt.channel;//byte & 0x0F, EvType = byte >> 4; - midCh += (uint8_t)current_device[tk]; - status = evt.type; - - switch(evt.type) - { - case MidiEvent::T_NOTEOFF: // Note off - { - uint8_t note = evt.data[0]; - realTime_NoteOff(midCh, note); - break; - } - - case MidiEvent::T_NOTEON: // Note on - { - uint8_t note = evt.data[0]; - uint8_t vol = evt.data[1]; - /*if(*/ realTime_NoteOn(midCh, note, vol); /*)*/ - //CurrentPosition.began = true; - break; - } - - case MidiEvent::T_NOTETOUCH: // Note touch - { - uint8_t note = evt.data[0]; - uint8_t vol = evt.data[1]; - realTime_NoteAfterTouch(midCh, note, vol); - break; - } - - case MidiEvent::T_CTRLCHANGE: // Controller change - { - uint8_t ctrlno = evt.data[0]; - uint8_t value = evt.data[1]; - realTime_Controller(midCh, ctrlno, value); - break; - } - - case MidiEvent::T_PATCHCHANGE: // Patch change - realTime_PatchChange(midCh, evt.data[0]); - break; - - case MidiEvent::T_CHANAFTTOUCH: // Channel after-touch - { - // TODO: Verify, is this correct action? - uint8_t vol = evt.data[0]; - realTime_ChannelAfterTouch(midCh, vol); - break; - } - - case MidiEvent::T_WHEEL: // Wheel/pitch bend - { - uint8_t a = evt.data[0]; - uint8_t b = evt.data[1]; - realTime_PitchBend(midCh, b, a); - break; - } - } -} -#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ - int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t) const { int64_t s = -ch[c].koff_time_until_neglible; @@ -2508,9 +1283,6 @@ void MIDIplay::UpdateVibrato(double amount) } } - - - uint64_t MIDIplay::ChooseDevice(const std::string &name) { std::map::iterator i = devices.find(name); -- cgit v1.2.3 From 2f23710e928876c785f19689ac13f81511c0c7ae Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Thu, 21 Jun 2018 04:24:31 +0300 Subject: Remove second Bend coefficient I have tested 14'th bank on DosBox emulator in current state, and seems everything is fine, no any hi-hats distortions. --- src/adlmidi_midiplay.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 5b7f0d9..f539b16 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -953,11 +953,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, if(vibrato && (!d || d->vibdelay >= Ch[MidCh].vibdelay)) bend += static_cast(vibrato) * Ch[MidCh].vibdepth * std::sin(Ch[MidCh].vibpos); -#ifdef ADLMIDI_USE_DOSBOX_OPL -# define BEND_COEFFICIENT 172.00093 -#else -# define BEND_COEFFICIENT 172.4387 -#endif +#define BEND_COEFFICIENT 172.4387 opl.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (currentTone + bend + phase))); #undef BEND_COEFFICIENT if(hooks.onNote) -- cgit v1.2.3 From ebee7962d7a2691e38d585dac9b9c0e3d286364c Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Thu, 21 Jun 2018 09:43:34 +0300 Subject: SysEx: Use `size_t` for size values instead of `unsigned int` --- src/adlmidi_midiplay.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 8b4f27f..ba5b1ef 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -712,7 +712,7 @@ void MIDIplay::setDeviceId(uint8_t id) m_sysExDeviceId = id; } -bool MIDIplay::realTime_SysEx(const uint8_t *msg, unsigned size) +bool MIDIplay::realTime_SysEx(const uint8_t *msg, size_t size) { if(size < 4 || msg[0] != 0xF0 || msg[size - 1] != 0xF7) return false; @@ -739,7 +739,7 @@ bool MIDIplay::realTime_SysEx(const uint8_t *msg, unsigned size) return false; } -bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, unsigned size) +bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size) { bool devicematch = dev == 0x7F || dev == m_sysExDeviceId; if(size < 2 || !devicematch) @@ -773,7 +773,7 @@ bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data return false; } -bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, unsigned size) +bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) { bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; if(size < 6 || !devicematch) @@ -788,7 +788,7 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, unsigned size) #if !defined(ADLMIDI_SKIP_ROLAND_CHECKSUM) { unsigned checkvalue = 0; - for(unsigned i = 0; i < size; ++i) + for(size_t i = 0; i < size; ++i) checkvalue += data[i] & 0x7F; checkvalue = (128 - (checkvalue & 127)) & 127; if(checkvalue != checksum) @@ -831,7 +831,7 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, unsigned size) return false; } -bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, unsigned size) +bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) { bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; if(size < 1 || !devicematch) -- cgit v1.2.3 From 8075701abfe8e5b1fc6705eb2b1d35b407784c82 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Fri, 22 Jun 2018 03:35:06 +0300 Subject: Portamento must use previously played note in a channel like S-YXG50 does --- src/adlmidi_midiplay.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index ba5b1ef..089fd43 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -478,7 +478,8 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) 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; + midiChan.portamentoSource = static_cast(note); + // midiChan.portamentoSource = portamentoEnable ? (int8_t)note : (int8_t)-1; // Enable gliding on portamento note if (portamentoEnable && currentPortamentoSource >= 0) -- cgit v1.2.3 From be2e41fe5d800520ee50ca93729c8633407fb1fc Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Fri, 22 Jun 2018 17:30:34 +0200 Subject: handle sysex resets --- src/adlmidi_midiplay.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 089fd43..bd979bd 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -606,7 +606,7 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) break; case 120: // All sounds off - NoteUpdate_All(channel, Upt_OffMute); + NoteUpdate_All(channel, Upd_OffMute); break; case 123: // All notes off @@ -755,10 +755,10 @@ bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data switch(((unsigned)realtime << 16) | address) { case (0 << 16) | 0x0901: // GM System On - /*TODO*/ + realTime_ResetState(); return true; case (0 << 16) | 0x0902: // GM System Off - /*TODO*/ + realTime_ResetState(); return true; case (1 << 16) | 0x0401: // MIDI Master Volume if(size != 2) @@ -814,8 +814,8 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) if(size != 1 || (dev & 0xF0) != 0x10) break; unsigned mode = data[0] & 0x7F; - /*TODO*/ (void)mode; + realTime_ResetState(); return true; } case (RolandModel_GS << 24) | 0x40007F: // Mode Set @@ -823,8 +823,8 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) if(size != 1 || (dev & 0xF0) != 0x10) break; unsigned value = data[0] & 0x7F; - /*TODO*/ (void)value; + realTime_ResetState(); return true; } } -- cgit v1.2.3 From 16831e97a68049d27329db4630dcfbfa58b1d8fd Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Fri, 22 Jun 2018 17:58:31 +0200 Subject: handle MIDI master volume --- src/adlmidi_midiplay.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index bd979bd..3aa307d 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -93,6 +93,8 @@ static const uint8_t PercussionMap[256] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +enum { MasterVolumeDefault = 127 }; + inline bool isXgPercChannel(uint8_t msb, uint8_t lsb) { return (msb == 0x7E || msb == 0x7F) && (lsb == 0); @@ -117,6 +119,7 @@ void MIDIplay::AdlChannel::AddAge(int64_t ms) MIDIplay::MIDIplay(unsigned long sampleRate): cmf_percussion_mode(false), + m_masterVolume(MasterVolumeDefault), m_sysExDeviceId(0), m_arpeggioCounter(0) #if defined(ADLMIDI_AUDIO_TICK_HANDLER) @@ -220,6 +223,7 @@ void MIDIplay::realTime_ResetState() NoteUpdate_All(uint16_t(ch), Upd_All); NoteUpdate_All(uint16_t(ch), Upd_Off); } + m_masterVolume = MasterVolumeDefault; } bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) @@ -766,8 +770,9 @@ bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data unsigned volume = (((unsigned)data[0] & 0x7F)) | (((unsigned)data[1] & 0x7F) << 7); - /*TODO*/ - (void)volume; + m_masterVolume = volume >> 7; + for(size_t ch = 0; ch < Ch.size(); ch++) + NoteUpdate_All(uint16_t(ch), Upd_Volume); return true; } @@ -1029,7 +1034,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, case OPL3::VOLUME_Generic: { - volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; + volume = vol * m_masterVolume * Ch[MidCh].volume * Ch[MidCh].expression; /* If the channel has arpeggio, the effective volume of * *this* instrument is actually lower due to timesharing. @@ -1040,10 +1045,10 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, */ //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() )); - // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) - volume = volume > 8725 ? static_cast(std::log(static_cast(volume)) * 11.541561 + (0.5 - 104.22845)) : 0; - // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) - //opl.Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); + // The formula below: SOLVE(V=127^4 * 2^( (A-63.49999) / 8), A) + volume = volume > (8725 * 127) ? static_cast(std::log(static_cast(volume) * (1.0 / 127.0)) * 11.541561 + (0.5 - 104.22845)) : 0; + // The incorrect formula below: SOLVE(V=127^4 * (2^(A/63)-1), A) + //opl.Touch_Real(c, volume>(11210*127) ? 91.61112 * std::log((4.8819E-7/127)*volume + 1.0)+0.5 : 0); opl.Touch_Real(c, volume, brightness); //opl.Touch(c, volume); @@ -1053,14 +1058,14 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, case OPL3::VOLUME_NATIVE: { volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; - volume = volume * 127 / (127 * 127 * 127) / 2; + volume = volume * m_masterVolume / (127 * 127 * 127) / 2; opl.Touch_Real(c, volume, brightness); } break; case OPL3::VOLUME_DMX: { - volume = 2 * ((Ch[MidCh].volume * Ch[MidCh].expression) * 127 / 16129) + 1; + volume = 2 * (Ch[MidCh].volume * Ch[MidCh].expression * m_masterVolume / 16129) + 1; //volume = 2 * (Ch[MidCh].volume) + 1; volume = (DMX_volume_mapping_table[(vol < 128) ? vol : 127] * volume) >> 9; opl.Touch_Real(c, volume, brightness); @@ -1069,7 +1074,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, case OPL3::VOLUME_APOGEE: { - volume = ((Ch[MidCh].volume * Ch[MidCh].expression) * 127 / 16129); + volume = (Ch[MidCh].volume * Ch[MidCh].expression * m_masterVolume / 16129); volume = ((64 * (vol + 0x80)) * volume) >> 15; //volume = ((63 * (vol + 0x80)) * Ch[MidCh].volume) >> 15; opl.Touch_Real(c, volume, brightness); @@ -1078,8 +1083,8 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, case OPL3::VOLUME_9X: { - //volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * 127 / 16129 /*2048383*/) >> 2)]; - volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume * Ch[MidCh].expression) * 127 / 2048383) >> 2)]; + //volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * m_masterVolume / 16129 /*2048383*/) >> 2)]; + volume = 63 - W9X_volume_mapping_table[((vol * Ch[MidCh].volume * Ch[MidCh].expression * m_masterVolume / 2048383) >> 2)]; //volume = W9X_volume_mapping_table[vol >> 2] + volume; opl.Touch_Real(c, volume, brightness); } -- cgit v1.2.3 From faaab13482d1e8334712232b7f64a59ec8ae6f07 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Fri, 22 Jun 2018 18:41:31 +0200 Subject: Yamaha XG reset --- src/adlmidi_midiplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 3aa307d..a6fc2a5 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -867,8 +867,8 @@ bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) if(size != 1) break; unsigned value = data[0] & 0x7F; - /*TODO*/ (void)value; + realTime_ResetState(); return true; } -- cgit v1.2.3 From 2e88f9b9303ce1b9ef5512d6b84b3bb190dbfe75 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Fri, 22 Jun 2018 19:08:17 +0200 Subject: simplify the volume formula --- src/adlmidi_midiplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index a6fc2a5..5637da4 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -1046,7 +1046,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() )); // The formula below: SOLVE(V=127^4 * 2^( (A-63.49999) / 8), A) - volume = volume > (8725 * 127) ? static_cast(std::log(static_cast(volume) * (1.0 / 127.0)) * 11.541561 + (0.5 - 104.22845)) : 0; + volume = volume > (8725 * 127) ? static_cast(std::log(static_cast(volume)) * 11.541560327111707 - 1.601379199767093e+02) : 0; // The incorrect formula below: SOLVE(V=127^4 * (2^(A/63)-1), A) //opl.Touch_Real(c, volume>(11210*127) ? 91.61112 * std::log((4.8819E-7/127)*volume + 1.0)+0.5 : 0); -- cgit v1.2.3 From 51081828bbc756f81ac4b5c58cd3605a31047f61 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Fri, 22 Jun 2018 21:22:44 +0300 Subject: Move `opl.Touch_Real()` call out of volume model switch --- src/adlmidi_midiplay.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 5637da4..625f8f7 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -1049,9 +1049,6 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, volume = volume > (8725 * 127) ? static_cast(std::log(static_cast(volume)) * 11.541560327111707 - 1.601379199767093e+02) : 0; // The incorrect formula below: SOLVE(V=127^4 * (2^(A/63)-1), A) //opl.Touch_Real(c, volume>(11210*127) ? 91.61112 * std::log((4.8819E-7/127)*volume + 1.0)+0.5 : 0); - - opl.Touch_Real(c, volume, brightness); - //opl.Touch(c, volume); } break; @@ -1059,7 +1056,6 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, { volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; volume = volume * m_masterVolume / (127 * 127 * 127) / 2; - opl.Touch_Real(c, volume, brightness); } break; @@ -1068,7 +1064,6 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, volume = 2 * (Ch[MidCh].volume * Ch[MidCh].expression * m_masterVolume / 16129) + 1; //volume = 2 * (Ch[MidCh].volume) + 1; volume = (DMX_volume_mapping_table[(vol < 128) ? vol : 127] * volume) >> 9; - opl.Touch_Real(c, volume, brightness); } break; @@ -1077,7 +1072,6 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, volume = (Ch[MidCh].volume * Ch[MidCh].expression * m_masterVolume / 16129); volume = ((64 * (vol + 0x80)) * volume) >> 15; //volume = ((63 * (vol + 0x80)) * Ch[MidCh].volume) >> 15; - opl.Touch_Real(c, volume, brightness); } break; @@ -1086,11 +1080,12 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, //volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * m_masterVolume / 16129 /*2048383*/) >> 2)]; volume = 63 - W9X_volume_mapping_table[((vol * Ch[MidCh].volume * Ch[MidCh].expression * m_masterVolume / 2048383) >> 2)]; //volume = W9X_volume_mapping_table[vol >> 2] + volume; - opl.Touch_Real(c, volume, brightness); } break; } + opl.Touch_Real(c, volume, brightness); + /* DEBUG ONLY!!! static uint32_t max = 0; -- cgit v1.2.3 From db96b37884c4e32f2d6b7cd1745f829e1c2cb564 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Fri, 22 Jun 2018 21:23:01 +0300 Subject: Added some debug message hooks into SysEx processors --- src/adlmidi_midiplay.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 625f8f7..336c40c 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -759,9 +759,13 @@ bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data switch(((unsigned)realtime << 16) | address) { case (0 << 16) | 0x0901: // GM System On + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System On"); realTime_ResetState(); return true; case (0 << 16) | 0x0902: // GM System Off + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System Off"); realTime_ResetState(); return true; case (1 << 16) | 0x0401: // MIDI Master Volume @@ -819,7 +823,9 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) if(size != 1 || (dev & 0xF0) != 0x10) break; unsigned mode = data[0] & 0x7F; - (void)mode; + ADL_UNUSED(mode); + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caugh Roland System Mode Set: %02X", mode); realTime_ResetState(); return true; } @@ -828,7 +834,9 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) if(size != 1 || (dev & 0xF0) != 0x10) break; unsigned value = data[0] & 0x7F; - (void)value; + ADL_UNUSED(value); + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caugh Roland Mode Set: %02X", value); realTime_ResetState(); return true; } @@ -867,7 +875,9 @@ bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) if(size != 1) break; unsigned value = data[0] & 0x7F; - (void)value; + ADL_UNUSED(value); + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caugh Yamaha XG System On: %02X", value); realTime_ResetState(); return true; } -- cgit v1.2.3 From a8fa66e8444da96b39699489bf75587a0ae94721 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Fri, 22 Jun 2018 21:30:00 +0300 Subject: Use Generic volume model by default when VM value has received some junk --- src/adlmidi_midiplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 336c40c..8a4e8cd 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -1041,7 +1041,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, switch(opl.m_volumeScale) { - + default: case OPL3::VOLUME_Generic: { volume = vol * m_masterVolume * Ch[MidCh].volume * Ch[MidCh].expression; -- cgit v1.2.3 From 2f30eb0596d3fc9788ffe99b72a09ceca596b792 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Fri, 22 Jun 2018 22:33:36 +0300 Subject: Fix the typo in "Caught" word in debug messages --- src/adlmidi_midiplay.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 8a4e8cd..eca5282 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -335,7 +335,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(hooks.onDebugMessage) { if(caugh_missing_instruments.insert(static_cast(midiins)).second) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caugh a blank instrument %i (offset %i) in the MIDI bank %u", channel, Ch[channel].patch, midiins, bank); + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caught a blank instrument %i (offset %i) in the MIDI bank %u", channel, Ch[channel].patch, midiins, bank); } bank = 0; midiins = midiChan.patch; @@ -825,7 +825,7 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) unsigned mode = data[0] & 0x7F; ADL_UNUSED(mode); if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caugh Roland System Mode Set: %02X", mode); + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland System Mode Set: %02X", mode); realTime_ResetState(); return true; } @@ -836,7 +836,7 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) unsigned value = data[0] & 0x7F; ADL_UNUSED(value); if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caugh Roland Mode Set: %02X", value); + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland Mode Set: %02X", value); realTime_ResetState(); return true; } @@ -877,7 +877,7 @@ bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) unsigned value = data[0] & 0x7F; ADL_UNUSED(value); if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caugh Yamaha XG System On: %02X", value); + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Yamaha XG System On: %02X", value); realTime_ResetState(); return true; } -- cgit v1.2.3 From 159bb5b202cd088db920ccc073d952122dea85ba Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Fri, 22 Jun 2018 23:25:13 +0300 Subject: Added support for synthesizer mode - in GS mode, RPN XG-related vibrato depth events will be ignored (GS does using NRPN values are stored separately and are NOT handled) - in GS mode ignore LSB value of the bank number --- src/adlmidi_midiplay.cpp | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index eca5282..dd5c9bf 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -121,6 +121,7 @@ MIDIplay::MIDIplay(unsigned long sampleRate): cmf_percussion_mode(false), m_masterVolume(MasterVolumeDefault), m_sysExDeviceId(0), + m_synthMode(Mode_XG), m_arpeggioCounter(0) #if defined(ADLMIDI_AUDIO_TICK_HANDLER) , m_audioTickCounter(0) @@ -262,10 +263,13 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) uint16_t bank = 0; if(midiChan.bank_msb || midiChan.bank_lsb) { - bank = (uint16_t(midiChan.bank_msb) * 256) + uint16_t(midiChan.bank_lsb); + if((m_synthMode & Mode_GS) != 0) //in GS mode ignore LSB + bank = (uint16_t(midiChan.bank_msb) * 256); + else + bank = (uint16_t(midiChan.bank_msb) * 256) + uint16_t(midiChan.bank_lsb); //0x7E00 - XG SFX1/SFX2 channel (16128 signed decimal) //0x7F00 - XG Percussion channel (16256 signed decimal) - if(bank == 0x7E00 || bank == 0x7F00) + if(((m_synthMode & Mode_XG) != 0) && (bank == 0x7E00 || bank == 0x7F00)) { //Let XG SFX1/SFX2 bank will have LSB==1 (128...255 range in WOPN file) //Let XG Percussion bank will use (0...127 range in WOPN file) @@ -761,11 +765,13 @@ bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data case (0 << 16) | 0x0901: // GM System On if(hooks.onDebugMessage) hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System On"); + m_synthMode = Mode_GM; realTime_ResetState(); return true; case (0 << 16) | 0x0902: // GM System Off if(hooks.onDebugMessage) hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System Off"); + m_synthMode = Mode_XG;//TODO: TEMPORARY, make something RIGHT realTime_ResetState(); return true; case (1 << 16) | 0x0401: // MIDI Master Volume @@ -826,6 +832,7 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) ADL_UNUSED(mode); if(hooks.onDebugMessage) hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland System Mode Set: %02X", mode); + m_synthMode = Mode_GS; realTime_ResetState(); return true; } @@ -837,6 +844,7 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) ADL_UNUSED(value); if(hooks.onDebugMessage) hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland Mode Set: %02X", value); + m_synthMode = Mode_GS; realTime_ResetState(); return true; } @@ -878,6 +886,7 @@ bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) ADL_UNUSED(value); if(hooks.onDebugMessage) hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Yamaha XG System On: %02X", value); + m_synthMode = Mode_XG; realTime_ResetState(); return true; } @@ -1396,17 +1405,26 @@ void MIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) Ch[MidCh].bendsense_lsb = value; Ch[MidCh].updateBendSensitivity(); break; - case 0x0108 + 1*0x10000 + 1*0x20000: // Vibrato speed - if(value == 64) Ch[MidCh].vibspeed = 1.0; - else if(value < 100) Ch[MidCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1)); - else Ch[MidCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385); - Ch[MidCh].vibspeed *= 2 * 3.141592653 * 5.0; + case 0x0108 + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato speed + { + if(value == 64) Ch[MidCh].vibspeed = 1.0; + else if(value < 100) Ch[MidCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1)); + else Ch[MidCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385); + Ch[MidCh].vibspeed *= 2 * 3.141592653 * 5.0; + } break; - case 0x0109 + 1*0x10000 + 1*0x20000: // Vibrato depth - Ch[MidCh].vibdepth = ((value - 64) * 0.15) * 0.01; + case 0x0109 + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato depth + { + Ch[MidCh].vibdepth = ((value - 64) * 0.15) * 0.01; + } break; - case 0x010A + 1*0x10000 + 1*0x20000: // Vibrato delay in millisecons - Ch[MidCh].vibdelay = value ? int64_t(0.2092 * std::exp(0.0795 * (double)value)) : 0; + case 0x010A + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato delay in millisecons + { + Ch[MidCh].vibdelay = value ? int64_t(0.2092 * std::exp(0.0795 * (double)value)) : 0; + } break; default:/* UI.PrintLn("%s %04X <- %d (%cSB) (ch %u)", "NRPN"+!nrpn, addr, value, "LM"[MSB], MidCh);*/ -- cgit v1.2.3 From de7550a4cc643b37449eb791f09b625bf1af17fb Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Fri, 22 Jun 2018 23:27:07 +0300 Subject: Added some TODOs [ci skip] --- src/adlmidi_midiplay.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index dd5c9bf..3faa299 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -829,7 +829,7 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) if(size != 1 || (dev & 0xF0) != 0x10) break; unsigned mode = data[0] & 0x7F; - ADL_UNUSED(mode); + ADL_UNUSED(mode);//TODO: Hook this correctly! if(hooks.onDebugMessage) hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland System Mode Set: %02X", mode); m_synthMode = Mode_GS; @@ -841,7 +841,7 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) if(size != 1 || (dev & 0xF0) != 0x10) break; unsigned value = data[0] & 0x7F; - ADL_UNUSED(value); + ADL_UNUSED(value);//TODO: Hook this correctly! if(hooks.onDebugMessage) hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland Mode Set: %02X", value); m_synthMode = Mode_GS; @@ -883,7 +883,7 @@ bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) if(size != 1) break; unsigned value = data[0] & 0x7F; - ADL_UNUSED(value); + ADL_UNUSED(value);//TODO: Hook this correctly! if(hooks.onDebugMessage) hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Yamaha XG System On: %02X", value); m_synthMode = Mode_XG; -- cgit v1.2.3 From be42c3c566c6f8b72f3ae9b526571a2c58326379 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Sat, 23 Jun 2018 05:14:32 +0300 Subject: GS way of custom drum channels now is working! --- src/adlmidi_midiplay.cpp | 99 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 32 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 3faa299..8664955 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -221,6 +221,8 @@ void MIDIplay::realTime_ResetState() chan.lastlrpn = 0; chan.lastmrpn = 0; chan.nrpn = false; + if((m_synthMode & Mode_GS) != 0)// Reset custom drum channels on GS + chan.is_xg_percussion = false; NoteUpdate_All(uint16_t(ch), Upd_All); NoteUpdate_All(uint16_t(ch), Upd_Off); } @@ -257,8 +259,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) MIDIchannel &midiChan = Ch[channel]; size_t midiins = midiChan.patch; - bool isPercussion = (channel % 16 == 9); - bool isXgPercussion = false; + bool isPercussion = (channel % 16 == 9) || midiChan.is_xg_percussion; uint16_t bank = 0; if(midiChan.bank_msb || midiChan.bank_lsb) @@ -267,25 +268,30 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) bank = (uint16_t(midiChan.bank_msb) * 256); else bank = (uint16_t(midiChan.bank_msb) * 256) + uint16_t(midiChan.bank_lsb); - //0x7E00 - XG SFX1/SFX2 channel (16128 signed decimal) - //0x7F00 - XG Percussion channel (16256 signed decimal) - if(((m_synthMode & Mode_XG) != 0) && (bank == 0x7E00 || bank == 0x7F00)) - { - //Let XG SFX1/SFX2 bank will have LSB==1 (128...255 range in WOPN file) - //Let XG Percussion bank will use (0...127 range in WOPN file) - bank = (uint16_t)midiins + ((bank == 0x7E00) ? 128 : 0); // MIDI instrument defines the patch - midiins = note; // Percussion instrument - isXgPercussion = true; - isPercussion = false; - } } if(isPercussion) { - bank = (uint16_t)midiins; // MIDI instrument defines the patch + // == XG bank numbers == + // 0x7E00 - XG "SFX Kits" SFX1/SFX2 channel (16128 signed decimal) + // 0x7F00 - XG "Drum Kits" Percussion channel (16256 signed decimal) + + // MIDI instrument defines the patch: + if((m_synthMode & Mode_XG) != 0) + { + // Let XG SFX1/SFX2 bank will go in 128...255 range of LSB in WOPN file) + // Let XG Percussion bank will use (0...127 LSB range in WOPN file) + + // Choose: SFX or Drum Kits + bank = (uint16_t)midiins + ((bank == 0x7E00) ? 128 : 0); + } + else + { + bank = (uint16_t)midiins; + } midiins = note; // Percussion instrument } - if(isPercussion || isXgPercussion) + if(isPercussion) bank += OPL3::PercussionTag; const adlinsdata2 *ains = &OPL3::emptyInstrument; @@ -302,9 +308,9 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) ains = &bnk->ins[midiins]; else if(hooks.onDebugMessage) { - std::set &missing = (isPercussion || isXgPercussion) ? + std::set &missing = (isPercussion) ? caugh_missing_banks_percussion : caugh_missing_banks_melodic; - const char *text = (isPercussion || isXgPercussion) ? + const char *text = (isPercussion) ? "percussion" : "melodic"; if(missing.insert(bank).second) hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing %s MIDI bank %i (patch %i)", channel, text, bank, midiins); @@ -321,18 +327,8 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) ains = &bnk->ins[midiins]; } - /* - 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 - //int meta = banks[opl.AdlBank][midiins]; - int16_t tone = note; - - if(!isPercussion && !isXgPercussion && (bank > 0)) // For non-zero banks + if(!isPercussion && (bank > 0)) // For non-zero banks { if(ains->flags & adlinsdata::Flag_NoSound) { @@ -483,8 +479,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) int8_t currentPortamentoSource = midiChan.portamentoSource; double currentPortamentoRate = midiChan.portamentoRate; bool portamentoEnable = - midiChan.portamentoEnable && currentPortamentoRate != HUGE_VAL && - !isPercussion && !isXgPercussion; + midiChan.portamentoEnable && currentPortamentoRate != HUGE_VAL && !isPercussion; // Record the last note on MIDI channel as source of portamento midiChan.portamentoSource = static_cast(note); // midiChan.portamentoSource = portamentoEnable ? (int8_t)note : (int8_t)-1; @@ -555,12 +550,14 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) case 0: // Set bank msb (GM bank) Ch[channel].bank_msb = value; - Ch[channel].is_xg_percussion = isXgPercChannel(Ch[channel].bank_msb, Ch[channel].bank_lsb); + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + Ch[channel].is_xg_percussion = isXgPercChannel(Ch[channel].bank_msb, Ch[channel].bank_lsb); break; case 32: // Set bank lsb (XG bank) Ch[channel].bank_lsb = value; - Ch[channel].is_xg_percussion = isXgPercChannel(Ch[channel].bank_msb, Ch[channel].bank_lsb); + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + Ch[channel].is_xg_percussion = isXgPercChannel(Ch[channel].bank_msb, Ch[channel].bank_lsb); break; case 5: // Set portamento msb @@ -808,7 +805,11 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) checkvalue += data[i] & 0x7F; checkvalue = (128 - (checkvalue & 127)) & 127; if(checkvalue != checksum) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught invalid roland SysEx message!"); return false; + } } #endif @@ -816,12 +817,28 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) (((unsigned)data[0] & 0x7F) << 16) | (((unsigned)data[1] & 0x7F) << 8) | (((unsigned)data[2] & 0x7F)); + unsigned target_channel = 0; + + /* F0 41 10 42 12 40 00 7F 00 41 F7 */ + + if((address & 0xFFF0FF) == 0x401015) // Turn channel 1 into percussion + { + address = 0x401015; + target_channel = data[1] & 0x0F; + } + data += 3; size -= 3; if(mode != RolandMode_Send) // don't have MIDI-Out reply ability return false; + // Mode Set + // F0 {41 10 42 12} {40 00 7F} {00 41} F7 + + // Custom drum channels + // F0 {41 10 42 12} {40 1 15} { } F7 + switch((model << 24) | address) { case (RolandModel_GS << 24) | 0x00007F: // System Mode Set @@ -848,6 +865,24 @@ bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) realTime_ResetState(); return true; } + case (RolandModel_GS << 24) | 0x401015: // Percussion channel + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + if(Ch.size() < 16) + break; + unsigned value = data[0] & 0x7F; + const uint8_t channels_map[16] = + { + 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15 + }; + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "SysEx: Caught Roland Percussion set: %02X on channel %u (from %X)", + value, channels_map[target_channel], target_channel); + Ch[channels_map[target_channel]].is_xg_percussion = ((value == 0x01)) || ((value == 0x02)); + return true; + } } return false; -- cgit v1.2.3 From f6c6b70c488ba392236e1a4f5d2a32ceda00bf24 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Sat, 23 Jun 2018 06:40:27 +0300 Subject: Added CC66 Sostenuto support! Sostenuto is the pedal hold that does hold of only currently playing notes and doesn't holds notes are will begin after turning of sostenuto on, unlike to the Pedal (CC64 Sustain) event. --- src/adlmidi_midiplay.cpp | 71 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 21 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 8664955..d4bb4ff 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -586,8 +586,16 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) break; case 64: // Enable/disable sustain - Ch[channel].sustain = value; - if(!value) KillSustainingNotes(channel); + Ch[channel].sustain = (value >= 64); + if(!Ch[channel].sustain) + KillSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Pedal); + break; + + case 66: // Enable/disable sostenuto + if(value >= 64) //Find notes and mark them as sostenutoed + MarkSostenutoNotes(channel); + else + KillSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Sostenuto); break; case 11: // Change expression (another volume factor) @@ -607,7 +615,7 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) Ch[channel].resetAllControllers(); NoteUpdate_All(channel, Upd_Pan + Upd_Volume + Upd_Pitch); // Kill all sustained notes - KillSustainingNotes(channel); + KillSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_ANY); break; case 120: // All sounds off @@ -936,7 +944,7 @@ bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) void MIDIplay::realTime_panic() { Panic(); - KillSustainingNotes(-1, -1); + KillSustainingNotes(-1, -1, AdlChannel::LocationData::Sustain_ANY); } void MIDIplay::realTime_deviceSwitch(size_t track, const char *data, size_t length) @@ -1006,7 +1014,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, AdlChannel::LocationData *d = ch[c].users_find_or_create(my_loc); if(d) // inserts if necessary { - d->sustained = false; + d->sustained = AdlChannel::LocationData::Sustain_None; d->vibdelay = 0; d->fixed_sustain = (ains.ms_sound_kon == static_cast(adlNoteOnMaxTime)); d->kon_time_until_neglible = ains.ms_sound_kon; @@ -1025,17 +1033,17 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, if(props_mask & Upd_Off) // note off { - if(Ch[MidCh].sustain == 0) + if(!Ch[MidCh].sustain) { AdlChannel::LocationData *k = ch[c].users_find(my_loc); - - if(k) + bool do_erase_user = (k && ((k->sustained & AdlChannel::LocationData::Sustain_Sostenuto) == 0)); + if(do_erase_user) ch[c].users_erase(k); if(hooks.onNote) hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, 0, 0.0); - if(ch[c].users_empty()) + if(do_erase_user && ch[c].users_empty()) { opl.NoteOff(c); if(props_mask & Upd_Mute) // Mute the note @@ -1055,7 +1063,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, // Also will avoid overwriting it very soon. AdlChannel::LocationData *d = ch[c].users_find_or_create(my_loc); if(d) - d->sustained = true; // note: not erased! + d->sustained |= AdlChannel::LocationData::Sustain_Pedal; // note: not erased! if(hooks.onNote) hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, -1, 0.0); } @@ -1159,7 +1167,7 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, AdlChannel::LocationData *d = ch[c].users_find(my_loc); // Don't bend a sustained note - if(!d || !d->sustained) + if(!d || (d->sustained == AdlChannel::LocationData::Sustain_None)) { double midibend = Ch[MidCh].bend * Ch[MidCh].bendsense; double bend = midibend + ins.ains.finetune; @@ -1212,7 +1220,7 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteI { s -= 4000; - if(!j->sustained) + if(j->sustained == AdlChannel::LocationData::Sustain_None) s -= j->kon_time_until_neglible; else s -= (j->kon_time_until_neglible / 2); @@ -1258,7 +1266,7 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteI for(AdlChannel::LocationData *m = ch[c2].users_first; m; m = m->next) { - if(m->sustained) continue; + if(m->sustained != AdlChannel::LocationData::Sustain_None) continue; if(m->vibdelay >= 200) continue; if(m->ins != j->ins) continue; n_evacuation_stations += 1; @@ -1282,7 +1290,7 @@ void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo AdlChannel::LocationData *j = jnext; jnext = jnext->next; - if(!j->sustained) + if(j->sustained == AdlChannel::LocationData::Sustain_None) { // Collision: Kill old note, // UNLESS we're going to do arpeggio @@ -1307,7 +1315,7 @@ void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo // Kill all sustained notes on this channel // Don't keep them for arpeggio, because arpeggio requires // an intact "activenotes" record. This is a design flaw. - KillSustainingNotes(-1, static_cast(c)); + KillSustainingNotes(-1, static_cast(c), AdlChannel::LocationData::Sustain_ANY); // Keyoff the channel so that it can be retriggered, // unless the new note will be introduced as just an arpeggio. @@ -1390,7 +1398,7 @@ void MIDIplay::Panic() } } -void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) +void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn, uint8_t sustain_type) { uint32_t first = 0, last = opl.NumChannels; @@ -1400,9 +1408,10 @@ void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) last = first + 1; } - for(unsigned c = first; c < last; ++c) + for(uint32_t c = first; c < last; ++c) { - if(ch[c].users_empty()) continue; // Nothing to do + if(ch[c].users_empty()) + continue; // Nothing to do for(AdlChannel::LocationData *jnext = ch[c].users_first; jnext;) { @@ -1410,12 +1419,14 @@ void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) jnext = jnext->next; if((MidCh < 0 || j->loc.MidCh == MidCh) - && j->sustained) + && ((j->sustained & sustain_type) != 0)) { int midiins = '?'; if(hooks.onNote) hooks.onNote(hooks.onNote_userData, (int)c, j->loc.note, midiins, 0, 0.0); - ch[c].users_erase(j); + j->sustained &= ~sustain_type; + if((j->sustained == AdlChannel::LocationData::Sustain_None)) + ch[c].users_erase(j);//Remove only when note is clean from any holders } } @@ -1425,6 +1436,24 @@ void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) } } +void MIDIplay::MarkSostenutoNotes(int32_t MidCh) +{ + uint32_t first = 0, last = opl.NumChannels; + for(uint32_t c = first; c < last; ++c) + { + if(ch[c].users_empty()) + continue; // Nothing to do + + for(AdlChannel::LocationData *jnext = ch[c].users_first; jnext;) + { + AdlChannel::LocationData *j = jnext; + jnext = jnext->next; + if((j->loc.MidCh == MidCh) && (j->sustained == AdlChannel::LocationData::Sustain_None)) + j->sustained |= AdlChannel::LocationData::Sustain_Sostenuto; + } + } +} + void MIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) { bool nrpn = Ch[MidCh].nrpn; @@ -1572,7 +1601,7 @@ retry_arpeggio: n = 0; n < count; ++n) i = i->next; - if(i->sustained == false) + if(i->sustained == AdlChannel::LocationData::Sustain_None) { if(i->kon_time_until_neglible <= 0l) { -- cgit v1.2.3 From efbbd382b84b73d706a16fb54c51bf48df7b22cf Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Sat, 23 Jun 2018 09:01:35 +0300 Subject: Added support for CC67-SoftPedal --- src/adlmidi_midiplay.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index d4bb4ff..a044859 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -464,6 +464,9 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) //if(hooks.onDebugMessage) // hooks.onDebugMessage(hooks.onDebugMessage_userData, "i1=%d:%d, i2=%d:%d", i[0],adlchannel[0], i[1],adlchannel[1]); + if(midiChan.softPedal) // Apply Soft Pedal level reducing + velocity = static_cast(std::floor(static_cast(velocity) * 0.8f)); + // Allocate active note for MIDI channel std::pair ir = midiChan.activenotes_insert(note); @@ -598,6 +601,10 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) KillSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Sostenuto); break; + case 67: // Enable/disable soft-pedal + Ch[channel].softPedal = (value >= 64); + break; + case 11: // Change expression (another volume factor) Ch[channel].expression = value; NoteUpdate_All(channel, Upd_Volume); -- cgit v1.2.3 From 60fa4f03f30553fc5440c0934cc1b7406094526c Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Sat, 23 Jun 2018 12:41:12 +0200 Subject: glide: handle for all the midi channels --- src/adlmidi_midiplay.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index a044859..b2497ca 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -1632,7 +1632,9 @@ retry_arpeggio: void MIDIplay::UpdateGlide(double amount) { - for(unsigned channel = 0; channel < 16; ++channel) + unsigned num_channels = Ch.size(); + + for(unsigned channel = 0; channel < num_channels; ++channel) { MIDIchannel &midiChan = Ch[channel]; if(midiChan.gliding_note_count == 0) -- cgit v1.2.3 From e248d9ddc8ba3023e265a18d44fb5dca6193f8ec Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Sat, 23 Jun 2018 23:29:29 +0300 Subject: Little clean-up and beautify --- src/adlmidi_midiplay.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index b2497ca..1012b77 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -161,6 +161,8 @@ MIDIplay::MIDIplay(unsigned long sampleRate): void MIDIplay::applySetup() { + opl.m_musicMode = OPL3::MODE_MIDI; + m_setup.tick_skip_samples_delay = 0; opl.runAtPcmRate = m_setup.runAtPcmRate; @@ -180,10 +182,12 @@ void MIDIplay::applySetup() opl.ScaleModulators = m_setup.ScaleModulators < 0 ? opl.dynamic_bank_setup.scaleModulators : (m_setup.ScaleModulators != 0); + if(m_setup.LogarithmicVolumes) opl.ChangeVolumeRangesModel(ADLMIDI_VolumeModel_NativeOPL3); - opl.m_musicMode = OPL3::MODE_MIDI; - opl.ChangeVolumeRangesModel(static_cast(m_setup.VolumeModel)); + else + opl.ChangeVolumeRangesModel(static_cast(m_setup.VolumeModel)); + if(m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model opl.m_volumeScale = (OPL3::VolumesScale)opl.dynamic_bank_setup.volumeModel; @@ -291,6 +295,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) } midiins = note; // Percussion instrument } + if(isPercussion) bank += OPL3::PercussionTag; @@ -1124,7 +1129,8 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, case OPL3::VOLUME_NATIVE: { volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; - volume = volume * m_masterVolume / (127 * 127 * 127) / 2; + // volume = volume * m_masterVolume / (127 * 127 * 127) / 2; + volume = (volume * m_masterVolume) / 4096766; } break; @@ -1632,9 +1638,9 @@ retry_arpeggio: void MIDIplay::UpdateGlide(double amount) { - unsigned num_channels = Ch.size(); + size_t num_channels = Ch.size(); - for(unsigned channel = 0; channel < num_channels; ++channel) + for(size_t channel = 0; channel < num_channels; ++channel) { MIDIchannel &midiChan = Ch[channel]; if(midiChan.gliding_note_count == 0) @@ -1656,7 +1662,7 @@ void MIDIplay::UpdateGlide(double amount) if(currentTone != previousTone) { it->currentTone = currentTone; - NoteUpdate(channel, it, Upd_Pitch); + NoteUpdate(static_cast(channel), it, Upd_Pitch); } } } -- cgit v1.2.3 From d519f6816e6dbc27ecfa81d2493ae4f40be7f497 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Sun, 24 Jun 2018 02:10:25 +0300 Subject: Make CMF files sound more correctly (Especially, Dyna Blaster and Xargon musics are sounding too messy. This fix allows to resolve that) --- src/adlmidi_midiplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/adlmidi_midiplay.cpp') diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 1012b77..190a521 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -1225,7 +1225,7 @@ void MIDIplay::setErrorString(const std::string &err) int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t) const { - int64_t s = -ch[c].koff_time_until_neglible; + int64_t s = (opl.m_musicMode != OPL3::MODE_CMF) ? -ch[c].koff_time_until_neglible : 0; // Same midi-instrument = some stability //if(c == MidCh) s += 4; -- cgit v1.2.3