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