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