/*---------------------------------------------------------------------------* Project: NintendoWare File: snd_Sound3DCalculator.cpp Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. 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. The content herein is highly confidential and should be handled accordingly. $Revision: $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include // nw::snd::Sound3DParam #include namespace nw { namespace snd { /* ------------------------------------------------------------------------ inline function ------------------------------------------------------------------------ */ namespace { // (x1,y1) (x2,y2) の2点を通る線形関数において、xに対応するyの値を求める inline f32 SolveLinerFunction( f32 x, f32 x1, f32 x2, f32 y1, f32 y2 ) { if ( x1 == x2 ) return ( y1 + y2 ) / 2.0f; f32 divider = x1 - x2; return x * ( y1 - y2 ) / divider + ( x1 * y2 - x2 * y1 ) / divider; } } /* ------------------------------------------------------------------------ member function ------------------------------------------------------------------------ */ /*---------------------------------------------------------------------------* Name: CalcVolumeAndPriority Description: 3Dサウンドの音量と優先度を計算する Arguments: manager - 3Dサウンドマネージャー listener - 3Dサウンドリスナー actorParam - 3Dサウンドアクターのパラメータ構造体 volumePtr - 計算した音量を格納するポインタ priorityPtr - 計算した優先度を格納するポインタ Returns: なし *---------------------------------------------------------------------------*/ void Sound3DCalculator::CalcVolumeAndPriority( const Sound3DManager& manager, const Sound3DListener& listener, const Sound3DParam& actorParam, f32* volumePtr, int* priorityPtr ) { //------------------------------------------------------------------ // リスナーから見たアクターの位置を取得 nw::math::VEC3 pos; nw::math::VEC3Sub( &pos, &actorParam.position, &listener.GetPosition() ); const f32 actorDistance = nw::math::VEC3Len( &pos ); CalcVolumeAndPriorityImpl( actorDistance, static_cast(actorParam.decayCurve), actorParam.decayRatio, manager.GetMaxPriorityReduction(), listener.GetMaxVolumeDistance(), listener.GetUnitDistance(), volumePtr, priorityPtr ); } /*---------------------------------------------------------------------------* Name: CalcPan Description: 3Dサウンドのパンを計算する Arguments: manager - 3Dサウンドマネージャー listener - 3Dサウンドリスナー actorParam - 3Dサウンドアクターのパラメータ構造体 calcPanParam - パン計算に使用する構造体 panPtr - 計算したパンを格納するポインタ spanPtr - 計算したサラウンドパンを格納するポインタ Returns: なし *---------------------------------------------------------------------------*/ void Sound3DCalculator::CalcPan( const Sound3DManager& manager, const Sound3DListener& listener, const Sound3DParam& actorParam, const CalcPanParam& calcPanParam, f32* panPtr, f32* spanPtr ) { //------------------------------------------------------------------ // リスナーから見たアクターの位置を取得 const math::MTX34& listenerMtx = listener.GetMatrix(); nw::math::VEC3 pos; nw::math::VEC3Transform( &pos, &listenerMtx, &actorParam.position ); const f32 actorDistance = nw::math::VEC3Len( &pos ); CalcPanImpl( pos, listener.GetInteriorSize(), actorDistance, manager.GetPanRange(), calcPanParam.stereoSpeakerAngle, calcPanParam.surroundSpeakerFrontAngle, calcPanParam.surroundSpeakerRearAngle, calcPanParam.surroundPanOffset, panPtr, spanPtr ); } /*---------------------------------------------------------------------------* Name: CalcPitch Description: 3Dサウンドの音程を計算する Arguments: manager - 3Dサウンドマネージャー listener - 3Dサウンドリスナー actorParam - 3Dサウンドアクターのパラメータ構造体 pitchPtr - 計算した音程を格納するポインタ Returns: なし *---------------------------------------------------------------------------*/ void Sound3DCalculator::CalcPitch( const Sound3DManager& manager, const Sound3DListener& listener, const Sound3DParam& actorParam, f32* pitchPtr ) { NW_NULL_ASSERT( pitchPtr ); const f32 sonicVelocity = manager.GetSonicVelocity(); if ( sonicVelocity == 0.0f ) { *pitchPtr = 1.0f; return; } //------------------------------------------------------------------ // リスナーから見たアクターの位置を取得 nw::math::VEC3 relPos; nw::math::VEC3Sub( &relPos, &actorParam.position, &listener.GetPosition() ); const f32 distance = nw::math::VEC3Len( &relPos ); if ( distance > 0.0f ) { relPos /= distance; } const f32 dopplerFactor = actorParam.dopplerFactor / 32.0f; f32 actorVelocity; f32 listenerVelocity; if ( distance > 0.0f ) { actorVelocity = - nw::math::VEC3Dot( &relPos, &actorParam.velocity ); listenerVelocity = - nw::math::VEC3Dot( &relPos, &listener.GetVelocity() ); } else { actorVelocity = - nw::math::VEC3Len( &actorParam.velocity ); listenerVelocity = nw::math::VEC3Len( &listener.GetVelocity() ); } actorVelocity *= dopplerFactor; listenerVelocity *= dopplerFactor; f32 pitch; if ( listenerVelocity > sonicVelocity ) { pitch = 0.0f; } else if ( actorVelocity >= sonicVelocity ) { pitch = 65535.0f; } else { pitch = ( sonicVelocity - listenerVelocity ) / ( sonicVelocity - actorVelocity ); } *pitchPtr = pitch; } /*---------------------------------------------------------------------------* Name: CalcBiquadValue Description: Biquadフィルタの値を計算する Arguments: manager - 3Dサウンドマネージャー listener - 3Dサウンドリスナー actorParam - 3Dサウンドアクターのパラメータ構造体 pitchPtr - 計算したBiquadフィルタ値を格納するポインタ Returns: なし *---------------------------------------------------------------------------*/ void Sound3DCalculator::CalcBiquadFilterValue( const Sound3DManager& manager, const Sound3DListener& listener, const Sound3DParam& actorParam, f32* biquadFilterValuePtr ) { NW_NULL_ASSERT( biquadFilterValuePtr ); NW_UNUSED_VARIABLE( manager ); //------------------------------------------------------------------ // リスナーから見たアクターの位置を取得 nw::math::VEC3 pos; nw::math::VEC3Sub( &pos, &actorParam.position, &listener.GetPosition() ); const f32 actorDistance = nw::math::VEC3Len( &pos ); // Biquadフィルタの値はボリューム距離に追従 f32 biquadFilterValue = 0.0f; f32 maxVolumeDistance = listener.GetMaxVolumeDistance(); f32 maxBiquadFilterValue = listener.GetMaxBiquadFilterValue(); if( actorDistance > maxVolumeDistance ) { biquadFilterValue = ( actorDistance - maxVolumeDistance ) / listener.GetUnitDistance() * listener.GetUnitBiquadFilterValue(); if ( biquadFilterValue > maxBiquadFilterValue ) { biquadFilterValue = maxBiquadFilterValue; } } *biquadFilterValuePtr = biquadFilterValue; } void Sound3DCalculator::CalcVolumeAndPriorityImpl( f32 actorDistance, SoundArchive::Sound3DInfo::DecayCurve decayCurve, f32 decayRatio, int maxPriorityReduction, f32 maxVolumeDistance, f32 unitDistance, f32* volumePtr, int* priorityPtr ) { NW_NULL_ASSERT( volumePtr ); NW_NULL_ASSERT( priorityPtr ); static const f32 MAX_VOLUME = 1.0f; f32 volume = MAX_VOLUME; if ( actorDistance > maxVolumeDistance ) { switch ( decayCurve ) { case DECAY_CURVE_LOG: volume = std::powf( decayRatio, ( actorDistance - maxVolumeDistance ) / unitDistance ); break; case DECAY_CURVE_LINEAR: volume = 1.0f - ( actorDistance - maxVolumeDistance ) / unitDistance * ( 1.0f - decayRatio ); if ( volume < 0.0f ) volume = 0.0f; break; } } *volumePtr = volume; *priorityPtr = - static_cast( ( 1.0f - volume ) * maxPriorityReduction ); } void Sound3DCalculator::CalcPanImpl( const nw::math::VEC3& pos, f32 interiorSize, f32 actorDistance, f32 panRange, f32 stereoSpeakerAngle, f32 surroundSpeakerFrontAngle, f32 surroundSpeakerRearAngle, f32 surroundPanOffset, f32* panPtr, f32* spanPtr ) { #if 0 switch ( SoundSystem::GetOutputMode() ) { #if 0 // TODO: 3D Surround case OUTPUT_MODE_DPL2: case OUTPUT_MODE_SURROUND: CalcPanSurround( pos, interiorSize, actorDistance, panRange, surroundSpeakerFrontAngle, surroundSpeakerRearAngle, surroundPanOffset, panPtr, spanPtr ); break; #endif case OUTPUT_MODE_STEREO: CalcPanStereo( pos, interiorSize, actorDistance, panRange, speakerAngleStereo, panPtr, spanPtr ); break; case OUTPUT_MODE_MONO: default: *panPtr = 0.0f; *spanPtr = 0.0f; break; } #else // TORIAEZU 必ずサラウンドで計算する NW_UNUSED_VARIABLE(stereoSpeakerAngle); CalcPanSurround( pos, interiorSize, actorDistance, panRange, surroundSpeakerFrontAngle, surroundSpeakerRearAngle, surroundPanOffset, panPtr, spanPtr ); #endif } void Sound3DCalculator::CalcPanSurround( const nw::math::VEC3& pos, f32 interiorSize, f32 actorDistance, f32 panRange, f32 surroundSpeakerFrontAngle, f32 surroundSpeakerRearAngle, f32 surroundPanOffset, f32* panPtr, f32* surroundPanPtr ) { NW_NULL_ASSERT( panPtr ); NW_NULL_ASSERT( surroundPanPtr ); // アクターの角度と距離を求める f32 angle; f32 distance; CalcAngleAndDistance( pos, actorDistance, interiorSize, &angle, &distance ); NW_FMINMAX_ASSERT( surroundSpeakerFrontAngle, 0.0f, nw::math::F_PI / 2.0f ); NW_FMINMAX_ASSERT( surroundSpeakerRearAngle, nw::math::F_PI / 2.0f, nw::math::F_PI ); f32 x = 0.0f; f32 z = 0.0f; // スピーカーの角度をパン座標の角度に変換する const f32 angleRearLeft = -surroundSpeakerRearAngle; const f32 angleFrontLeft = -surroundSpeakerFrontAngle; const f32 angleFrontRight = surroundSpeakerFrontAngle; const f32 angleRearRight = surroundSpeakerRearAngle; if ( angle < angleRearLeft ) { x = SolveLinerFunction( angle, -nw::math::F_PI, angleRearLeft, 0.0f, -1.0f ); z = 1.0f; } else if ( angle < -nw::math::F_PI / 2.0f ) { x = -1.0f; z = SolveLinerFunction( angle, angleRearLeft, -nw::math::F_PI / 2.0f, 1.0f, 0.0f ); } else if ( angle < angleFrontLeft ) { x = -1.0f; z = SolveLinerFunction( angle, -nw::math::F_PI / 2.0f, angleFrontLeft, 0.0f, -1.0f ); } else if ( angle < angleFrontRight ) { x = SolveLinerFunction( angle, angleFrontLeft, angleFrontRight, -1.0f, 1.0f ); z = -1.0f; } else if ( angle < nw::math::F_PI / 2.0f ) { x = 1.0f; z = SolveLinerFunction( angle, angleFrontRight, nw::math::F_PI / 2.0f, -1.0f, 0.0f ); } else if ( angle < angleRearRight ) { x = 1.0f; z = SolveLinerFunction( angle, nw::math::F_PI / 2.0f, angleRearRight, 0.0f, 1.0f ); } else { x = SolveLinerFunction( angle, angleRearRight, nw::math::F_PI, 1.0f, 0.0f ); z = 1.0f; } // 原点の補正 f32 a = ( std::cosf( surroundSpeakerFrontAngle ) + std::cosf( surroundSpeakerRearAngle ) ) / 2.0f; f32 speakerOffset = a / ( a + ( -std::cosf( surroundSpeakerRearAngle ) ) ); const f32 center_x = 0.0f; const f32 center_z = speakerOffset; // パンを設定する x *= panRange; z *= panRange; *panPtr = x * distance + center_x * ( 1.0f - distance ); *surroundPanPtr = z * distance + center_z * ( 1.0f - distance ) + 1.0f + surroundPanOffset; } #if 0 // 不要かも void Sound3DCalculator::CalcPanStereo( const nw::math::VEC3& pos, f32 interiorSize, f32 actorDistance, f32 panRange, f32 speakerAngleStereo, f32* panPtr, f32* spanPtr ) { NW_NULL_ASSERT( panPtr ); // NW_NULL_ASSERT( spanPtr ); // TODO: NW_UNUSED_VARIABLE( spanPtr ); // アクターの角度と距離を求める f32 angle; f32 distance; CalcAngleAndDistance( pos, actorDistance, interiorSize, &angle, &distance ); NW_FMINMAX_ASSERT( speakerAngleStereo, 0.0f, nw::math::F_PI / 2.0f ); f32 x = 0.0f; const f32 angleRearLeft = - nw::math::F_PI + speakerAngleStereo; const f32 angleFrontLeft = -speakerAngleStereo; const f32 angleFrontRight = speakerAngleStereo; const f32 angleRearRight = nw::math::F_PI - speakerAngleStereo; if ( angle < angleRearLeft ) { x = SolveLinerFunction( angle, -nw::math::F_PI, angleRearLeft, 0.0f, -1.0f ); } else if ( angle < angleFrontLeft ) { x = -1.0f; } else if ( angle < angleFrontRight ) { x = SolveLinerFunction( angle, angleFrontLeft, angleFrontRight, -1.0f, 1.0f ); } else if ( angle < angleRearRight ) { x = 1.0f; } else { x = SolveLinerFunction( angle, angleRearRight, nw::math::F_PI, 1.0f, 0.0f ); } // パンを設定する x *= panRange; *panPtr = x * distance; // *spanPtr = 0.0f; // TODO: } #endif void Sound3DCalculator::CalcAngleAndDistance( const nw::math::VEC3& pos, f32 actorDistance, f32 interiorSize, f32* anglePtr, f32* distancePtr ) { NW_NULL_ASSERT( anglePtr ); NW_NULL_ASSERT( distancePtr ); nw::math::VEC3 interiorPos; if ( actorDistance == 0.0f ) { interiorPos.x = interiorPos.y = interiorPos.z = 0.0f; } else { nw::math::VEC3 yPlanePoint( pos.x, 0, pos.z ); f32 d = nw::math::VEC3Len( &yPlanePoint ); if ( d > interiorSize ) { // 音源がインテリアサイズを超えた場合はインテリアサイズ内に音源を置く yPlanePoint.x *= interiorSize / d; yPlanePoint.z *= interiorSize / d; } f32 yPlaneDistance = nw::math::VEC3Len( &yPlanePoint ); interiorPos.x = pos.x * yPlaneDistance / actorDistance; interiorPos.y = 0; interiorPos.z = pos.z * yPlaneDistance / actorDistance; } *anglePtr = std::atan2f( interiorPos.x, -interiorPos.z ); *distancePtr = nw::math::VEC3Len( &interiorPos ) / interiorSize; } } // namespace nw::snd } // namespace nw