the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2
3#include "SoundEngine.h"
4#include "..\Consoles_App.h"
5#include "..\..\MultiplayerLocalPlayer.h"
6#include "..\..\..\Minecraft.World\net.minecraft.world.level.h"
7#include "..\..\Minecraft.World\leveldata.h"
8#include "..\..\Minecraft.World\mth.h"
9#include "..\..\TexturePackRepository.h"
10#include "..\..\DLCTexturePack.h"
11#include "Common\DLC\DLCAudioFile.h"
12
13#ifdef __PSVITA__
14#include <audioout.h>
15#endif
16
17#ifdef _WINDOWS64
18#include "..\..\Minecraft.Client\Windows64\Windows64_App.h"
19#include "..\..\Minecraft.Client\Windows64\Miles\include\imssapi.h"
20#endif
21
22#ifdef __ORBIS__
23#include <audioout.h>
24//#define __DISABLE_MILES__ // MGH disabled for now as it crashes if we call sceNpMatching2Initialize
25#endif
26
27// take out Orbis until they are done
28#if defined _XBOX
29
30SoundEngine::SoundEngine() {}
31void SoundEngine::init(Options *pOptions)
32{
33}
34
35void SoundEngine::tick(shared_ptr<Mob> *players, float a)
36{
37}
38void SoundEngine::destroy() {}
39void SoundEngine::play(int iSound, float x, float y, float z, float volume, float pitch)
40{
41 app.DebugPrintf("PlaySound - %d\n",iSound);
42}
43void SoundEngine::playStreaming(const wstring& name, float x, float y , float z, float volume, float pitch, bool bMusicDelay) {}
44void SoundEngine::playUI(int iSound, float volume, float pitch) {}
45
46void SoundEngine::updateMusicVolume(float fVal) {}
47void SoundEngine::updateSoundEffectVolume(float fVal) {}
48
49void SoundEngine::add(const wstring& name, File *file) {}
50void SoundEngine::addMusic(const wstring& name, File *file) {}
51void SoundEngine::addStreaming(const wstring& name, File *file) {}
52char *SoundEngine::ConvertSoundPathToName(const wstring& name, bool bConvertSpaces) { return NULL; }
53bool SoundEngine::isStreamingWavebankReady() { return true; }
54void SoundEngine::playMusicTick() {};
55
56#else
57
58#ifdef _WINDOWS64
59char SoundEngine::m_szSoundPath[]={"Windows64Media\\Sound\\"};
60char SoundEngine::m_szMusicPath[]={"music\\"};
61char SoundEngine::m_szRedistName[]={"redist64"};
62#elif defined _DURANGO
63char SoundEngine::m_szSoundPath[]={"Sound\\"};
64char SoundEngine::m_szMusicPath[]={"music\\"};
65char SoundEngine::m_szRedistName[]={"redist64"};
66#elif defined __ORBIS__
67
68#ifdef _CONTENT_PACKAGE
69char SoundEngine::m_szSoundPath[]={"Sound/"};
70#elif defined _ART_BUILD
71char SoundEngine::m_szSoundPath[]={"Sound/"};
72#else
73// just use the host Durango folder for the sound. In the content package, we'll have moved this in the .gp4 file
74char SoundEngine::m_szSoundPath[]={"Durango/Sound/"};
75#endif
76char SoundEngine::m_szMusicPath[]={"music/"};
77char SoundEngine::m_szRedistName[]={"redist64"};
78#elif defined __PSVITA__
79char SoundEngine::m_szSoundPath[]={"PSVita/Sound/"};
80char SoundEngine::m_szMusicPath[]={"music/"};
81char SoundEngine::m_szRedistName[]={"redist"};
82#elif defined __PS3__
83//extern const char* getPS3HomePath();
84char SoundEngine::m_szSoundPath[]={"PS3/Sound/"};
85char SoundEngine::m_szMusicPath[]={"music/"};
86char SoundEngine::m_szRedistName[]={"redist"};
87
88#define USE_SPURS
89
90#ifdef USE_SPURS
91#include <cell/spurs.h>
92#else
93#include <sys/spu_image.h>
94#endif
95
96#endif
97
98F32 AILCALLBACK custom_falloff_function (HSAMPLE S,
99 F32 distance,
100 F32 rolloff_factor,
101 F32 min_dist,
102 F32 max_dist);
103
104char *SoundEngine::m_szStreamFileA[eStream_Max]=
105{
106 "calm1",
107 "calm2",
108 "calm3",
109 "hal1",
110 "hal2",
111 "hal3",
112 "hal4",
113 "nuance1",
114 "nuance2",
115#ifndef _XBOX
116 // add the new music tracks
117 "creative1",
118 "creative2",
119 "creative3",
120 "creative4",
121 "creative5",
122 "creative6",
123 "menu1",
124 "menu2",
125 "menu3",
126 "menu4",
127#endif
128 "piano1",
129 "piano2",
130 "piano3",
131
132 // Nether
133 "nether1",
134 "nether2",
135 "nether3",
136 "nether4",
137 // The End
138 "the_end_dragon_alive",
139 "the_end_end",
140 // CDs
141 "11",
142 "13",
143 "blocks",
144 "cat",
145 "chirp",
146 "far",
147 "mall",
148 "mellohi",
149 "stal",
150 "strad",
151 "ward",
152 "where_are_we_now"
153};
154
155/////////////////////////////////////////////
156//
157// ErrorCallback
158//
159/////////////////////////////////////////////
160void AILCALL ErrorCallback(S64 i_Id, char const* i_Details)
161{
162 char *pchLastError=AIL_last_error();
163
164 if(pchLastError[0]!=0)
165 {
166 app.DebugPrintf("\rErrorCallback Error Category: %s\n", pchLastError);
167 }
168
169 if (i_Details)
170 {
171 app.DebugPrintf("ErrorCallback - Details: %s\n", i_Details);
172 }
173}
174
175#ifdef __PSVITA__
176// AP - this is the callback when the driver is about to mix. At this point the mutex is locked by Miles so we can now call all Miles functions without
177// the possibility of incurring a stall.
178static bool SoundEngine_Change = false; // has tick been called?
179static CRITICAL_SECTION SoundEngine_MixerMutex;
180
181void AILCALL MilesMixerCB(HDIGDRIVER dig)
182{
183 // has the tick function been called since the last callback
184 if( SoundEngine_Change )
185 {
186 SoundEngine_Change = false;
187
188 EnterCriticalSection(&SoundEngine_MixerMutex);
189
190 Minecraft *pMinecraft = Minecraft::GetInstance();
191 pMinecraft->soundEngine->updateMiles();
192 pMinecraft->soundEngine->playMusicUpdate();
193
194 LeaveCriticalSection(&SoundEngine_MixerMutex);
195 }
196}
197#endif
198
199/////////////////////////////////////////////
200//
201// init
202//
203/////////////////////////////////////////////
204void SoundEngine::init(Options *pOptions)
205{
206 app.DebugPrintf("---SoundEngine::init\n");
207#ifdef __DISABLE_MILES__
208 return;
209#endif
210#ifdef __ORBIS__
211 C4JThread::PushAffinityAllCores();
212#endif
213#if defined _DURANGO || defined __ORBIS__ || defined __PS3__ || defined __PSVITA__
214 Register_RIB(BinkADec);
215#endif
216
217 char *redistpath;
218
219#if (defined _WINDOWS64 || defined __PSVITA__)// || defined _DURANGO || defined __ORBIS__ )
220 redistpath=AIL_set_redist_directory(m_szRedistName);
221#endif
222
223 app.DebugPrintf("---SoundEngine::init - AIL_startup\n");
224 S32 ret = AIL_startup();
225
226 int iNumberOfChannels=initAudioHardware(8);
227
228 // Create a driver to render our audio - 44khz, 16 bit,
229#ifdef __PS3__
230 // On the Sony PS3, the driver is always opened in 48 kHz, 32-bit floating point. The only meaningful configurations are MSS_MC_STEREO, MSS_MC_51_DISCRETE, and MSS_MC_71_DISCRETE.
231 m_hDriver = AIL_open_digital_driver( 48000, 16, iNumberOfChannels, AIL_OPEN_DIGITAL_USE_SPU0 );
232#elif defined __PSVITA__
233
234 // maximum of 16 samples
235 AIL_set_preference(DIG_MIXER_CHANNELS, 16);
236
237 m_hDriver = AIL_open_digital_driver( 48000, 16, MSS_MC_STEREO, 0 );
238
239 // AP - For some reason the submit thread defaults to a priority of zero (invalid). Make sure it has the highest priority to avoid audio breakup.
240 SceUID threadID;
241 AIL_platform_property( m_hDriver, PSP2_SUBMIT_THREAD, &threadID, 0, 0);
242 S32 g_DefaultCPU = sceKernelGetThreadCpuAffinityMask(threadID);
243 S32 Old = sceKernelChangeThreadPriority(threadID, 64);
244
245 // AP - register a callback when the mixer starts
246 AILMIXERCB temp = AIL_register_mix_callback(m_hDriver, MilesMixerCB);
247
248 InitializeCriticalSection(&SoundEngine_MixerMutex);
249
250#elif defined(__ORBIS__)
251 m_hDriver = AIL_open_digital_driver( 48000, 16, 2, 0 );
252 app.DebugPrintf("---SoundEngine::init - AIL_open_digital_driver\n");
253
254#else
255 m_hDriver = AIL_open_digital_driver(44100, 16, MSS_MC_USE_SYSTEM_CONFIG, 0);
256#endif
257 if (m_hDriver == 0)
258 {
259 app.DebugPrintf("Couldn't open digital sound driver. (%s)\n", AIL_last_error());
260 AIL_shutdown();
261#ifdef __ORBIS__
262 C4JThread::PopAffinity();
263#endif
264 return;
265 }
266 app.DebugPrintf("---SoundEngine::init - driver opened\n");
267
268#ifdef __PSVITA__
269
270 // set high falloff power for maximum spatial effect in software mode
271 AIL_set_speaker_configuration( m_hDriver, 0, 0, 4.0F );
272
273#endif
274
275 AIL_set_event_error_callback(ErrorCallback);
276
277 AIL_set_3D_rolloff_factor(m_hDriver,1.0);
278
279 // Create an event system tied to that driver - let Miles choose memory defaults.
280 //if (AIL_startup_event_system(m_hDriver, 0, 0, 0) == 0)
281 // 4J-PB - Durango complains that the default memory (64k)isn't enough
282 // Error: MilesEvent: Out of event system memory (pool passed to event system startup exhausted).
283 // AP - increased command buffer from the default 5K to 20K for Vita
284
285 if (AIL_startup_event_system(m_hDriver, 1024*20, 0, 1024*128) == 0)
286 {
287 app.DebugPrintf("Couldn't init event system (%s).\n", AIL_last_error());
288 AIL_close_digital_driver(m_hDriver);
289 AIL_shutdown();
290#ifdef __ORBIS__
291 C4JThread::PopAffinity();
292#endif
293 app.DebugPrintf("---SoundEngine::init - AIL_startup_event_system failed\n");
294 return;
295 }
296 char szBankName[255];
297#if defined __PS3__
298 if(app.GetBootedFromDiscPatch())
299 {
300 char szTempSoundFilename[255];
301 sprintf(szTempSoundFilename,"%s%s",m_szSoundPath, "Minecraft.msscmp" );
302
303 app.DebugPrintf("SoundEngine::playMusicUpdate - (booted from disc patch) looking for %s\n",szTempSoundFilename);
304 sprintf(szBankName,"%s/%s",app.GetBDUsrDirPath(szTempSoundFilename), m_szSoundPath );
305 app.DebugPrintf("SoundEngine::playMusicUpdate - (booted from disc patch) music path - %s\n",szBankName);
306 }
307 else
308 {
309 sprintf(szBankName,"%s/%s",getUsrDirPath(), m_szSoundPath );
310 }
311
312#elif defined __PSVITA__
313 sprintf(szBankName,"%s/%s",getUsrDirPath(), m_szSoundPath );
314#elif defined __ORBIS__
315 sprintf(szBankName,"%s/%s",getUsrDirPath(), m_szSoundPath );
316#else
317 strcpy((char *)szBankName,m_szSoundPath);
318#endif
319
320 strcat((char *)szBankName,"Minecraft.msscmp");
321
322 m_hBank=AIL_add_soundbank(szBankName, 0);
323
324 if(m_hBank == NULL)
325 {
326 char *Error=AIL_last_error();
327 app.DebugPrintf("Couldn't open soundbank: %s (%s)\n", szBankName, Error);
328 AIL_close_digital_driver(m_hDriver);
329 AIL_shutdown();
330#ifdef __ORBIS__
331 C4JThread::PopAffinity();
332#endif
333 return;
334 }
335
336 //#ifdef _DEBUG
337 HMSSENUM token = MSS_FIRST;
338 char const* Events[1] = {0};
339 S32 EventCount = 0;
340 while (AIL_enumerate_events(m_hBank, &token, 0, &Events[0]))
341 {
342 app.DebugPrintf(4,"%d - %s\n", EventCount, Events[0]);
343
344 EventCount++;
345 }
346 //#endif
347
348 U64 u64Result;
349 u64Result=AIL_enqueue_event_by_name("Minecraft/CacheSounds");
350
351 m_MasterMusicVolume=1.0f;
352 m_MasterEffectsVolume=1.0f;
353
354 //AIL_set_variable_float(0,"UserEffectVol",1);
355
356 m_bSystemMusicPlaying = false;
357
358 m_openStreamThread = NULL;
359
360#ifdef __ORBIS__
361 C4JThread::PopAffinity();
362#endif
363
364#ifdef __PSVITA__
365 // AP - By default the mixer won't start up and nothing will process. Kick off a blank sample to force the mixer to start up.
366 HSAMPLE Sample = AIL_allocate_sample_handle(m_hDriver);
367 AIL_init_sample(Sample, DIG_F_STEREO_16);
368 static U64 silence = 0;
369 AIL_set_sample_address(Sample, &silence, sizeof(U64));
370 AIL_start_sample(Sample);
371
372 // wait for 1 mix...
373 AIL_release_sample_handle(Sample);
374#endif
375}
376
377#ifdef __ORBIS__
378// void SoundEngine::SetHandle(int32_t hAudio)
379// {
380// //m_hAudio=hAudio;
381// }
382#endif
383
384void SoundEngine::SetStreamingSounds(int iOverworldMin, int iOverWorldMax, int iNetherMin, int iNetherMax, int iEndMin, int iEndMax, int iCD1)
385{
386 m_iStream_Overworld_Min=iOverworldMin;
387 m_iStream_Overworld_Max=iOverWorldMax;
388 m_iStream_Nether_Min=iNetherMin;
389 m_iStream_Nether_Max=iNetherMax;
390 m_iStream_End_Min=iEndMin;
391 m_iStream_End_Max=iEndMax;
392 m_iStream_CD_1=iCD1;
393
394 // array to monitor recently played tracks
395 if(m_bHeardTrackA)
396 {
397 delete [] m_bHeardTrackA;
398 }
399 m_bHeardTrackA = new bool[iEndMax+1];
400 memset(m_bHeardTrackA,0,sizeof(bool)*iEndMax+1);
401}
402
403// AP - moved to a separate function so it can be called from the mixer callback on Vita
404void SoundEngine::updateMiles()
405{
406#ifdef __PSVITA__
407 //CD - We must check for Background Music [BGM] at any point
408 //If it's playing disable our audio, otherwise enable
409 int NoBGMPlaying = sceAudioOutGetAdopt(SCE_AUDIO_OUT_PORT_TYPE_BGM);
410 updateSystemMusicPlaying( !NoBGMPlaying );
411#elif defined __ORBIS__
412 // is the system playing background music?
413 SceAudioOutPortState outPortState;
414 sceAudioOutGetPortState(m_hBGMAudio,&outPortState);
415 updateSystemMusicPlaying( outPortState.output==SCE_AUDIO_OUT_STATE_OUTPUT_UNKNOWN );
416#endif
417
418 if( m_validListenerCount == 1 )
419 {
420 for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ )
421 {
422 // set the listener as the first player we find
423 if( m_ListenerA[i].bValid )
424 {
425 AIL_set_listener_3D_position(m_hDriver,m_ListenerA[i].vPosition.x,m_ListenerA[i].vPosition.y,-m_ListenerA[i].vPosition.z); // Flipped sign of z as Miles is expecting left handed coord system
426 AIL_set_listener_3D_orientation(m_hDriver,-m_ListenerA[i].vOrientFront.x,m_ListenerA[i].vOrientFront.y,m_ListenerA[i].vOrientFront.z,0,1,0); // Flipped sign of z as Miles is expecting left handed coord system
427 break;
428 }
429 }
430 }
431 else
432 {
433 // 4J-PB - special case for splitscreen
434 // the shortest distance between any listener and a sound will be used to play a sound a set distance away down the z axis.
435 // The listener position will be set to 0,0,0, and the orientation will be facing down the z axis
436
437 AIL_set_listener_3D_position(m_hDriver,0,0,0);
438 AIL_set_listener_3D_orientation(m_hDriver,0,0,1,0,1,0);
439 }
440
441 AIL_begin_event_queue_processing();
442
443 // Iterate over the sounds
444 S32 StartedCount = 0, CompletedCount = 0, TotalCount = 0;
445 HMSSENUM token = MSS_FIRST;
446 MILESEVENTSOUNDINFO SoundInfo;
447 int Playing = 0;
448 while (AIL_enumerate_sound_instances(0, &token, 0, 0, 0, &SoundInfo))
449 {
450 AUDIO_INFO* game_data= (AUDIO_INFO*)( SoundInfo.UserBuffer );
451
452 if( SoundInfo.Status == MILESEVENT_SOUND_STATUS_PLAYING )
453 {
454 Playing += 1;
455 }
456
457 if ( SoundInfo.Status != MILESEVENT_SOUND_STATUS_COMPLETE )
458 {
459 // apply the master volume
460 // watch for the 'special' volume levels
461 bool isThunder = false;
462 if( game_data->volume == 10000.0f )
463 {
464 isThunder = true;
465 }
466 if(game_data->volume>1)
467 {
468 game_data->volume=1;
469 }
470 AIL_set_sample_volume_levels( SoundInfo.Sample, game_data->volume*m_MasterEffectsVolume, game_data->volume*m_MasterEffectsVolume);
471
472 float distanceScaler = 16.0f;
473 switch(SoundInfo.Status)
474 {
475 case MILESEVENT_SOUND_STATUS_PENDING:
476 // 4J-PB - causes the falloff to be calculated on the PPU instead of the SPU, and seems to resolve our distorted sound issue
477 AIL_register_falloff_function_callback(SoundInfo.Sample,&custom_falloff_function);
478
479 if(game_data->bIs3D)
480 {
481 AIL_set_sample_is_3D( SoundInfo.Sample, 1 );
482
483 int iSound = game_data->iSound - eSFX_MAX;
484 switch(iSound)
485 {
486 // Is this the Dragon?
487 case eSoundType_MOB_ENDERDRAGON_GROWL:
488 case eSoundType_MOB_ENDERDRAGON_MOVE:
489 case eSoundType_MOB_ENDERDRAGON_END:
490 case eSoundType_MOB_ENDERDRAGON_HIT:
491 distanceScaler=100.0f;
492 break;
493 case eSoundType_FIREWORKS_BLAST:
494 case eSoundType_FIREWORKS_BLAST_FAR:
495 case eSoundType_FIREWORKS_LARGE_BLAST:
496 case eSoundType_FIREWORKS_LARGE_BLAST_FAR:
497 distanceScaler=100.0f;
498 break;
499 case eSoundType_MOB_GHAST_MOAN:
500 case eSoundType_MOB_GHAST_SCREAM:
501 case eSoundType_MOB_GHAST_DEATH:
502 case eSoundType_MOB_GHAST_CHARGE:
503 case eSoundType_MOB_GHAST_FIREBALL:
504 distanceScaler=30.0f;
505 break;
506 }
507
508 // Set a special distance scaler for thunder, which we respond to by having no attenutation
509 if( isThunder )
510 {
511 distanceScaler = 10000.0f;
512 }
513 }
514 else
515 {
516 AIL_set_sample_is_3D( SoundInfo.Sample, 0 );
517 }
518
519 AIL_set_sample_3D_distances(SoundInfo.Sample,distanceScaler,1,0);
520 // set the pitch
521 if(!game_data->bUseSoundsPitchVal)
522 {
523 AIL_set_sample_playback_rate_factor(SoundInfo.Sample,game_data->pitch);
524 }
525
526 if(game_data->bIs3D)
527 {
528 if(m_validListenerCount>1)
529 {
530 float fClosest=10000.0f;
531 int iClosestListener=0;
532 float fClosestX=0.0f,fClosestY=0.0f,fClosestZ=0.0f,fDist;
533 // need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision
534 for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ )
535 {
536 if( m_ListenerA[i].bValid )
537 {
538 float x,y,z;
539
540 x=fabs(m_ListenerA[i].vPosition.x-game_data->x);
541 y=fabs(m_ListenerA[i].vPosition.y-game_data->y);
542 z=fabs(m_ListenerA[i].vPosition.z-game_data->z);
543 fDist=x+y+z;
544
545 if(fDist<fClosest)
546 {
547 fClosest=fDist;
548 fClosestX=x;
549 fClosestY=y;
550 fClosestZ=z;
551 iClosestListener=i;
552 }
553 }
554 }
555
556 // our distances in the world aren't very big, so floats rather than casts to doubles should be fine
557 fDist=sqrtf((fClosestX*fClosestX)+(fClosestY*fClosestY)+(fClosestZ*fClosestZ));
558 AIL_set_sample_3D_position( SoundInfo.Sample, 0, 0, fDist );
559
560 //app.DebugPrintf("Playing sound %d %f from nearest listener [%d]\n",SoundInfo.EventID,fDist,iClosestListener);
561 }
562 else
563 {
564 AIL_set_sample_3D_position( SoundInfo.Sample, game_data->x, game_data->y, -game_data->z ); // Flipped sign of z as Miles is expecting left handed coord system
565 }
566 }
567 break;
568
569 default:
570 if(game_data->bIs3D)
571 {
572 if(m_validListenerCount>1)
573 {
574 float fClosest=10000.0f;
575 int iClosestListener=0;
576 float fClosestX=0.0f,fClosestY=0.0f,fClosestZ=0.0f,fDist;
577 // need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision
578 for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ )
579 {
580 if( m_ListenerA[i].bValid )
581 {
582 float x,y,z;
583
584 x=fabs(m_ListenerA[i].vPosition.x-game_data->x);
585 y=fabs(m_ListenerA[i].vPosition.y-game_data->y);
586 z=fabs(m_ListenerA[i].vPosition.z-game_data->z);
587 fDist=x+y+z;
588
589 if(fDist<fClosest)
590 {
591 fClosest=fDist;
592 fClosestX=x;
593 fClosestY=y;
594 fClosestZ=z;
595 iClosestListener=i;
596 }
597 }
598 }
599 // our distances in the world aren't very big, so floats rather than casts to doubles should be fine
600 fDist=sqrtf((fClosestX*fClosestX)+(fClosestY*fClosestY)+(fClosestZ*fClosestZ));
601 AIL_set_sample_3D_position( SoundInfo.Sample, 0, 0, fDist );
602
603 //app.DebugPrintf("Playing sound %d %f from nearest listener [%d]\n",SoundInfo.EventID,fDist,iClosestListener);
604 }
605 else
606 {
607 AIL_set_sample_3D_position( SoundInfo.Sample, game_data->x, game_data->y, -game_data->z ); // Flipped sign of z as Miles is expecting left handed coord system
608 }
609 }
610 break;
611 }
612 }
613 }
614 AIL_complete_event_queue_processing();
615}
616
617//#define DISTORTION_TEST
618#ifdef DISTORTION_TEST
619static float fVal=0.0f;
620#endif
621/////////////////////////////////////////////
622//
623// tick
624//
625/////////////////////////////////////////////
626
627#ifdef __PSVITA__
628static S32 running = AIL_ms_count();
629#endif
630
631void SoundEngine::tick(shared_ptr<Mob> *players, float a)
632{
633 ConsoleSoundEngine::tick();
634#ifdef __DISABLE_MILES__
635 return;
636#endif
637
638#ifdef __PSVITA__
639 EnterCriticalSection(&SoundEngine_MixerMutex);
640#endif
641
642 // update the listener positions
643 int listenerCount = 0;
644#ifdef DISTORTION_TEST
645 float fX,fY,fZ;
646#endif
647 if( players )
648 {
649 bool bListenerPostionSet=false;
650 for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ )
651 {
652 if( players[i] != NULL )
653 {
654 m_ListenerA[i].bValid=true;
655 F32 x,y,z;
656 x=players[i]->xo + (players[i]->x - players[i]->xo) * a;
657 y=players[i]->yo + (players[i]->y - players[i]->yo) * a;
658 z=players[i]->zo + (players[i]->z - players[i]->zo) * a;
659
660 float yRot = players[i]->yRotO + (players[i]->yRot - players[i]->yRotO) * a;
661 float yCos = (float)cos(-yRot * Mth::RAD_TO_GRAD - PI);
662 float ySin = (float)sin(-yRot * Mth::RAD_TO_GRAD - PI);
663
664 // store the listener positions for splitscreen
665 m_ListenerA[i].vPosition.x = x;
666 m_ListenerA[i].vPosition.y = y;
667 m_ListenerA[i].vPosition.z = z;
668
669 m_ListenerA[i].vOrientFront.x = ySin;
670 m_ListenerA[i].vOrientFront.y = 0;
671 m_ListenerA[i].vOrientFront.z = yCos;
672
673 listenerCount++;
674 }
675 else
676 {
677 m_ListenerA[i].bValid=false;
678 }
679 }
680 }
681
682
683 // If there were no valid players set, make up a default listener
684 if( listenerCount == 0 )
685 {
686 m_ListenerA[0].vPosition.x = 0;
687 m_ListenerA[0].vPosition.y = 0;
688 m_ListenerA[0].vPosition.z = 0;
689 m_ListenerA[0].vOrientFront.x = 0;
690 m_ListenerA[0].vOrientFront.y = 0;
691 m_ListenerA[0].vOrientFront.z = 1.0f;
692 listenerCount++;
693 }
694 m_validListenerCount = listenerCount;
695
696#ifdef __PSVITA__
697 // AP - Show that a change has occurred so we know to update the values at the next Mixer callback
698 SoundEngine_Change = true;
699
700 LeaveCriticalSection(&SoundEngine_MixerMutex);
701#else
702 updateMiles();
703#endif
704}
705
706/////////////////////////////////////////////
707//
708// SoundEngine
709//
710/////////////////////////////////////////////
711SoundEngine::SoundEngine()
712{
713 random = new Random();
714 m_hStream=0;
715 m_StreamState=eMusicStreamState_Idle;
716 m_iMusicDelay=0;
717 m_validListenerCount=0;
718
719 m_bHeardTrackA=NULL;
720
721 // Start the streaming music playing some music from the overworld
722 SetStreamingSounds(eStream_Overworld_Calm1,eStream_Overworld_piano3,
723 eStream_Nether1,eStream_Nether4,
724 eStream_end_dragon,eStream_end_end,
725 eStream_CD_1);
726
727 m_musicID=getMusicID(LevelData::DIMENSION_OVERWORLD);
728
729 m_StreamingAudioInfo.bIs3D=false;
730 m_StreamingAudioInfo.x=0;
731 m_StreamingAudioInfo.y=0;
732 m_StreamingAudioInfo.z=0;
733 m_StreamingAudioInfo.volume=1;
734 m_StreamingAudioInfo.pitch=1;
735
736 memset(CurrentSoundsPlaying,0,sizeof(int)*(eSoundType_MAX+eSFX_MAX));
737 memset(m_ListenerA,0,sizeof(AUDIO_LISTENER)*XUSER_MAX_COUNT);
738
739#ifdef __ORBIS__
740 m_hBGMAudio=GetAudioBGMHandle();
741#endif
742}
743
744void SoundEngine::destroy() {}
745
746#ifdef _DEBUG
747void SoundEngine::GetSoundName(char *szSoundName,int iSound)
748{
749 strcpy((char *)szSoundName,"Minecraft/");
750 wstring name = wchSoundNames[iSound];
751 char *SoundName = (char *)ConvertSoundPathToName(name);
752 strcat((char *)szSoundName,SoundName);
753}
754#endif
755
756/////////////////////////////////////////////
757//
758// play
759//
760/////////////////////////////////////////////
761void SoundEngine::play(int iSound, float x, float y, float z, float volume, float pitch)
762{
763 U8 szSoundName[256];
764
765 if(iSound==-1)
766 {
767 app.DebugPrintf(6,"PlaySound with sound of -1 !!!!!!!!!!!!!!!\n");
768 return;
769 }
770
771 // AP removed old counting system. Now relying on Miles' Play Count Limit
772 /* // if we are already playing loads of this sounds ignore this one
773 if(CurrentSoundsPlaying[iSound+eSFX_MAX]>MAX_SAME_SOUNDS_PLAYING)
774 {
775 // wstring name = wchSoundNames[iSound];
776 // char *SoundName = (char *)ConvertSoundPathToName(name);
777 // app.DebugPrintf("Too many %s sounds playing!\n",SoundName);
778 return;
779 }*/
780
781 //if (iSound != eSoundType_MOB_IRONGOLEM_WALK) return;
782
783 // build the name
784 strcpy((char *)szSoundName,"Minecraft/");
785
786#ifdef DISTORTION_TEST
787 wstring name = wchSoundNames[eSoundType_MOB_ENDERDRAGON_GROWL];
788#else
789 wstring name = wchSoundNames[iSound];
790#endif
791
792 char *SoundName = (char *)ConvertSoundPathToName(name);
793 strcat((char *)szSoundName,SoundName);
794
795// app.DebugPrintf(6,"PlaySound - %d - %s - %s (%f %f %f, vol %f, pitch %f)\n",iSound, SoundName, szSoundName,x,y,z,volume,pitch);
796
797 AUDIO_INFO AudioInfo;
798 AudioInfo.x=x;
799 AudioInfo.y=y;
800 AudioInfo.z=z;
801 AudioInfo.volume=volume;
802 AudioInfo.pitch=pitch;
803 AudioInfo.bIs3D=true;
804 AudioInfo.bUseSoundsPitchVal=false;
805 AudioInfo.iSound=iSound+eSFX_MAX;
806#ifdef _DEBUG
807 strncpy(AudioInfo.chName,(char *)szSoundName,64);
808#endif
809
810 S32 token = AIL_enqueue_event_start();
811 AIL_enqueue_event_buffer(&token, &AudioInfo, sizeof(AUDIO_INFO), 0);
812 AIL_enqueue_event_end_named(token, (char *)szSoundName);
813}
814
815/////////////////////////////////////////////
816//
817// playUI
818//
819/////////////////////////////////////////////
820void SoundEngine::playUI(int iSound, float volume, float pitch)
821{
822 U8 szSoundName[256];
823 wstring name;
824 // we have some game sounds played as UI sounds...
825 // Not the best way to do this, but it seems to only be the portal sounds
826
827 if(iSound>=eSFX_MAX)
828 {
829 // AP removed old counting system. Now relying on Miles' Play Count Limit
830 /* // if we are already playing loads of this sounds ignore this one
831 if(CurrentSoundsPlaying[iSound+eSFX_MAX]>MAX_SAME_SOUNDS_PLAYING) return;*/
832
833 // build the name
834 strcpy((char *)szSoundName,"Minecraft/");
835 name = wchSoundNames[iSound];
836 }
837 else
838 {
839 // AP removed old counting system. Now relying on Miles' Play Count Limit
840 /* // if we are already playing loads of this sounds ignore this one
841 if(CurrentSoundsPlaying[iSound]>MAX_SAME_SOUNDS_PLAYING) return;*/
842
843 // build the name
844 strcpy((char *)szSoundName,"Minecraft/UI/");
845 name = wchUISoundNames[iSound];
846 }
847
848 char *SoundName = (char *)ConvertSoundPathToName(name);
849 strcat((char *)szSoundName,SoundName);
850// app.DebugPrintf("UI: Playing %s, volume %f, pitch %f\n",SoundName,volume,pitch);
851
852 //app.DebugPrintf("PlaySound - %d - %s\n",iSound, SoundName);
853
854 AUDIO_INFO AudioInfo;
855 memset(&AudioInfo,0,sizeof(AUDIO_INFO));
856 AudioInfo.volume=volume; // will be multiplied by the master volume
857 AudioInfo.pitch=pitch;
858 AudioInfo.bUseSoundsPitchVal=true;
859 if(iSound>=eSFX_MAX)
860 {
861 AudioInfo.iSound=iSound+eSFX_MAX;
862 }
863 else
864 {
865 AudioInfo.iSound=iSound;
866 }
867#ifdef _DEBUG
868 strncpy(AudioInfo.chName,(char *)szSoundName,64);
869#endif
870
871 // 4J-PB - not going to stop UI events happening based on the number of currently playing sounds
872 S32 token = AIL_enqueue_event_start();
873 AIL_enqueue_event_buffer(&token, &AudioInfo, sizeof(AUDIO_INFO), 0);
874 AIL_enqueue_event_end_named(token, (char *)szSoundName);
875}
876
877/////////////////////////////////////////////
878//
879// playStreaming
880//
881/////////////////////////////////////////////
882void SoundEngine::playStreaming(const wstring& name, float x, float y , float z, float volume, float pitch, bool bMusicDelay)
883{
884 // This function doesn't actually play a streaming sound, just sets states and an id for the music tick to play it
885 // Level audio will be played when a play with an empty name comes in
886 // CD audio will be played when a named stream comes in
887
888 m_StreamingAudioInfo.x=x;
889 m_StreamingAudioInfo.y=y;
890 m_StreamingAudioInfo.z=z;
891 m_StreamingAudioInfo.volume=volume;
892 m_StreamingAudioInfo.pitch=pitch;
893
894 if(m_StreamState==eMusicStreamState_Playing)
895 {
896 m_StreamState=eMusicStreamState_Stop;
897 }
898 else if(m_StreamState==eMusicStreamState_Opening)
899 {
900 m_StreamState=eMusicStreamState_OpeningCancel;
901 }
902
903 if(name.empty())
904 {
905 // music, or stop CD
906 m_StreamingAudioInfo.bIs3D=false;
907
908 // we need a music id
909 // random delay of up to 3 minutes for music
910 m_iMusicDelay = random->nextInt(20 * 60 * 3);//random->nextInt(20 * 60 * 10) + 20 * 60 * 10;
911
912#ifdef _DEBUG
913 m_iMusicDelay=0;
914#endif
915 Minecraft *pMinecraft=Minecraft::GetInstance();
916
917 bool playerInEnd=false;
918 bool playerInNether=false;
919
920 for(unsigned int i=0;i<MAX_LOCAL_PLAYERS;i++)
921 {
922 if(pMinecraft->localplayers[i]!=NULL)
923 {
924 if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_END)
925 {
926 playerInEnd=true;
927 }
928 else if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_NETHER)
929 {
930 playerInNether=true;
931 }
932 }
933 }
934 if(playerInEnd)
935 {
936 m_musicID = getMusicID(LevelData::DIMENSION_END);
937 }
938 else if(playerInNether)
939 {
940 m_musicID = getMusicID(LevelData::DIMENSION_NETHER);
941 }
942 else
943 {
944 m_musicID = getMusicID(LevelData::DIMENSION_OVERWORLD);
945 }
946 }
947 else
948 {
949 // jukebox
950 m_StreamingAudioInfo.bIs3D=true;
951 m_musicID=getMusicID(name);
952 m_iMusicDelay=0;
953 }
954}
955
956
957int SoundEngine::GetRandomishTrack(int iStart,int iEnd)
958{
959 // 4J-PB - make it more likely that we'll get a track we've not heard for a while, although repeating tracks sometimes is fine
960
961 // if all tracks have been heard, clear the flags
962 bool bAllTracksHeard=true;
963 int iVal=iStart;
964 for(int i=iStart;i<=iEnd;i++)
965 {
966 if(m_bHeardTrackA[i]==false)
967 {
968 bAllTracksHeard=false;
969 app.DebugPrintf("Not heard all tracks yet\n");
970 break;
971 }
972 }
973
974 if(bAllTracksHeard)
975 {
976 app.DebugPrintf("Heard all tracks - resetting the tracking array\n");
977
978 for(int i=iStart;i<=iEnd;i++)
979 {
980 m_bHeardTrackA[i]=false;
981 }
982 }
983
984 // trying to get a track we haven't heard, but not too hard
985 for(int i=0;i<=((iEnd-iStart)/2);i++)
986 {
987 // random->nextInt(1) will always return 0
988 iVal=random->nextInt((iEnd-iStart)+1)+iStart;
989 if(m_bHeardTrackA[iVal]==false)
990 {
991 // not heard this
992 app.DebugPrintf("(%d) Not heard track %d yet, so playing it now\n",i,iVal);
993 m_bHeardTrackA[iVal]=true;
994 break;
995 }
996 else
997 {
998 app.DebugPrintf("(%d) Skipping track %d already heard it recently\n",i,iVal);
999 }
1000 }
1001
1002 app.DebugPrintf("Select track %d\n",iVal);
1003 return iVal;
1004}
1005/////////////////////////////////////////////
1006//
1007// getMusicID
1008//
1009/////////////////////////////////////////////
1010int SoundEngine::getMusicID(int iDomain)
1011{
1012 int iRandomVal=0;
1013 Minecraft *pMinecraft=Minecraft::GetInstance();
1014
1015 // Before the game has started?
1016 if(pMinecraft==NULL)
1017 {
1018 // any track from the overworld
1019 return GetRandomishTrack(m_iStream_Overworld_Min,m_iStream_Overworld_Max);
1020 }
1021
1022 if(pMinecraft->skins->isUsingDefaultSkin())
1023 {
1024 switch(iDomain)
1025 {
1026 case LevelData::DIMENSION_END:
1027 // the end isn't random - it has different music depending on whether the dragon is alive or not, but we've not added the dead dragon music yet
1028 return m_iStream_End_Min;
1029 case LevelData::DIMENSION_NETHER:
1030 return GetRandomishTrack(m_iStream_Nether_Min,m_iStream_Nether_Max);
1031 //return m_iStream_Nether_Min + random->nextInt(m_iStream_Nether_Max-m_iStream_Nether_Min);
1032 default: //overworld
1033 //return m_iStream_Overworld_Min + random->nextInt(m_iStream_Overworld_Max-m_iStream_Overworld_Min);
1034 return GetRandomishTrack(m_iStream_Overworld_Min,m_iStream_Overworld_Max);
1035 }
1036 }
1037 else
1038 {
1039 // using a texture pack - may have multiple End music tracks
1040 switch(iDomain)
1041 {
1042 case LevelData::DIMENSION_END:
1043 return GetRandomishTrack(m_iStream_End_Min,m_iStream_End_Max);
1044 case LevelData::DIMENSION_NETHER:
1045 //return m_iStream_Nether_Min + random->nextInt(m_iStream_Nether_Max-m_iStream_Nether_Min);
1046 return GetRandomishTrack(m_iStream_Nether_Min,m_iStream_Nether_Max);
1047 default: //overworld
1048 //return m_iStream_Overworld_Min + random->nextInt(m_iStream_Overworld_Max-m_iStream_Overworld_Min);
1049 return GetRandomishTrack(m_iStream_Overworld_Min,m_iStream_Overworld_Max);
1050 }
1051 }
1052}
1053
1054/////////////////////////////////////////////
1055//
1056// getMusicID
1057//
1058/////////////////////////////////////////////
1059// check what the CD is
1060int SoundEngine::getMusicID(const wstring& name)
1061{
1062 int iCD=0;
1063 char *SoundName = (char *)ConvertSoundPathToName(name,true);
1064
1065 // 4J-PB - these will always be the game cds, so use the m_szStreamFileA for this
1066 for(int i=0;i<12;i++)
1067 {
1068 if(strcmp(SoundName,m_szStreamFileA[i+eStream_CD_1])==0)
1069 {
1070 iCD=i;
1071 break;
1072 }
1073 }
1074
1075 // adjust for cd start position on normal or mash-up pack
1076 return iCD+m_iStream_CD_1;
1077}
1078
1079/////////////////////////////////////////////
1080//
1081// getMasterMusicVolume
1082//
1083/////////////////////////////////////////////
1084float SoundEngine::getMasterMusicVolume()
1085{
1086 if( m_bSystemMusicPlaying )
1087 {
1088 return 0.0f;
1089 }
1090 else
1091 {
1092 return m_MasterMusicVolume;
1093 }
1094}
1095
1096/////////////////////////////////////////////
1097//
1098// updateMusicVolume
1099//
1100/////////////////////////////////////////////
1101void SoundEngine::updateMusicVolume(float fVal)
1102{
1103 m_MasterMusicVolume=fVal;
1104}
1105
1106/////////////////////////////////////////////
1107//
1108// updateSystemMusicPlaying
1109//
1110/////////////////////////////////////////////
1111void SoundEngine::updateSystemMusicPlaying(bool isPlaying)
1112{
1113 m_bSystemMusicPlaying = isPlaying;
1114}
1115
1116/////////////////////////////////////////////
1117//
1118// updateSoundEffectVolume
1119//
1120/////////////////////////////////////////////
1121void SoundEngine::updateSoundEffectVolume(float fVal)
1122{
1123 m_MasterEffectsVolume=fVal;
1124 //AIL_set_variable_float(0,"UserEffectVol",fVal);
1125}
1126
1127void SoundEngine::add(const wstring& name, File *file) {}
1128void SoundEngine::addMusic(const wstring& name, File *file) {}
1129void SoundEngine::addStreaming(const wstring& name, File *file) {}
1130bool SoundEngine::isStreamingWavebankReady() { return true; }
1131
1132int SoundEngine::OpenStreamThreadProc( void* lpParameter )
1133{
1134#ifdef __DISABLE_MILES__
1135 return 0;
1136#endif
1137 SoundEngine *soundEngine = (SoundEngine *)lpParameter;
1138 soundEngine->m_hStream = AIL_open_stream(soundEngine->m_hDriver,soundEngine->m_szStreamName,0);
1139
1140 if(soundEngine->m_hStream==0)
1141 {
1142 app.DebugPrintf("SoundEngine::OpenStreamThreadProc - Could not open - %s\n",soundEngine->m_szStreamName);
1143 }
1144 return 0;
1145}
1146
1147/////////////////////////////////////////////
1148//
1149// playMusicTick
1150//
1151/////////////////////////////////////////////
1152void SoundEngine::playMusicTick()
1153{
1154// AP - vita will update the music during the mixer callback
1155#ifndef __PSVITA__
1156 playMusicUpdate();
1157#endif
1158}
1159
1160// AP - moved to a separate function so it can be called from the mixer callback on Vita
1161void SoundEngine::playMusicUpdate()
1162{
1163 //return;
1164 static bool firstCall = true;
1165 static float fMusicVol = 0.0f;
1166 if( firstCall )
1167 {
1168 fMusicVol = getMasterMusicVolume();
1169 firstCall = false;
1170 }
1171
1172 switch(m_StreamState)
1173 {
1174 case eMusicStreamState_Idle:
1175
1176 // start a stream playing
1177 if (m_iMusicDelay > 0)
1178 {
1179 m_iMusicDelay--;
1180 return;
1181 }
1182
1183 if(m_musicID!=-1)
1184 {
1185 // start playing it
1186
1187
1188#if ( defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ )
1189
1190#ifdef __PS3__
1191 // 4J-PB - Need to check if we are a patched BD build
1192 if(app.GetBootedFromDiscPatch())
1193 {
1194 sprintf(m_szStreamName,"%s/%s",app.GetBDUsrDirPath(m_szMusicPath), m_szMusicPath );
1195 app.DebugPrintf("SoundEngine::playMusicUpdate - (booted from disc patch) music path - %s",m_szStreamName);
1196 }
1197 else
1198 {
1199 sprintf(m_szStreamName,"%s/%s",getUsrDirPath(), m_szMusicPath );
1200 }
1201#else
1202 sprintf(m_szStreamName,"%s/%s",getUsrDirPath(), m_szMusicPath );
1203#endif
1204
1205#else
1206 strcpy((char *)m_szStreamName,m_szMusicPath);
1207#endif
1208 // are we using a mash-up pack?
1209 //if(pMinecraft && !pMinecraft->skins->isUsingDefaultSkin() && pMinecraft->skins->getSelected()->hasAudio())
1210 if(Minecraft::GetInstance()->skins->getSelected()->hasAudio())
1211 {
1212 // It's a mash-up - need to use the DLC path for the music
1213 TexturePack *pTexPack=Minecraft::GetInstance()->skins->getSelected();
1214 DLCTexturePack *pDLCTexPack=(DLCTexturePack *)pTexPack;
1215 DLCPack *pack = pDLCTexPack->getDLCInfoParentPack();
1216 DLCAudioFile *dlcAudioFile = (DLCAudioFile *) pack->getFile(DLCManager::e_DLCType_Audio, 0);
1217
1218 app.DebugPrintf("Mashup pack \n");
1219
1220 // build the name
1221
1222 // if the music ID is beyond the end of the texture pack music files, then it's a CD
1223 if(m_musicID<m_iStream_CD_1)
1224 {
1225 SetIsPlayingStreamingGameMusic(true);
1226 SetIsPlayingStreamingCDMusic(false);
1227 m_MusicType=eMusicType_Game;
1228 m_StreamingAudioInfo.bIs3D=false;
1229
1230#ifdef _XBOX_ONE
1231 wstring &wstrSoundName=dlcAudioFile->GetSoundName(m_musicID);
1232 wstring wstrFile=L"TPACK:\\Data\\" + wstrSoundName +L".binka";
1233 std::wstring mountedPath = StorageManager.GetMountedPath(wstrFile);
1234 wcstombs(m_szStreamName,mountedPath.c_str(),255);
1235#else
1236 wstring &wstrSoundName=dlcAudioFile->GetSoundName(m_musicID);
1237 char szName[255];
1238 wcstombs(szName,wstrSoundName.c_str(),255);
1239
1240#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__
1241 string strFile="TPACK:/Data/" + string(szName) + ".binka";
1242#else
1243 string strFile="TPACK:\\Data\\" + string(szName) + ".binka";
1244#endif
1245 std::string mountedPath = StorageManager.GetMountedPath(strFile);
1246 strcpy(m_szStreamName,mountedPath.c_str());
1247#endif
1248 }
1249 else
1250 {
1251 SetIsPlayingStreamingGameMusic(false);
1252 SetIsPlayingStreamingCDMusic(true);
1253 m_MusicType=eMusicType_CD;
1254 m_StreamingAudioInfo.bIs3D=true;
1255
1256 // Need to adjust to index into the cds in the game's m_szStreamFileA
1257 strcat((char *)m_szStreamName,"cds/");
1258 strcat((char *)m_szStreamName,m_szStreamFileA[m_musicID-m_iStream_CD_1+eStream_CD_1]);
1259 strcat((char *)m_szStreamName,".binka");
1260 }
1261 }
1262 else
1263 {
1264 // 4J-PB - if this is a PS3 disc patch, we have to check if the music file is in the patch data
1265#ifdef __PS3__
1266 if(app.GetBootedFromDiscPatch() && (m_musicID<m_iStream_CD_1))
1267 {
1268 // rebuild the path for the music
1269 strcpy((char *)m_szStreamName,m_szMusicPath);
1270 strcat((char *)m_szStreamName,"music/");
1271 strcat((char *)m_szStreamName,m_szStreamFileA[m_musicID]);
1272 strcat((char *)m_szStreamName,".binka");
1273
1274 // check if this is in the patch data
1275 sprintf(m_szStreamName,"%s/%s",app.GetBDUsrDirPath(m_szStreamName), m_szMusicPath );
1276 strcat((char *)m_szStreamName,"music/");
1277 strcat((char *)m_szStreamName,m_szStreamFileA[m_musicID]);
1278 strcat((char *)m_szStreamName,".binka");
1279
1280 SetIsPlayingStreamingGameMusic(true);
1281 SetIsPlayingStreamingCDMusic(false);
1282 m_MusicType=eMusicType_Game;
1283 m_StreamingAudioInfo.bIs3D=false;
1284 }
1285 else if(m_musicID<m_iStream_CD_1)
1286 {
1287 SetIsPlayingStreamingGameMusic(true);
1288 SetIsPlayingStreamingCDMusic(false);
1289 m_MusicType=eMusicType_Game;
1290 m_StreamingAudioInfo.bIs3D=false;
1291 // build the name
1292 strcat((char *)m_szStreamName,"music/");
1293 strcat((char *)m_szStreamName,m_szStreamFileA[m_musicID]);
1294 strcat((char *)m_szStreamName,".binka");
1295 }
1296
1297 else
1298 {
1299 SetIsPlayingStreamingGameMusic(false);
1300 SetIsPlayingStreamingCDMusic(true);
1301 m_MusicType=eMusicType_CD;
1302 m_StreamingAudioInfo.bIs3D=true;
1303 // build the name
1304 strcat((char *)m_szStreamName,"cds/");
1305 strcat((char *)m_szStreamName,m_szStreamFileA[m_musicID]);
1306 strcat((char *)m_szStreamName,".binka");
1307 }
1308#else
1309 if(m_musicID<m_iStream_CD_1)
1310 {
1311 SetIsPlayingStreamingGameMusic(true);
1312 SetIsPlayingStreamingCDMusic(false);
1313 m_MusicType=eMusicType_Game;
1314 m_StreamingAudioInfo.bIs3D=false;
1315 // build the name
1316 strcat((char *)m_szStreamName,"music/");
1317 }
1318 else
1319 {
1320 SetIsPlayingStreamingGameMusic(false);
1321 SetIsPlayingStreamingCDMusic(true);
1322 m_MusicType=eMusicType_CD;
1323 m_StreamingAudioInfo.bIs3D=true;
1324 // build the name
1325 strcat((char *)m_szStreamName,"cds/");
1326 }
1327 strcat((char *)m_szStreamName,m_szStreamFileA[m_musicID]);
1328 strcat((char *)m_szStreamName,".binka");
1329
1330#endif
1331 }
1332
1333 // wstring name = m_szStreamFileA[m_musicID];
1334 // char *SoundName = (char *)ConvertSoundPathToName(name);
1335 // strcat((char *)szStreamName,SoundName);
1336
1337 const bool isCD = (m_musicID >= m_iStream_CD_1);
1338 const char* folder = isCD ? "cds/" : "music/";
1339
1340 FILE* pFile = nullptr;
1341 if (fopen_s(&pFile, reinterpret_cast<char*>(m_szStreamName), "rb") == 0 && pFile)
1342 {
1343 fclose(pFile);
1344 }
1345 else
1346 {
1347 const char* extensions[] = { ".wav" }; // only wav works outside of binka files to my knowledge, i've only tested ogg, wav, mp3 and only wav worked out of the bunch
1348 size_t count = sizeof(extensions) / sizeof(extensions[0]);
1349 bool found = false;
1350
1351 for (size_t i = 0; i < count; i++)
1352 {
1353 int n = sprintf_s(reinterpret_cast<char*>(m_szStreamName), 512, "%s%s%s%s", m_szMusicPath, folder, m_szStreamFileA[m_musicID], extensions[i]);
1354 if (n < 0) continue;
1355
1356 if (fopen_s(&pFile, reinterpret_cast<char*>(m_szStreamName), "rb") == 0 && pFile)
1357 {
1358 fclose(pFile);
1359 found = true;
1360 break;
1361 }
1362 }
1363
1364 if (!found)
1365 {
1366 return;
1367 }
1368 }
1369
1370 app.DebugPrintf("Starting streaming - %s\n",m_szStreamName);
1371
1372 // Don't actually open in this thread, as it can block for ~300ms.
1373 m_openStreamThread = new C4JThread(OpenStreamThreadProc, this, "OpenStreamThreadProc");
1374 m_openStreamThread->Run();
1375 m_StreamState = eMusicStreamState_Opening;
1376 }
1377 break;
1378
1379 case eMusicStreamState_Opening:
1380 // If the open stream thread is complete, then we are ready to proceed to actually playing
1381 if( !m_openStreamThread->isRunning() )
1382 {
1383 delete m_openStreamThread;
1384 m_openStreamThread = NULL;
1385
1386 HSAMPLE hSample = AIL_stream_sample_handle( m_hStream);
1387
1388 // 4J-PB - causes the falloff to be calculated on the PPU instead of the SPU, and seems to resolve our distorted sound issue
1389 AIL_register_falloff_function_callback(hSample,&custom_falloff_function);
1390
1391 if(m_StreamingAudioInfo.bIs3D)
1392 {
1393 AIL_set_sample_3D_distances(hSample,64.0f,1,0); // Larger distance scaler for music discs
1394 if(m_validListenerCount>1)
1395 {
1396 float fClosest=10000.0f;
1397 int iClosestListener=0;
1398 float fClosestX=0.0f,fClosestY=0.0f,fClosestZ=0.0f,fDist;
1399 // need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision
1400 for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ )
1401 {
1402 if( m_ListenerA[i].bValid )
1403 {
1404 float x,y,z;
1405
1406 x=fabs(m_ListenerA[i].vPosition.x-m_StreamingAudioInfo.x);
1407 y=fabs(m_ListenerA[i].vPosition.y-m_StreamingAudioInfo.y);
1408 z=fabs(m_ListenerA[i].vPosition.z-m_StreamingAudioInfo.z);
1409 fDist=x+y+z;
1410
1411 if(fDist<fClosest)
1412 {
1413 fClosest=fDist;
1414 fClosestX=x;
1415 fClosestY=y;
1416 fClosestZ=z;
1417 iClosestListener=i;
1418 }
1419 }
1420 }
1421
1422 // our distances in the world aren't very big, so floats rather than casts to doubles should be fine
1423 fDist=sqrtf((fClosestX*fClosestX)+(fClosestY*fClosestY)+(fClosestZ*fClosestZ));
1424 AIL_set_sample_3D_position( hSample, 0, 0, fDist );
1425 }
1426 else
1427 {
1428 AIL_set_sample_3D_position( hSample, m_StreamingAudioInfo.x, m_StreamingAudioInfo.y, -m_StreamingAudioInfo.z ); // Flipped sign of z as Miles is expecting left handed coord system
1429 }
1430 }
1431 else
1432 {
1433 // clear the 3d flag on the stream after a jukebox finishes and streaming music starts
1434 AIL_set_sample_is_3D( hSample, 0 );
1435 }
1436 // set the pitch
1437 app.DebugPrintf("Sample rate:%d\n", AIL_sample_playback_rate(hSample));
1438 AIL_set_sample_playback_rate_factor(hSample,m_StreamingAudioInfo.pitch);
1439 // set the volume
1440 AIL_set_sample_volume_levels( hSample, m_StreamingAudioInfo.volume*getMasterMusicVolume(), m_StreamingAudioInfo.volume*getMasterMusicVolume());
1441
1442 AIL_start_stream( m_hStream );
1443
1444 m_StreamState=eMusicStreamState_Playing;
1445 }
1446 break;
1447 case eMusicStreamState_OpeningCancel:
1448 if( !m_openStreamThread->isRunning() )
1449 {
1450 delete m_openStreamThread;
1451 m_openStreamThread = NULL;
1452 m_StreamState = eMusicStreamState_Stop;
1453 }
1454 break;
1455 case eMusicStreamState_Stop:
1456 // should gradually take the volume down in steps
1457 AIL_pause_stream(m_hStream,1);
1458 AIL_close_stream(m_hStream);
1459 m_hStream=0;
1460 SetIsPlayingStreamingCDMusic(false);
1461 SetIsPlayingStreamingGameMusic(false);
1462 m_StreamState=eMusicStreamState_Idle;
1463 break;
1464 case eMusicStreamState_Stopping:
1465 break;
1466 case eMusicStreamState_Play:
1467 break;
1468 case eMusicStreamState_Playing:
1469 if(GetIsPlayingStreamingGameMusic())
1470 {
1471 //if(m_MusicInfo.pCue!=NULL)
1472 {
1473 bool playerInEnd = false;
1474 bool playerInNether=false;
1475 Minecraft *pMinecraft = Minecraft::GetInstance();
1476 for(unsigned int i = 0; i < MAX_LOCAL_PLAYERS; ++i)
1477 {
1478 if(pMinecraft->localplayers[i]!=NULL)
1479 {
1480 if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_END)
1481 {
1482 playerInEnd=true;
1483 }
1484 else if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_NETHER)
1485 {
1486 playerInNether=true;
1487 }
1488 }
1489 }
1490
1491 if(playerInEnd && !GetIsPlayingEndMusic())
1492 {
1493 m_StreamState=eMusicStreamState_Stop;
1494
1495 // Set the end track
1496 m_musicID = getMusicID(LevelData::DIMENSION_END);
1497 SetIsPlayingEndMusic(true);
1498 SetIsPlayingNetherMusic(false);
1499 }
1500 else if(!playerInEnd && GetIsPlayingEndMusic())
1501 {
1502 if(playerInNether)
1503 {
1504 m_StreamState=eMusicStreamState_Stop;
1505
1506 // Set the end track
1507 m_musicID = getMusicID(LevelData::DIMENSION_NETHER);
1508 SetIsPlayingEndMusic(false);
1509 SetIsPlayingNetherMusic(true);
1510 }
1511 else
1512 {
1513 m_StreamState=eMusicStreamState_Stop;
1514
1515 // Set the end track
1516 m_musicID = getMusicID(LevelData::DIMENSION_OVERWORLD);
1517 SetIsPlayingEndMusic(false);
1518 SetIsPlayingNetherMusic(false);
1519 }
1520 }
1521 else if (playerInNether && !GetIsPlayingNetherMusic())
1522 {
1523 m_StreamState=eMusicStreamState_Stop;
1524 // set the Nether track
1525 m_musicID = getMusicID(LevelData::DIMENSION_NETHER);
1526 SetIsPlayingNetherMusic(true);
1527 SetIsPlayingEndMusic(false);
1528 }
1529 else if(!playerInNether && GetIsPlayingNetherMusic())
1530 {
1531 if(playerInEnd)
1532 {
1533 m_StreamState=eMusicStreamState_Stop;
1534 // set the Nether track
1535 m_musicID = getMusicID(LevelData::DIMENSION_END);
1536 SetIsPlayingNetherMusic(false);
1537 SetIsPlayingEndMusic(true);
1538 }
1539 else
1540 {
1541 m_StreamState=eMusicStreamState_Stop;
1542 // set the Nether track
1543 m_musicID = getMusicID(LevelData::DIMENSION_OVERWORLD);
1544 SetIsPlayingNetherMusic(false);
1545 SetIsPlayingEndMusic(false);
1546 }
1547 }
1548
1549 // volume change required?
1550 if(fMusicVol!=getMasterMusicVolume())
1551 {
1552 fMusicVol=getMasterMusicVolume();
1553 HSAMPLE hSample = AIL_stream_sample_handle( m_hStream);
1554 //AIL_set_sample_3D_position( hSample, m_StreamingAudioInfo.x, m_StreamingAudioInfo.y, m_StreamingAudioInfo.z );
1555 AIL_set_sample_volume_levels( hSample, fMusicVol, fMusicVol);
1556 }
1557 }
1558 }
1559 else
1560 {
1561 // Music disc playing - if it's a 3D stream, then set the position - we don't have any streaming audio in the world that moves, so this isn't
1562 // required unless we have more than one listener, and are setting the listening position to the origin and setting a fake position
1563 // for the sound down the z axis
1564 if(m_StreamingAudioInfo.bIs3D)
1565 {
1566 if(m_validListenerCount>1)
1567 {
1568 float fClosest=10000.0f;
1569 int iClosestListener=0;
1570 float fClosestX=0.0f,fClosestY=0.0f,fClosestZ=0.0f,fDist;
1571
1572 // need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision
1573 for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ )
1574 {
1575 if( m_ListenerA[i].bValid )
1576 {
1577 float x,y,z;
1578
1579 x=fabs(m_ListenerA[i].vPosition.x-m_StreamingAudioInfo.x);
1580 y=fabs(m_ListenerA[i].vPosition.y-m_StreamingAudioInfo.y);
1581 z=fabs(m_ListenerA[i].vPosition.z-m_StreamingAudioInfo.z);
1582 fDist=x+y+z;
1583
1584 if(fDist<fClosest)
1585 {
1586 fClosest=fDist;
1587 fClosestX=x;
1588 fClosestY=y;
1589 fClosestZ=z;
1590 iClosestListener=i;
1591 }
1592 }
1593 }
1594
1595 // our distances in the world aren't very big, so floats rather than casts to doubles should be fine
1596 HSAMPLE hSample = AIL_stream_sample_handle( m_hStream);
1597 fDist=sqrtf((fClosestX*fClosestX)+(fClosestY*fClosestY)+(fClosestZ*fClosestZ));
1598 AIL_set_sample_3D_position( hSample, 0, 0, fDist );
1599 }
1600 }
1601 }
1602
1603 break;
1604
1605 case eMusicStreamState_Completed:
1606 {
1607 // random delay of up to 3 minutes for music
1608 m_iMusicDelay = random->nextInt(20 * 60 * 3);//random->nextInt(20 * 60 * 10) + 20 * 60 * 10;
1609 // Check if we have a local player in The Nether or in The End, and play that music if they are
1610 Minecraft *pMinecraft=Minecraft::GetInstance();
1611 bool playerInEnd=false;
1612 bool playerInNether=false;
1613
1614 for(unsigned int i=0;i<MAX_LOCAL_PLAYERS;i++)
1615 {
1616 if(pMinecraft->localplayers[i]!=NULL)
1617 {
1618 if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_END)
1619 {
1620 playerInEnd=true;
1621 }
1622 else if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_NETHER)
1623 {
1624 playerInNether=true;
1625 }
1626 }
1627 }
1628 if(playerInEnd)
1629 {
1630 m_musicID = getMusicID(LevelData::DIMENSION_END);
1631 SetIsPlayingEndMusic(true);
1632 SetIsPlayingNetherMusic(false);
1633 }
1634 else if(playerInNether)
1635 {
1636 m_musicID = getMusicID(LevelData::DIMENSION_NETHER);
1637 SetIsPlayingNetherMusic(true);
1638 SetIsPlayingEndMusic(false);
1639 }
1640 else
1641 {
1642 m_musicID = getMusicID(LevelData::DIMENSION_OVERWORLD);
1643 SetIsPlayingNetherMusic(false);
1644 SetIsPlayingEndMusic(false);
1645 }
1646
1647 m_StreamState=eMusicStreamState_Idle;
1648 }
1649 break;
1650 }
1651
1652 // check the status of the stream - this is for when a track completes rather than is stopped by the user action
1653
1654 if(m_hStream!=0)
1655 {
1656 if(AIL_stream_status(m_hStream)==SMP_DONE ) // SMP_DONE
1657 {
1658 AIL_close_stream(m_hStream);
1659 m_hStream=0;
1660 SetIsPlayingStreamingCDMusic(false);
1661 SetIsPlayingStreamingGameMusic(false);
1662
1663 m_StreamState=eMusicStreamState_Completed;
1664 }
1665 }
1666}
1667
1668
1669/////////////////////////////////////////////
1670//
1671// ConvertSoundPathToName
1672//
1673/////////////////////////////////////////////
1674char *SoundEngine::ConvertSoundPathToName(const wstring& name, bool bConvertSpaces)
1675{
1676 static char buf[256];
1677 assert(name.length()<256);
1678 for(unsigned int i = 0; i < name.length(); i++ )
1679 {
1680 wchar_t c = name[i];
1681 if(c=='.') c='/';
1682 if(bConvertSpaces)
1683 {
1684 if(c==' ') c='_';
1685 }
1686 buf[i] = (char)c;
1687 }
1688 buf[name.length()] = 0;
1689 return buf;
1690}
1691
1692#endif
1693
1694
1695F32 AILCALLBACK custom_falloff_function (HSAMPLE S,
1696 F32 distance,
1697 F32 rolloff_factor,
1698 F32 min_dist,
1699 F32 max_dist)
1700{
1701 F32 result;
1702
1703 // This is now emulating the linear fall-off function that we used on the Xbox 360. The parameter which is passed as "max_dist" is the only one actually used,
1704 // and is generally used as CurveDistanceScaler is used on XACT on the Xbox. A special value of 10000.0f is passed for thunder, which has no attenuation
1705
1706 if( max_dist == 10000.0f )
1707 {
1708 return 1.0f;
1709 }
1710
1711 result = 1.0f - ( distance / max_dist );
1712 if( result < 0.0f ) result = 0.0f;
1713 if( result > 1.0f ) result = 1.0f;
1714
1715 return result;
1716}