Skip to content

Commit

Permalink
Adds notemaster utility to avoid code duplication.
Browse files Browse the repository at this point in the history
  • Loading branch information
Quixotic7 committed Mar 1, 2024
1 parent 7208e67 commit 00c9ca5
Show file tree
Hide file tree
Showing 4 changed files with 443 additions and 143 deletions.
216 changes: 216 additions & 0 deletions OMX-27-firmware/src/midifx/midifx_notemaster.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
#include "midifx_notemaster.h"
#include "../hardware/omx_disp.h"

namespace midifx
{
MidiFXNoteMaster::MidiFXNoteMaster()
{
}

MidiFXNoteMaster::~MidiFXNoteMaster()
{
}

void MidiFXNoteMaster::trackNoteInputPassthrough(MidiNoteGroup *note)
{
if (note->unknownLength || note->noteOff)
{
handleNoteInputPassthrough(note);
if(note->noteOff)
{
handleNoteInput(note);
}
}
else
{
// Notes with known lengths don't need to be tracked.
sendNoteOut(note);
}
}

void MidiFXNoteMaster::trackNoteInput(MidiNoteGroup *note)
{
if (note->unknownLength || note->noteOff)
{
// only notes of unknown lengths need to be tracked
// notes with fixed lengths will turn off automatically.
if(note->noteOff)
{
// Whole purpose of this tracking madness is to pass note offs through the chain
// that were previously passed through
handleNoteInputPassthrough(note);
}
handleNoteInput(note);
}
else
{
processNoteInput(note);
}
}

// bool MidiFXNoteMaster::findEmptySlot(MidiNoteGroup *trackingArray, uint8_t size, uint8_t *emptyIndex)
// {
// for (uint8_t i = 0; i < size; i++)
// {
// // Found empty slot
// if (trackingArray[i].prevNoteNumber == kEmptyIndex)
// {
// *emptyIndex = i;
// return true;
// }
// }
// return false;
// }

void MidiFXNoteMaster::removeFromTracking(MidiNoteGroup *note)
{
for (uint8_t i = 0; i < kTrackingSize; i++)
{
if (trackingNoteGroups[i].prevNoteNumber != kEmptyIndex)
{
if (trackingNoteGroups[i].channel == note->channel && trackingNoteGroups[i].prevNoteNumber == note->prevNoteNumber)
{
trackingNoteGroups[i].prevNoteNumber = kEmptyIndex; // mark empty
}
}
}
}

void MidiFXNoteMaster::clear()
{
for (uint8_t i = 0; i < kTrackingSize; i++)
{
trackingNoteGroups[i].prevNoteNumber = kEmptyIndex;
trackingNoteGroupsPassthrough[i].prevNoteNumber = kEmptyIndex;
}
}

// If chance is less than 100% and passing through, notes need to be tracked
// and if the same note comes in without passthrough for a noteoff event, it needs to
// be passed through the effect to send noteoff to prevent stuck notes
void MidiFXNoteMaster::handleNoteInputPassthrough(MidiNoteGroup *note)
{
// Note off
if (note->noteOff)
{
// Search to see if this note is in trackingNoteGroupsPassthrough
// If a note is found, it means the note previously had a note on that passed
// through and the note off needs to be sent through or else notes could be stuck on.
// PrevNoteNumber should be the origin note number before being modified by MidiFX
for (uint8_t i = 0; i < kTrackingSize; i++)
{
if (trackingNoteGroupsPassthrough[i].prevNoteNumber != kEmptyIndex)
{
if (trackingNoteGroupsPassthrough[i].channel == note->channel && trackingNoteGroupsPassthrough[i].prevNoteNumber == note->prevNoteNumber)
{
note->noteNumber = trackingNoteGroupsPassthrough[i].noteNumber;
sendNoteOut(note);
trackingNoteGroupsPassthrough[i].prevNoteNumber = kEmptyIndex; // mark empty
}
}
}
}
// Note on
else
{
// Search for an empty slot in trackingNoteGroupsPassthrough
// If no slots are available/more than 8 notes/ note gets killed.
for (uint8_t i = 0; i < kTrackingSize; i++)
{
// Found empty slot
if (trackingNoteGroupsPassthrough[i].prevNoteNumber == kEmptyIndex)
{
trackingNoteGroupsPassthrough[i].channel = note->channel;
trackingNoteGroupsPassthrough[i].prevNoteNumber = note->prevNoteNumber;
trackingNoteGroupsPassthrough[i].noteNumber = note->noteNumber;

// Send it forward through chain
sendNoteOut(note);
return;
}
}
}
}

void MidiFXNoteMaster::handleNoteInput(MidiNoteGroup *note)
{
// Same implementation with more comments in submode_midifx
// Keeps track of previous note ons and and adjusts note number
// for note offs using the prevNoteNumber parameter.
// Why is this necessary?
// If the note is modified by midifx like randomize before the arp
// Then the arp can end up having notes stuck on
// This ensures that notes don't get stuck on.
if (note->noteOff)
{
bool noteFound = false;

for (uint8_t i = 0; i < kTrackingSize; i++)
{
if (trackingNoteGroups[i].prevNoteNumber != kEmptyIndex)
{
if (trackingNoteGroups[i].channel == note->channel && trackingNoteGroups[i].prevNoteNumber == note->prevNoteNumber)
{
// Serial.println("trackNoteInput note off found in trackingNoteGroups");
note->noteNumber = trackingNoteGroups[i].noteNumber;
processNoteInput(note);
trackingNoteGroups[i].prevNoteNumber = kEmptyIndex; // mark empty
noteFound = true;
}
}
}

if (!noteFound)
{
// Serial.println("trackNoteInput note off not found in trackingNoteGroups");

processNoteInput(note); // Not sure we need to process note if not found
}
}
else // Note on
{
for (uint8_t i = 0; i < kTrackingSize; i++)
{
// Find empty slot
if (trackingNoteGroups[i].prevNoteNumber == kEmptyIndex)
{
trackingNoteGroups[i].channel = note->channel;
trackingNoteGroups[i].prevNoteNumber = note->prevNoteNumber;
trackingNoteGroups[i].noteNumber = note->noteNumber;

processNoteInput(note);
return;
}
}
}
}

void MidiFXNoteMaster::setContext(void *context)
{
outFunctionContext_ = context;
}
void MidiFXNoteMaster::setProcessNoteFptr(void (*fptr)(void *, MidiNoteGroup *))
{
processNoteFptr = fptr;
}
void MidiFXNoteMaster::setSendNoteOutFptr(void (*fptr)(void *, MidiNoteGroup *))
{
sendNoteOutFptr = fptr;
}

void MidiFXNoteMaster::processNoteInput(MidiNoteGroup *note)
{
if (outFunctionContext_ != nullptr)
{
processNoteFptr(outFunctionContext_, note);
}
}

void MidiFXNoteMaster::sendNoteOut(MidiNoteGroup *note)
{
if (outFunctionContext_ != nullptr)
{
sendNoteOutFptr(outFunctionContext_, note);
}
}
}
62 changes: 62 additions & 0 deletions OMX-27-firmware/src/midifx/midifx_notemaster.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#pragma once
#include "../config.h"
// #include "../ClearUI/ClearUI_Input.h"
#include "../hardware/omx_keypad.h"
#include "../utils/param_manager.h"

namespace midifx
{
/// I am the note master
/// I am the master of the notes
/// all your notes are belong to me
/// Not a MidiFX, but a utility for MidiFX to use
class MidiFXNoteMaster
{
public:
MidiFXNoteMaster();
~MidiFXNoteMaster();

void setContext(void *context);
void setProcessNoteFptr(void (*fptr)(void *, MidiNoteGroup*));
void setSendNoteOutFptr(void (*fptr)(void *, MidiNoteGroup*));

// Send note here in the case that it is passing through effect
// due to chance. IE, passing to next MidiFX slot
// If effect is off, you can send note straight through
void trackNoteInputPassthrough(MidiNoteGroup *note);

// Send note here in the case that is is going through the effect
void trackNoteInput(MidiNoteGroup *note);

void removeFromTracking(MidiNoteGroup *note);

void clear();


private:
struct TrackingGroup
{
uint8_t channel : 5;
uint8_t noteNumber;
uint8_t prevNoteNumber;
};

void *outFunctionContext_;
void (*processNoteFptr)(void *, MidiNoteGroup*);
void (*sendNoteOutFptr)(void *, MidiNoteGroup*);

static const uint8_t kTrackingSize = 8;
static const uint8_t kEmptyIndex = 255;

TrackingGroup trackingNoteGroups[kTrackingSize];
TrackingGroup trackingNoteGroupsPassthrough[kTrackingSize];

void handleNoteInputPassthrough(MidiNoteGroup *note);
void handleNoteInput(MidiNoteGroup *note);

// static bool findEmptySlot(TrackingGroup *trackingArray, uint8_t size, uint8_t *emptyIndex);

void processNoteInput(MidiNoteGroup *note);
void sendNoteOut(MidiNoteGroup *note);
};
}
Loading

0 comments on commit 00c9ca5

Please sign in to comment.