void TVA::recalcSustain() { // We get pinged periodically by the pitch code to recalculate our values when in sustain. // This is done so that the TVA will respond to things like MIDI expression and volume changes while it's sustaining, which it otherwise wouldn't do. // The check for envLevel[3] == 0 strikes me as slightly dumb. FIXME: Explain why if (phase != TVA_PHASE_SUSTAIN || partialParam->tva.envLevel[3] == 0) { return; } // We're sustaining. Recalculate all the values const Tables *tables = &Tables::getInstance(); int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); newTarget += partialParam->tva.envLevel[3]; // Since we're in TVA_PHASE_SUSTAIN at this point, we know that target has been reached and an interrupt fired, so we can rely on it being the current amp. int targetDelta = newTarget - target; // Calculate an increment to get to the new amp value in a short, more or less consistent amount of time Bit8u newIncrement; if (targetDelta >= 0) { newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - 2; } else { newIncrement = (tables->envLogarithmicTime[(Bit8u)-targetDelta] - 2) | 0x80; } // Configure so that once the transition's complete and nextPhase() is called, we'll just re-enter sustain phase (or decay phase, depending on parameters at the time). startRamp(newTarget, newIncrement, TVA_PHASE_SUSTAIN - 1); }
void ParameterRamper::dezipperCheck(uint32_t rampDuration) { // check to see if the UI has changed and if so, start a ramp to dezipper it. int32_t changeCounterSnapshot = data->changeCounter; if (data->updateCounter != changeCounterSnapshot) { data->updateCounter = changeCounterSnapshot; startRamp(data->uiValue, rampDuration); } }
void TVA::startDecay() { if (phase >= TVA_PHASE_RELEASE) { return; } Bit8u newIncrement; if (partialParam->tva.envTime[4] == 0) { newIncrement = 1; } else { newIncrement = -partialParam->tva.envTime[4]; } // The next time nextPhase() is called, it will think TVA_PHASE_RELEASE has finished and the partial will be aborted startRamp(0, newIncrement, TVA_PHASE_RELEASE); }
void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartialParam, const MemParams::RhythmTemp *newRhythmTemp) { part = newPart; partialParam = newPartialParam; patchTemp = newPart->getPatchTemp(); rhythmTemp = newRhythmTemp; playing = true; const Tables *tables = &Tables::getInstance(); int key = partial->getPoly()->getKey(); int velocity = partial->getPoly()->getVelocity(); keyTimeSubtraction = calcKeyTimeSubtraction(partialParam->tva.envTimeKeyfollow, key); biasAmpSubtraction = calcBiasAmpSubtractions(partialParam, key); veloAmpSubtraction = calcVeloAmpSubtraction(partialParam->tva.veloSensitivity, velocity); int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); int newPhase; if (partialParam->tva.envTime[0] == 0) { // Initially go to the TVA_PHASE_ATTACK target amp, and spend the next phase going from there to the TVA_PHASE_2 target amp // Note that this means that velocity never affects time for this partial. newTarget += partialParam->tva.envLevel[0]; newPhase = TVA_PHASE_ATTACK; // The first target used in nextPhase() will be TVA_PHASE_2 } else { // Initially go to the base amp determined by TVA level, part volume, etc., and spend the next phase going from there to the full TVA_PHASE_ATTACK target amp. newPhase = TVA_PHASE_BASIC; // The first target used in nextPhase() will be TVA_PHASE_ATTACK } ampRamp->reset();//currentAmp = 0; // "Go downward as quickly as possible". // Since the current value is 0, the LA32Ramp will notice that we're already at or below the target and trying to go downward, // and therefore jump to the target immediately and raise an interrupt. startRamp((Bit8u)newTarget, 0x80 | 127, newPhase); }
void TVA::recalcSustain() { // We get pinged periodically by the pitch code to recalculate our values when in sustain. // This is done so that the TVA will respond to things like MIDI expression and volume changes while it's sustaining, which it otherwise wouldn't do. // The check for envLevel[3] == 0 strikes me as slightly dumb. FIXME: Explain why if (phase != TVA_PHASE_SUSTAIN || partialParam->tva.envLevel[3] == 0) { return; } // We're sustaining. Recalculate all the values const Tables *tables = &Tables::getInstance(); int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression(), partial->getSynth()->controlROMFeatures->quirkRingModulationNoMix); newTarget += partialParam->tva.envLevel[3]; // Although we're in TVA_PHASE_SUSTAIN at this point, we cannot be sure that there is no active ramp at the moment. // In case the channel volume or the expression changes frequently, the previously started ramp may still be in progress. // Real hardware units ignore this possibility and rely on the assumption that the target is the current amp. // This is OK in most situations but when the ramp that is currently in progress needs to change direction // due to a volume/expression update, this leads to a jump in the amp that is audible as an unpleasant click. // To avoid that, we compare the newTarget with the the actual current ramp value and correct the direction if necessary. int targetDelta = newTarget - target; // Calculate an increment to get to the new amp value in a short, more or less consistent amount of time Bit8u newIncrement; bool descending = targetDelta < 0; if (!descending) { newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - 2; } else { newIncrement = (tables->envLogarithmicTime[Bit8u(-targetDelta)] - 2) | 0x80; } if (part->getSynth()->isNiceAmpRampEnabled() && (descending != ampRamp->isBelowCurrent(newTarget))) { newIncrement ^= 0x80; } // Configure so that once the transition's complete and nextPhase() is called, we'll just re-enter sustain phase (or decay phase, depending on parameters at the time). startRamp(newTarget, newIncrement, TVA_PHASE_SUSTAIN - 1); }
void TVA::startAbort() { startRamp(64, 0x80 | 127, TVA_PHASE_RELEASE); }