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