Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
music.cpp
Go to the documentation of this file.
1 #include "vegastrike.h"
2 #include "vs_globals.h"
3 
4 #include "audiolib.h"
5 #include "universe.h"
6 #include "star_system.h"
7 #include "vs_globals.h"
8 #include "config_xml.h"
9 #include "lin_time.h"
10 #include "collection.h"
11 #include "unit_generic.h"
12 #include "vsfilesystem.h"
13 #ifdef _WIN32
14 #ifndef NOMINMAX
15 #define NOMINMAX
16 #endif //tells VCC not to generate min/max macros
17 #include <windows.h>
18 #include <io.h>
19 #include <fcntl.h>
20 #include <process.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <direct.h>
24 #include <stdlib.h>
25 #endif
26 #include "music.h"
27 #include "base.h"
28 #include "networking/inet_file.h"
29 #include "networking/inet.h"
30 #include "python/python_compile.h"
31 
32 //To allow for loading in another thread, we must handle some AL vars ourselves...
33 #include "aldrv/al_globals.h"
34 #include <map>
35 #include <set>
36 #include <algorithm>
37 
38 #define MAX_RECENT_HISTORY "5"
39 
40 Music *muzak = NULL;
41 int muzak_count = 0;
43 
44 static void print_check_err( int errorcode, const char *str )
45 {
46 #ifndef _WIN32
47  if (errorcode) {
48  static char const unknown_error[16] = "Unknown error"; //added by chuck_starchaser to get rid of warning
49  char const *err = strerror( errorcode );
50  if (!err) err = unknown_error;
51  fprintf( stderr, "ERROR IN PTHREAD FUNCTION %s: %s (%d)\n", str, err, errorcode );
52  }
53 #endif
54 }
55 
56 //where func is the evaluation of func, and #func is the string form.
57 #define checkerr(func) do{print_check_err(((func)),#func);}while(0)
58 
60 {
61  static bool ret = XMLSupport::parse_bool( vs_config->getVariable( "audio", "pierce_firewall", "true" ) );
62  return ret;
63 }
64 Music::Music( Unit *parent ) : random( false )
65  , p( parent )
66  , song( -1 )
67  , thread_initialized( false )
68 {
69  loopsleft = 0;
70  socketw = socketr = -1;
71  music_load_info = NULL;
72  music_loaded = false;
73  music_loading = false;
74  killthread = 0;
75  threadalive = 0;
76  freeWav = true;
77 #ifdef HAVE_AL
78  music_load_info = new AUDSoundProperties;
79 #endif
80 
81 #ifndef USE_SOUNDSERVER
82 #ifdef _WIN32
83  musicinfo_mutex = CreateMutex( NULL, TRUE, NULL );
84 #else //_WIN32
85 #ifdef ERRORCHECK_MUTEX
86  pthread_mutexattr_t checkme;
87  pthread_mutexattr_init( &checkme );
88  pthread_mutexattr_settype( &checkme, PTHREAD_MUTEX_ERRORCHECK );
89  checkerr( pthread_mutex_init( &musicinfo_mutex, &checkme ) );
90 #else //ERRORCHECK_MUTEX
91  checkerr( pthread_mutex_init( &musicinfo_mutex, NULL ) );
92 #endif
93 
94  //Lock it immediately, since the loader will want to wait for its first data upon creation.
95  checkerr( pthread_mutex_lock( &musicinfo_mutex ) );
96 #endif
97 #endif
98  if (!g_game.music_enabled)
99  return;
100  lastlist = PEACELIST;
101  if (parent)
102  maxhull = parent->GetHull();
103  else
104  maxhull = 1;
105  int i;
106  const char *listvars[MAXLIST] = {"battleplaylist", "peaceplaylist", "panicplaylist", "victoryplaylist", "lossplaylist"};
107  const char *deflistvars[MAXLIST] = {"battle.m3u", "peace.m3u", "panic.m3u", "victory.m3u", "loss.m3u"};
108  for (i = 0; i < MAXLIST; i++)
109  LoadMusic( vs_config->getVariable( "audio", listvars[i], deflistvars[i] ).c_str() );
110 #ifdef USE_SOUNDSERVER
111 #if !defined (_WIN32)
112  if ( g_game.music_enabled && !soundServerPipes() ) {
113  int pid = fork();
114  if (!pid) {
115  string soundserver_path = VSFileSystem::datadir+"/bin/soundserver";
116  pid = execlp( soundserver_path.c_str(), soundserver_path.c_str(), NULL );
117  soundserver_path = VSFileSystem::datadir+"/soundserver";
118  pid = execlp( soundserver_path.c_str(), soundserver_path.c_str(), NULL );
119  g_game.music_enabled = false;
120  VSFileSystem::vs_fprintf( stderr, "Unable to spawn music player server\n" );
121  exit( 0 );
122  } else if (pid == -1) {
123  g_game.music_enabled = false;
124  }
125  }
126 #endif
127 #if defined (_WIN32) && !defined (__CYGWIN__)
128  if ( g_game.music_enabled && !soundServerPipes() ) {
129  string ss_path = VSFileSystem::datadir+"/soundserver.exe";
130  int pid = spawnl( P_NOWAIT, ss_path.c_str(), ss_path.c_str(), NULL );
131  if (pid == -1) {
132  ss_path = VSFileSystem::datadir+"/bin/soundserver.exe";
133  chdir( "bin" );
134  int pid = spawnl( P_NOWAIT, ss_path.c_str(), ss_path.c_str(), NULL );
135  if (pid == -1) {
136  g_game.music_enabled = false;
137  VSFileSystem::vs_fprintf( stderr, "Unable to spawn music player server Error (%d)\n", pid );
138  }
139  }
140  }
141 #endif
142  if ( soundServerPipes() ) {
143  fNET_startup();
144  int pipesw[2];
145  int pipesr[2];
146  socketw = socketr = -1; //FIXME --"How?" --chuck_starchaser
147 #ifdef _WIN32
148 #define pipe _pipe
149 #endif
150  if ( 0 == pipe( pipesw
151 #ifdef _WIN32
152  , 32, O_BINARY
153 #endif
154  )
155  && 0 == pipe( pipesr
156 #ifdef _WIN32
157  , 32, O_BINARY
158 #endif
159  ) ) {
160  socketw = pipesw[1];
161  socketr = pipesr[0];
162  char buffer1[32];
163  char buffer2[32];
164  sprintf( buffer1, "%d", pipesw[0] );
165  sprintf( buffer2, "%d", pipesr[1] );
166 
167 #if defined (_WIN32) && !defined (__CYGWIN__)
168  string ss_path = VSFileSystem::datadir+"/soundserver.exe";
169  int pid = spawnl( P_NOWAIT, ss_path.c_str(), ss_path.c_str(), buffer1, buffer2, NULL );
170  if (pid == -1) {
171  ss_path = VSFileSystem::datadir+"/bin/soundserver.exe";
172  bool chsuccess = (chdir( "bin" ) == 0);
173  int pid = spawnl( P_NOWAIT, ss_path.c_str(), ss_path.c_str(), buffer1, buffer2, NULL );
174  if (chsuccess) chdir( ".." );
175  if (pid == -1) {
176  g_game.music_enabled = false;
177  VSFileSystem::vs_fprintf( stderr, "Unable to spawn music player server Error (%d)\n", pid );
178  }
179  }
180 #else
181  if (g_game.music_enabled) {
182  std::string tmp = VSFileSystem::datadir+"/bin/soundserver";
183  FILE *fp = fopen( tmp.c_str(), "rb" );
184  if (!fp) {
185  tmp = VSFileSystem::datadir+"/soundserver";
186  fp = fopen( tmp.c_str(), "rb" );
187  if (!fp) {
188  g_game.music_enabled = false;
189  socketw = -1;
190  socketr = -1;
191  } else {fclose( fp ); }} else {fclose( fp ); }}
192  if (g_game.music_enabled) {
193  int pid = fork();
194  if (!pid) {
195  string soundserver_path = VSFileSystem::datadir+"/bin/soundserver";
196  pid = execlp( soundserver_path.c_str(), soundserver_path.c_str(), buffer1, buffer2, NULL );
197  soundserver_path = VSFileSystem::datadir+"/soundserver";
198  pid = execlp( soundserver_path.c_str(), soundserver_path.c_str(), buffer1, buffer2, NULL );
199  g_game.music_enabled = false;
200  VSFileSystem::vs_fprintf( stderr, "Unable to spawn music player server\n" );
201  close( atoi( buffer1 ) );
202  close( atoi( buffer2 ) );
203  exit( 0 );
204  } else if (pid == -1) {
205  g_game.music_enabled = false;
206  }
207  }
208 #endif
209  }
210  } else {
211  int socket = -1;
212  INET_startup();
213  for (i = 0; (i < 10) && (socket == -1); i++)
214  socket = INET_ConnectTo( "localhost", 4364 );
215  socketw = socketr = socket;
216  }
217  if (socketw == -1 || socketr == -1) {
218  g_game.music_enabled = false;
219  } else {
220  string data = string( "i" )+vs_config->getVariable( "audio", "music_fadein", "0" )+"\n"
221  "o"+vs_config->getVariable( "audio", "music_fadeout", "0" )+"\n";
222  if ( soundServerPipes() )
223  fNET_Write( socketw, data.size(), data.c_str() );
224  else
225  INET_Write( socketw, data.size(), data.c_str() );
226  }
227 #endif /* USE_SOUNDSERVER */
228  soft_vol_up_latency = XMLSupport::parse_float( vs_config->getVariable( "audio", "music_volume_up_latency", "15" ) );
229  soft_vol_down_latency = XMLSupport::parse_float( vs_config->getVariable( "audio", "music_volume_down_latency", "2" ) );
230  //Hardware volume = 1
231  _SetVolume( 1, true );
232  //Software volume = from config
233  _SetVolume( XMLSupport::parse_float( vs_config->getVariable( "audio", "music_volume", ".5" ) ), false );
234 }
235 
236 void Music::ChangeVolume( float inc, int layer )
237 {
238  if (!g_game.music_enabled)
239  return;
240  if (muzak) {
241  if (layer < 0)
242  for (int i = 0; i < muzak_count; i++)
243  muzak[i]._SetVolume( muzak[i].soft_vol+inc, false, 0.1 );
244  else if ( (layer >= 0) && (layer < muzak_count) )
245  muzak[layer]._SetVolume( muzak[layer].soft_vol+inc, false, 0.1 );
246  }
247 }
248 
249 #ifdef USE_SOUNDSERVER
250 static float tmpmin( float a, float b )
251 {
252  return a < b ? a : b;
253 }
254 static float tmpmax( float a, float b )
255 {
256  return a < b ? b : a;
257 }
258 #endif
259 
260 void Music::_SetVolume( float vol, bool hardware, float latency_override )
261 {
262  if (!g_game.music_enabled)
263  return;
264 #ifdef USE_SOUNDSERVER
265  vol = tmpmax( 0.0f, tmpmin( (hardware ? 1.0f : 2.0f), vol ) );
266  char tempbuf[100];
267  if ( ( hardware && (this->vol == vol) )
268  || ( !hardware && (this->soft_vol == vol) ) )
269  return;
270  if (hardware)
271  sprintf( tempbuf, "vh%f\n", vol );
272 
273  else
274  sprintf( tempbuf, "vs%f\n%f\n", vol,
275  ( (latency_override
276  < 0) ? ( (vol > soft_vol) ? soft_vol_up_latency : soft_vol_down_latency ) : latency_override ) );
277  if (hardware)
278  this->vol = vol;
279 
280  else
281  this->soft_vol = vol;
282  if ( soundServerPipes() )
283  fNET_Write( socketw, strlen( tempbuf ), tempbuf );
284 
285  else
286  INET_Write( socketw, strlen( tempbuf ), tempbuf );
287 #else
288  if (vol < 0) vol = 0;
289  this->vol = vol;
290  this->soft_vol = vol; //for now fixme for fading
291  for (std::list< int >::const_iterator iter = playingSource.begin(); iter != playingSource.end(); iter++)
292  AUDSoundGain( *iter, soft_vol, true );
293 #endif
294 }
295 
296 bool Music::LoadMusic( const char *file )
297 {
298  using namespace VSFileSystem;
299  if (!g_game.music_enabled)
300  return true;
301  //Loads a playlist so try to open a file in datadir or homedir
302  VSFile f;
303  VSError err = f.OpenReadOnly( file, UnknownFile );
304  if (err > Ok)
305  err = f.OpenReadOnly( VSFileSystem::HOMESUBDIR+"/"+file, UnknownFile );
306  char songname[1024];
307  this->playlist.push_back( PlayList() );
308  if (err <= Ok) {
309  while ( !f.Eof() ) {
310  songname[0] = '\0';
311  f.ReadLine( songname, 1022 );
312  int size = strlen( songname );
313  if (size >= 1)
314  if (songname[size-1] == '\n')
315  songname[size-1] = '\0';
316  if (size > 1)
317  if (songname[size-2] == '\r' || songname[size-2] == '\n')
318  songname[size-2] = '\0';
319  if (songname[0] == '\0')
320  continue;
321  if (songname[0] == '#') {
322  if (strncmp( songname, "#pragma ", 8 ) == 0) {
323  char *sep = strchr( songname+8, ' ' );
324  if (sep) {
325  *sep = 0;
326  this->playlist.back().pragmas[songname+8] = sep+1;
327  } else if (songname[8]) {
328  this->playlist.back().pragmas[songname+8] = "1";
329  }
330  }
331  continue;
332  }
333  this->playlist.back().songs.push_back( std::string( songname ) );
334  }
335  f.Close();
336  } else {
337  return false;
338  }
339  return true;
340 }
341 
342 static int randInt( int max )
343 {
344  int ans = int( ( ( (double) rand() )/( (double) RAND_MAX ) )*max );
345  if (ans == max)
346  return max-1;
347  return ans;
348 }
349 
350 int Music::SelectTracks( int layer )
351 {
352  if (!g_game.music_enabled)
353  return 0;
354  static bool random = XMLSupport::parse_bool( vs_config->getVariable( "audio", "shuffle_songs", "true" ) );
355  static size_t maxrecent =
356  XMLSupport::parse_int( vs_config->getVariable( "audio", "shuffle_songs.history_depth", MAX_RECENT_HISTORY ) );
357  static std::string dj_script = vs_config->getVariable( "sound", "dj_script", "modules/dj.py" );
358  if ( (BaseInterface::CurrentBase || loopsleft > 0) && lastlist < (int) playlist.size() && lastlist >= 0 ) {
359  if (loopsleft > 0)
360  loopsleft--;
361  if ( !playlist[lastlist].empty() && !playlist[lastlist].haspragma( "norepeat" ) ) {
362  int whichsong = (random ? rand() : playlist[lastlist].counter++)%playlist[lastlist].size();
363  int spincount = 10;
364  std::list< std::string > &recent = muzak[(layer >= 0) ? layer : 0].recent_songs;
365  while ( random && (--spincount > 0)
366  && ( std::find( recent.begin(), recent.end(), playlist[lastlist][whichsong] ) != recent.end() ) )
367  whichsong = (random ? rand() : playlist[lastlist].counter++)%playlist[lastlist].size();
368  if (spincount <= 0)
369  recent.clear();
370  recent.push_back( playlist[lastlist][whichsong] );
371  while (recent.size() > maxrecent)
372  recent.pop_front();
373  GotoSong( lastlist, whichsong, true, layer );
374  return whichsong;
375  }
376  }
377  if ( _Universe && _Universe->numPlayers() ) {
378  CompileRunPython( dj_script );
379  } else {
380  static std::string loading_tune = vs_config->getVariable( "audio", "loading_sound", "../music/loading.ogg" );
381  GotoSong( loading_tune, layer );
382  }
383  return 0;
384 }
385 
386 namespace Muzak
387 {
388 #ifndef USE_SOUNDSERVER
389 std::map< std::string, AUDSoundProperties >cachedSongs;
390 #endif
391 
392 #ifndef _WIN32
393 void*
394 #else
395 DWORD WINAPI
396 #endif
398 #ifdef _WIN32
399  PVOID
400 #else
401  void*
402 #endif
403  input )
404 {
405  Music *me = (Music*) input;
406  me->threadalive = 1;
407 #ifdef USE_SOUNDSERVER
408  int socketr = me->socketr;
409  while (!me->killthread) {
410  printf( "Reading from socket %d\n", socketr );
411  char data = fNET_fgetc( socketr );
412  printf( "Got data from socket %c\n", data );
413  if (data == 'e')
414  me->moredata = 1;
415  micro_sleep( 100000 );
416  }
417 #else
418  while (!me->killthread) {
419 #ifdef _WIN32
420  WaitForSingleObject( me->musicinfo_mutex, INFINITE );
421 #else
422  checkerr( pthread_mutex_lock( &me->musicinfo_mutex ) );
423 #endif
424  if (me->killthread) break;
425  me->music_loading = true;
426  me->music_loaded = false;
427  me->music_load_info->success = false;
428  size_t len = me->music_load_info->hashname.length();
429  char *songname = (char*) malloc( len+1 );
430  songname[len] = '\0';
431  memcpy( songname, me->music_load_info->hashname.data(), len );
432  std::map< std::string, AUDSoundProperties >::iterator wherecache = cachedSongs.find( songname );
433  bool foundcache = wherecache != cachedSongs.end();
434  static std::string cachable_songs = vs_config->getVariable( "audio", "cache_songs", "../music/land.ogg" );
435  bool docacheme = cachable_songs.find( songname ) != std::string::npos;
436  if (foundcache == false && docacheme) {
437  me->music_load_info->wave = NULL;
438  cachedSongs[songname] = *me->music_load_info;
439  wherecache = cachedSongs.find( songname );
440  }
441 #ifdef _WIN32
442  ReleaseMutex( me->musicinfo_mutex );
443 #else
444  checkerr( pthread_mutex_unlock( &me->musicinfo_mutex ) );
445 #endif
446  {
447  me->freeWav = true;
448  if (foundcache) {
449  *me->music_load_info = wherecache->second;
450  me->freeWav = false;
451  }else if
452  ( !AUDLoadSoundFile( songname, me->music_load_info, true ) )
453  {VSFileSystem::vs_dprintf(1, "Failed to load music file \"%s\"", songname);}
454 
455  }
456  if (me->freeWav && docacheme) {
457  me->freeWav = false;
458  wherecache->second = *me->music_load_info;
459  }
460  free( songname );
461  me->music_loaded = true;
462  while (me->music_loaded)
463  micro_sleep( 10000 ); //10ms of busywait for now... wait until end of frame.
464  }
465 #endif /* !USE_SOUNDSERVER */
466  me->threadalive = 0;
467  return NULL;
468 }
469 }
470 
471 void Music::_LoadLastSongAsync()
472 {
473 #ifndef USE_SOUNDSERVER
474 #ifdef HAVE_AL
475  if (!g_game.music_enabled)
476  return;
477  if (!music_load_info) return;
478  if (music_loading)
479  //No touching anything here!
480  return;
481  std::string song = music_load_list.back();
482 
483  std::map< std::string, AUDSoundProperties >::iterator where = Muzak::cachedSongs.find( song );
484  if ( where != Muzak::cachedSongs.end() )
485  if (where->second.wave != NULL) {
486  int source = AUDBufferSound( &where->second, true );
487  AUDStreamingSound( source );
488 
489  music_load_info->wave = NULL;
490  if (source != -1)
491  playingSource.push_back( source );
492  if (playingSource.size() == 1) {
493  //Start playing if first in list.
494  _StopNow();
495  AUDStartPlaying( playingSource.front() );
496  AUDSoundGain( playingSource.front(), vol, true );
497  }
498  return;
499  }
500  music_load_info->hashname = song;
501 #endif
502  music_loading = true;
503 #ifdef _WIN32
504  ReleaseMutex( musicinfo_mutex );
505 #else
506  checkerr( pthread_mutex_unlock( &musicinfo_mutex ) );
507 #endif
508 #endif
509 }
510 
512 {
513  if (g_game.music_enabled) {
514 #ifdef USE_SOUNDSERVER
515  if ( soundServerPipes() ) {
516  killthread = 0;
517  threadalive = 0;
518  if (!thread_initialized) {
519 #ifdef _WIN32
520  a_thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) Muzak::readerThread, (PVOID) this, 0, NULL );
521 #else
522  int res = pthread_create( &a_thread, NULL, Muzak::readerThread, (void*) this );
523 #endif
524  thread_initialized = 1;
525  }
526  //int bytes=fNET_BytesToRead(socketr);
527  if (moredata) {
528  moredata = 0;
529  cur_song_file = "";
530  _Skip();
531  }
532  } else {
533  int bytes = INET_BytesToRead( socketr );
534  if (bytes) {
535  char data;
536  while (bytes) {
537  data = INET_fgetc( socketr );
538  bytes--;
539  }
540  if (data == 'e') {
541  cur_song_file = "";
542  _Skip();
543  } else {
544  g_game.music_enabled = false;
545  }
546  }
547  }
548 #else /* USE_SOUNDSERVER */
549  if ( !music_load_list.empty() ) {
550  if (music_loaded) {
551  //fprintf(stderr,"LOADED is true\n");
552 #ifdef _WIN32
553  int trylock_ret = 0;
554  if (WaitForSingleObject( musicinfo_mutex, 0 ) == WAIT_TIMEOUT) {
555 #else
556  int trylock_ret = pthread_mutex_trylock( &musicinfo_mutex );
557  if (trylock_ret == EBUSY) {
558 #endif
559  fprintf( stderr, "Failed to lock music loading mutex despite loaded flag being set...\n" );
560  return;
561  } else {
562  checkerr( trylock_ret );
563  }
564  music_loading = false;
565  music_loaded = false; //once the loading thread sees this, it will try to grab a lock and wait.
566  //The lock will only be achieved once the next song is put in the queue.
567 
568 #ifdef HAVE_AL
569  if (music_load_info->success && music_load_info->wave) {
570  int source = AUDBufferSound( music_load_info, true );
571  if (freeWav)
572  free( music_load_info->wave );
573  music_load_info->wave = NULL;
574  if (source != -1)
575  playingSource.push_back( source );
576  }
577 #endif
578  if (playingSource.size() == 1) {
579  //Start playing if first in list.
580  _StopNow();
581  AUDStartPlaying( playingSource.front() );
582  AUDStreamingSound( playingSource.front() );
583  AUDSoundGain( playingSource.front(), vol, true );
584  }
585  music_load_list.pop_back();
586  if ( !music_load_list.empty() )
587  _LoadLastSongAsync();
588  return; //Returns if finished loading, since the AUDIsPlaying() could fail right now.
589  }
590  }
591  if ( !playingSource.empty() )
592  if ( !AUDIsPlaying( playingSource.front() ) ) {
593  AUDDeleteSound( playingSource.front(), true );
594  playingSource.pop_front();
595  if ( !playingSource.empty() ) {
596  _StopNow();
597  AUDStartPlaying( playingSource.front() );
598  AUDSoundGain( playingSource.front(), vol, true );
599  }
600  }
601  if ( playingSource.empty() && muzak[muzak_cross_index].playingSource.empty()
602  && music_load_list.empty() && muzak[muzak_cross_index].music_load_list.empty() ) {
603  cur_song_file = "";
604  _Skip();
605  }
606 #endif /* !USE_SOUNDSERVER */
607  }
608 }
609 
610 void Music::GotoSong( std::string mus, int layer )
611 {
612  if (!g_game.music_enabled)
613  return;
614  static bool cross = XMLSupport::parse_bool( vs_config->getVariable( "audio", "cross_fade_music", "true" ) );
615  if ( cross && (muzak_count >= 2) ) {
616  if (layer < 0) {
617  if (mus == muzak[muzak_cross_index].cur_song_file) return;
618  muzak[muzak_cross_index]._StopLater();
620  muzak[muzak_cross_index]._GotoSong( mus );
621  } else if ( (layer >= 0) && (layer < muzak_count) ) {
622  if (mus == muzak[layer].cur_song_file) return;
623  muzak[layer]._GotoSong( mus );
624  }
625  } else {
626  muzak->_GotoSong( mus );
627  }
628 }
629 
630 std::vector< std::string >rsplit( std::string tmpstr, std::string splitter )
631 {
632  std::string::size_type where;
633  std::vector< std::string >ret;
634  while ( ( where = tmpstr.rfind( splitter ) ) != std::string::npos ) {
635  ret.push_back( tmpstr.substr( where+1 ) );
636  tmpstr = tmpstr.substr( 0, where );
637  }
638  if ( tmpstr.length() )
639  ret.push_back( tmpstr );
640  return ret;
641 }
642 
643 void Music::_GotoSong( std::string mus )
644 {
645  if (g_game.music_enabled) {
646  if (mus == cur_song_file || mus.length() == 0) return;
647  cur_song_file = mus;
648 
649 #ifndef USE_SOUNDSERVER
650  _StopLater(); //Kill all our currently playing songs.
651 
652  music_load_list = rsplit( mus, "|" ); //reverse order.
653  if (!thread_initialized) {
654 #ifdef _WIN32
655  a_thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) Muzak::readerThread, (PVOID) this, 0, NULL );
656  if (a_thread)
657  thread_initialized = true;
658  else
659  fprintf( stderr, "Error creating music load thread: %d\n", GetLastError() );
660 #else
661  int thread_create_ret = pthread_create( &a_thread, NULL, Muzak::readerThread, this );
662  if (thread_create_ret == 0)
663  thread_initialized = true;
664  else
665  checkerr( thread_create_ret );
666 #endif
667  }
668  _LoadLastSongAsync();
669 #else /* !USE_SOUNDSERVER */
670  string data = string( "p" )+mus+string( "\n" );
671  if ( soundServerPipes() )
672  fNET_Write( socketw, data.size(), data.c_str() );
673  else
674  INET_Write( socketw, data.size(), data.c_str() );
675 #endif
676  }
677 }
678 
679 void Music::GotoSong( int whichlist, int whichsong, bool skip, int layer )
680 {
681  if (g_game.music_enabled) {
682  if ( whichsong != NOLIST && whichlist != NOLIST && whichlist < (int) playlist.size() && whichsong
683  < (int) playlist[whichlist].size() ) {
684  if (muzak[(layer >= 0) ? layer : 0].lastlist != whichlist) {
685  static bool clear =
686  XMLSupport::parse_bool( vs_config->getVariable( "audio", "shuffle_songs.clear_history_on_list_change",
687  "true" ) );
688  if (clear) {
689  std::list< std::string > &recent = muzak[(layer >= 0) ? layer : 0].recent_songs;
690  recent.clear();
691  }
692  }
693  if ( (layer < 0) && (muzak_count >= 2) )
694  muzak[0].lastlist = muzak[1].lastlist = whichlist;
695 
696  else
697  lastlist = whichlist;
698  GotoSong( playlist[whichlist][whichsong], layer );
699  } else {
700  _SkipRandList( layer );
701  }
702  }
703 }
704 
705 void Music::SkipRandSong( int whichlist, int layer )
706 {
707  if (muzak) {
708  if (layer < 0) {
709  if (muzak_count >= 2)
710  muzak[muzak_cross_index]._SkipRandSong( whichlist );
711 
712  else
713  muzak->_SkipRandSong( whichlist );
714  } else if ( (layer >= 0) && (layer < muzak_count) ) {
715  muzak[layer]._SkipRandSong( whichlist, layer );
716  }
717  }
718 }
719 
720 void Music::_SkipRandSong( int whichlist, int layer )
721 {
722  if (!g_game.music_enabled)
723  return;
724  if (this != NULL) {
725  if ( whichlist != NOLIST && whichlist >= 0 && whichlist < (int) playlist.size() ) {
726  lastlist = whichlist;
727  static bool random = XMLSupport::parse_bool( vs_config->getVariable( "audio", "shuffle_songs", "true" ) );
728  if ( playlist[whichlist].size() )
729  GotoSong( whichlist, random ? randInt( playlist[whichlist].size() ) : playlist[whichlist].counter++
730  %playlist[whichlist].size(), true, layer );
731  else
732  fprintf( stderr, "Error no songs in playlist %d\n", whichlist );
733  return;
734  }
735  }
736  _SkipRandList( layer );
737 }
738 
739 void Music::SkipRandList( int layer )
740 {
741  if (!g_game.music_enabled)
742  return;
743  if (muzak) {
744  if (layer < 0) {
745  if (muzak_count >= 2)
746  muzak[muzak_cross_index]._SkipRandList();
747 
748  else
749  muzak->_SkipRandList();
750  } else if ( (layer >= 0) && (layer < muzak_count) ) {
751  muzak[layer]._SkipRandList( layer );
752  }
753  }
754 }
755 
756 void Music::_SkipRandList( int layer )
757 {
758  if (!g_game.music_enabled)
759  return;
760  for (unsigned int i = 0; i < playlist.size(); i++) {
761  static bool random = XMLSupport::parse_bool( vs_config->getVariable( "audio", "shuffle_songs", "true" ) );
762  if ( !playlist[i].empty() )
763  GotoSong( i, random ? randInt( playlist[i].size() ) : playlist[i].counter++%playlist[i].size(), false, layer );
764  }
765 }
766 
767 int Music::Addlist( std::string listfile )
768 {
769  if (!g_game.music_enabled)
770  return -1;
771  int res = -1;
772  if (muzak) res = muzak->_Addlist( listfile );
773  if (muzak)
774  for (int i = 1; i < muzak_count; i++)
775  muzak[i]._Addlist( listfile );
776  return res;
777 }
778 
779 int Music::_Addlist( std::string listfile )
780 {
781  if (!g_game.music_enabled)
782  return -1;
783  bool retval = LoadMusic( listfile.c_str() );
784  if (retval)
785  return playlist.size()-1;
786  else
787  return -1;
788 }
789 
790 void Music::Skip( int layer )
791 {
792  if (!g_game.music_enabled)
793  return;
794  if (muzak) {
795  if (layer < 0) {
796  if (muzak_count >= 2)
797  muzak[muzak_cross_index]._Skip();
798 
799  else
800  muzak->_Skip();
801  } else if ( (layer >= 0) && (layer < muzak_count) ) {
802  muzak[layer]._Skip( layer );
803  }
804  }
805 }
806 
807 void Music::_Skip( int layer )
808 {
809  if (g_game.music_enabled)
810  SelectTracks( layer );
811 }
812 
814 {
815 #ifdef USE_SOUNDSERVER
816  char send[2] = {'t', '\n'};
817  if (socketw != -1) {
818  if ( soundServerPipes() ) {
819  if (threadalive && thread_initialized) {
820  killthread = 1;
821  int spindown = 50; //Thread has 5 seconds to close down.
822  while ( threadalive && (spindown-- > 0) )
823  micro_sleep( 100000 );
824  }
825  fNET_Write( socketw, 2, send );
826  fNET_close( socketw );
827  //fNET_close(socketr);
828  fNET_cleanup();
829  } else {
830  INET_Write( socketw, 2, send );
831  INET_close( socketw );
832  INET_cleanup();
833  }
834  socketw = -1;
835  }
836 #else /* USE_SOUNDSERVER */
837  if (threadalive && thread_initialized) {
838  killthread = 1;
839 #ifdef _WIN32
840  ReleaseMutex( musicinfo_mutex );
841 #else
842  checkerr( pthread_mutex_unlock( &musicinfo_mutex ) );
843 #endif
844  int spindown = 50; //Thread has 5 seconds to close down.
845  while ( threadalive && (spindown-- > 0) )
846  micro_sleep( 100000 );
847  if (threadalive)
848  threadalive = false;
849  }
850  //Kill the thread.
851 #endif /* !USE_SOUNDSERVER */
852 }
853 void incmusicvol( const KBData&, KBSTATE a )
854 {
855  if (a == PRESS) Music::ChangeVolume( .0625 );
856 }
857 void decmusicvol( const KBData&, KBSTATE a )
858 {
859  if (a == PRESS) Music::ChangeVolume( -.0625 );
860 }
861 
862 void Music::SetParent( Unit *parent )
863 {
864  p = parent;
865 }
866 
868 {
869  muzak_count = XMLSupport::parse_int( vs_config->getVariable( "audio", "music_layers", "1" ) );
870  muzak = new Music[muzak_count];
871 }
872 
874 {
875  if (muzak) {
876 #ifdef USE_SOUNDSERVER
877  delete[] muzak;
878 #endif
879  //Multithreading issues... don't care to waste time here waiting to get the lock back.
880  //Let the OS clean up this mess!
881  muzak = NULL;
882  muzak_count = 0;
883  }
884 }
885 
887 {
888  if (muzak) {
889  if (BaseInterface::CurrentBase != NULL) {
890  if (!BaseInterface::CurrentBase->isDJEnabled()) {
891  // Bail out... they don't want us running
892  return;
893  }
894  }
895 
896  for (int i = 0; i < muzak_count; i++)
897  muzak[i].Listen();
898  }
899 }
900 
901 void Music::Stop( int layer )
902 {
903  if (muzak) {
904  if (layer < 0) {
905  if (muzak_count >= 2)
906  muzak[muzak_cross_index]._Stop();
907 
908  else
909  muzak->_Stop();
910  } else if ( (layer >= 0) && (layer < muzak_count) ) {
911  muzak[layer]._Stop();
912  }
913  }
914 }
915 void Music::_StopNow()
916 {
917  if (g_game.music_enabled) {
918 #ifdef USE_SOUNDSERVER
919  _Stop();
920 #else
921  for (std::vector< int >::const_iterator iter = sounds_to_stop.begin(); iter != sounds_to_stop.end(); iter++) {
922  int sound = *iter;
923  AUDStopPlaying( sound );
924  AUDDeleteSound( sound, true );
925  }
926  sounds_to_stop.clear();
927 #endif
928  }
929 }
930 void Music::_StopLater()
931 {
932  if (g_game.music_enabled) {
933 #ifdef USE_SOUNDSERVER
934  _Stop();
935 #else
936  for (std::list< int >::const_iterator iter = playingSource.begin(); iter != playingSource.end(); iter++) {
937  int sound = *iter;
938  sounds_to_stop.push_back( sound );
939  }
940  playingSource.clear();
941 #endif
942  }
943 }
944 
945 void Music::_Stop()
946 {
947  if (g_game.music_enabled) {
948 #ifdef USE_SOUNDSERVER
949  cur_song_file = "";
950  char send[1] = {'s'};
951  if ( soundServerPipes() )
952  fNET_Write( socketw, 1, send );
953  else
954  INET_Write( socketw, 1, send );
955 #else
956  for (std::list< int >::const_iterator iter = playingSource.begin(); iter != playingSource.end(); iter++) {
957  int sound = *iter;
958  AUDStopPlaying( sound );
959  AUDDeleteSound( sound, true );
960  }
961  playingSource.clear();
962 #endif
963  }
964 }
965 
966 void Music::SetVolume( float vol, int layer, bool hardware, float latency_override )
967 {
968  if (muzak) {
969  if (layer < 0)
970  for (int i = 0; i < muzak_count; i++)
971  muzak[i]._SetVolume( vol, hardware, latency_override );
972  else if ( (layer >= 0) && (layer < muzak_count) )
973  muzak[layer]._SetVolume( vol, hardware, latency_override );
974  }
975 }
976 
977 void Music::Mute( bool mute, int layer )
978 {
979  static vector< float >saved_vol;
980  saved_vol.resize( muzak_count, -1 );
981  if (!g_game.music_enabled)
982  return;
983  if (muzak) {
984  static float muting_fadeout = XMLSupport::parse_float( vs_config->getVariable( "audio", "music_muting_fadeout", "0.2" ) );
985  static float muting_fadein = XMLSupport::parse_float( vs_config->getVariable( "audio", "music_muting_fadeout", "0.5" ) );
986  if (layer < 0) {
987  for (int i = 0; i < muzak_count; i++) {
988  if (mute) {
989  if (muzak[i].soft_vol != 0) {
990  saved_vol[i] = muzak[i].soft_vol;
991  muzak[i]._SetVolume( 0, false, muting_fadeout );
992  }
993  } else if (saved_vol[i] >= 0) {
994  muzak[i]._SetVolume( saved_vol[i], false, muting_fadein );
995  }
996  }
997  } else if ( (layer >= 0) && (layer < muzak_count) ) {
998  if (mute) {
999  if (muzak[layer].soft_vol != 0) {
1000  saved_vol[layer] = muzak[layer].soft_vol;
1001  muzak[layer]._SetVolume( 0, false, muting_fadeout );
1002  }
1003  } else if (saved_vol[layer] >= 0) {
1004  muzak[layer]._SetVolume( saved_vol[layer], false, muting_fadein );
1005  }
1006  }
1007  }
1008 }
1009 
1010 void Music::SetLoops( int numloops, int layer )
1011 {
1012  if (!g_game.music_enabled)
1013  return;
1014  if (muzak) {
1015  if (layer < 0) {
1016  //This only will apply to the crossfading channel (layers 0 && 1)
1017  SetLoops( numloops, 0 );
1018  SetLoops( numloops, 1 );
1019  } else if ( (layer >= 0) && (layer < muzak_count) ) {
1020  //Specific channel
1021  muzak[layer].loopsleft = numloops;
1022  }
1023  }
1024 }
1025