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