/*---------------------------------------------------------------------------* Project: NintendoWare File: snd_Util.h Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 27889 $ *---------------------------------------------------------------------------*/ #ifndef NW_SND_UTIL_H_ #define NW_SND_UTIL_H_ #include #include // nn::os::GetDeviceMemoryAddress #include #include // ResU32 など #include #include #include // NW_DISALLOW_COPY_AND_ASSIGN #include #include // #define NW_SND_DEBUG_NOUSE_CTRSDK // 有効なときは、nn::snd へのアクセスを遮断 (デバッグ用) #define NW_SND_ROUND_UP_32B(x) (((u32)(x) + 32 - 1) & ~(32 - 1)) namespace nw { namespace snd { class SoundArchive; class SoundArchivePlayer; namespace internal { class SoundArchiveLoader; class PlayerHeapDataManager; struct LoadItemInfo; class Util { public: static const int VOLUME_DB_MIN = -904; // -90.4dB = -inf static const int VOLUME_DB_MAX = 60; // + 6.0dB static const int PITCH_DIVISION_BIT = 8; // 半音分解能.(ビット数) static const int PITCH_DIVISION_RANGE = 1 << PITCH_DIVISION_BIT; // 半音分解能. // ------------------------------------------------------------------------ // パン設定 // static const int PAN_CURVE_NUM = 3; enum PanCurve { PAN_CURVE_SQRT, PAN_CURVE_SINCOS, PAN_CURVE_LINEAR }; struct PanInfo { PanCurve curve; bool centerZeroFlag; // 中央で0dBにするかどうか bool zeroClampFlag; // 0dBを超えたときにClampするかどうか PanInfo() : curve( PAN_CURVE_SQRT ), centerZeroFlag( false ), zeroClampFlag( false ) {} }; static u16 CalcLpfFreq( f32 scale ); static f32 CalcPanRatio( f32 pan, const PanInfo& info ); static f32 CalcSurroundPanRatio( f32 surroundPan, const PanInfo& info ); static f32 CalcPitchRatio( int pitch ); static f32 CalcVolumeRatio( f32 dB ); static u16 CalcRandom(); #if 0 // デバッグ用 (3D サラウンドモード時の音割れ調整) static f32 SetPanCurveMax( f32 max ); // 設定後の現在値を返す static f32 GetPanCurveMax(); static f32 GetPanCurveMin(); #endif static nn::snd::InterpolationType GetInterpolationType( SrcType type ) { nn::snd::InterpolationType interpolationType = nn::snd::INTERPOLATION_TYPE_POLYPHASE; switch ( type ) { case SRC_TYPE_NONE: interpolationType = nn::snd::INTERPOLATION_TYPE_NONE; break; case SRC_TYPE_LINEAR: interpolationType = nn::snd::INTERPOLATION_TYPE_LINEAR; break; case SRC_TYPE_4TAP: default: break; } return interpolationType; } static unsigned long GetSampleByByte( unsigned long byte, SampleFormat format ); static unsigned long GetByteBySample( unsigned long sample, SampleFormat format ); private: // CalcLpfFreq Table static const int CALC_LPF_FREQ_TABLE_SIZE = 24; static const f32 CALC_LPF_FREQ_INTERCEPT; static const u16 CalcLpfFreqTable[ CALC_LPF_FREQ_TABLE_SIZE ]; public: template< typename ITEM_TYPE, typename COUNT_TYPE=nw::ut::ResU32 > struct Table { COUNT_TYPE count; ITEM_TYPE item[ 1 ]; }; struct Reference { nw::ut::ResU16 typeId; // snd_ElementType.h で定義されているタイプ u16 padding; nw::ut::ResS32 offset; // INVALID_OFFSET が入っていたら 無効値 static const s32 INVALID_OFFSET = -1; NW_INLINE bool IsValidTypeId( u16 validId ) const { if ( validId == typeId ) { return true; } return false; } NW_INLINE bool IsValidOffset() const { if ( offset != INVALID_OFFSET ) { return true; } return false; } }; struct ReferenceWithSize : public Reference { nw::ut::ResU32 size; }; struct ReferenceTable : public Table { const void* GetReferedItem( u32 index ) const { if ( index >= count ) return NULL; return ut::AddOffsetToPtr( this, item[ index ].offset ); } const void* GetReferedItem( u32 index, u16 typeId ) const { if ( index >= count ) return NULL; if ( item[ index ].typeId != typeId ) return NULL; return ut::AddOffsetToPtr( this, item[ index ].offset ); } const void* FindReferedItemBy( u16 typeId ) const { for ( u32 i = 0; i < count; i++ ) { if ( item[ i ].IsValidTypeId( typeId ) ) { return ut::AddOffsetToPtr( this, item[i].offset ); } } return NULL; } }; struct ReferenceWithSizeTable : public Table { const void* GetReferedItem( u32 index ) const { NW_ASSERT( index < count ); return ut::AddOffsetToPtr( this, item[ index ].offset ); } const void* GetReferedItemBy( u16 typeId ) const { for ( u32 i = 0; i < count; i++ ) { if ( item[ i ].IsValidTypeId( typeId ) ) { return ut::AddOffsetToPtr( this, item[i].offset ); } } return NULL; } u32 GetReferedItemSize( u32 index ) const { NW_ASSERT( index < count ); return item[ index ].size; } }; // ブロック参照テーブル // (通常の Util::ReferenceTable ではないので、 // count やオフセット起点を外からもらう必要がある) struct BlockReferenceTable { // データ ReferenceWithSize item[ 1 ]; // アクセサ NW_INLINE const void* GetReferedItemByIndex( const void* origin, int index, u16 count ) const { NW_UNUSED_VARIABLE(count); NW_ASSERT( index < count ); return ut::AddOffsetToPtr( origin, item[ index ].offset ); } NW_INLINE const ReferenceWithSize* GetReference( u16 typeId, u16 count ) const { for ( int i = 0; i < count; i++ ) { if ( item[ i ].IsValidTypeId( typeId ) ) { return &item[ i ]; } } return NULL; } const void* GetReferedItem( const void* origin, u16 typeId, u16 count ) const { const ReferenceWithSize* ref = GetReference( typeId, count ); if ( ref == NULL ) return NULL; if ( ref->offset == 0 ) return NULL; return ut::AddOffsetToPtr( origin, ref->offset ); } u32 GetReferedItemSize( u16 typeId, u16 count ) const { const ReferenceWithSize* ref = GetReference( typeId, count ); if ( ref == NULL ) return 0; return ref->size; } u32 GetReferedItemOffset( u16 typeId, u16 count ) const { const ReferenceWithSize* ref = GetReference( typeId, count ); if ( ref == NULL ) return 0; return ref->offset; } }; // サウンドファイルの共通ヘッダー struct SoundFileHeader { ut::BinaryFileHeader header; BlockReferenceTable blockReferenceTable; NW_INLINE s32 GetBlockCount() const { return header.dataBlocks; } protected: NW_INLINE const void* GetBlock( u16 typeId ) const { return blockReferenceTable.GetReferedItem( this, typeId, header.dataBlocks ); } NW_INLINE u32 GetBlockSize( u16 typeId ) const { return blockReferenceTable.GetReferedItemSize( typeId, header.dataBlocks ); } NW_INLINE u32 GetBlockOffset( u16 typeId ) const { return blockReferenceTable.GetReferedItemOffset( typeId, header.dataBlocks ); } }; // オプションパラメータ struct BitFlag { nw::ut::ResU32 bitFlag; public: // binNumber で指定したビットの指す値を value に格納します。 // ビットが false なら false を返します。 bool GetValue( u32* value, u32 bitNumber ) const { u32 count = GetTrueCount( bitNumber ); // bitNumber 番目のビットが無効だった場合 if ( count == 0 ) return false; *value = *reinterpret_cast( ut::AddOffsetToPtr( this, ( count * sizeof(nw::ut::ResU32) ) ) ); return true; } bool GetValueF32( f32* value, u32 bitNumber ) const { u32 count = GetTrueCount( bitNumber ); if ( count == 0 ) return false; *value = *reinterpret_cast( ut::AddOffsetToPtr( this, ( count * sizeof(nw::ut::ResF32) ) ) ); return true; } private: // アクセサ // // bitNumber - 「何」ビット目をチェックしたいか? 0 からスタート。 // return - bitNumber ビットが 1 なら、下位から数えていくつめの有効フラグか // を返す。 // // 例: カンタンのため、4 bit の bitFlag とする。 // bitFlag = 1001 [2進] のとき、 // GetTrueCount( 0 ) => 1 // GetTrueCount( 1 ) => 0 // GetTrueCount( 2 ) => 0 // GetTrueCount( 3 ) => 2 // GetTrueCount( 4 ) => ASSERT static const int BIT_NUMBER_MAX = 31; NW_INLINE u32 GetTrueCount( u32 bitNumber ) const { NW_ASSERT( bitNumber <= BIT_NUMBER_MAX ); bool ret = false; // bitNumber ビット目が有効かどうか int count = 0; for ( u32 i = 0; i <= bitNumber; i++ ) { if ( bitFlag & ( 0x1 << i ) ) { count++; if ( i == bitNumber ) { ret = true; } } } if ( ret ) { return count; } else { return 0; } } }; // BitFlag を扱うときに使う便利関数 static NW_INLINE u8 DevideBy8bit( u32 value, int index ) { return static_cast( ( value >> (8*index) ) & 0xff ); } static NW_INLINE u16 DevideBy16bit( u32 value, int index ) { return static_cast( ( value >> (16*index) ) & 0xffff ); } // ID が warcID の波形アーカイブの index 番目の波形アドレスを返します。 // 未ロードの場合、NULL が返ります。 static const void* GetWaveFile( u32 waveArchiveId, u32 waveIndex, const SoundArchive& arc, const SoundArchivePlayer& player ); static const void* GetWaveFile( u32 waveArchiveId, u32 waveIndex, const SoundArchive& arc, const PlayerHeapDataManager* mgr ); static bool IsLoadedWaveArchive( const void* bankFile, const SoundArchive& arc, const SoundArchiveLoader& mgr ); static bool IsLoadedWaveArchive( const void* wsdFile, u32 index, // bcwsd 内でいくつめのウェーブサウンドか? const SoundArchive& arc, const SoundArchiveLoader& mgr ); static NW_INLINE ItemType GetItemType( u32 id ) { // id == SoundArchive::ItemId を想定。 // 上位 8 bit がアイテムタイプ (nw::snd::internal::ItemType) に相当する return static_cast( id >> 24 ); } static NW_INLINE u32 GetItemIndex( u32 id ) { // id == SoundArchive::ItemId を想定。 // 下位 24 bit がアイテムインデックスに相当する return ( id & 0x00ffffff ); } static NW_INLINE u32 GetMaskedItemId( u32 id, internal::ItemType type ) { return ( id | ( type << 24 ) ); } struct WaveId { ut::ResU32 waveArchiveId; // 波形アーカイブ ID ut::ResU32 waveIndex; // 波形アーカイブ内インデックス }; struct WaveIdTable { // データ Table table; // アクセサ const WaveId& GetWaveId( u32 index ) const { NW_ASSERT( index < table.count ); return table.item[ index ]; } NW_INLINE u32 GetCount() const { return table.count; } }; /* 継承可能なシングルトンクラス */ template class Singleton { public: static CHILD& GetInstance() { static CHILD instance; return instance; } }; // デバッグ用処理時間計測ルーチン (規定回数分ためてプリントします) static void CalcTick(); static bool IsDeviceMemory( uptr memory, size_t size ) { #ifdef NW_PLATFORM_CTRWIN return true; #else const uptr head = nn::os::GetDeviceMemoryAddress(); const uptr tail = head + nn::os::GetDeviceMemorySize(); if ( head <= memory && (memory + size) <= tail ) { return true; } return false; #endif } class AutoStopWatch { public: #ifdef NW_PLATFORM_CTRWIN AutoStopWatch( OSTick& tick ) : m_Tick( tick ) { m_TmpTick = OS_GetTick(); } ~AutoStopWatch() { m_Tick = static_cast( OS_DiffTick( OS_GetTick(), m_TmpTick ) ); } #else AutoStopWatch( nn::os::Tick& tick ) : m_Tick( tick ) { m_TmpTick = nn::os::Tick::GetSystemCurrent(); } ~AutoStopWatch() { m_Tick = nn::os::Tick::GetSystemCurrent() - m_TmpTick; } #endif private: #ifdef NW_PLATFORM_CTRWIN OSTick& m_Tick; OSTick m_TmpTick; #else nn::os::Tick& m_Tick; nn::os::Tick m_TmpTick; #endif }; // 負荷計測補助。ピーク値を DEFAULT_HOLD_FRAME だけ保存する。 template class PeakHoldValue { public: PeakHoldValue() : m_CurrentValue(0), m_PeakValue(0) {} void Update( T value ) { m_CurrentValue = value; if ( m_PeakHoldCounter > 0 ) { --m_PeakHoldCounter; } if ( m_PeakHoldCounter == 0 ) { m_PeakValue = 0; } if ( m_PeakValue < m_CurrentValue ) { m_PeakValue = m_CurrentValue; m_PeakHoldCounter = DEFAULT_HOLD_FRAME; } } T GetValue() const { return m_CurrentValue; } T GetPeakValue() const { return m_PeakValue; } private: static const int DEFAULT_HOLD_FRAME = 20; // NW4R は 75 T m_CurrentValue; T m_PeakValue; int m_PeakHoldCounter; }; // 負荷計測補助。サウンド再生時は値が揺れやすいので、ヒストグラムをつくることにした。 // 全サンプルのうち「負荷○○%以下」におさまっているのは、何%かを出力する。 class PerfHistogram { public: // コンストラクタ PerfHistogram() { Reset(); } // 負荷%値の設定 void SetLoad( f32 load ) { // 異常な値 if ( load < 0.0f || load > 100.0f ) { m_IrregularCount += 1; } // 最大値更新 if ( load > m_MaxLoad ) { m_MaxLoad = load; } int loadIndex = static_cast( load ); if ( loadIndex < LOAD_COUNT_NUM ) { m_LoadCount[ loadIndex ] += 1; } m_Count += 1; m_SumLoad += load; } // 処理負荷 load % 以下は、全体の何%か? f32 GetPercentage( int load ) { int count = 0; for ( int i = 0; i <= load; i++ ) { count += m_LoadCount[ i ]; } f32 ret = 100.f * count / m_Count; return ret; } // 全体の percent %のなかで、最大の処理負荷はいくらか? int GetMaxLoadByPercent( f32 percent ) const { int sum = 0; int maxLoad = 0; for ( int i = 0; i < LOAD_COUNT_NUM; i++ ) { sum += m_LoadCount[ i ]; if ( 100.f * sum / m_Count > percent ) { maxLoad = i; break; } } return maxLoad; } f32 GetAverage() const { return m_SumLoad / m_Count; } f32 GetMaxLoad() const { return m_MaxLoad; } int GetCount() const { return m_Count; } int GetIrregularCount() const { return m_IrregularCount; } // リセット void Reset() { m_MaxLoad = m_SumLoad = 0.0; m_Count = m_IrregularCount = 0; std::memset( m_LoadCount, 0x00, sizeof(m_LoadCount) ); } private: static int const LOAD_COUNT_NUM = 101; // 0-100 まで合計 101 個に分ける f32 m_MaxLoad; f32 m_SumLoad; int m_LoadCount[ LOAD_COUNT_NUM ]; int m_Count, m_IrregularCount; }; }; } // namespace nw::snd::internal } // namespace nw::snd } // namespace nw #endif /* NW_SND_UTIL_H_ */