mirror of
https://github.com/bbswitzer/PianoProject
synced 2026-01-16 19:05:11 -05:00
285 lines
9.6 KiB
C++
285 lines
9.6 KiB
C++
#include "note.h"
|
|
#include "serial.h"
|
|
#include "main.h"
|
|
|
|
Note notes[88];
|
|
|
|
int Note::scheduledLeftNotes = 0;
|
|
int Note::scheduledRightNotes = 0;
|
|
int Note::idGenerator = 0;
|
|
int Note::noteVelocityMs[127];
|
|
|
|
Note::Note()
|
|
{
|
|
id = idGenerator;
|
|
idGenerator++;
|
|
//initialize one row of vectors so program won't crash while comparing
|
|
//initialize note as off by default
|
|
for(int index = 0; index < 6; index++)
|
|
{
|
|
schedule[index].reserve(8);
|
|
schedule[index].resize(1);
|
|
}
|
|
schedule[OFF].push_back(millis());
|
|
}
|
|
|
|
void Note::prepareToSchedule(uint8_t velocity)
|
|
{
|
|
if(Setting::handleNotes)
|
|
{
|
|
calculateVolume(velocity);
|
|
if(Setting::scheduleNotes)
|
|
{
|
|
if(velocity == 0)
|
|
scheduleNote(velocity);
|
|
else if(canBeScheduled())
|
|
scheduleNote(velocity);
|
|
} else
|
|
sendMidiToProMicro(id, velocity);
|
|
} else
|
|
sendMidiToProMicro(id, velocity); //without modifying the volume
|
|
}
|
|
|
|
void Note::checkSchedule()
|
|
{
|
|
//reverse stack behavior: erases stack from the bottom
|
|
unsigned long ms = millis();
|
|
|
|
if(schedule[OFF].size() > 1 && schedule[DEACTIVATION].size() > 1 &&
|
|
ms >= schedule[OFF].at(1) && schedule[OFF].at(1) >= schedule[DEACTIVATION].at(1)) //first because sometimes off and on times are the same
|
|
{
|
|
schedule[DEACTIVATION].erase(++schedule[DEACTIVATION].begin());
|
|
}
|
|
if(schedule[STARTUP].size() > 1 && schedule[OFF].size() > 1 && ms >= schedule[STARTUP].at(1) &&
|
|
schedule[STARTUP].at(1) >= schedule[OFF].at(1))
|
|
{
|
|
schedule[OFF].erase(++schedule[OFF].begin());
|
|
sendMidiToProMicro(id, 127);
|
|
}
|
|
if(schedule[ACTIVATION].size() > 1 && schedule[STARTUP].size() > 1 && schedule[VELOCITY].size() > 1 &&
|
|
ms >= schedule[ACTIVATION].at(1) && schedule[ACTIVATION].at(1) >= schedule[STARTUP].at(1))
|
|
{
|
|
schedule[STARTUP].erase(++schedule[STARTUP].begin());
|
|
sendMidiToProMicro(id, schedule[VELOCITY].at(1));
|
|
}
|
|
if(schedule[ON].size() > 1 && schedule[ACTIVATION].size() > 1 &&
|
|
ms >= schedule[ON].at(1) && schedule[ON].at(1) >= schedule[ACTIVATION].at(1))
|
|
{
|
|
schedule[ACTIVATION].erase(++schedule[ACTIVATION].begin());
|
|
schedule[VELOCITY].erase(++schedule[VELOCITY].begin());
|
|
sendMidiToProMicro(id, 127);
|
|
}
|
|
if(schedule[DEACTIVATION].size() > 1 && schedule[ON].size() > 1 &&
|
|
ms >= schedule[DEACTIVATION].at(1) && schedule[DEACTIVATION].at(1) >= schedule[ON].at(1))
|
|
{
|
|
schedule[ON].erase(++schedule[ON].begin());
|
|
sendMidiToProMicro(id, 0);
|
|
}
|
|
}
|
|
|
|
void Note::checkForErrors()
|
|
{
|
|
unsigned long ms = millis();
|
|
if(ms >= timeSinceActivation + Setting::noteTimeoutMs && timeSinceActivation > 0) resetSchedule();
|
|
if(schedule[ON].size() > 1 && ms >= schedule[ON].at(1) + Setting::noteTimeoutMs) resetSchedule();
|
|
}
|
|
|
|
void Note::resetSchedule()
|
|
{
|
|
if(DEBUG_MODE) Serial.print("Resetting schedule for note: ");
|
|
if(DEBUG_MODE) Serial.println(id);
|
|
for(int index = 0; index < 6; index++)
|
|
{
|
|
schedule[index].resize(1);
|
|
schedule[index].at(0) = 0;
|
|
}
|
|
schedule[OFF].push_back(millis());
|
|
if(timeSinceActivation > 0)
|
|
updateInstance(false);
|
|
timeSinceActivation = 0;
|
|
instances = 0;
|
|
sendMidiToProMicro(id, 0);
|
|
}
|
|
|
|
void Note::resetInstances()
|
|
{
|
|
//warning: only call this function in conjunction with resetSchedule()!
|
|
scheduledLeftNotes = 0;
|
|
scheduledRightNotes = 0;
|
|
}
|
|
|
|
void Note::scheduleNote(uint8_t velocity)
|
|
{
|
|
if(DEBUG_MODE) sendScheduleToSerial();
|
|
unsigned long ms = millis();
|
|
unsigned long msAndDelay = ms + fullDelay;
|
|
using namespace Setting;
|
|
|
|
if(velocity > 0) //if note on command
|
|
{
|
|
int velocityMs = noteVelocityMs[velocity - 1];
|
|
instances++;
|
|
if(instances == 1) //if note is scheduled to deactivate (was 0 before instances++)
|
|
{
|
|
if(msAndDelay - velocityMs - startupMs >= schedule[OFF].back()) //if new note can be scheduled with current scheduling
|
|
{
|
|
schedule[STARTUP]. push_back(msAndDelay - velocityMs - startupMs);
|
|
schedule[ACTIVATION].push_back(msAndDelay - velocityMs);
|
|
schedule[ON]. push_back(msAndDelay);
|
|
schedule[VELOCITY]. push_back(velocity);
|
|
timeSinceActivation == ms;
|
|
updateInstance(true);
|
|
} else if(msAndDelay - deactivateMs - velocityMs - startupMs >= schedule[ON].back()) //if current scheduling can be modified to still schedule the new note
|
|
{
|
|
schedule[DEACTIVATION].push_back(msAndDelay - velocityMs - startupMs - deactivateMs);
|
|
schedule[DEACTIVATION].erase(----schedule[DEACTIVATION].end());
|
|
schedule[OFF]. push_back(msAndDelay - velocityMs - startupMs);
|
|
schedule[OFF]. erase(----schedule[OFF].end());
|
|
schedule[STARTUP]. push_back(msAndDelay - velocityMs - startupMs);
|
|
schedule[ACTIVATION]. push_back(msAndDelay - velocityMs);
|
|
schedule[ON]. push_back(msAndDelay);
|
|
schedule[VELOCITY]. push_back(velocity);
|
|
timeSinceActivation == ms;
|
|
updateInstance(true);
|
|
} else if(msAndDelay - fastDeactivateMs - velocityMs - startupMs >= schedule[ACTIVATION].back()) //if current scheduling can be modified with fast deactivation to schedule the new note
|
|
{
|
|
schedule[ON]. push_back(msAndDelay - velocityMs - startupMs - fastDeactivateMs);
|
|
schedule[ON]. erase(----schedule[ON].end());
|
|
schedule[DEACTIVATION].push_back(msAndDelay - velocityMs - startupMs - fastDeactivateMs);
|
|
schedule[DEACTIVATION].erase(----schedule[DEACTIVATION].end());
|
|
schedule[OFF]. push_back(msAndDelay - velocityMs - startupMs);
|
|
schedule[OFF]. erase(----schedule[OFF].end());
|
|
schedule[STARTUP]. push_back(msAndDelay - velocityMs - startupMs);
|
|
schedule[ACTIVATION]. push_back(msAndDelay - velocityMs);
|
|
schedule[ON]. push_back(msAndDelay);
|
|
schedule[VELOCITY]. push_back(velocity);
|
|
timeSinceActivation == ms;
|
|
updateInstance(true);
|
|
}
|
|
} else //note is scheduled to activate and not deactivate
|
|
{
|
|
if(msAndDelay - deactivateMs - velocityMs - startupMs >= schedule[ON].back()) //if current scheduling can be modified to still schedule the new note
|
|
{
|
|
schedule[DEACTIVATION].push_back(msAndDelay - velocityMs - startupMs - deactivateMs);
|
|
schedule[OFF]. push_back(msAndDelay - velocityMs - startupMs);
|
|
schedule[STARTUP]. push_back(msAndDelay - velocityMs - startupMs);
|
|
schedule[ACTIVATION]. push_back(msAndDelay - velocityMs);
|
|
schedule[ON]. push_back(msAndDelay);
|
|
schedule[VELOCITY]. push_back(velocity);
|
|
} else if(msAndDelay - fastDeactivateMs - velocityMs - startupMs - 35 >= schedule[ACTIVATION].back() && schedule[ACTIVATION].back() > 0) //if current scheduling can be modified with fast deactivation to still schedule the new note
|
|
{
|
|
schedule[ON]. push_back(msAndDelay - velocityMs - startupMs - fastDeactivateMs);
|
|
schedule[ON]. erase(----schedule[ON].end());
|
|
schedule[DEACTIVATION].push_back(msAndDelay - velocityMs - startupMs - fastDeactivateMs);
|
|
schedule[OFF]. push_back(msAndDelay - velocityMs - startupMs);
|
|
schedule[STARTUP]. push_back(msAndDelay - velocityMs - startupMs);
|
|
schedule[ACTIVATION]. push_back(msAndDelay - velocityMs);
|
|
schedule[ON]. push_back(msAndDelay);
|
|
schedule[VELOCITY]. push_back(velocity);
|
|
}
|
|
}
|
|
} else if(instances > 0 /*&& velocity == 0*/) //if note off command and note is not already off
|
|
{
|
|
if(instances > 1) //if this isn't the last instance
|
|
{
|
|
//remove instance and exit
|
|
instances--;
|
|
} else //this is the last instance of the note and it should be scheduled
|
|
{
|
|
instances = 0;
|
|
timeSinceActivation == 0;
|
|
updateInstance(false);
|
|
|
|
if(msAndDelay - fastDeactivateMs - 35 >= schedule[ACTIVATION].back() && msAndDelay - fastDeactivateMs - 35 <= schedule[ON].back() && schedule[ACTIVATION].back() > 0) //if it's efficient to use fast deactivation
|
|
{
|
|
schedule[ON]. push_back(msAndDelay - fastDeactivateMs);
|
|
schedule[ON]. erase(----schedule[ON].end());
|
|
schedule[DEACTIVATION].push_back(msAndDelay - fastDeactivateMs);
|
|
schedule[OFF]. push_back(msAndDelay);
|
|
} else if(msAndDelay - deactivateMs >= schedule[ON].back()) //if regular deactivation works
|
|
{
|
|
schedule[DEACTIVATION].push_back(msAndDelay - deactivateMs);
|
|
schedule[OFF]. push_back(msAndDelay);
|
|
} else //if all else fails the key shouldn't stay stuck on
|
|
{
|
|
if(schedule[ACTIVATION].back() > 0)
|
|
{
|
|
//immediately deactivate the key as soon as it makes sound
|
|
schedule[ON]. push_back(schedule[ACTIVATION].back() + 35);
|
|
schedule[ON]. erase(----schedule[ON].end());
|
|
schedule[DEACTIVATION].push_back(schedule[ACTIVATION].back());
|
|
schedule[OFF]. push_back(schedule[ACTIVATION].back() + fastDeactivateMs);
|
|
} else //this should never happen
|
|
{
|
|
schedule[DEACTIVATION].push_back(msAndDelay);
|
|
schedule[OFF]. push_back(msAndDelay + deactivateMs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(DEBUG_MODE) sendScheduleToSerial();
|
|
}
|
|
|
|
void Note::calculateVolume(uint8_t& velocity)
|
|
{
|
|
if(velocity > 0)
|
|
{
|
|
velocity = round((velocity * Setting::volume) / (double)100);
|
|
if(velocity > 127)
|
|
velocity = 127;
|
|
else if(velocity < 1)
|
|
velocity = 0;
|
|
}
|
|
}
|
|
|
|
void Note::updateInstance(bool state)
|
|
{
|
|
if(id < 44)
|
|
{
|
|
if(state)
|
|
scheduledLeftNotes++;
|
|
else
|
|
scheduledLeftNotes--;
|
|
} else
|
|
{
|
|
if(state)
|
|
scheduledRightNotes++;
|
|
else
|
|
scheduledRightNotes--;
|
|
}
|
|
}
|
|
|
|
void Note::sendScheduleToSerial()
|
|
{
|
|
Serial.println("-----------------------");
|
|
Serial.print("Schedule for note: ");
|
|
Serial.print(id);
|
|
Serial.print(" At the time: ");
|
|
Serial.println(millis());
|
|
for(int i = 0; i < 6; i++)
|
|
{
|
|
for(int j = 0; j < schedule[i].size(); j++)
|
|
{
|
|
Serial.print(schedule[i].at(j), DEC);
|
|
Serial.print(" ");
|
|
}
|
|
Serial.println(" ");
|
|
Serial.print(" ");
|
|
}
|
|
}
|
|
|
|
bool Note::canBeScheduled()
|
|
{
|
|
if(id < 44)
|
|
{
|
|
if(scheduledLeftNotes < Setting::maxLeftNotes)
|
|
return true;
|
|
} else
|
|
if(id >= 44)
|
|
{
|
|
if(scheduledRightNotes < Setting::maxRightNotes)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|