hydrogen 1.2.6
Adsr.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2025 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
23#include <core/Basics/Adsr.h>
24
25namespace H2Core
26{
27
28/* Attack parameters */
29const float fAttackExponent = 0.038515241777294117,
30 fAttackInit = 1.039835771720117430;
31
32const float fDecayExponent = 0.044796211247505179,
33 fDecayInit = 1.046934808452493870,
34 fDecayYOffset = -0.046934663351557632;
35
36ADSR::ADSR( unsigned int attack, unsigned int decay, float sustain, unsigned int release ) :
38 m_nDecay( decay ),
39 m_fSustain( sustain ),
42 m_fFramesInState( 0.0 ),
43 m_fValue( 0.0 ),
44 m_fReleaseValue( 0.0 ),
46{
47 normalise();
48}
49
50ADSR::ADSR( const std::shared_ptr<ADSR> other ) :
51 m_nAttack( other->m_nAttack ),
52 m_nDecay( other->m_nDecay ),
53 m_fSustain( other->m_fSustain ),
54 m_nRelease( other->m_nRelease ),
55 m_state( other->m_state ),
57 m_fValue( other->m_fValue ),
59{
60 normalise();
61}
62
64
66{
67 if (m_nAttack < 0.0) {
68 m_nAttack = 0.0;
69 }
70 if (m_nDecay < 0.0) {
71 m_nDecay = 0.0;
72 }
73 if (m_fSustain < 0.0) {
74 m_fSustain = 0.0;
75 }
76 if (m_nRelease < 256) {
77 m_nRelease = 256;
78 }
79 if (m_nAttack > 100000) {
80 m_nAttack = 100000;
81 }
82 if (m_nDecay > 100000) {
83 m_nDecay = 100000;
84 }
85 if (m_fSustain > 1.0) {
86 m_fSustain = 1.0;
87 }
88 if (m_nRelease > 100256) {
89 m_nRelease = 100256;
90 }
91}
92
116inline double applyExponential( const float fExponent, const float fXOffset, const float fYOffset,
117 const float fScale,
118 float * __restrict__ pA, float * __restrict__ pB,
119 float fQ, int nFrames, int nFramesTotal, float fStep,
120 float * __restrict__ pfADSRVal ) {
121
122 int i = 0;
123 float fVal = *pfADSRVal;
124
125 float fFactor = pow( fExponent, (double)fStep / nFramesTotal );
126
127 if ( nFrames > 4) {
128 float fFactor4 = fFactor * fFactor * fFactor * fFactor;
129 float fQ0 = fQ,
130 fQ1 = fQ0 * fFactor,
131 fQ2 = fQ1 * fFactor,
132 fQ3 = fQ2 * fFactor;
133 for (; i < nFrames - 4; i += 4) {
134 float fVal0 = ( fQ0 - fXOffset ) * fScale + fYOffset,
135 fVal1 = ( fQ1 - fXOffset ) * fScale + fYOffset,
136 fVal2 = ( fQ2 - fXOffset ) * fScale + fYOffset,
137 fVal3 = ( fQ3 - fXOffset ) * fScale + fYOffset;
138
139 pA[i] *= fVal0;
140 pA[i+1] *= fVal1;
141 pA[i+2] *= fVal2;
142 pA[i+3] *= fVal3;
143
144 pB[i] *= fVal0;
145 pB[i+1] *= fVal1;
146 pB[i+2] *= fVal2;
147 pB[i+3] *= fVal3;
148
149 fQ0 *= fFactor4;
150 fQ1 *= fFactor4;
151 fQ2 *= fFactor4;
152 fQ3 *= fFactor4;
153
154 fVal = fVal0;
155 }
156 fQ = fQ0;
157 }
158
159 for (; i < nFrames; i++) {
160 fVal = ( fQ - fXOffset ) * fScale + fYOffset;
161 pA[i] *= fVal;
162 pB[i] *= fVal;
163 fQ *= fFactor;
164 }
165 *pfADSRVal = fVal;
166 return fQ;
167}
168
175bool ADSR::applyADSR( float *pLeft, float *pRight, int nFinalBufferPos, int nReleaseFrame, float fStep )
176{
177 int nBufferPos = 0;
178
179 // If the release point is somehow in the past, move direcly to Release
180 if ( nReleaseFrame <= 0 && m_state != State::Release && m_state != State::Idle ) {
181 WARNINGLOG( QString( "Impossibly early release for ADSR: " ).arg( this->toQString() ) );
182 nReleaseFrame = 0;
184 }
185
186 if ( m_state == State::Attack ) {
187 int nAttackFrames = std::min( nFinalBufferPos, nReleaseFrame );
188 if ( nAttackFrames * fStep > m_nAttack ) {
189 // Attack must end before nFinalBufferPos, so trim it
190 nAttackFrames = ceil( m_nAttack / fStep );
191 }
192
194 pLeft, pRight, m_fQ, nAttackFrames, m_nAttack,
195 fStep, &m_fValue );
196
197 nBufferPos += nAttackFrames;
198
199 m_fFramesInState += nAttackFrames * fStep;
200
201 if ( m_fFramesInState >= m_nAttack ) {
205 }
206 }
207
208 if ( m_state == State::Decay ) {
209 int nDecayFrames = std::min( nFinalBufferPos, nReleaseFrame ) - nBufferPos;
210 if ( nDecayFrames * fStep > m_nDecay ) {
211 nDecayFrames = ceil( m_nDecay / fStep );
212 }
213
215 &pLeft[nBufferPos], &pRight[nBufferPos], m_fQ, nDecayFrames, m_nDecay, fStep, &m_fValue );
216
217 nBufferPos += nDecayFrames;
218 m_fFramesInState += nDecayFrames * fStep;
219
220 if ( m_fFramesInState >= m_nDecay ) {
223 }
224 }
225
226 if ( m_state == State::Sustain ) {
227
228 int nSustainFrames = std::min( nFinalBufferPos, nReleaseFrame ) - nBufferPos;
229 if ( nSustainFrames != 0 ) {
231 if ( m_fSustain != 1.0 ) {
232 for ( int i = 0; i < nSustainFrames; i++ ) {
233 pLeft[ nBufferPos + i ] *= m_fSustain;
234 pRight[ nBufferPos + i ] *= m_fSustain;
235 }
236 }
237 nBufferPos += nSustainFrames;
238 }
239 }
240
241 if ( m_state != State::Release && m_state != State::Idle && nBufferPos >= nReleaseFrame ) {
246 }
247
248 if ( m_state == State::Release ) {
249
250 int nReleaseFrames = nFinalBufferPos - nBufferPos;
251 if ( nReleaseFrames * fStep > m_nRelease ) {
252 nReleaseFrames = ceil( m_nRelease / fStep );
253 }
254
256 &pLeft[nBufferPos], &pRight[nBufferPos], m_fQ, nReleaseFrames, m_nRelease, fStep, &m_fValue );
257
258 nBufferPos += nReleaseFrames;
259 m_fFramesInState += nReleaseFrames * fStep;
260
261 if ( m_fFramesInState >= m_nRelease ) {
263 }
264 }
265
266 if ( m_state == State::Idle ) {
267 for ( ; nBufferPos < nFinalBufferPos; nBufferPos++ ) {
268 pLeft[ nBufferPos ] = pRight[ nBufferPos ] = 0.0;
269 }
270 return true;
271 }
272 return false;
273}
274
276{
280}
281
283{
284 if ( m_state == State::Idle ) {
285 return 0;
286 }
287 else if ( m_state == State::Release ) {
288 return m_fValue;
289 }
290
295 return m_fReleaseValue;
296}
297
298QString ADSR::StateToQString( State state ) {
299 switch( state ) {
300 case State::Attack:
301 return std::move( "Attack" );
302 case State::Decay:
303 return std::move( "Decay" );
304 case State::Sustain:
305 return std::move( "Sustain" );
306 case State::Release:
307 return std::move( "Release" );
308 case State::Idle:
309 return std::move( "Idle" );
310 }
311
312 return std::move( "Attack" );
313}
314
315QString ADSR::toQString( const QString& sPrefix, bool bShort ) const {
316 QString s = Base::sPrintIndention;
317 QString sOutput;
318 if ( ! bShort ) {
319 sOutput = QString( "%1[ADSR]\n" ).arg( sPrefix )
320 .append( QString( "%1%2attack: %3\n" ).arg( sPrefix ).arg( s ).arg( m_nAttack ) )
321 .append( QString( "%1%2decay: %3\n" ).arg( sPrefix ).arg( s ).arg( m_nDecay ) )
322 .append( QString( "%1%2sustain: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fSustain ) )
323 .append( QString( "%1%2release: %3\n" ).arg( sPrefix ).arg( s ).arg( m_nRelease ) )
324 .append( QString( "%1%2state: %3\n" ).arg( sPrefix ).arg( s )
325 .arg( StateToQString( m_state ) ) )
326 .append( QString( "%1%2ticks: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fFramesInState ) )
327 .append( QString( "%1%2value: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fValue ) )
328 .append( QString( "%1%2release_value: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fReleaseValue ) );
329 } else {
330 sOutput = QString( "[ADSR]" )
331 .append( QString( " attack: %1" ).arg( m_nAttack ) )
332 .append( QString( ", decay: %1" ).arg( m_nDecay ) )
333 .append( QString( ", sustain: %1" ).arg( m_fSustain ) )
334 .append( QString( ", release: %1" ).arg( m_nRelease ) )
335 .append( QString( ", state: %1" ).arg( StateToQString( m_state ) ) )
336 .append( QString( ", ticks: %1" ).arg( m_fFramesInState ) )
337 .append( QString( ", value: %1" ).arg( m_fValue ) )
338 .append( QString( ", release_value: %1\n" ).arg( m_fReleaseValue ) );
339 }
340
341 return sOutput;
342}
343
344};
345
346/* vim: set softtabstop=4 noexpandtab: */
#define WARNINGLOG(x)
Definition Object.h:241
void attack()
Sets m_state to State::Attack.
Definition Adsr.cpp:275
~ADSR()
destructor
Definition Adsr.cpp:63
static QString StateToQString(State state)
Definition Adsr.cpp:298
ADSR(unsigned int attack=0, unsigned int decay=0, float sustain=1.0, unsigned int release=1000)
constructor
Definition Adsr.cpp:36
float m_fValue
current value
Definition Adsr.h:149
float release()
Sets m_state to State::Release and return the current m_fReleaseValue.
Definition Adsr.cpp:282
unsigned int m_nDecay
Decay phase duration in frames.
Definition Adsr.h:133
unsigned int m_nAttack
Attack phase duration in frames.
Definition Adsr.h:132
State
possible states
Definition Adsr.h:110
double m_fQ
exponential decay state
Definition Adsr.h:152
float m_fSustain
Sustain level.
Definition Adsr.h:134
State m_state
current state
Definition Adsr.h:136
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition Adsr.cpp:315
bool applyADSR(float *pLeft, float *pRight, int nFinalBufferPos, int nReleaseFrame, float fStep)
Compute and apply successive ADSR values to stereo buffers.
Definition Adsr.cpp:175
unsigned int m_nRelease
Release phase duration in frames.
Definition Adsr.h:135
float m_fReleaseValue
value when the release state was entered
Definition Adsr.h:150
void normalise()
Definition Adsr.cpp:65
float m_fFramesInState
Tracks the number of frames passed in the current m_state.
Definition Adsr.h:148
static QString sPrintIndention
String used to format the debugging string output of some core classes.
Definition Object.h:127
const float fDecayInit
Definition Adsr.cpp:33
const float fDecayExponent
Definition Adsr.cpp:32
const float fAttackExponent
Definition Adsr.cpp:29
const float fAttackInit
Definition Adsr.cpp:30
double applyExponential(const float fExponent, const float fXOffset, const float fYOffset, const float fScale, float *__restrict__ pA, float *__restrict__ pB, float fQ, int nFrames, int nFramesTotal, float fStep, float *__restrict__ pfADSRVal)
Apply an exponential envelope to a stereo pair of sample fragments.
Definition Adsr.cpp:116
const float fDecayYOffset
Definition Adsr.cpp:34