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