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