-1

I am working on modifying a simple audio envelope posted here. The posted version runs simple exponential attack, decay, sustain, and release cases of a switch and progresses through the envelope based on expected sample length duration of each phase.

I require more complex and individualized equations for each case. I could calculate the predicted length of each case based on my equations and maintain this time-based methodology. However, I was thinking it would be easier to control the progress through the cases based on amplitude cutoffs. ie. If level >= 1.0, move from attack case to decay case .

But I am having trouble making this work or figuring out why it won't.

Here is the pertinent original code:

enum EnvelopeStage {
        ENVELOPE_STAGE_OFF = 0,
        ENVELOPE_STAGE_ATTACK,
        ENVELOPE_STAGE_DECAY,
        ENVELOPE_STAGE_SUSTAIN,
        ENVELOPE_STAGE_RELEASE,
        kNumEnvelopeStages
    };

double EnvelopeGenerator::nextSample() {
    if (currentStage != ENVELOPE_STAGE_OFF &&
        currentStage != ENVELOPE_STAGE_SUSTAIN) {
        if (currentSampleIndex == nextStageSampleIndex) {
            EnvelopeStage newStage = static_cast<EnvelopeStage>(
                                                                (currentStage + 1) % kNumEnvelopeStages
                                                                );
            enterStage(newStage);
        }
        currentLevel *= multiplier;
        currentSampleIndex++;
    }

    return currentLevel;
}

void EnvelopeGenerator::calculateMultiplier(double startLevel,
                                            double endLevel,
                                            unsigned long long lengthInSamples) {
    multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples);
}

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) {
    currentStage = newStage;
    currentSampleIndex = 0;
    if (currentStage == ENVELOPE_STAGE_OFF ||
        currentStage == ENVELOPE_STAGE_SUSTAIN) {
        nextStageSampleIndex = 0;
    } else {
        nextStageSampleIndex = stageValue[currentStage] * sampleRate;
    }
    switch (newStage) {
        case ENVELOPE_STAGE_OFF:
            currentLevel = 0.0;
            multiplier = 1.0;
            break;
        case ENVELOPE_STAGE_ATTACK:
            currentLevel = minimumLevel;
            calculateMultiplier(currentLevel,
                                1.0,
                                nextStageSampleIndex);
            break;
        case ENVELOPE_STAGE_DECAY:
            currentLevel = 1.0;
            calculateMultiplier(currentLevel,
                                fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel),
                                nextStageSampleIndex);
            break;
        case ENVELOPE_STAGE_SUSTAIN:
            currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN];
            multiplier = 1.0;
            break;
        case ENVELOPE_STAGE_RELEASE:
            // We could go from ATTACK/DECAY to RELEASE,
            // so we're not changing currentLevel here.
            calculateMultiplier(currentLevel,
                                minimumLevel,
                                nextStageSampleIndex);
            break;
        default:
            break;
    }
}

So progress through the cases is being controlled by double EnvelopeGenerator::nextSample().

Here is what I am trying instead to do to control it differently:

double EnvelopeGenerator::nextSample() {

    if (currentStage = ENVELOPE_STAGE_ATTACK) {

            currentLevel = pow(currentSampleIndex / 30000.0f, 2.0f);
            currentSampleIndex++;

            if (currentLevel >= 1.0) {
                enterStage(ENVELOPE_STAGE_DECAY);
            }
    }

    else if (currentStage = ENVELOPE_STAGE_DECAY) {

            currentLevel = 1/ pow((currentSampleIndex / 30000.0f)+1, 2.0f);
            currentSampleIndex++;

            if (currentLevel = 0.1) {
                enterStage(ENVELOPE_STAGE_RELEASE);
            }   
    }
    else if (currentStage = ENVELOPE_STAGE_RELEASE) {

            currentLevel = currentLevel = 1 / pow((currentSampleIndex / 30000.0f) + 1, 2.0f);
            currentSampleIndex++;

            if (currentLevel = 0.0001) {
                enterStage(ENVELOPE_STAGE_OFF);
            }
    }
    else {}

Those are not the final equations I need for each case. They're just simple test equations. But currently with this instead for the nextSample function, I am just getting a looping attack phase indefinitely.

I cannot understand why it is looping infinitely on attack. I thought I made it clear that if the level hits 1.0, it should switch to decay case.

What am I doing wrong and how do I fix it? I do not have sufficient experience with C++ to know how to do this properly.

mike
  • 131
  • 10
  • Log the value of `currentLevel`, maybe it never really gets to be more than `1.0`. – Kingsley Nov 01 '18 at 04:43
  • I am not sure how to log it. But it is definitely hitting the target level. The problem is it is looping once it hits the cutoff rather than switching cases. For example, if I set the attack end cutoff at 1.0 it takes just under a second to loop. If I set it to 0.2 it loops very rapidly at lower volume (because it hits this cutoff faster). So it definitely seems to be hitting the cutoff and doing something. But what it's doing is looping the attack phase rather than switching cases to decay. And I can't figure out why. The original code worked fine. But this doesn't. – mike Nov 01 '18 at 04:49
  • Answer was posted. Problem was single "=" signs in the "if" conditions. Needed "==". – mike Nov 01 '18 at 05:12

1 Answers1

0

You're using a single = in your if statements, which is an assignment. To compare you want to use ==.

if (currentStage == ENVELOPE_STAGE_ATTACK)
1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
  • You are my hero! Thanks. That fixed it. I switched all the single "=" in the "if" statements to "==", ">=", or "<=" and it works! Appreciated. – mike Nov 01 '18 at 05:11
  • @mike as a side note if you turn on warnings on your compiler, a lot of them will warn about errors like this. – samuelnj Nov 01 '18 at 05:12
  • well spotted, I read over that code - never saw it. Geeze :/ – Kingsley Nov 01 '18 at 05:46