ref: f0d8dedb45838219f1e56debb2dd24cf2341d804
dir: /LEAF/Src/leaf-midi.c/
/*============================================================================== leaf-midi.c Created: 30 Nov 2018 11:29:16am Author: airship ==============================================================================*/ #if _WIN32 || _WIN64 #include "..\Inc\leaf-midi.h" #else #include "../Inc/leaf-midi.h" #endif // POLY void tPoly_init(tPoly* const poly, int numVoices) { poly->numVoices = numVoices; poly->lastVoiceToChange = 0; // Arp mode stuff poly->currentVoice = 0; poly->maxLength = 128; poly->currentNote = -1; //default learned CCs and notes are just the CCs 1-128 - notes are skipped for (int i = 0; i < 128; i++) { poly->notes[i][0] = 0; poly->notes[i][1] = -1; } poly->glideTime = 5.0f; for (int i = 0; i < POLY_NUM_MAX_VOICES; ++i) { poly->voices[i][0] = -1; poly->firstReceived[i] = OFALSE; poly->ramp[i] = (tRamp*) leaf_alloc(sizeof(tRamp)); tRamp_init(poly->ramp[i], poly->glideTime, 1); } poly->pitchBend = 0.0f; poly->pitchBendRamp = (tRamp*) leaf_alloc(sizeof(tRamp)); tRamp_init(poly->pitchBendRamp, 1.0f, 1); poly->stack = (tStack*) leaf_alloc(sizeof(tStack)); tStack_init(poly->stack); poly->orderStack = (tStack*) leaf_alloc(sizeof(tStack)); tStack_init(poly->orderStack); poly->pitchGlideIsActive = OFALSE; } void tPoly_tickPitch(tPoly* poly) { tPoly_tickPitchGlide(poly); tPoly_tickPitchBend(poly); } void tPoly_tickPitchGlide(tPoly* poly) { for (int i = 0; i < POLY_NUM_MAX_VOICES; ++i) { tRamp_tick(poly->ramp[i]); } } void tPoly_tickPitchBend(tPoly* poly) { tRamp_tick(poly->pitchBendRamp); } //instead of including in dacsend, should have a separate pitch bend ramp, that is added when the ramps are ticked and sent to DAC void tPoly_setPitchBend(tPoly* const poly, float pitchBend) { poly->pitchBend = pitchBend; tRamp_setDest(poly->pitchBendRamp, poly->pitchBend); } int tPoly_noteOn(tPoly* const poly, int note, uint8_t vel) { // if not in keymap or already on stack, dont do anything. else, add that note. if (tStack_contains(poly->stack, note) >= 0) return -1; else { tPoly_orderedAddToStack(poly, note); tStack_add(poly->stack, note); int alteredVoice = -1; oBool found = OFALSE; for (int i = 0; i < poly->numVoices; i++) { if (poly->voices[i][0] < 0) // if inactive voice, give this note to voice { if (!poly->firstReceived[i] || !poly->pitchGlideIsActive) { tRamp_setVal(poly->ramp[i], note); poly->firstReceived[i] = OTRUE; } found = OTRUE; poly->voices[i][0] = note; poly->voices[i][1] = vel; poly->lastVoiceToChange = i; poly->notes[note][0] = vel; poly->notes[note][1] = i; tRamp_setDest(poly->ramp[i], poly->voices[i][0]); alteredVoice = i; break; } } if (!found) //steal { int whichVoice, whichNote; for (int j = tStack_getSize(poly->stack) - 1; j >= 0; j--) { whichNote = tStack_get(poly->stack, j); whichVoice = poly->notes[whichNote][1]; if (whichVoice >= 0) { poly->lastVoiceToChange = j; int oldNote = poly->voices[whichVoice][0]; poly->voices[whichVoice][0] = note; poly->voices[whichVoice][1] = vel; poly->notes[oldNote][1] = -1; //mark the stolen voice as inactive (in the second dimension of the notes array) poly->notes[note][0] = vel; poly->notes[note][1] = whichVoice; tRamp_setTime(poly->ramp[whichVoice], poly->glideTime); tRamp_setDest(poly->ramp[whichVoice], poly->voices[whichVoice][0]); alteredVoice = whichVoice; break; } } } return alteredVoice; } } int16_t noteToTest = -1; int tPoly_noteOff(tPoly* const poly, uint8_t note) { tStack_remove(poly->stack, note); tStack_remove(poly->orderStack, note); poly->notes[note][0] = 0; poly->notes[note][1] = -1; int deactivatedVoice = -1; for (int i = 0; i < poly->numVoices; i++) { if (poly->voices[i][0] == note) { poly->voices[i][0] = -1; poly->voices[i][1] = 0; poly->lastVoiceToChange = i; deactivatedVoice = i; break; } } /* //monophonic handling if ((poly->numVoices == 1) && (tStack_getSize(poly->stack) > 0)) { int oldNote = tStack_first(poly->stack); poly->voices[0][0] = oldNote; poly->voices[0][1] = poly->notes[oldNote][0]; poly->lastVoiceToChange = 0; } */ //grab old notes off the stack if there are notes waiting to replace the free voice if (deactivatedVoice >= 0) { for (int j = 0; j < tStack_getSize(poly->stack); ++j) { noteToTest = tStack_get(poly->stack, j); if (poly->notes[noteToTest][1] < 0) //if there is a stolen note waiting (marked inactive but on the stack) { poly->voices[deactivatedVoice][0] = noteToTest; //set the newly free voice to use the old stolen note tRamp_setDest(poly->ramp[deactivatedVoice], poly->voices[deactivatedVoice][0]); poly->voices[deactivatedVoice][1] = poly->notes[noteToTest][0]; // set the velocity of the voice to be the velocity of that note poly->notes[noteToTest][1] = deactivatedVoice; //mark that it is no longer stolen and is now active return -1; } } } return deactivatedVoice; } void tPoly_orderedAddToStack(tPoly* const poly, uint8_t noteVal) { uint8_t j; int myPitch, thisPitch, nextPitch; tStack* ns = poly->orderStack; int whereToInsert = 0; for (j = 0; j < ns->size; j++) { myPitch = noteVal; thisPitch = ns->data[j]; nextPitch = ns->data[j+1]; if (myPitch > thisPitch) { if ((myPitch < nextPitch) || (nextPitch == -1)) { whereToInsert = j+1; break; } } } //first move notes that are already in the stack one position to the right for (j = ns->size; j > whereToInsert; j--) { ns->data[j] = ns->data[(j - 1)]; } //then, insert the new note into the front of the stack ns->data[whereToInsert] = noteVal; ns->size++; } void tPoly_setNumVoices(tPoly* const poly, uint8_t numVoices) { poly->numVoices = (numVoices > POLY_NUM_MAX_VOICES) ? POLY_NUM_MAX_VOICES : numVoices; } void tPoly_setPitchGlideActive(tPoly* const poly, oBool isActive) { poly->pitchGlideIsActive = isActive; } void tPoly_setPitchGlideTime(tPoly* const poly, float t) { poly->glideTime = t; for (int i = 0; i < POLY_NUM_MAX_VOICES; ++i) { tRamp_setTime(poly->ramp[i], poly->glideTime); } } int tPoly_getNumVoices(tPoly* const poly) { return poly->numVoices; } float tPoly_getPitch(tPoly* const poly, uint8_t voice) { return tRamp_sample(poly->ramp[voice]) + tRamp_sample(poly->pitchBendRamp); } int tPoly_getKey(tPoly* const poly, uint8_t voice) { return poly->voices[voice][0]; } int tPoly_getVelocity(tPoly* const poly, uint8_t voice) { return poly->voices[voice][1]; } int tPoly_isOn(tPoly* const poly, uint8_t voice) { return (poly->voices[voice][0] > 0) ? 1 : 0; }