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