1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     snd_Util.h
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: 24979 $
14  *---------------------------------------------------------------------------*/
15 
16 #ifndef NW_SND_UTIL_H_
17 #define NW_SND_UTIL_H_
18 
19 #include <nn/types.h>
20 #include <nn/os.h>                          // nn::os::GetDeviceMemoryAddress
21 #include <nn/snd.h>
22 #include <nw/ut/ut_BinaryReader.h>          // ResU32 など
23 #include <nw/ut/ut_BinaryFileFormat.h>
24 #include <nw/ut/ut_Inlines.h>
25 #include <nw/ut/ut_Preprocessor.h>          // NW_DISALLOW_COPY_AND_ASSIGN
26 #include <nw/snd/snd_ItemType.h>
27 #include <nw/snd/snd_Global.h>
28 
29 // #define NW_SND_DEBUG_NOUSE_CTRSDK   // 有効なときは、nn::snd へのアクセスを遮断 (デバッグ用)
30 
31 #define NW_SND_ROUND_UP_32B(x) (((u32)(x) + 32 - 1) & ~(32 - 1))
32 
33 namespace nw {
34 namespace snd {
35 
36 class SoundArchive;
37 class SoundArchivePlayer;
38 
39 namespace internal {
40 
41 class Util
42 {
43 public:
44     static const int VOLUME_DB_MIN              = -904;     // -90.4dB = -inf
45     static const int VOLUME_DB_MAX              = 60;       // + 6.0dB
46 
47     static const int PITCH_DIVISION_BIT = 8; // 半音分解能.(ビット数)
48     static const int PITCH_DIVISION_RANGE       = 1 << PITCH_DIVISION_BIT;  // 半音分解能.
49 
50     // ------------------------------------------------------------------------
51     //  パン設定
52     // static const int PAN_CURVE_NUM = 3;
53     enum PanCurve
54     {
55         PAN_CURVE_SQRT,
56         PAN_CURVE_SINCOS,
57         PAN_CURVE_LINEAR
58     };
59     struct PanInfo
60     {
61         PanCurve curve;
62         bool centerZeroFlag;    // 中央で0dBにするかどうか
63         bool zeroClampFlag;     // 0dBを超えたときにClampするかどうか
PanInfoPanInfo64         PanInfo() : curve( PAN_CURVE_SQRT ), centerZeroFlag( false ), zeroClampFlag( false ) {}
65     };
66 
67 
68     static u16 CalcLpfFreq( f32 scale );
69     static f32 CalcPanRatio( f32 pan, const PanInfo& info );
70     static f32 CalcSurroundPanRatio( f32 surroundPan, const PanInfo& info );
71 
72     static f32 CalcPitchRatio( int pitch );
73     static f32 CalcVolumeRatio( f32 dB );
74     static u16 CalcRandom();
75 
76 #if 0
77     // デバッグ用 (3D サラウンドモード時の音割れ調整)
78     static f32 SetPanCurveMax( f32 max );   // 設定後の現在値を返す
79     static f32 GetPanCurveMax();
80     static f32 GetPanCurveMin();
81 #endif
82 
GetInterpolationType(SrcType type)83     static nn::snd::InterpolationType GetInterpolationType( SrcType type )
84     {
85         nn::snd::InterpolationType interpolationType = nn::snd::INTERPOLATION_TYPE_POLYPHASE;
86         switch ( type )
87         {
88             case SRC_TYPE_NONE:
89                 interpolationType = nn::snd::INTERPOLATION_TYPE_NONE;
90                 break;
91             case SRC_TYPE_LINEAR:
92                 interpolationType = nn::snd::INTERPOLATION_TYPE_LINEAR;
93                 break;
94             case SRC_TYPE_4TAP:
95             default:
96                 break;
97         }
98         return interpolationType;
99     }
100 
101     static unsigned long GetSampleByByte( unsigned long byte, SampleFormat format );
102     static unsigned long GetByteBySample( unsigned long sample, SampleFormat format );
103 
104 private:
105     // CalcLpfFreq Table
106     static const int CALC_LPF_FREQ_TABLE_SIZE   = 24;
107     static const f32 CALC_LPF_FREQ_INTERCEPT;
108     static const u16 CalcLpfFreqTable[ CALC_LPF_FREQ_TABLE_SIZE ];
109 
110 public:
111     template< typename ITEM_TYPE, typename COUNT_TYPE=nw::ut::ResU32 >
112     struct Table
113     {
114         COUNT_TYPE count;
115         ITEM_TYPE item[ 1 ];
116     };
117 
118     struct Reference
119     {
120         nw::ut::ResU16  typeId;     // snd_ElementType.h で定義されているタイプ
121         u16             padding;
122         nw::ut::ResS32  offset;     // INVALID_OFFSET が入っていたら 無効値
123 
124         static const s32 INVALID_OFFSET = -1;
125 
IsValidTypeIdReference126         NW_INLINE bool IsValidTypeId( u16 validId ) const
127         {
128             if ( validId == typeId )
129             {
130                 return true;
131             }
132             return false;
133         }
IsValidOffsetReference134         NW_INLINE bool IsValidOffset() const
135         {
136             if ( offset != INVALID_OFFSET )
137             {
138                 return true;
139             }
140             return false;
141         }
142     };
143 
144     struct ReferenceWithSize : public Reference
145     {
146         nw::ut::ResU32  size;
147     };
148 
149     struct ReferenceTable : public Table<Reference>
150     {
GetReferedItemReferenceTable151         const void* GetReferedItem( u32 index ) const
152         {
153             if ( index >= count ) return NULL;
154             return ut::AddOffsetToPtr( this, item[ index ].offset );
155         }
GetReferedItemReferenceTable156         const void* GetReferedItem( u32 index, u16 typeId ) const
157         {
158             if ( index >= count ) return NULL;
159             if ( item[ index ].typeId != typeId ) return NULL;
160             return ut::AddOffsetToPtr( this, item[ index ].offset );
161         }
FindReferedItemByReferenceTable162         const void* FindReferedItemBy( u16 typeId ) const
163         {
164             for ( u32 i = 0; i < count; i++ )
165             {
166                 if ( item[ i ].IsValidTypeId( typeId ) )
167                 {
168                     return ut::AddOffsetToPtr( this, item[i].offset );
169                 }
170             }
171             return NULL;
172         }
173     };
174 
175     struct ReferenceWithSizeTable : public Table<ReferenceWithSize>
176     {
GetReferedItemReferenceWithSizeTable177         const void* GetReferedItem( u32 index ) const
178         {
179             NW_ASSERT( index < count );
180             return ut::AddOffsetToPtr( this, item[ index ].offset );
181         }
GetReferedItemByReferenceWithSizeTable182         const void* GetReferedItemBy( u16 typeId ) const
183         {
184             for ( u32 i = 0; i < count; i++ )
185             {
186                 if ( item[ i ].IsValidTypeId( typeId ) )
187                 {
188                     return ut::AddOffsetToPtr( this, item[i].offset );
189                 }
190             }
191             return NULL;
192         }
GetReferedItemSizeReferenceWithSizeTable193         u32 GetReferedItemSize( u32 index ) const
194         {
195             NW_ASSERT( index < count );
196             return item[ index ].size;
197         }
198     };
199 
200     // ブロック参照テーブル
201     // (通常の Util::ReferenceTable ではないので、
202     //  count やオフセット起点を外からもらう必要がある)
203     struct BlockReferenceTable
204     {
205         // データ
206         ReferenceWithSize item[ 1 ];
207 
208         // アクセサ
GetReferedItemByIndexBlockReferenceTable209         NW_INLINE const void* GetReferedItemByIndex(
210                 const void* origin, int index, u16 count ) const
211         {
212             NW_UNUSED_VARIABLE(count);
213             NW_ASSERT( index < count );
214             return ut::AddOffsetToPtr( origin, item[ index ].offset );
215         }
GetReferenceBlockReferenceTable216         NW_INLINE const ReferenceWithSize* GetReference(
217                 u16 typeId, u16 count ) const
218         {
219             for ( int i = 0; i < count; i++ )
220             {
221                 if ( item[ i ].IsValidTypeId( typeId ) )
222                 {
223                     return &item[ i ];
224                 }
225             }
226             return NULL;
227         }
GetReferedItemBlockReferenceTable228         const void* GetReferedItem( const void* origin, u16 typeId, u16 count ) const
229         {
230             const ReferenceWithSize* ref = GetReference( typeId, count );
231             if ( ref == NULL ) return NULL;
232             if ( ref->offset == 0 ) return NULL;
233             return ut::AddOffsetToPtr( origin, ref->offset );
234         }
GetReferedItemSizeBlockReferenceTable235         u32 GetReferedItemSize( u16 typeId, u16 count ) const
236         {
237             const ReferenceWithSize* ref = GetReference( typeId, count );
238             if ( ref == NULL ) return 0;
239             return ref->size;
240         }
GetReferedItemOffsetBlockReferenceTable241         u32 GetReferedItemOffset( u16 typeId, u16 count ) const
242         {
243             const ReferenceWithSize* ref = GetReference( typeId, count );
244             if ( ref == NULL ) return 0;
245             return ref->offset;
246         }
247     };
248 
249     // サウンドファイルの共通ヘッダー
250     struct SoundFileHeader
251     {
252         ut::BinaryFileHeader header;
253         BlockReferenceTable blockReferenceTable;
254 
GetBlockCountSoundFileHeader255         NW_INLINE s32 GetBlockCount() const { return header.dataBlocks; }
256 
257     protected:
GetBlockSoundFileHeader258         NW_INLINE const void* GetBlock( u16 typeId ) const
259         {
260             return blockReferenceTable.GetReferedItem( this, typeId, header.dataBlocks );
261         }
GetBlockSizeSoundFileHeader262         NW_INLINE u32 GetBlockSize( u16 typeId ) const
263         {
264             return blockReferenceTable.GetReferedItemSize( typeId, header.dataBlocks );
265         }
GetBlockOffsetSoundFileHeader266         NW_INLINE u32 GetBlockOffset( u16 typeId ) const
267         {
268             return blockReferenceTable.GetReferedItemOffset( typeId, header.dataBlocks );
269         }
270     };
271 
272     // オプションパラメータ
273     struct BitFlag
274     {
275         nw::ut::ResU32  bitFlag;
276 
277     public:
278         // binNumber で指定したビットの指す値を value に格納します。
279         // ビットが false なら false を返します。
GetValueBitFlag280         bool GetValue( u32* value, u32 bitNumber ) const
281         {
282             u32 count = GetTrueCount( bitNumber );
283 
284             // bitNumber 番目のビットが無効だった場合
285             if ( count == 0 ) return false;
286 
287             *value = *reinterpret_cast<const nw::ut::ResU32*>(
288                     ut::AddOffsetToPtr( this, ( count * sizeof(nw::ut::ResU32) ) ) );
289             return true;
290         }
GetValueF32BitFlag291         bool GetValueF32( f32* value, u32 bitNumber ) const
292         {
293             u32 count = GetTrueCount( bitNumber );
294             if ( count == 0 ) return false;
295             *value = *reinterpret_cast<const nw::ut::ResF32*>(
296                     ut::AddOffsetToPtr( this, ( count * sizeof(nw::ut::ResF32) ) ) );
297             return true;
298         }
299 
300     private:
301         // アクセサ
302         //
303         //  bitNumber   - 「何」ビット目をチェックしたいか? 0 からスタート。
304         //  return      - bitNumber ビットが 1 なら、下位から数えていくつめの有効フラグか
305         //                を返す。
306         //
307         //  例: カンタンのため、4 bit の bitFlag とする。
308         //      bitFlag = 1001 [2進] のとき、
309         //      GetTrueCount( 0 ) => 1
310         //      GetTrueCount( 1 ) => 0
311         //      GetTrueCount( 2 ) => 0
312         //      GetTrueCount( 3 ) => 2
313         //      GetTrueCount( 4 ) => ASSERT
314         static const int BIT_NUMBER_MAX = 31;
GetTrueCountBitFlag315         NW_INLINE u32 GetTrueCount( u32 bitNumber ) const
316         {
317             NW_ASSERT( bitNumber <= BIT_NUMBER_MAX );
318 
319             bool ret = false;   // bitNumber ビット目が有効かどうか
320             int count = 0;
321             for ( u32 i = 0; i <= bitNumber; i++ )
322             {
323                 if ( bitFlag & ( 0x1 << i ) )
324                 {
325                     count++;
326                     if ( i == bitNumber )
327                     {
328                         ret = true;
329                     }
330                 }
331             }
332 
333             if ( ret )
334             {
335                 return count;
336             }
337             else
338             {
339                 return 0;
340             }
341         }
342     };
343 
344     // BitFlag を扱うときに使う便利関数
DevideBy8bit(u32 value,int index)345     static NW_INLINE u8 DevideBy8bit( u32 value, int index )
346     {
347         return static_cast<u8>( ( value >> (8*index) ) & 0xff );
348     }
349 
DevideBy16bit(u32 value,int index)350     static NW_INLINE u16 DevideBy16bit( u32 value, int index )
351     {
352         return static_cast<u16>( ( value >> (16*index) ) & 0xffff );
353     }
354 
355     // ID が warcID の波形アーカイブの index 番目の波形アドレスを返します。
356     // 未ロードの場合、NULL が返ります。
357     static const void* GetWaveFile(
358             u32 waveArchiveId,
359             u32 waveIndex,
360             const SoundArchive& arc,
361             const SoundArchivePlayer& player );
362 
363 
GetItemType(u32 id)364     static NW_INLINE ItemType GetItemType( u32 id )
365     {
366         // id == SoundArchive::ItemId を想定。
367         // 上位 8 bit がアイテムタイプ (nw::snd::internal::ItemType) に相当する
368         return static_cast<ItemType>( id >> 24 );
369     }
370 
GetItemIndex(u32 id)371     static NW_INLINE u32 GetItemIndex( u32 id )
372     {
373         // id == SoundArchive::ItemId を想定。
374         // 下位 24 bit がアイテムインデックスに相当する
375         return ( id & 0x00ffffff );
376     }
377 
GetMaskedItemId(u32 id,internal::ItemType type)378     static NW_INLINE u32 GetMaskedItemId( u32 id, internal::ItemType type )
379     {
380         return ( id | ( type << 24 ) );
381     }
382 
383     struct WaveId
384     {
385         ut::ResU32 waveArchiveId;   // 波形アーカイブ ID
386         ut::ResU32 waveIndex;       // 波形アーカイブ内インデックス
387     };
388 
389     struct WaveIdTable
390     {
391         // データ
392         Table<WaveId> table;
393 
394         // アクセサ
GetWaveIdWaveIdTable395         const WaveId& GetWaveId( u32 index ) const
396         {
397             NW_ASSERT( index < table.count );
398             return table.item[ index ];
399         }
GetCountWaveIdTable400         NW_INLINE u32 GetCount() const { return table.count; }
401     };
402 
403 
404     /*
405        継承可能なシングルトンクラス
406     */
407     template <class CHILD>
408     class Singleton
409     {
410     public:
GetInstance()411         static CHILD& GetInstance()
412         {
413             static CHILD instance;
414             return instance;
415         }
416     };
417 
418     // デバッグ用処理時間計測ルーチン (規定回数分ためてプリントします)
419     static void CalcTick();
420 
IsDeviceMemory(uptr memory,size_t size)421     static bool IsDeviceMemory( uptr memory, size_t size )
422     {
423       #ifdef NW_PLATFORM_CTRWIN
424         return true;
425       #else
426         const uptr head = nn::os::GetDeviceMemoryAddress();
427         const uptr tail = head + nn::os::GetDeviceMemorySize();
428         if ( head <= memory && (memory + size) <= tail )
429         {
430             return true;
431         }
432         return false;
433       #endif
434     }
435 
436     class AutoStopWatch
437     {
438     public:
439       #ifdef NW_PLATFORM_CTRWIN
AutoStopWatch(OSTick & tick)440         AutoStopWatch( OSTick& tick ) : m_Tick( tick )
441         {
442             m_TmpTick = OS_GetTick();
443         }
~AutoStopWatch()444         ~AutoStopWatch()
445         {
446             m_Tick = static_cast<OSTick>( OS_DiffTick( OS_GetTick(), m_TmpTick ) );
447         }
448       #else
449         AutoStopWatch( nn::os::Tick& tick ) : m_Tick( tick )
450         {
451             m_TmpTick = nn::os::Tick::GetSystemCurrent();
452         }
453         ~AutoStopWatch()
454         {
455             m_Tick = nn::os::Tick::GetSystemCurrent() - m_TmpTick;
456         }
457       #endif
458     private:
459       #ifdef NW_PLATFORM_CTRWIN
460         OSTick& m_Tick;
461         OSTick  m_TmpTick;
462       #else
463         nn::os::Tick& m_Tick;
464         nn::os::Tick  m_TmpTick;
465       #endif
466     };
467 
468     // 負荷計測補助。ピーク値を DEFAULT_HOLD_FRAME だけ保存する。
469     template<typename T>
470     class PeakHoldValue
471     {
472     public:
PeakHoldValue()473         PeakHoldValue() : m_CurrentValue(0), m_PeakValue(0) {}
Update(T value)474         void Update( T value )
475         {
476             m_CurrentValue = value;
477             if ( m_PeakHoldCounter > 0 )
478             {
479                 --m_PeakHoldCounter;
480             }
481             if ( m_PeakHoldCounter == 0 )
482             {
483                 m_PeakValue = 0;
484             }
485             if ( m_PeakValue < m_CurrentValue )
486             {
487                 m_PeakValue = m_CurrentValue;
488                 m_PeakHoldCounter = DEFAULT_HOLD_FRAME;
489             }
490         }
GetValue()491         T GetValue() const { return m_CurrentValue; }
GetPeakValue()492         T GetPeakValue() const { return m_PeakValue; }
493     private:
494         static const int DEFAULT_HOLD_FRAME = 20;   // NW4R は 75
495         T m_CurrentValue;
496         T m_PeakValue;
497         int m_PeakHoldCounter;
498     };
499 
500     // 負荷計測補助。サウンド再生時は値が揺れやすいので、ヒストグラムをつくることにした。
501     // 全サンプルのうち「負荷○○%以下」におさまっているのは、何%かを出力する。
502     class PerfHistogram
503     {
504     public:
505         // コンストラクタ
PerfHistogram()506         PerfHistogram() { Reset(); }
507 
508         // 負荷%値の設定
SetLoad(f32 load)509         void SetLoad( f32 load )
510         {
511             // 異常な値
512             if ( load < 0.0f || load > 100.0f )
513             {
514                 m_IrregularCount += 1;
515             }
516 
517             // 最大値更新
518             if ( load > m_MaxLoad )
519             {
520                 m_MaxLoad = load;
521             }
522 
523             int loadIndex = static_cast<int>( load );
524             if ( loadIndex < LOAD_COUNT_NUM )
525             {
526                 m_LoadCount[ loadIndex ] += 1;
527             }
528             m_Count += 1;
529             m_SumLoad += load;
530         }
531 
532         // 処理負荷 load % 以下は、全体の何%か?
GetPercentage(int load)533         f32 GetPercentage( int load )
534         {
535             int count = 0;
536             for ( int i = 0; i <= load; i++ )
537             {
538                 count += m_LoadCount[ i ];
539             }
540             f32 ret = 100.f * count / m_Count;
541             return ret;
542         }
543 
544         // 全体の percent %のなかで、最大の処理負荷はいくらか?
GetMaxLoadByPercent(f32 percent)545         int GetMaxLoadByPercent( f32 percent ) const
546         {
547             int sum = 0;
548             int maxLoad = 0;
549             for ( int i = 0; i < LOAD_COUNT_NUM; i++ )
550             {
551                 sum += m_LoadCount[ i ];
552                 if ( 100.f * sum / m_Count > percent )
553                 {
554                     maxLoad = i;
555                     break;
556                 }
557             }
558             return maxLoad;
559         }
560 
GetAverage()561         f32 GetAverage() const { return m_SumLoad / m_Count; }
GetMaxLoad()562         f32 GetMaxLoad() const { return m_MaxLoad; }
GetCount()563         int GetCount() const { return m_Count; }
GetIrregularCount()564         int GetIrregularCount() const { return m_IrregularCount; }
565 
566         // リセット
Reset()567         void Reset()
568         {
569             m_MaxLoad = m_SumLoad = 0.0;
570             m_Count = m_IrregularCount = 0;
571             std::memset( m_LoadCount, 0x00, sizeof(m_LoadCount) );
572         }
573 
574     private:
575         static int const LOAD_COUNT_NUM = 101; // 0-100 まで合計 101 個に分ける
576         f32 m_MaxLoad;
577         f32 m_SumLoad;
578         int m_LoadCount[ LOAD_COUNT_NUM ];
579         int m_Count, m_IrregularCount;
580     };
581 };
582 
583 
584 } // namespace nw::snd::internal
585 } // namespace nw::snd
586 } // namespace nw
587 
588 
589 #endif /* NW_SND_UTIL_H_ */
590 
591