package jp.sourceforge.ocmml.android; import java.nio.DoubleBuffer; public class Envelope { private static void initialize() { if (!sInitialized) { sVolumeMap[0] = 0.0; for (int i = 1; i < sVolumeLength; i++) sVolumeMap[i] = Math.pow(10.0, (i - 255.0) * (48.0 / (255.0 * 20.0))); sInitialized = true; } } public Envelope(double attack, double decay, double sustain, double release) { initialize(); setASDR(attack, decay, sustain, release); mPlaying = false; mReleasing = true; mCurrentValue = 0; mReleaseStep = 0; } public void setASDR(double attack, double decay, double sustain, double release) { if (attack != 0.0) { mAttackTime = (int) (attack * Sample.RATE); mAttackRcpr = 1.0 / mAttackTime; } if (decay != 0.0) { mDecayTime = (int) (decay * Sample.RATE); mDecayRcpr = 1.0 / mDecayTime; } mSustainLevel = sustain; mReleaseTime = 1.0 / mDecayTime; } public void trigger(Boolean zeroStart) { mPlaying = true; mReleasing = false; mStartAmplitude = zeroStart ? 0 : mCurrentValue; mTimeInSamples = 1; } public void release() { mReleasing = true; mReleaseStep = mCurrentValue / mReleaseTime; } public void getAmplitudeSamplesLinear(DoubleBuffer samples, int start, int end, double velocity) { for (int i = start; i < end; i++) { if (!mPlaying) { samples.put(i, 0.0); continue; } double n = samples.get(i); updateCurrentValue(); samples.put(i, n * mCurrentValue * velocity); } } public void getAmplitudeSamplesNonLinear(DoubleBuffer samples, int start, int end, double velocity) { for (int i = start; i < end; i++) { if (!mPlaying) { samples.put(i, 0.0); continue; } double n = samples.get(i); updateCurrentValue(); samples.put(i, n * sVolumeMap[(int) (Math.min(mCurrentValue, 1.0) * 255)] * velocity); } } public double getNextAmplitudeLinear() { if (!mPlaying) return 0; updateCurrentValue(); return mCurrentValue; } public Boolean isPlaying() { return mPlaying; } private void updateCurrentValue() { if (!mReleasing) { if (mTimeInSamples < mAttackTime) mCurrentValue = mStartAmplitude + (1 - mStartAmplitude) * mTimeInSamples * mAttackRcpr; else if (mTimeInSamples < mAttackTime + mDecayTime) mCurrentValue = 1.0 - ((mTimeInSamples - mAttackTime) * mDecayRcpr) * (1.0 - mSustainLevel); else mCurrentValue = mSustainLevel; } else mCurrentValue -= mReleaseStep; if (mCurrentValue <= 0) { mPlaying = false; mCurrentValue = 0; } mTimeInSamples++; } private static int sVolumeLength = 256; private static double[] sVolumeMap = new double[sVolumeLength]; private static Boolean sInitialized = false; private int mAttackTime; private double mAttackRcpr; private int mDecayTime; private double mDecayRcpr; private double mSustainLevel; private double mReleaseTime; private double mCurrentValue; private double mReleaseStep; private Boolean mReleasing; private int mTimeInSamples; private double mStartAmplitude; private Boolean mPlaying; }