1 /*---------------------------------------------------------------------------*
2 Project: NintendoWare
3 File: snd_FxDelay.cpp
4
5 Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Revision: $
14 *---------------------------------------------------------------------------*/
15
16 #include "precompiled.h"
17
18 #include <nw/snd/snd_FxDelay.h>
19
20 namespace
21 {
22 const u32 SAMPLE_PER_FRAME = NN_SND_SAMPLES_PER_FRAME; // samples
23 } // namespace
24
25 namespace nw {
26 namespace snd {
27
28 #ifdef NW_SND_FX_DELAY_CALC_LOAD
29 s64 FxDelay::s_FxLoad = 0;
30 s32 FxDelay::s_SampleLength = 0;
31 f32 FxDelay::s_SampleRate = 32000.f;
32 #endif
33
34
35 //------------------------------------------------------------------------------
FxDelay()36 FxDelay::FxDelay()
37 : m_pBuffer( NULL ),
38 m_BufferSize( 0 ),
39 m_CurFrame( 0 ),
40 m_FeedbackGain( 0x0 ),
41 m_LpfCoef1( 0x10000 ),
42 m_LpfCoef2( 0x0 ),
43 m_ProcessChannelCount( 4 ),
44 m_IsActive( false )
45 {
46 FreeBuffer();
47
48 for ( int ch = 0; ch < m_ProcessChannelCount; ch++ )
49 {
50 m_WorkBuffer.m_Lpf[ch] = 0;
51 }
52 }
53
54
55 //------------------------------------------------------------------------------
~FxDelay()56 FxDelay::~FxDelay()
57 {
58 if ( m_IsActive )
59 {
60 Finalize();
61 }
62 if ( m_pBuffer != NULL )
63 {
64 ReleaseWorkBuffer();
65 }
66 }
67
68
69 //------------------------------------------------------------------------------
SetParam(const FxDelay::Param & param)70 bool FxDelay::SetParam( const FxDelay::Param& param )
71 {
72 // 範囲チェック
73 {
74 NW_MINMAX_ASSERT( param.m_Damping, 0.0f, 1.0f );
75 if ( param.m_Damping < 0.0f || param.m_Damping > 1.0f )
76 {
77 return false;
78 }
79
80 NW_MINMAX_ASSERT( param.m_FeedbackGain, 0.0f, 1.0f );
81 if ( param.m_FeedbackGain < 0.0f || param.m_FeedbackGain > 1.0f )
82 {
83 return false;
84 }
85 }
86
87 // バッファ長に影響するパラメータ
88 {
89 if ( m_IsActive == true )
90 {
91 if ( param.m_DelayTime > m_DelayTimeAtInitialize )
92 {
93 return false;
94 }
95 if ( m_IsEnableSurroundAtInitialize == false && param.m_IsEnableSurround == true )
96 {
97 return false;
98 }
99 }
100
101 // ディレイ長
102 m_DelayFrames = ( param.m_DelayTime * 1000 ) / NN_SND_USECS_PER_FRAME;
103 if ( m_DelayFrames == 0 )
104 {
105 m_DelayFrames = 1;
106 }
107
108 // 計算チャンネル数
109 if ( param.m_IsEnableSurround == false )
110 {
111 m_ProcessChannelCount = 2;
112 }
113 else
114 {
115 m_ProcessChannelCount = 4;
116 }
117
118 }
119
120 // バッファ長に影響しないパラメータ
121 {
122 // フィードバックゲイン
123 m_FeedbackGain = static_cast<s32>( static_cast<s32>(0x80L) * param.m_FeedbackGain );
124
125 // ダンピング
126 f32 lpf_coef = param.m_Damping;
127 if ( lpf_coef > 0.95f )
128 {
129 lpf_coef = 0.95f;
130 }
131 f32 lpf_coef_1 = 1.f - lpf_coef;
132 f32 lpf_coef_2 = lpf_coef;
133
134 m_LpfCoef1 = static_cast<s32>( static_cast<s32>(0x80L) * lpf_coef_1 );
135 m_LpfCoef2 = static_cast<s32>( static_cast<s32>(0x80L) * lpf_coef_2 );
136 }
137
138 m_Param = param; // 構造体コピー
139 return true;
140 }
141
142
143 //------------------------------------------------------------------------------
GetRequiredMemSize()144 size_t FxDelay::GetRequiredMemSize()
145 {
146 NW_ASSERT( m_DelayFrames != 0 );
147 size_t result =
148 ( sizeof(s32) * SAMPLE_PER_FRAME * m_DelayFrames ) * m_ProcessChannelCount;
149 result += 32; // 念のためバッファを 32 バイト境界におくため +32 する
150 return result;
151 }
152
153
154 //------------------------------------------------------------------------------
AssignWorkBuffer(uptr buffer,size_t size)155 bool FxDelay::AssignWorkBuffer( uptr buffer, size_t size )
156 {
157 NW_NULL_ASSERT( buffer );
158 if ( buffer == NULL )
159 {
160 return false;
161 }
162
163 m_pBuffer = buffer;
164 m_BufferSize = size;
165
166 return true;
167 }
168
169
170 //------------------------------------------------------------------------------
ReleaseWorkBuffer()171 void FxDelay::ReleaseWorkBuffer()
172 {
173 NW_NULL_ASSERT( m_pBuffer );
174 m_pBuffer = NULL;
175 }
176
177
178 //------------------------------------------------------------------------------
Initialize()179 bool FxDelay::Initialize()
180 {
181 NW_ASSERT( ! m_IsActive );
182 if ( m_IsActive )
183 {
184 return false;
185 }
186
187 m_DelayTimeAtInitialize = m_Param.m_DelayTime;
188 m_IsEnableSurroundAtInitialize = m_Param.m_IsEnableSurround;
189
190 AllocBuffer();
191 InitializeParam();
192
193 m_IsActive = true;
194
195 return true;
196 }
197
198
199 //------------------------------------------------------------------------------
Finalize()200 void FxDelay::Finalize()
201 {
202 if ( ! m_IsActive )
203 {
204 return;
205 }
206
207 m_IsActive = false;
208 std::memset( reinterpret_cast<void*>( &m_Param ), 0, sizeof(Param) );
209
210 FreeBuffer();
211 }
212
213
214 //------------------------------------------------------------------------------
UpdateBuffer(int numChannels,nn::snd::AuxBusData * data,s32 sampleLength,nw::snd::SampleFormat format,f32 sampleRate,nw::snd::OutputMode mode)215 void FxDelay::UpdateBuffer(
216 int numChannels,
217 nn::snd::AuxBusData* data,
218 s32 sampleLength,
219 nw::snd::SampleFormat format,
220 f32 sampleRate,
221 nw::snd::OutputMode mode )
222 {
223 NW_UNUSED_VARIABLE( format );
224 NW_UNUSED_VARIABLE( mode );
225 NW_UNUSED_VARIABLE( numChannels );
226
227 if( ! m_IsActive )
228 {
229 return;
230 }
231
232 #ifdef NW_SND_FX_DELAY_CALC_LOAD
233 s_SampleLength = sampleLength;
234 s_SampleRate = sampleRate;
235
236 nn::os::Tick st,end;
237 st = nn::os::Tick::GetSystemCurrent();
238 #else
239 NW_UNUSED_VARIABLE( sampleRate );
240 #endif
241
242 NW_NULL_ASSERT( data );
243 NW_ASSERT( m_DelayFrames != 0 );
244
245 s32* input[ nn::snd::CHANNEL_INDEX_NUM ];
246 input[ nn::snd::CHANNEL_INDEX_FRONT_LEFT ] = data->frontLeft;
247 input[ nn::snd::CHANNEL_INDEX_FRONT_RIGHT ] = data->frontRight;
248 input[ nn::snd::CHANNEL_INDEX_REAR_LEFT ] = data->rearLeft;
249 input[ nn::snd::CHANNEL_INDEX_REAR_RIGHT ] = data->rearRight;
250
251 const u32 start_pos = SAMPLE_PER_FRAME * m_CurFrame;
252
253 for ( u32 ch = 0; ch < m_ProcessChannelCount; ch++ )
254 {
255 u32 cur_pos = start_pos;
256
257 s32* in_ptr = &input[ch][0];
258 s32* delay_ptr = &m_WorkBuffer.m_Delay[ch][0];
259 s32* lpf_ptr = &m_WorkBuffer.m_Lpf[ch];
260
261 for ( u32 samp = 0; samp < sampleLength; samp++ )
262 {
263 //---------------------------------------------------------
264 // Delay
265 //---------------------------------------------------------
266 s32 delay_out = delay_ptr[cur_pos];
267
268 s32 feedback = delay_out;
269 if ( feedback < 0 )
270 {
271 s32 tmp = -feedback;
272 tmp = ( tmp * m_FeedbackGain ) >> 7;
273 feedback = -tmp;
274 // NOTE: 算術右シフト時の負値の扱いが面倒だったので、こんな感じにした。
275 // 誤差が出ないように楽に実装できる方法があれば、そちらで…。
276 }
277 else
278 {
279 feedback = ( feedback * m_FeedbackGain ) >> 7;
280 }
281
282 feedback = in_ptr[samp] - feedback; // フィードバックの位相を反転
283
284 //---------------------------------------------------------
285 // LPF
286 //---------------------------------------------------------
287 s32 lpf_out = m_LpfCoef1 * feedback
288 + m_LpfCoef2 * *lpf_ptr;
289 lpf_out >>= 7;
290 // NOTE: 算術右シフトの場合、負値では 0 になるべきところが
291 // -1 で残るケースが出てくるが、誤差範囲として許容
292
293 *lpf_ptr = lpf_out;
294
295 delay_ptr[cur_pos] = lpf_out;
296
297 //---------------------------------------------------------
298 // 出力
299 //---------------------------------------------------------
300 in_ptr[samp] = delay_out;
301
302 //---------------------------------------------------------
303 // end
304 //---------------------------------------------------------
305 ++cur_pos;
306 }
307 }
308
309 if ( ++m_CurFrame >= m_DelayFrames )
310 {
311 m_CurFrame = 0;
312 }
313
314 #ifdef NW_SND_FX_DELAY_CALC_LOAD
315 end = nn::os::Tick::GetSystemCurrent();
316 s_FxLoad = (s64)end-(s64)st;
317 #endif
318
319 }
320
321
322 //------------------------------------------------------------------------------
AllocBuffer()323 void FxDelay::AllocBuffer()
324 {
325 NW_NULL_ASSERT( m_pBuffer );
326 NW_ASSERT( m_DelayFrames != 0 );
327
328 const size_t ch_buffer_size = sizeof(s32) * SAMPLE_PER_FRAME * m_DelayFrames;
329
330 uptr ptr = nw::ut::RoundUp( m_pBuffer, 32 );
331
332 for( int ch = 0; ch < m_ProcessChannelCount; ch++ )
333 {
334 m_WorkBuffer.m_Delay[ch] = reinterpret_cast<s32*>(ptr);
335 ptr += ch_buffer_size;
336 }
337
338 NW_ASSERT( ptr <= m_pBuffer + m_BufferSize );
339 }
340
341
342 //------------------------------------------------------------------------------
FreeBuffer()343 void FxDelay::FreeBuffer()
344 {
345 for ( int ch = 0; ch < m_ProcessChannelCount; ch++ )
346 {
347 m_WorkBuffer.m_Delay[ch] = NULL;
348 }
349 }
350
351
352 //------------------------------------------------------------------------------
InitializeParam()353 void FxDelay::InitializeParam()
354 {
355 m_CurFrame = 0;
356
357 std::memset( reinterpret_cast<void*>( m_pBuffer ), 0, m_BufferSize );
358
359 for ( int ch = 0; ch < m_ProcessChannelCount; ch++ )
360 {
361 m_WorkBuffer.m_Lpf[ch] = 0;
362 }
363 }
364
365
366 //------------------------------------------------------------------------------
367
368 #ifdef NW_SND_FX_DELAY_CALC_LOAD
GetLoad()369 f32 FxDelay::GetLoad()
370 {
371 if ( s_SampleLength == 0 )
372 {
373 return 0.0f;
374 }
375 NW_ASSERT( s_SampleRate != 0 );
376
377 int maxLoad = ( s_SampleLength * nn::os::Tick::TICKS_PER_SECOND) / s_SampleRate;
378 return ( (f32)s_FxLoad / (f32)maxLoad ) * 100.0f;
379 }
380
381
382 #endif
383
384 } // namespace nw::snd
385 } // namespace nw
386
387