aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVitaly Novichkov <admin@wohlnet.ru>2018-06-23 06:40:27 +0300
committerVitaly Novichkov <admin@wohlnet.ru>2018-06-23 06:40:27 +0300
commitf6c6b70c488ba392236e1a4f5d2a32ceda00bf24 (patch)
tree8b5fc04dbb466a2635e422c894de7257b53dffbd
parentbe42c3c566c6f8b72f3ae9b526571a2c58326379 (diff)
downloadlibADLMIDI-f6c6b70c488ba392236e1a4f5d2a32ceda00bf24.tar.gz
libADLMIDI-f6c6b70c488ba392236e1a4f5d2a32ceda00bf24.tar.bz2
libADLMIDI-f6c6b70c488ba392236e1a4f5d2a32ceda00bf24.zip
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.
-rw-r--r--src/adlmidi_midiplay.cpp71
-rw-r--r--src/adlmidi_private.hpp20
2 files changed, 65 insertions, 26 deletions
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<uint16_t>(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<int32_t>(c));
+ KillSustainingNotes(-1, static_cast<int32_t>(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)
{
diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp
index ec194c6..1d68bae 100644
--- a/src/adlmidi_private.hpp
+++ b/src/adlmidi_private.hpp
@@ -349,8 +349,9 @@ public:
uint8_t bank_lsb, bank_msb;
uint8_t patch;
uint8_t volume, expression;
- uint8_t panning, vibrato, aftertouch, sustain;
+ uint8_t panning, vibrato, aftertouch;
uint16_t portamento;
+ bool sustain;
bool portamentoEnable;
int8_t portamentoSource; // note number or -1
double portamentoRate;
@@ -564,7 +565,7 @@ public:
updateBendSensitivity();
volume = 100;
expression = 127;
- sustain = 0;
+ sustain = false;
vibrato = 0;
aftertouch = 0;
std::memset(noteAftertouch, 0, 128);
@@ -612,8 +613,14 @@ public:
{
LocationData *prev, *next;
Location loc;
- bool sustained;
- char ____padding[7];
+ enum {
+ Sustain_None = 0x00,
+ Sustain_Pedal = 0x01,
+ Sustain_Sostenuto = 0x02,
+ Sustain_ANY = Sustain_Pedal | Sustain_Sostenuto,
+ };
+ uint8_t sustained;
+ char ____padding[6];
MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[]
//! Has fixed sustain, don't iterate "on" timeout
bool fixed_sustain;
@@ -1011,7 +1018,10 @@ private:
AdlChannel::LocationData *j,
MIDIchannel::activenoteiterator i);
void Panic();
- void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1);
+ void KillSustainingNotes(int32_t MidCh = -1,
+ int32_t this_adlchn = -1,
+ uint8_t sustain_type = AdlChannel::LocationData::Sustain_ANY);
+ void MarkSostenutoNotes(int32_t MidCh = -1);
void SetRPN(unsigned MidCh, unsigned value, bool MSB);
void UpdatePortamento(unsigned MidCh);
void NoteUpdate_All(uint16_t MidCh, unsigned props_mask);