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