1 /*---------------------------------------------------------------------------*
2 Project: NintendoWare
3 File: snd_Sound3DCalculator.cpp
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: $
16 *---------------------------------------------------------------------------*/
17
18 #include "precompiled.h"
19
20 #include <nw/snd/snd_Sound3DCalculator.h>
21
22 #include <nw/snd/snd_Sound3DManager.h> // nw::snd::Sound3DParam
23 #include <nw/snd/snd_Sound3DListener.h>
24
25 namespace nw {
26 namespace snd {
27
28 /* ------------------------------------------------------------------------
29 inline function
30 ------------------------------------------------------------------------ */
31 namespace {
32
33 // (x1,y1) (x2,y2) の2点を通る線形関数において、xに対応するyの値を求める
SolveLinerFunction(f32 x,f32 x1,f32 x2,f32 y1,f32 y2)34 inline f32 SolveLinerFunction( f32 x, f32 x1, f32 x2, f32 y1, f32 y2 )
35 {
36 if ( x1 == x2 ) return ( y1 + y2 ) / 2.0f;
37 f32 divider = x1 - x2;
38 return x * ( y1 - y2 ) / divider + ( x1 * y2 - x2 * y1 ) / divider;
39 }
40
41 }
42
43 /* ------------------------------------------------------------------------
44 member function
45 ------------------------------------------------------------------------ */
46
47 /*---------------------------------------------------------------------------*
48 Name: CalcVolumeAndPriority
49
50 Description: 3Dサウンドの音量と優先度を計算する
51
52 Arguments: manager - 3Dサウンドマネージャー
53 listener - 3Dサウンドリスナー
54 actorParam - 3Dサウンドアクターのパラメータ構造体
55 volumePtr - 計算した音量を格納するポインタ
56 priorityPtr - 計算した優先度を格納するポインタ
57
58 Returns: なし
59 *---------------------------------------------------------------------------*/
CalcVolumeAndPriority(const Sound3DManager & manager,const Sound3DListener & listener,const Sound3DParam & actorParam,f32 * volumePtr,int * priorityPtr)60 void Sound3DCalculator::CalcVolumeAndPriority(
61 const Sound3DManager& manager,
62 const Sound3DListener& listener,
63 const Sound3DParam& actorParam,
64 f32* volumePtr,
65 int* priorityPtr
66 )
67 {
68 //------------------------------------------------------------------
69 // リスナーから見たアクターの位置を取得
70 nw::math::VEC3 pos;
71 nw::math::VEC3Sub( &pos, &actorParam.position, &listener.GetPosition() );
72 const f32 actorDistance = nw::math::VEC3Len( &pos );
73
74 CalcVolumeAndPriorityImpl(
75 actorDistance,
76 static_cast<SoundArchive::Sound3DInfo::DecayCurve>(actorParam.decayCurve),
77 actorParam.decayRatio,
78 manager.GetMaxPriorityReduction(),
79 listener.GetMaxVolumeDistance(),
80 listener.GetUnitDistance(),
81 volumePtr,
82 priorityPtr
83 );
84 }
85
86 /*---------------------------------------------------------------------------*
87 Name: CalcPan
88
89 Description: 3Dサウンドのパンを計算する
90
91 Arguments: manager - 3Dサウンドマネージャー
92 listener - 3Dサウンドリスナー
93 actorParam - 3Dサウンドアクターのパラメータ構造体
94 calcPanParam - パン計算に使用する構造体
95 panPtr - 計算したパンを格納するポインタ
96 spanPtr - 計算したサラウンドパンを格納するポインタ
97
98 Returns: なし
99 *---------------------------------------------------------------------------*/
CalcPan(const Sound3DManager & manager,const Sound3DListener & listener,const Sound3DParam & actorParam,const CalcPanParam & calcPanParam,f32 * panPtr,f32 * spanPtr)100 void Sound3DCalculator::CalcPan(
101 const Sound3DManager& manager,
102 const Sound3DListener& listener,
103 const Sound3DParam& actorParam,
104 const CalcPanParam& calcPanParam,
105 f32* panPtr,
106 f32* spanPtr
107 )
108 {
109 //------------------------------------------------------------------
110 // リスナーから見たアクターの位置を取得
111 const math::MTX34& listenerMtx = listener.GetMatrix();
112 nw::math::VEC3 pos;
113 nw::math::VEC3Transform( &pos, &listenerMtx, &actorParam.position );
114 const f32 actorDistance = nw::math::VEC3Len( &pos );
115
116 CalcPanImpl(
117 pos,
118 listener.GetInteriorSize(),
119 actorDistance,
120 manager.GetPanRange(),
121 calcPanParam.stereoSpeakerAngle,
122 calcPanParam.surroundSpeakerFrontAngle,
123 calcPanParam.surroundSpeakerRearAngle,
124 calcPanParam.surroundPanOffset,
125 panPtr,
126 spanPtr
127 );
128 }
129
130 /*---------------------------------------------------------------------------*
131 Name: CalcPitch
132
133 Description: 3Dサウンドの音程を計算する
134
135 Arguments: manager - 3Dサウンドマネージャー
136 listener - 3Dサウンドリスナー
137 actorParam - 3Dサウンドアクターのパラメータ構造体
138 pitchPtr - 計算した音程を格納するポインタ
139
140 Returns: なし
141 *---------------------------------------------------------------------------*/
CalcPitch(const Sound3DManager & manager,const Sound3DListener & listener,const Sound3DParam & actorParam,f32 * pitchPtr)142 void Sound3DCalculator::CalcPitch(
143 const Sound3DManager& manager,
144 const Sound3DListener& listener,
145 const Sound3DParam& actorParam,
146 f32* pitchPtr
147 )
148 {
149 NW_NULL_ASSERT( pitchPtr );
150
151 const f32 sonicVelocity = manager.GetSonicVelocity();
152 if ( sonicVelocity == 0.0f ) {
153 *pitchPtr = 1.0f;
154 return;
155 }
156
157 //------------------------------------------------------------------
158 // リスナーから見たアクターの位置を取得
159 nw::math::VEC3 relPos;
160 nw::math::VEC3Sub( &relPos, &actorParam.position, &listener.GetPosition() );
161 const f32 distance = nw::math::VEC3Len( &relPos );
162 if ( distance > 0.0f ) {
163 relPos /= distance;
164 }
165
166 const f32 dopplerFactor = actorParam.dopplerFactor / 32.0f;
167
168 f32 actorVelocity;
169 f32 listenerVelocity;
170 if ( distance > 0.0f ) {
171 actorVelocity = - nw::math::VEC3Dot( &relPos, &actorParam.velocity );
172 listenerVelocity = - nw::math::VEC3Dot( &relPos, &listener.GetVelocity() );
173 }
174 else {
175 actorVelocity = - nw::math::VEC3Len( &actorParam.velocity );
176 listenerVelocity = nw::math::VEC3Len( &listener.GetVelocity() );
177 }
178 actorVelocity *= dopplerFactor;
179 listenerVelocity *= dopplerFactor;
180
181 f32 pitch;
182 if ( listenerVelocity > sonicVelocity ) {
183 pitch = 0.0f;
184 }
185 else if ( actorVelocity >= sonicVelocity ) {
186 pitch = 65535.0f;
187 }
188 else {
189 pitch = ( sonicVelocity - listenerVelocity ) / ( sonicVelocity - actorVelocity );
190 }
191
192 *pitchPtr = pitch;
193 }
194
195 /*---------------------------------------------------------------------------*
196 Name: CalcBiquadValue
197
198 Description: Biquadフィルタの値を計算する
199
200 Arguments: manager - 3Dサウンドマネージャー
201 listener - 3Dサウンドリスナー
202 actorParam - 3Dサウンドアクターのパラメータ構造体
203 pitchPtr - 計算したBiquadフィルタ値を格納するポインタ
204
205 Returns: なし
206 *---------------------------------------------------------------------------*/
CalcBiquadFilterValue(const Sound3DManager & manager,const Sound3DListener & listener,const Sound3DParam & actorParam,f32 * biquadFilterValuePtr)207 void Sound3DCalculator::CalcBiquadFilterValue(
208 const Sound3DManager& manager,
209 const Sound3DListener& listener,
210 const Sound3DParam& actorParam,
211 f32* biquadFilterValuePtr
212 )
213 {
214 NW_NULL_ASSERT( biquadFilterValuePtr );
215
216 NW_UNUSED_VARIABLE( manager );
217
218 //------------------------------------------------------------------
219 // リスナーから見たアクターの位置を取得
220 nw::math::VEC3 pos;
221 nw::math::VEC3Sub( &pos, &actorParam.position, &listener.GetPosition() );
222 const f32 actorDistance = nw::math::VEC3Len( &pos );
223
224 // Biquadフィルタの値はボリューム距離に追従
225 f32 biquadFilterValue = 0.0f;
226 f32 maxVolumeDistance = listener.GetMaxVolumeDistance();
227 f32 maxBiquadFilterValue = listener.GetMaxBiquadFilterValue();
228
229 if( actorDistance > maxVolumeDistance )
230 {
231 biquadFilterValue =
232 ( actorDistance - maxVolumeDistance ) /
233 listener.GetUnitDistance() *
234 listener.GetUnitBiquadFilterValue();
235
236 if ( biquadFilterValue > maxBiquadFilterValue )
237 {
238 biquadFilterValue = maxBiquadFilterValue;
239 }
240 }
241
242 *biquadFilterValuePtr = biquadFilterValue;
243 }
244
CalcVolumeAndPriorityImpl(f32 actorDistance,SoundArchive::Sound3DInfo::DecayCurve decayCurve,f32 decayRatio,int maxPriorityReduction,f32 maxVolumeDistance,f32 unitDistance,f32 * volumePtr,int * priorityPtr)245 void Sound3DCalculator::CalcVolumeAndPriorityImpl(
246 f32 actorDistance,
247 SoundArchive::Sound3DInfo::DecayCurve decayCurve,
248 f32 decayRatio,
249 int maxPriorityReduction,
250 f32 maxVolumeDistance,
251 f32 unitDistance,
252 f32* volumePtr,
253 int* priorityPtr
254 )
255 {
256 NW_NULL_ASSERT( volumePtr );
257 NW_NULL_ASSERT( priorityPtr );
258
259 static const f32 MAX_VOLUME = 1.0f;
260 f32 volume = MAX_VOLUME;
261
262 if ( actorDistance > maxVolumeDistance )
263 {
264 switch ( decayCurve )
265 {
266 case DECAY_CURVE_LOG:
267 volume = std::powf( decayRatio, ( actorDistance - maxVolumeDistance ) / unitDistance );
268 break;
269 case DECAY_CURVE_LINEAR:
270 volume = 1.0f - ( actorDistance - maxVolumeDistance ) / unitDistance * ( 1.0f - decayRatio );
271 if ( volume < 0.0f ) volume = 0.0f;
272 break;
273 }
274 }
275
276 *volumePtr = volume;
277 *priorityPtr = - static_cast<int>( ( 1.0f - volume ) * maxPriorityReduction );
278 }
279
CalcPanImpl(const nw::math::VEC3 & pos,f32 interiorSize,f32 actorDistance,f32 panRange,f32 stereoSpeakerAngle,f32 surroundSpeakerFrontAngle,f32 surroundSpeakerRearAngle,f32 surroundPanOffset,f32 * panPtr,f32 * spanPtr)280 void Sound3DCalculator::CalcPanImpl(
281 const nw::math::VEC3& pos,
282 f32 interiorSize,
283 f32 actorDistance,
284 f32 panRange,
285 f32 stereoSpeakerAngle,
286 f32 surroundSpeakerFrontAngle,
287 f32 surroundSpeakerRearAngle,
288 f32 surroundPanOffset,
289 f32* panPtr,
290 f32* spanPtr
291 )
292 {
293 #if 0
294 switch ( SoundSystem::GetOutputMode() )
295 {
296 #if 0 // TODO: 3D Surround
297 case OUTPUT_MODE_DPL2:
298 case OUTPUT_MODE_SURROUND:
299 CalcPanSurround(
300 pos,
301 interiorSize,
302 actorDistance,
303 panRange,
304 surroundSpeakerFrontAngle,
305 surroundSpeakerRearAngle,
306 surroundPanOffset,
307 panPtr,
308 spanPtr
309 );
310 break;
311 #endif
312 case OUTPUT_MODE_STEREO:
313 CalcPanStereo(
314 pos,
315 interiorSize,
316 actorDistance,
317 panRange,
318 speakerAngleStereo,
319 panPtr,
320 spanPtr
321 );
322 break;
323 case OUTPUT_MODE_MONO:
324 default:
325 *panPtr = 0.0f;
326 *spanPtr = 0.0f;
327 break;
328 }
329 #else
330 // TORIAEZU 必ずサラウンドで計算する
331 NW_UNUSED_VARIABLE(stereoSpeakerAngle);
332 CalcPanSurround( pos, interiorSize, actorDistance, panRange,
333 surroundSpeakerFrontAngle, surroundSpeakerRearAngle,
334 surroundPanOffset, panPtr, spanPtr );
335 #endif
336 }
337
CalcPanSurround(const nw::math::VEC3 & pos,f32 interiorSize,f32 actorDistance,f32 panRange,f32 surroundSpeakerFrontAngle,f32 surroundSpeakerRearAngle,f32 surroundPanOffset,f32 * panPtr,f32 * surroundPanPtr)338 void Sound3DCalculator::CalcPanSurround(
339 const nw::math::VEC3& pos,
340 f32 interiorSize,
341 f32 actorDistance,
342 f32 panRange,
343 f32 surroundSpeakerFrontAngle,
344 f32 surroundSpeakerRearAngle,
345 f32 surroundPanOffset,
346 f32* panPtr,
347 f32* surroundPanPtr
348 )
349 {
350 NW_NULL_ASSERT( panPtr );
351 NW_NULL_ASSERT( surroundPanPtr );
352
353 // アクターの角度と距離を求める
354 f32 angle;
355 f32 distance;
356 CalcAngleAndDistance(
357 pos,
358 actorDistance,
359 interiorSize,
360 &angle,
361 &distance
362 );
363
364 NW_FMINMAX_ASSERT( surroundSpeakerFrontAngle, 0.0f, nw::math::F_PI / 2.0f );
365 NW_FMINMAX_ASSERT( surroundSpeakerRearAngle, nw::math::F_PI / 2.0f, nw::math::F_PI );
366
367 f32 x = 0.0f;
368 f32 z = 0.0f;
369
370 // スピーカーの角度をパン座標の角度に変換する
371 const f32 angleRearLeft = -surroundSpeakerRearAngle;
372 const f32 angleFrontLeft = -surroundSpeakerFrontAngle;
373 const f32 angleFrontRight = surroundSpeakerFrontAngle;
374 const f32 angleRearRight = surroundSpeakerRearAngle;
375
376 if ( angle < angleRearLeft )
377 {
378 x = SolveLinerFunction( angle, -nw::math::F_PI, angleRearLeft, 0.0f, -1.0f );
379 z = 1.0f;
380 }
381 else if ( angle < -nw::math::F_PI / 2.0f )
382 {
383 x = -1.0f;
384 z = SolveLinerFunction( angle, angleRearLeft, -nw::math::F_PI / 2.0f, 1.0f, 0.0f );
385 }
386 else if ( angle < angleFrontLeft )
387 {
388 x = -1.0f;
389 z = SolveLinerFunction( angle, -nw::math::F_PI / 2.0f, angleFrontLeft, 0.0f, -1.0f );
390 }
391 else if ( angle < angleFrontRight )
392 {
393 x = SolveLinerFunction( angle, angleFrontLeft, angleFrontRight, -1.0f, 1.0f );
394 z = -1.0f;
395 }
396 else if ( angle < nw::math::F_PI / 2.0f )
397 {
398 x = 1.0f;
399 z = SolveLinerFunction( angle, angleFrontRight, nw::math::F_PI / 2.0f, -1.0f, 0.0f );
400 }
401 else if ( angle < angleRearRight )
402 {
403 x = 1.0f;
404 z = SolveLinerFunction( angle, nw::math::F_PI / 2.0f, angleRearRight, 0.0f, 1.0f );
405 }
406 else
407 {
408 x = SolveLinerFunction( angle, angleRearRight, nw::math::F_PI, 1.0f, 0.0f );
409 z = 1.0f;
410 }
411
412 // 原点の補正
413 f32 a = ( std::cosf( surroundSpeakerFrontAngle ) + std::cosf( surroundSpeakerRearAngle ) ) / 2.0f;
414 f32 speakerOffset = a / ( a + ( -std::cosf( surroundSpeakerRearAngle ) ) );
415
416 const f32 center_x = 0.0f;
417 const f32 center_z = speakerOffset;
418
419 // パンを設定する
420 x *= panRange;
421 z *= panRange;
422 *panPtr = x * distance + center_x * ( 1.0f - distance );
423 *surroundPanPtr = z * distance + center_z * ( 1.0f - distance ) + 1.0f + surroundPanOffset;
424 }
425
426 #if 0 // 不要かも
427 void Sound3DCalculator::CalcPanStereo(
428 const nw::math::VEC3& pos,
429 f32 interiorSize,
430 f32 actorDistance,
431 f32 panRange,
432 f32 speakerAngleStereo,
433 f32* panPtr,
434 f32* spanPtr
435 )
436 {
437 NW_NULL_ASSERT( panPtr );
438 // NW_NULL_ASSERT( spanPtr ); // TODO:
439 NW_UNUSED_VARIABLE( spanPtr );
440
441 // アクターの角度と距離を求める
442 f32 angle;
443 f32 distance;
444 CalcAngleAndDistance(
445 pos,
446 actorDistance,
447 interiorSize,
448 &angle,
449 &distance
450 );
451
452 NW_FMINMAX_ASSERT( speakerAngleStereo, 0.0f, nw::math::F_PI / 2.0f );
453
454 f32 x = 0.0f;
455
456 const f32 angleRearLeft = - nw::math::F_PI + speakerAngleStereo;
457 const f32 angleFrontLeft = -speakerAngleStereo;
458 const f32 angleFrontRight = speakerAngleStereo;
459 const f32 angleRearRight = nw::math::F_PI - speakerAngleStereo;
460
461 if ( angle < angleRearLeft )
462 {
463 x = SolveLinerFunction( angle, -nw::math::F_PI, angleRearLeft, 0.0f, -1.0f );
464 }
465 else if ( angle < angleFrontLeft )
466 {
467 x = -1.0f;
468 }
469 else if ( angle < angleFrontRight )
470 {
471 x = SolveLinerFunction( angle, angleFrontLeft, angleFrontRight, -1.0f, 1.0f );
472 }
473 else if ( angle < angleRearRight )
474 {
475 x = 1.0f;
476 }
477 else
478 {
479 x = SolveLinerFunction( angle, angleRearRight, nw::math::F_PI, 1.0f, 0.0f );
480 }
481
482 // パンを設定する
483 x *= panRange;
484 *panPtr = x * distance;
485 // *spanPtr = 0.0f; // TODO:
486 }
487 #endif
488
CalcAngleAndDistance(const nw::math::VEC3 & pos,f32 actorDistance,f32 interiorSize,f32 * anglePtr,f32 * distancePtr)489 void Sound3DCalculator::CalcAngleAndDistance(
490 const nw::math::VEC3& pos,
491 f32 actorDistance,
492 f32 interiorSize,
493 f32* anglePtr,
494 f32* distancePtr
495 )
496 {
497 NW_NULL_ASSERT( anglePtr );
498 NW_NULL_ASSERT( distancePtr );
499
500 nw::math::VEC3 interiorPos;
501
502 if ( actorDistance == 0.0f )
503 {
504 interiorPos.x = interiorPos.y = interiorPos.z = 0.0f;
505 }
506 else
507 {
508 nw::math::VEC3 yPlanePoint( pos.x, 0, pos.z );
509 f32 d = nw::math::VEC3Len( &yPlanePoint );
510 if ( d > interiorSize )
511 {
512 // 音源がインテリアサイズを超えた場合はインテリアサイズ内に音源を置く
513 yPlanePoint.x *= interiorSize / d;
514 yPlanePoint.z *= interiorSize / d;
515 }
516
517 f32 yPlaneDistance = nw::math::VEC3Len( &yPlanePoint );
518 interiorPos.x = pos.x * yPlaneDistance / actorDistance;
519 interiorPos.y = 0;
520 interiorPos.z = pos.z * yPlaneDistance / actorDistance;
521 }
522
523 *anglePtr = std::atan2f( interiorPos.x, -interiorPos.z );
524 *distancePtr = nw::math::VEC3Len( &interiorPos ) / interiorSize;
525 }
526
527 } // namespace nw::snd
528 } // namespace nw
529
530