Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

juce_ADSR.h 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. namespace juce
  18. {
  19. //==============================================================================
  20. /**
  21. A very simple ADSR envelope class.
  22. To use it, call setSampleRate() with the current sample rate and give it some parameters
  23. with setParameters() then call getNextSample() to get the envelope value to be applied
  24. to each audio sample or applyEnvelopeToBuffer() to apply the envelope to a whole buffer.
  25. Do not change the parameters during playback. If you change the parameters before the
  26. release stage has completed then you must call reset() before the next call to
  27. noteOn().
  28. @tags{Audio}
  29. */
  30. class JUCE_API ADSR
  31. {
  32. public:
  33. //==============================================================================
  34. ADSR()
  35. {
  36. recalculateRates();
  37. }
  38. //==============================================================================
  39. /**
  40. Holds the parameters being used by an ADSR object.
  41. @tags{Audio}
  42. */
  43. struct JUCE_API Parameters
  44. {
  45. Parameters() = default;
  46. Parameters (float attackTimeSeconds,
  47. float decayTimeSeconds,
  48. float sustainLevel,
  49. float releaseTimeSeconds)
  50. : attack (attackTimeSeconds),
  51. decay (decayTimeSeconds),
  52. sustain (sustainLevel),
  53. release (releaseTimeSeconds)
  54. {
  55. }
  56. float attack = 0.1f, decay = 0.1f, sustain = 1.0f, release = 0.1f;
  57. };
  58. /** Sets the parameters that will be used by an ADSR object.
  59. You must have called setSampleRate() with the correct sample rate before
  60. this otherwise the values may be incorrect!
  61. @see getParameters
  62. */
  63. void setParameters (const Parameters& newParameters)
  64. {
  65. // need to call setSampleRate() first!
  66. jassert (sampleRate > 0.0);
  67. parameters = newParameters;
  68. recalculateRates();
  69. }
  70. /** Returns the parameters currently being used by an ADSR object.
  71. @see setParameters
  72. */
  73. const Parameters& getParameters() const noexcept { return parameters; }
  74. /** Returns true if the envelope is in its attack, decay, sustain or release stage. */
  75. bool isActive() const noexcept { return state != State::idle; }
  76. //==============================================================================
  77. /** Sets the sample rate that will be used for the envelope.
  78. This must be called before the getNextSample() or setParameters() methods.
  79. */
  80. void setSampleRate (double newSampleRate) noexcept
  81. {
  82. jassert (newSampleRate > 0.0);
  83. sampleRate = newSampleRate;
  84. }
  85. //==============================================================================
  86. /** Resets the envelope to an idle state. */
  87. void reset() noexcept
  88. {
  89. envelopeVal = 0.0f;
  90. state = State::idle;
  91. }
  92. /** Starts the attack phase of the envelope. */
  93. void noteOn() noexcept
  94. {
  95. if (attackRate > 0.0f)
  96. {
  97. state = State::attack;
  98. }
  99. else if (decayRate > 0.0f)
  100. {
  101. envelopeVal = 1.0f;
  102. state = State::decay;
  103. }
  104. else
  105. {
  106. envelopeVal = parameters.sustain;
  107. state = State::sustain;
  108. }
  109. }
  110. /** Starts the release phase of the envelope. */
  111. void noteOff() noexcept
  112. {
  113. if (state != State::idle)
  114. {
  115. if (parameters.release > 0.0f)
  116. {
  117. releaseRate = (float) (envelopeVal / (parameters.release * sampleRate));
  118. state = State::release;
  119. }
  120. else
  121. {
  122. reset();
  123. }
  124. }
  125. }
  126. //==============================================================================
  127. /** Returns the next sample value for an ADSR object.
  128. @see applyEnvelopeToBuffer
  129. */
  130. float getNextSample() noexcept
  131. {
  132. switch (state)
  133. {
  134. case State::idle:
  135. {
  136. return 0.0f;
  137. }
  138. case State::attack:
  139. {
  140. envelopeVal += attackRate;
  141. if (envelopeVal >= 1.0f)
  142. {
  143. envelopeVal = 1.0f;
  144. goToNextState();
  145. }
  146. break;
  147. }
  148. case State::decay:
  149. {
  150. envelopeVal -= decayRate;
  151. if (envelopeVal <= parameters.sustain)
  152. {
  153. envelopeVal = parameters.sustain;
  154. goToNextState();
  155. }
  156. break;
  157. }
  158. case State::sustain:
  159. {
  160. envelopeVal = parameters.sustain;
  161. break;
  162. }
  163. case State::release:
  164. {
  165. envelopeVal -= releaseRate;
  166. if (envelopeVal <= 0.0f)
  167. goToNextState();
  168. break;
  169. }
  170. }
  171. return envelopeVal;
  172. }
  173. /** This method will conveniently apply the next numSamples number of envelope values
  174. to an AudioBuffer.
  175. @see getNextSample
  176. */
  177. template <typename FloatType>
  178. void applyEnvelopeToBuffer (AudioBuffer<FloatType>& buffer, int startSample, int numSamples)
  179. {
  180. jassert (startSample + numSamples <= buffer.getNumSamples());
  181. if (state == State::idle)
  182. {
  183. buffer.clear (startSample, numSamples);
  184. return;
  185. }
  186. if (state == State::sustain)
  187. {
  188. buffer.applyGain (startSample, numSamples, parameters.sustain);
  189. return;
  190. }
  191. auto numChannels = buffer.getNumChannels();
  192. while (--numSamples >= 0)
  193. {
  194. auto env = getNextSample();
  195. for (int i = 0; i < numChannels; ++i)
  196. buffer.getWritePointer (i)[startSample] *= env;
  197. ++startSample;
  198. }
  199. }
  200. private:
  201. //==============================================================================
  202. void recalculateRates() noexcept
  203. {
  204. auto getRate = [] (float distance, float timeInSeconds, double sr)
  205. {
  206. return timeInSeconds > 0.0f ? (float) (distance / (timeInSeconds * sr)) : -1.0f;
  207. };
  208. attackRate = getRate (1.0f, parameters.attack, sampleRate);
  209. decayRate = getRate (1.0f - parameters.sustain, parameters.decay, sampleRate);
  210. releaseRate = getRate (parameters.sustain, parameters.release, sampleRate);
  211. if ((state == State::attack && attackRate <= 0.0f)
  212. || (state == State::decay && (decayRate <= 0.0f || envelopeVal <= parameters.sustain))
  213. || (state == State::release && releaseRate <= 0.0f))
  214. {
  215. goToNextState();
  216. }
  217. }
  218. void goToNextState() noexcept
  219. {
  220. if (state == State::attack)
  221. {
  222. state = (decayRate > 0.0f ? State::decay : State::sustain);
  223. return;
  224. }
  225. if (state == State::decay)
  226. {
  227. state = State::sustain;
  228. return;
  229. }
  230. if (state == State::release)
  231. reset();
  232. }
  233. //==============================================================================
  234. enum class State { idle, attack, decay, sustain, release };
  235. State state = State::idle;
  236. Parameters parameters;
  237. double sampleRate = 44100.0;
  238. float envelopeVal = 0.0f, attackRate = 0.0f, decayRate = 0.0f, releaseRate = 0.0f;
  239. };
  240. } // namespace juce