1 /*---------------------------------------------------------------------------*
2 Project: NintendoWare
3 File: snd_FxReverb.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_FxReverb.h>
21 #include <nw/snd/snd_HardwareManager.h>
22 #include <nw/ut/ut_Inlines.h>
23
24 namespace
25 {
26 const f32 MSEC_PER_FRAME = NN_SND_USECS_PER_FRAME / 1000.f;
27
RoundUpToMsecPerFrame(u32 msec)28 inline f32 RoundUpToMsecPerFrame( u32 msec )
29 {
30 return ( msec > MSEC_PER_FRAME ) ? msec : MSEC_PER_FRAME;
31 }
32
ConvertMsecToSamples(f32 msec)33 inline u32 ConvertMsecToSamples( f32 msec )
34 {
35 return static_cast<u32>( msec / MSEC_PER_FRAME ) * NN_SND_SAMPLES_PER_FRAME;
36 }
37 } // anonymous namespace
38
39 namespace nw {
40 namespace snd {
41
42 FxReverb::FilterSize FxReverb::s_DefaultFilterSize;
43
FxReverb()44 FxReverb::FxReverb()
45 : m_pBuffer( NULL ),
46 m_FilterSize( s_DefaultFilterSize ),
47 m_EarlyGain( 0 ),
48 m_FusedGain( 0 ),
49 m_LpfCoef1( 0 ),
50 m_LpfCoef2( 0 ),
51 m_ProcessChannelCount( 4 ),
52 m_IsActive( false )
53 {
54 m_EarlyLength = NN_SND_SAMPLES_PER_FRAME;
55 m_EarlyPos = 0;
56
57 m_PreDelayLength = NN_SND_SAMPLES_PER_FRAME;
58 m_PreDelayPos = 0;
59
60 for ( int i = 0; i < 2; i++ )
61 {
62 m_CombFilterLength[i] = NN_SND_SAMPLES_PER_FRAME;
63 m_CombFilterPos[i] = 0;
64 m_CombFilterCoef[i] = 0;
65 }
66
67 m_AllPassFilterLength = NN_SND_SAMPLES_PER_FRAME;
68 m_AllPassFilterPos = 0;
69 m_AllPassFilterCoef = 0;
70
71 for ( int ch = 0; ch < 4; ch++ )
72 {
73 m_WorkBuffer.m_EarlyReflection[ch] = NULL;
74 m_WorkBuffer.m_PreDelay[ch] = NULL;
75
76 for ( int i = 0; i < 2; i++ )
77 {
78 m_WorkBuffer.m_CombFilter[ch][i] = NULL;
79 }
80 m_WorkBuffer.m_AllPassFilter[ch] = NULL;
81 m_WorkBuffer.m_Lpf[ch] = 0;
82 m_LastLpfOut[ch] = 0;
83 }
84 }
85
~FxReverb()86 FxReverb::~FxReverb()
87 {
88 if ( m_IsActive )
89 {
90 Finalize();
91 }
92 if ( m_pBuffer != NULL )
93 {
94 ReleaseWorkBuffer();
95 }
96 }
97
Initialize()98 bool FxReverb::Initialize()
99 {
100 NW_ASSERT( ! m_IsActive );
101
102 m_EarlyReflectionTimeAtInitialize = m_Param.m_EarlyReflectionTime;
103 m_PreDelayTimeAtInitialize = m_Param.m_PreDelayTime;
104 m_FilterSizeAtInitialize = m_FilterSize;
105 m_IsEnableSurroundAtInitialize = m_Param.m_IsEnableSurround;
106
107 AllocBuffer();
108 InitializeParam();
109 m_IsActive = true;
110
111 return true;
112 }
113
Finalize()114 void FxReverb::Finalize()
115 {
116 if ( ! m_IsActive )
117 {
118 return;
119 }
120 m_IsActive = false;
121 FreeBuffer();
122 }
123
SetParam(const FxReverb::Param & param)124 bool FxReverb::SetParam( const FxReverb::Param& param )
125 {
126 // 範囲チェック
127 {
128 NW_MINMAX_ASSERT( param.m_Coloration, 0.0f, 1.0f );
129 if ( param.m_Coloration < 0.0f || param.m_Coloration > 1.f )
130 {
131 return false;
132 }
133
134 NW_MINMAX_ASSERT( param.m_Damping, 0.0f, 1.0f );
135 if ( param.m_Damping < 0.0f || param.m_Damping > 1.f )
136 {
137 return false;
138 }
139
140 NW_MINMAX_ASSERT( param.m_EarlyGain, 0.0f, 1.0f );
141 if ( param.m_EarlyGain < 0.0f || param.m_EarlyGain > 1.f )
142 {
143 return false;
144 }
145
146 NW_MINMAX_ASSERT( param.m_FusedGain, 0.0f, 1.0f );
147 if ( param.m_FusedGain < 0.0f || param.m_FusedGain > 1.f )
148 {
149 return false;
150 }
151
152 if ( param.m_pFilterSize != NULL )
153 {
154 NW_ASSERT( param.m_pFilterSize->m_Comb0 != 0 );
155 NW_ASSERT( param.m_pFilterSize->m_Comb1 != 0 );
156 NW_ASSERT( param.m_pFilterSize->m_AllPass != 0 );
157
158 if ( param.m_pFilterSize->m_Comb0 == 0 ||
159 param.m_pFilterSize->m_Comb1 == 0 ||
160 param.m_pFilterSize->m_AllPass == 0 )
161 {
162 return false;
163 }
164 }
165 }
166
167 // バッファ長に影響するパラメータ
168 {
169 if ( m_IsActive == true )
170 {
171 if ( param.m_EarlyReflectionTime > m_EarlyReflectionTimeAtInitialize )
172 {
173 return false;
174 }
175 if ( param.m_PreDelayTime > m_PreDelayTimeAtInitialize )
176 {
177 return false;
178 }
179 if ( param.m_pFilterSize != NULL )
180 {
181 if ( param.m_pFilterSize->m_Comb0 > m_FilterSizeAtInitialize.m_Comb0 )
182 {
183 return false;
184 }
185 if ( param.m_pFilterSize->m_Comb1 > m_FilterSizeAtInitialize.m_Comb1 )
186 {
187 return false;
188 }
189 if ( param.m_pFilterSize->m_AllPass > m_FilterSizeAtInitialize.m_AllPass )
190 {
191 return false;
192 }
193 }
194 if ( m_IsEnableSurroundAtInitialize == false && param.m_IsEnableSurround == true )
195 {
196 return false;
197 }
198 }
199
200 if ( param.m_IsEnableSurround == false )
201 {
202 m_ProcessChannelCount = 2;
203 }
204 else
205 {
206 m_ProcessChannelCount = 4;
207 }
208
209 }
210
211 m_Param = param; // 構造体コピー
212
213 if ( m_Param.m_pFilterSize != NULL )
214 {
215 m_FilterSize = *m_Param.m_pFilterSize; // 構造体コピー
216 m_Param.m_pFilterSize = &m_FilterSize;
217 }
218
219 if ( m_IsActive == true )
220 {
221 InitializeParam();
222 }
223 return true;
224 }
225
AssignWorkBuffer(uptr buffer,size_t size)226 bool FxReverb::AssignWorkBuffer( uptr buffer, size_t size )
227 {
228 NW_NULL_ASSERT( buffer );
229 if ( buffer == NULL )
230 {
231 return false;
232 }
233
234 m_pBuffer = buffer;
235 m_BufferSize = size;
236 return true;
237 }
238
ReleaseWorkBuffer()239 void FxReverb::ReleaseWorkBuffer()
240 {
241 NW_NULL_ASSERT( m_pBuffer );
242 m_pBuffer = NULL;
243 }
244
GetRequiredMemSize()245 size_t FxReverb::GetRequiredMemSize()
246 {
247 const size_t bufSizeForEarlyReflection = sizeof(s32)
248 * ConvertMsecToSamples( RoundUpToMsecPerFrame( m_Param.m_EarlyReflectionTime ) );
249 const size_t bufSizeForPreDelay = sizeof(s32)
250 * ConvertMsecToSamples( RoundUpToMsecPerFrame( m_Param.m_PreDelayTime ) );
251 const size_t bufSizeForFilterComp0 = sizeof(s32) * m_FilterSize.m_Comb0;
252 const size_t bufSizeForFilterComp1 = sizeof(s32) * m_FilterSize.m_Comb1;
253 const size_t bufSizeForFilterAllPass = sizeof(s32) * m_FilterSize.m_AllPass;
254
255 size_t result = (
256 bufSizeForEarlyReflection +
257 bufSizeForPreDelay +
258 bufSizeForFilterComp0 +
259 bufSizeForFilterComp1 +
260 bufSizeForFilterAllPass ) * m_ProcessChannelCount;
261
262 result += 32;
263 // 特に制約は無いはずだが、32 バイト境界に置くため、+32 しておく。
264 return result;
265 }
266
AllocBuffer()267 void FxReverb::AllocBuffer()
268 {
269 NW_NULL_ASSERT( m_pBuffer );
270
271 const size_t bufSizeForEarlyReflection = sizeof(s32)
272 * ConvertMsecToSamples( RoundUpToMsecPerFrame( m_Param.m_EarlyReflectionTime ) );
273 const size_t bufSizeForPreDelay = sizeof(s32)
274 * ConvertMsecToSamples( RoundUpToMsecPerFrame( m_Param.m_PreDelayTime ) );
275 const size_t bufSizeForFilterComp0 = sizeof(s32) * m_FilterSize.m_Comb0;
276 const size_t bufSizeForFilterComp1 = sizeof(s32) * m_FilterSize.m_Comb1;
277 const size_t bufSizeForFilterAllPass = sizeof(s32) * m_FilterSize.m_AllPass;
278
279 uptr ptr = ut::RoundUp( m_pBuffer, 32 );
280 // NN_LOG("[%s] ptr(%p) m_pBuffer(%p) +bufSize(%p)\n",
281 // __FUNCTION__, ptr, m_pBuffer, m_pBuffer+m_BufferSize);
282 for ( int ch = 0; ch < m_ProcessChannelCount; ch++ )
283 {
284 m_WorkBuffer.m_EarlyReflection[ch] = reinterpret_cast<s32*>(ptr);
285 ptr += bufSizeForEarlyReflection;
286
287 m_WorkBuffer.m_PreDelay[ch] = reinterpret_cast<s32*>(ptr);
288 ptr += bufSizeForPreDelay;
289
290 m_WorkBuffer.m_CombFilter[ch][0] = reinterpret_cast<s32*>(ptr);
291 ptr += bufSizeForFilterComp0;
292
293 m_WorkBuffer.m_CombFilter[ch][1] = reinterpret_cast<s32*>(ptr);
294 ptr += bufSizeForFilterComp1;
295
296 m_WorkBuffer.m_AllPassFilter[ch] = reinterpret_cast<s32*>(ptr);
297 ptr += bufSizeForFilterAllPass;
298 }
299 // NN_LOG(" => ptr(%p)\n", ptr );
300 NW_ASSERT( ptr <= m_pBuffer + m_BufferSize );
301 }
302
FreeBuffer()303 void FxReverb::FreeBuffer()
304 {
305 for ( int ch = 0; ch < m_ProcessChannelCount; ch++ )
306 {
307 m_WorkBuffer.m_EarlyReflection[ch] = NULL;
308 m_WorkBuffer.m_PreDelay[ch] = NULL;
309 m_WorkBuffer.m_CombFilter[ch][0] = NULL;
310 m_WorkBuffer.m_CombFilter[ch][1] = NULL;
311 m_WorkBuffer.m_AllPassFilter[ch] = NULL;
312 }
313 }
314
InitializeParam()315 void FxReverb::InitializeParam()
316 {
317 // 初期反射音(One-Shotディレイ)
318 f32 early_time = RoundUpToMsecPerFrame( m_Param.m_EarlyReflectionTime );
319 m_EarlyLength = ConvertMsecToSamples( early_time );
320 m_EarlyPos = 0;
321
322 // 後置残響音
323
324 // プリディレイ
325 f32 pre_delay_time = RoundUpToMsecPerFrame( m_Param.m_PreDelayTime );
326 m_PreDelayLength = ConvertMsecToSamples( pre_delay_time );
327 m_PreDelayPos = 0;
328
329 // くし型フィルタ
330 f32 fused_time_sec = static_cast<f32>( m_Param.m_FusedTime ) / 1000.f;
331 NW_ASSERT( fused_time_sec != 0.f );
332
333 m_CombFilterLength[0] = static_cast<s32>( m_FilterSize.m_Comb0 );
334 m_CombFilterLength[1] = static_cast<s32>( m_FilterSize.m_Comb1 );
335
336 for ( s32 i = 0; i < 2; i++ )
337 {
338 m_CombFilterPos[i] = 0;
339
340 // 減衰時間から係数(フィードバックゲインへ変換)
341 f32 comb_coef = std::powf( 10.f,
342 (-3.f * static_cast<f32>(m_CombFilterLength[i]) /
343 (fused_time_sec * internal::driver::HardwareManager::FX_SAMPLE_RATE) ) );
344 m_CombFilterCoef[i] = static_cast<s32>( static_cast<f32>(0x80L) * comb_coef );
345 }
346
347 // 全域通過フィルタ
348 m_AllPassFilterLength = static_cast<s32>( m_FilterSize.m_AllPass );
349 m_AllPassFilterPos = 0;
350 f32 all_pass_coef = m_Param.m_Coloration;
351 m_AllPassFilterCoef = static_cast<s32>( static_cast<f32>(0x80L) * all_pass_coef );
352
353 // 各係数
354 m_EarlyGain = static_cast<s32>( static_cast<f32>(0x80L) * m_Param.m_EarlyGain );
355 m_FusedGain = static_cast<s32>( static_cast<f32>(0x80L) * m_Param.m_FusedGain );
356
357 f32 lpf_coef = m_Param.m_Damping;
358 if ( lpf_coef > 0.95f ) lpf_coef = 0.95f;
359 m_LpfCoef1 = static_cast<s32>( static_cast<s32>(0x80L) * ( 1.f - lpf_coef ) );
360 m_LpfCoef2 = static_cast<s32>( static_cast<s32>(0x80L) * lpf_coef );
361
362
363 // バッファを0クリア
364 std::memset( reinterpret_cast<void*>(m_pBuffer), 0, m_BufferSize );
365 }
366
UpdateBuffer(int numChannels,nn::snd::AuxBusData * data,s32 sampleLength,SampleFormat format,f32 sampleRate,OutputMode mode)367 void FxReverb::UpdateBuffer(
368 int numChannels,
369 nn::snd::AuxBusData* data,
370 s32 sampleLength,
371 SampleFormat format,
372 f32 sampleRate,
373 OutputMode mode )
374 {
375 NW_UNUSED_VARIABLE( sampleRate );
376 NW_UNUSED_VARIABLE( format );
377 NW_UNUSED_VARIABLE( mode );
378 NW_UNUSED_VARIABLE( numChannels );
379
380 if ( m_IsActive == false )
381 {
382 return;
383 }
384 NW_NULL_ASSERT( data );
385
386 s32* input[nn::snd::CHANNEL_INDEX_NUM];
387 input[nn::snd::CHANNEL_INDEX_FRONT_LEFT] = data->frontLeft;
388 input[nn::snd::CHANNEL_INDEX_FRONT_RIGHT] = data->frontRight;
389 input[nn::snd::CHANNEL_INDEX_REAR_LEFT] = data->rearLeft;
390 input[nn::snd::CHANNEL_INDEX_REAR_RIGHT] = data->rearRight;
391
392
393 //-----------------------------------------------------------------
394 // NOTE : 高速化のポイント
395 //
396 // ・固定小数計算に変更
397 // (浮動小数計算はレイテンシがあるので)
398 // ・メモリに置かれているデータを数回参照する箇所は、一度ローカル変数に
399 // 入れて使う
400 // (バスアクセスが遅いので、レジスタに移してから使った方が速い。
401 // 配列とメンバがこれに相当する)
402 // ・その他、処理の調整(式の変形など)
403 //-----------------------------------------------------------------
404 u32 early_pos = 0;
405 u32 pre_delay_pos = 0;
406 u32 comb_filter_pos0 = 0;
407 u32 comb_filter_pos1 = 0;
408 u32 allpass_filter_pos = 0;
409
410 for ( int ch = 0; ch < m_ProcessChannelCount; ch++ )
411 {
412 early_pos = m_EarlyPos;
413 pre_delay_pos = m_PreDelayPos;
414 comb_filter_pos0 = m_CombFilterPos[0];
415 comb_filter_pos1 = m_CombFilterPos[1];
416 allpass_filter_pos = m_AllPassFilterPos;
417
418 s32* early_reflection = m_WorkBuffer.m_EarlyReflection[ch];
419 s32* pre_delay = m_WorkBuffer.m_PreDelay[ch];
420
421 for ( s32 samp = 0; samp < sampleLength; samp++ )
422 {
423 s32 indata = input[ch][samp];
424
425 // 初期反射音
426 s32 early_out = early_reflection[early_pos] * m_EarlyGain;
427 // early_out >>= 7; // NOTE : 後で fused_out と纏めてシフトする
428 early_reflection[early_pos] = indata;
429
430
431 //
432 // 以下、後置残響音
433 //
434
435 // プリディレイ
436 s32 pre_delay_out = pre_delay[pre_delay_pos];
437 pre_delay[pre_delay_pos] = indata;
438
439 s32 filter_out = 0;
440
441
442 // くし型フィルタ (1段目)
443 s32* comb_line = m_WorkBuffer.m_CombFilter[ch][0];
444 s32 out_tmp = comb_line[comb_filter_pos0];
445
446 s32 comb_fb_0 = out_tmp;
447 if( comb_fb_0 < 0 )
448 {
449 s32 tmp = -comb_fb_0;
450 tmp = ( tmp * m_CombFilterCoef[0] ) >> 7;
451 comb_fb_0 = -tmp;
452 }
453 else
454 {
455 comb_fb_0 = ( comb_fb_0 * m_CombFilterCoef[0] ) >> 7;
456 }
457
458 comb_line[comb_filter_pos0] = pre_delay_out + comb_fb_0;
459 filter_out += out_tmp;
460
461
462 // くし型フィルタ (2段目)
463 comb_line = m_WorkBuffer.m_CombFilter[ch][1];
464 out_tmp = comb_line[comb_filter_pos1];
465
466 s32 comb_fb_1 = out_tmp;
467 if( comb_fb_1 < 0 )
468 {
469 s32 tmp = -comb_fb_1;
470 tmp = ( tmp * m_CombFilterCoef[1] ) >> 7;
471 comb_fb_1 = -tmp;
472 }
473 else
474 {
475 comb_fb_1 = ( comb_fb_1 * m_CombFilterCoef[1] ) >> 7;
476 }
477
478 comb_line[comb_filter_pos1] = pre_delay_out + comb_fb_1;
479 filter_out -= out_tmp; // 逆相にして、出力のリプルを抑える
480
481
482 // 全域通過フィルタ (1段)
483 s32* allpass_line = m_WorkBuffer.m_AllPassFilter[ch];
484 out_tmp = allpass_line[allpass_filter_pos];
485 s32 allpass_coef = m_AllPassFilterCoef;
486
487 s32 allpass_in = out_tmp;
488 if( allpass_in < 0 ){
489 s32 tmp = -allpass_in;
490 tmp = ( tmp * allpass_coef ) >> 7;
491 allpass_in = -tmp;
492 }
493 else
494 {
495 allpass_in = ( allpass_in * allpass_coef ) >> 7;
496 }
497 allpass_in += filter_out;
498
499 allpass_line[allpass_filter_pos] = allpass_in;
500
501
502 s32 fo_2 = allpass_in;
503 if( fo_2 < 0 )
504 {
505 s32 tmp = -fo_2;
506 tmp = ( tmp * allpass_coef ) >> 7;
507 fo_2 = -tmp;
508 }
509 else
510 {
511 fo_2 = ( fo_2 * allpass_coef ) >> 7;
512 }
513 filter_out = out_tmp - fo_2;
514
515
516 // 単極 LPF
517
518 // NOTE : 以下の変形で乗算を1つ減らせます
519 // また、m_LpfCoef1 が不要になります
520 // (m_LpfCoef1 は残してありますので、以下の方が良ければ
521 // 消して下さい)
522 //
523 // OUT = ( 1 - Coef ) * In + Coef * Histry
524 // = In - Coef * In + Coef * Histry
525 // = In - Coef * ( In + History )
526
527 s32 tmp = m_LpfCoef2 * ( filter_out + m_LastLpfOut[ch] );
528 tmp >>= 7;
529 s32 fused_out = filter_out - tmp;
530 m_LastLpfOut[ch] = fused_out;
531
532
533 // 出力
534 fused_out *= m_FusedGain;
535 fused_out += early_out;
536 fused_out >>= 7;
537 input[ch][samp] = fused_out;
538
539 ++early_pos;
540 ++pre_delay_pos;
541 ++comb_filter_pos0;
542 ++comb_filter_pos1;
543 ++allpass_filter_pos;
544 }
545 }
546
547 // バッファ位置をメモリに書き戻す
548 if ( early_pos >= m_EarlyLength )
549 {
550 m_EarlyPos = 0;
551 }
552 else
553 {
554 m_EarlyPos = early_pos;
555 }
556
557 if ( pre_delay_pos >= m_PreDelayLength )
558 {
559 m_PreDelayPos = 0;
560 }
561 else
562 {
563 m_PreDelayPos = pre_delay_pos;
564 }
565
566 if ( comb_filter_pos0 >= m_CombFilterLength[0] )
567 {
568 m_CombFilterPos[0] = 0;
569 }
570 else
571 {
572 m_CombFilterPos[0] = comb_filter_pos0;
573 }
574
575 if ( comb_filter_pos1 >= m_CombFilterLength[1] )
576 {
577 m_CombFilterPos[1] = 0;
578 }
579 else
580 {
581 m_CombFilterPos[1] = comb_filter_pos1;
582 }
583
584 if ( allpass_filter_pos >= m_AllPassFilterLength )
585 {
586 m_AllPassFilterPos = 0;
587 }
588 else
589 {
590 m_AllPassFilterPos = allpass_filter_pos;
591 }
592 }
593
594 } // namespace nw::snd
595 } // namespace nw
596
597