Skip to content

Commit

Permalink
adjust MIDI notes in case we have overlaps
Browse files Browse the repository at this point in the history
re #27
  • Loading branch information
x37v committed Dec 20, 2023
1 parent daab06e commit de4f557
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 5 deletions.
81 changes: 81 additions & 0 deletions Source/RNBOMetasound/Private/RNBOMIDI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ void FMIDIPacket::Advance(int32 InNumFrames)
mFrame -= InNumFrames;
}

bool FMIDIPacket::IsNoteOff(uint8_t chan, uint8_t num) const
{
return mLength == 3 && mData[0] == (chan | 0x80) && mData[1] == num;
}

bool FMIDIPacket::IsNoteOn(uint8_t chan, uint8_t num) const
{
return mLength == 3 && mData[0] == (chan | 0x90) && mData[1] == num;
}

FMIDIPacket FMIDIPacket::CloneTo(int32 frame) const
{
FMIDIPacket r = *this;
r.mFrame = frame;
return r;
}

FMIDIBuffer::FMIDIBuffer(const Metasound::FOperatorSettings& InSettings)
: NumFramesPerBlock(InSettings.GetNumFramesPerBlock())
{
Expand Down Expand Up @@ -142,6 +159,70 @@ void FMIDIBuffer::Push(FMIDIPacket packet)
}
}

void FMIDIBuffer::PushNote(int32 start, int32 dur, uint8_t chan, uint8_t note, uint8_t onvel, uint8_t offvel)
{
/* situations where we have to move an OFF
* 1. Our new note happens inside another scheduled duration
* * off after our off without an on after our off, move old off right before our new on
* 2. Our new note spans the off of another note
* * off between our on and our off, move old off to right before our new on
* 3. Our new note spans the an entire note
* * on and off come between our new on and off, move old off right before our new on
*
*/
const auto count = Packets.Num();
const auto end = start + dur;
for (auto i = 0; i < count; i++) {
auto& p = Packets[i];
const auto f = p.Frame();
if (f < start) {
continue;
}
// in range
if (p.IsNoteOn(chan, note)) {
// existing note starts before our note ends, we need to shorten our note
if (f < end) {
// insert our Off before the existing On, updated time
Packets.Insert(FMIDIPacket::NoteOff(f, note, offvel, chan), i);
// adjust count
if (f < NumFramesPerBlock) {
CountInBlock++;
}
// push our on after so we don't screw up above index
Push(FMIDIPacket::NoteOn(start, note, onvel, chan));
return;
}
else {
break;
}
}
else if (p.IsNoteOff(chan, note) && f > start) {
// should only hit this case if a note on isn't found either within the new note bounds or after it.
// our new note either spans the off of an existing on note or is contained within an existing note
// move the off just before our on
auto off = p.CloneTo(start);
Packets.RemoveAt(i);

// adjust count
if (f < NumFramesPerBlock) {
CountInBlock--; // will get incremented in the Push below
}
// adjust last frame
if (Packets.Num() > 0) {
LastFrame = Packets.Last(0).Frame();
}
else {
LastFrame = -1;
}
Push(off); // XXX assumes there isn't another matching On at the exact time
break;
}
}

Push(FMIDIPacket::NoteOn(start, note, onvel, chan));
Push(FMIDIPacket::NoteOff(end, note, offvel, chan));
}

void FMIDIBuffer::Reset()
{
Packets.Reset();
Expand Down
7 changes: 2 additions & 5 deletions Source/RNBOMetasound/Private/RNBOMakeNote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,11 @@ class FMakeNoteOperator : public TExecutableOperator<FMakeNoteOperator>
const auto vel = static_cast<uint8_t>(std::clamp(*NoteVel, 1, 127));
const auto offvel = static_cast<uint8_t>(std::clamp(*NoteOffVel, 0, 127));
const auto chan = static_cast<uint8_t>(std::clamp(*NoteChan, 0, 15));
const int32 frames = std::max(static_cast<int32>(ceil(SampleRate.GetSeconds() * NoteDur->GetSeconds())), 1);
const int32 dur = std::max(static_cast<int32>(ceil(SampleRate.GetSeconds() * NoteDur->GetSeconds())), 1);

for (auto i = 0; i < num; i++) {
auto start = (*Trigger)[i];
auto end = start + frames;

MIDIOut->Push(FMIDIPacket::NoteOn(start, note, vel, chan));
MIDIOut->Push(FMIDIPacket::NoteOff(end, note, offvel, chan));
MIDIOut->PushNote(start, dur, chan, note, vel, offvel);
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions Source/RNBOMetasound/Public/RNBOMIDI.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ class RNBOMETASOUND_API FMIDIPacket
/** Advance internal frame counters by specific frame count. */
void Advance(int32 InNumFrames);

bool IsNoteOff(uint8_t chan, uint8_t num) const;
bool IsNoteOn(uint8_t chan, uint8_t num) const;

// Make a copy with an updated frame
FMIDIPacket CloneTo(int32 frame) const;

private:
int32 mFrame;
std::array<uint8_t, 3> mData;
Expand All @@ -49,6 +55,9 @@ class RNBOMETASOUND_API FMIDIBuffer

void Push(FMIDIPacket packet);

// Pushes a new note and manages updating any overlapping matching notes
void PushNote(int32 start, int32 dur, uint8_t chan, uint8_t note, uint8_t onvel, uint8_t offvel);

void Reset();

private:
Expand Down

0 comments on commit de4f557

Please sign in to comment.