Merge branch 'master' of git://github.com/BTAxis/naev into testmission
[naev.git] / src / sound.c
blobadc328522c71a55d23a87c2fa1ba984a137fae08
1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
5 /**
6 * @file sound.c
8 * @brief Handles all the sound details.
9 */
12 #include "sound.h"
14 #include "naev.h"
16 #include <sys/stat.h>
18 #include "SDL.h"
19 #include "SDL_thread.h"
20 #include "SDL_mutex.h"
22 #include "sound_priv.h"
23 #include "sound_openal.h"
24 #include "sound_sdlmix.h"
25 #include "log.h"
26 #include "ndata.h"
27 #include "music.h"
28 #include "physics.h"
29 #include "conf.h"
32 #define SOUND_PREFIX "snd/sounds/" /**< Prefix of where to find sounds. */
33 #define SOUND_SUFFIX_WAV ".wav" /**< Suffix of sounds. */
34 #define SOUND_SUFFIX_OGG ".ogg" /**< Suffix of sounds. */
37 #define voiceLock() SDL_LockMutex(voice_mutex)
38 #define voiceUnlock() SDL_UnlockMutex(voice_mutex)
42 * Global sound properties.
44 int sound_disabled = 0; /**< Whether sound is disabled. */
48 * Sound list.
50 static alSound *sound_list = NULL; /**< List of available sounds. */
51 static int sound_nlist = 0; /**< Number of available sounds. */
55 * Voices.
57 static int voice_genid = 0; /**< Voice identifier generator. */
58 alVoice *voice_active = NULL; /**< Active voices. */
59 static alVoice *voice_pool = NULL; /**< Pool of free voices. */
60 static SDL_mutex *voice_mutex = NULL; /**< Lock for voices. */
65 * Function pointers for backends.
67 /* Creation. */
68 int (*sound_sys_init) (void) = NULL;
69 void (*sound_sys_exit) (void) = NULL;
70 /* Sound creation. */
71 int (*sound_sys_load) ( alSound *snd, const char *filename ) = NULL;
72 void (*sound_sys_free) ( alSound *snd ) = NULL;
73 /* Sound settings. */
74 int (*sound_sys_volume) ( const double vol ) = NULL;
75 double (*sound_sys_getVolume) (void) = NULL;
76 /* Sound playing. */
77 int (*sound_sys_play) ( alVoice *v, alSound *s ) = NULL;
78 int (*sound_sys_playPos) ( alVoice *v, alSound *s,
79 double px, double py, double vx, double vy ) = NULL;
80 int (*sound_sys_updatePos) ( alVoice *v, double px, double py,
81 double vx, double vy ) = NULL;
82 void (*sound_sys_updateVoice) ( alVoice *v ) = NULL;
83 /* Sound management. */
84 void (*sound_sys_update) (void) = NULL;
85 void (*sound_sys_stop) ( alVoice *v ) = NULL;
86 void (*sound_sys_pause) (void) = NULL;
87 void (*sound_sys_resume) (void) = NULL;
88 void (*sound_sys_setSpeed) (double s ) = NULL;
89 /* Listener. */
90 int (*sound_sys_updateListener) ( double dir, double px, double py,
91 double vx, double vy ) = NULL;
92 /* Groups. */
93 int (*sound_sys_createGroup) ( int size ) = NULL;
94 int (*sound_sys_playGroup) ( int group, alSound *s, int once ) = NULL;
95 void (*sound_sys_stopGroup) ( int group ) = NULL;
96 void (*sound_sys_pauseGroup) (int group ) = NULL;
97 void (*sound_sys_resumeGroup) (int group ) = NULL;
98 /* Env. */
99 int (*sound_sys_env) ( SoundEnv_t env, double param ) = NULL;
103 * prototypes
105 /* General. */
106 static int sound_makeList (void);
107 static int sound_load( alSound *snd, const char *filename );
108 static void sound_free( alSound *snd );
109 /* Voices. */
113 * @brief Initializes the sound subsystem.
115 * @return 0 on success.
117 int sound_init (void)
119 int ret;
121 /* See if sound is disabled. */
122 if (conf.nosound) {
123 sound_disabled = 1;
124 music_disabled = 1;
127 /* Parse conf. */
128 if (sound_disabled && music_disabled)
129 return 0;
131 /* Choose sound system. */
132 if ((sound_sys_init == NULL) && (conf.sound_backend != NULL) &&
133 (strcmp(conf.sound_backend,"openal")==0)) {
134 #if USE_OPENAL
136 * OpenAL Sound.
138 /* Creation. */
139 sound_sys_init = sound_al_init;
140 sound_sys_exit = sound_al_exit;
141 /* Sound Creation. */
142 sound_sys_load = sound_al_load;
143 sound_sys_free = sound_al_free;
144 /* Sound settings. */
145 sound_sys_volume = sound_al_volume;
146 sound_sys_getVolume = sound_al_getVolume;
147 /* Sound playing. */
148 sound_sys_play = sound_al_play;
149 sound_sys_playPos = sound_al_playPos;
150 sound_sys_updatePos = sound_al_updatePos;
151 sound_sys_updateVoice = sound_al_updateVoice;
152 /* Sound management. */
153 sound_sys_update = sound_al_update;
154 sound_sys_stop = sound_al_stop;
155 sound_sys_pause = sound_al_pause;
156 sound_sys_resume = sound_al_resume;
157 sound_sys_setSpeed = sound_al_setSpeed;
158 /* Listener. */
159 sound_sys_updateListener = sound_al_updateListener;
160 /* Groups. */
161 sound_sys_createGroup = sound_al_createGroup;
162 sound_sys_playGroup = sound_al_playGroup;
163 sound_sys_stopGroup = sound_al_stopGroup;
164 sound_sys_pauseGroup = sound_al_pauseGroup;
165 sound_sys_resumeGroup = sound_al_resumeGroup;
166 /* Env. */
167 sound_sys_env = sound_al_env;
168 #else /* USE_OPENAL */
169 WARN("OpenAL support not compiled in!");
170 #endif /* USE_OPENAL */
172 if ((sound_sys_init == NULL) && (conf.sound_backend != NULL) &&
173 (strcmp(conf.sound_backend,"sdlmix")==0)) {
174 #if USE_SDLMIX
176 * SDL_mixer Sound.
178 /* Creation. */
179 sound_sys_init = sound_mix_init;
180 sound_sys_exit = sound_mix_exit;
181 /* Sound Creation. */
182 sound_sys_load = sound_mix_load;
183 sound_sys_free = sound_mix_free;
184 /* Sound settings. */
185 sound_sys_volume = sound_mix_volume;
186 sound_sys_getVolume = sound_mix_getVolume;
187 /* Sound playing. */
188 sound_sys_play = sound_mix_play;
189 sound_sys_playPos = sound_mix_playPos;
190 sound_sys_updatePos = sound_mix_updatePos;
191 sound_sys_updateVoice = sound_mix_updateVoice;
192 /* Sound management. */
193 sound_sys_update = sound_mix_update;
194 sound_sys_stop = sound_mix_stop;
195 sound_sys_pause = sound_mix_pause;
196 sound_sys_resume = sound_mix_resume;
197 sound_sys_setSpeed = sound_mix_setSpeed;
198 /* Listener. */
199 sound_sys_updateListener = sound_mix_updateListener;
200 /* Groups. */
201 sound_sys_createGroup = sound_mix_createGroup;
202 sound_sys_playGroup = sound_mix_playGroup;
203 sound_sys_stopGroup = sound_mix_stopGroup;
204 sound_sys_pauseGroup = sound_mix_pauseGroup;
205 sound_sys_resumeGroup = sound_mix_resumeGroup;
206 /* Env. */
207 sound_sys_env = sound_mix_env;
208 #else /* USE_SDLMIX */
209 WARN("SDL_mixer support not compiled in!");
210 #endif /* USE_SDLMIX */
212 if (sound_sys_init == NULL) {
213 WARN("Unknown/Unavailable sound backend '%s'.", conf.sound_backend);
214 sound_disabled = 1;
215 WARN("Sound disabled.");
216 music_disabled = 1;
217 return 0;
220 /* Initialize sound backend. */
221 ret = sound_sys_init();
222 if (ret != 0) {
223 sound_disabled = 1;
224 music_disabled = 1;
225 WARN("Sound disabled.");
226 return ret;
229 /* Create voice lock. */
230 voice_mutex = SDL_CreateMutex();
231 if (voice_mutex == NULL) {
232 WARN("Unable to create voice mutex.");
235 /* Load available sounds. */
236 ret = sound_makeList();
237 if (ret != 0)
238 return ret;
240 /* Initialize music. */
241 ret = music_init();
242 if (ret != 0) {
243 music_disabled = 1;
244 WARN("Music disabled.");
247 /* Set volume. */
248 if ((conf.sound > 1.) || (conf.sound < 0.))
249 WARN("Sound has invalid value, clamping to [0:1].");
250 sound_volume(conf.sound);
252 return 0;
257 * @brief Cleans up after the sound subsytem.
259 void sound_exit (void)
261 int i;
262 alVoice *v;
264 /* Nothing to disable. */
265 if (sound_disabled)
266 return;
268 /* Exit music subsystem. */
269 music_exit();
271 if (voice_mutex != NULL) {
272 voiceLock();
273 /* free the voices. */
274 while (voice_active != NULL) {
275 v = voice_active;
276 voice_active = v->next;
277 free(v);
279 while (voice_pool != NULL) {
280 v = voice_pool;
281 voice_pool = v->next;
282 free(v);
284 voiceUnlock();
286 /* Destroy voice lock. */
287 SDL_DestroyMutex(voice_mutex);
288 voice_mutex = NULL;
291 /* free the sounds */
292 for (i=0; i<sound_nlist; i++)
293 sound_free( &sound_list[i] );
294 free( sound_list );
295 sound_list = NULL;
296 sound_nlist = 0;
298 /* Exit sound subsystem. */
299 sound_sys_exit();
304 * @brief Gets the buffer to sound of name.
306 * @param name Name of the sound to get it's id.
307 * @return ID of the sound matching name.
309 int sound_get( char* name )
311 int i;
313 if (sound_disabled)
314 return 0;
316 for (i=0; i<sound_nlist; i++)
317 if (strcmp(name, sound_list[i].name)==0) {
318 return i;
320 WARN("Sound '%s' not found in sound list", name);
321 return -1;
326 * @brief Gets the length of the sound buffer.
328 * @param id ID of the buffer to get it's length.
329 * @return The length of the buffer.
331 double sound_length( int sound )
333 if (sound_disabled)
334 return 0.;
336 return sound_list[sound].length;
341 * @brief Plays the sound in the first available channel.
343 * @param sound Sound to play.
344 * @return Voice identifier on success.
346 int sound_play( int sound )
348 alVoice *v;
349 alSound *s;
351 if (sound_disabled)
352 return 0;
354 if ((sound < 0) || (sound > sound_nlist))
355 return -1;
357 /* Gets a new voice. */
358 v = voice_new();
360 /* Get the sound. */
361 s = &sound_list[sound];
363 /* Try to play the sound. */
364 if (sound_sys_play( v, s ))
365 return -1;
367 /* Set state and add to list. */
368 v->state = VOICE_PLAYING;
369 v->id = ++voice_genid;
370 voice_add(v);
372 return v->id;
377 * @brief Plays a sound based on position.
379 * @param sound Sound to play.
380 * @param px X position of the sound.
381 * @param py Y position of the sound.
382 * @param vx X velocity of the sound.
383 * @param vy Y velocity of the sound.
384 * @return 0 on success.
386 int sound_playPos( int sound, double px, double py, double vx, double vy )
388 alVoice *v;
389 alSound *s;
391 if (sound_disabled)
392 return 0;
394 if ((sound < 0) || (sound > sound_nlist))
395 return -1;
397 /* Gets a new voice. */
398 v = voice_new();
400 /* Get the sound. */
401 s = &sound_list[sound];
403 /* Try to play the sound. */
404 if (sound_sys_playPos( v, s, px, py, vx, vy ))
405 return -1;
407 /* Actually add the voice to the list. */
408 v->state = VOICE_PLAYING;
409 v->id = ++voice_genid;
410 voice_add(v);
412 return v->id;
417 * @brief Updates the position of a voice.
419 * @param voice Identifier of the voice to update.
420 * @param x New x position to update to.
421 * @param y New y position to update to.
423 int sound_updatePos( int voice, double px, double py, double vx, double vy )
425 alVoice *v;
427 if (sound_disabled)
428 return 0;
430 v = voice_get(voice);
431 if (v != NULL) {
433 /* Update the voice. */
434 if (sound_sys_updatePos( v, px, py, vx, vy))
435 return -1;
438 return 0;
443 * @brief Updates the sonuds removing obsolete ones and such.
445 * @return 0 on success.
447 int sound_update( double dt )
449 alVoice *v, *tv;
451 /* Update music if needed. */
452 music_update(dt);
454 if (sound_disabled)
455 return 0;
457 /* System update. */
458 sound_sys_update();
460 if (voice_active == NULL)
461 return 0;
463 voiceLock();
465 /* The actual control loop. */
466 for (v=voice_active; v!=NULL; v=v->next) {
468 /* Run first to clear in same iteration. */
469 sound_sys_updateVoice( v );
471 /* Destroy and toss into pool. */
472 if ((v->state == VOICE_STOPPED) || (v->state == VOICE_DESTROY)) {
474 /* Remove from active list. */
475 tv = v->prev;
476 if (tv == NULL) {
477 voice_active = v->next;
478 if (voice_active != NULL)
479 voice_active->prev = NULL;
481 else {
482 tv->next = v->next;
483 if (tv->next != NULL)
484 tv->next->prev = tv;
487 /* Add to free pool. */
488 v->next = voice_pool;
489 v->prev = NULL;
490 voice_pool = v;
491 if (v->next != NULL)
492 v->next->prev = v;
494 /* Avoid loop blockage. */
495 v = (tv != NULL) ? tv->next : voice_active;
496 if (v == NULL)
497 break;
501 voiceUnlock();
503 return 0;
508 * @brief Pauses all the sounds.
510 void sound_pause (void)
512 if (sound_disabled)
513 return;
515 sound_sys_pause();
520 * @brief Resumes all the sounds.
522 void sound_resume (void)
524 if (sound_disabled)
525 return;
527 sound_sys_resume();
532 * @brief Stops all the playing voices.
534 void sound_stopAll (void)
536 alVoice *v;
538 if (sound_disabled)
539 return;
541 /* Make sure there are voices. */
542 if (voice_active==NULL)
543 return;
545 voiceLock();
546 for (v=voice_active; v!=NULL; v=v->next) {
547 sound_sys_stop( v );
548 v->state = VOICE_STOPPED;
550 voiceUnlock();
555 * @brief Stops a voice from playing.
557 * @param voice Identifier of the voice to stop.
559 void sound_stop( int voice )
561 alVoice *v;
563 if (sound_disabled)
564 return;
566 v = voice_get(voice);
567 if (v != NULL) {
568 sound_sys_stop( v );
569 v->state = VOICE_STOPPED;
576 * @brief Updates the sound listener.
578 * @param dir Direction listener is facing.
579 * @param px X position of the listener.
580 * @param py Y position of the listener.
581 * @param vx X velocity of the listener.
582 * @param vy Y velocity of the listener.
583 * @return 0 on success.
585 * @sa sound_playPos
587 int sound_updateListener( double dir, double px, double py,
588 double vx, double vy )
590 if (sound_disabled)
591 return 0;
593 return sound_sys_updateListener( dir, px, py, vx, vy );
598 * @brief Sets the speed to play the sound at.
600 * @param s Speed to play sound at.
602 void sound_setSpeed( double s )
604 if (sound_disabled)
605 return;
607 return sound_sys_setSpeed( s );
612 * @brief Makes the list of available sounds.
614 static int sound_makeList (void)
616 char** files;
617 uint32_t nfiles,i;
618 char path[PATH_MAX];
619 char tmp[64];
620 int len, suflen, flen;
621 int mem;
623 if (sound_disabled)
624 return 0;
626 /* get the file list */
627 files = ndata_list( SOUND_PREFIX, &nfiles );
629 /* load the profiles */
630 mem = 0;
631 suflen = strlen(SOUND_SUFFIX_WAV);
632 for (i=0; i<nfiles; i++) {
633 flen = strlen(files[i]);
635 /* Must be longer than suffix. */
636 if (flen < suflen) {
637 free(files[i]);
638 continue;
641 /* Make sure is wav or ogg. */
642 if ((strncmp( &files[i][flen - suflen], SOUND_SUFFIX_WAV, suflen)!=0) &&
643 (strncmp( &files[i][flen - suflen], SOUND_SUFFIX_OGG, suflen)!=0)) {
644 free(files[i]);
645 continue;
648 /* grow the selection size */
649 sound_nlist++;
650 if (sound_nlist > mem) { /* we must grow */
651 mem += 32; /* we'll overallocate most likely */
652 sound_list = realloc( sound_list, mem*sizeof(alSound));
655 /* remove the suffix */
656 len = flen - suflen;
657 strncpy( tmp, files[i], len );
658 tmp[len] = '\0';
660 /* Load the sound. */
661 sound_list[sound_nlist-1].name = strdup(tmp);
662 snprintf( path, PATH_MAX, SOUND_PREFIX"%s", files[i] );
663 if (sound_load( &sound_list[sound_nlist-1], path )) {
664 sound_nlist--; /* Song not actually added. */
667 /* Clean up. */
668 free(files[i]);
670 /* shrink to minimum ram usage */
671 sound_list = realloc( sound_list, sound_nlist*sizeof(alSound));
673 DEBUG("Loaded %d sound%s", sound_nlist, (sound_nlist==1)?"":"s");
675 /* More clean up. */
676 free(files);
678 return 0;
683 * @brief Sets the volume.
685 * @param vol Volume to set to.
686 * @return 0 on success.
688 int sound_volume( const double vol )
690 if (sound_disabled)
691 return 0;
693 return sound_sys_volume( vol );
698 * @brief Gets the current sound volume.
700 * @return The current sound volume level.
702 double sound_getVolume (void)
704 if (sound_disabled)
705 return 0.;
707 return sound_sys_getVolume();
712 * @brief Loads a sound into the sound_list.
714 * @param snd Sound to load into.
715 * @param filename Name fo the file to load.
716 * @return 0 on success.
718 * @sa sound_makeList
720 static int sound_load( alSound *snd, const char *filename )
722 if (sound_disabled)
723 return -1;
725 return sound_sys_load( snd, filename );
730 * @brief Frees the sound.
732 * @param snd Sound to free.
734 static void sound_free( alSound *snd )
736 /* Free general stuff. */
737 if (snd->name) {
738 free(snd->name);
739 snd->name = NULL;
742 /* Free internals. */
743 sound_sys_free(snd);
748 * @brief Creates a sound group.
750 * @param start Where to start creating the group.
751 * @param size Size of the group.
752 * @return ID of the group created on success, 0 on error.
754 int sound_createGroup( int size )
756 if (sound_disabled)
757 return 0;
759 return sound_sys_createGroup( size );
764 * @brief Plays a sound in a group.
766 * @param group Group to play sound in.
767 * @param sound Sound to play.
768 * @param once Whether to play only once.
769 * @return 0 on success.
771 int sound_playGroup( int group, int sound, int once )
773 if (sound_disabled)
774 return 0;
776 if ((sound < 0) || (sound > sound_nlist))
777 return -1;
779 return sound_sys_playGroup( group, &sound_list[sound], once );
784 * @brief Stops all the sounds in a group.
786 * @param group Group to stop all it's sounds.
788 void sound_stopGroup( int group )
790 if (sound_disabled)
791 return;
793 sound_sys_stopGroup( group );
798 * @brief Pauses all the sounds in a group.
800 * @param Group to pause sounds.
802 void sound_pauseGroup( int group )
804 if (sound_disabled)
805 return;
807 sound_sys_pauseGroup( group );
812 * @brief Resumes all the sounds in a group.
814 * @param Group to resume sounds.
816 void sound_resumeGroup( int group )
818 if (sound_disabled)
819 return;
821 sound_sys_resumeGroup( group );
826 * @brief Sets up the sound environment.
828 * @param env Type of environment to set up.
829 * @param param Environment parameter.
830 * @return 0 on success.
832 int sound_env( SoundEnv_t env, double param )
834 if (sound_disabled)
835 return 0;
837 return sound_sys_env( env, param );
842 * @brief Locks the voices.
844 void voice_lock (void)
846 voiceLock();
851 * @brief Unlocks the voices.
853 void voice_unlock (void)
855 voiceUnlock();
860 * @brief Gets a new voice ready to be used.
862 * @return New voice ready to use.
864 alVoice* voice_new (void)
866 alVoice *v;
868 /* No free voices, allocate a new one. */
869 if (voice_pool == NULL) {
870 v = malloc(sizeof(alVoice));
871 memset(v, 0, sizeof(alVoice));
872 voice_pool = v;
873 return v;
876 /* First free voice. */
877 v = voice_pool; /* We do not touch the next nor prev, it's still in the pool. */
878 return v;
883 * @brief Adds a voice to the active voice stack.
885 * @param v Voice to add to the active voice stack.
886 * @return 0 on success.
888 int voice_add( alVoice* v )
890 alVoice *tv;
892 /* Remove from pool. */
893 if (v->prev != NULL) {
894 tv = v->prev;
895 tv->next = v->next;
896 if (tv->next != NULL)
897 tv->next->prev = tv;
899 else { /* Set pool to be the next. */
900 voice_pool = v->next;
901 if (voice_pool != NULL)
902 voice_pool->prev = NULL;
905 /* Insert to the front of active voices. */
906 voiceLock();
907 tv = voice_active;
908 v->next = tv;
909 v->prev = NULL;
910 voice_active = v;
911 if (tv != NULL)
912 tv->prev = v;
913 voiceUnlock();
914 return 0;
919 * @brief Gets a voice by identifier.
921 * @param id Identifier to look for.
922 * @return Voice matching identifier or NULL if not found.
924 alVoice* voice_get( int id )
926 alVoice *v;
928 /* Make sure there are voices. */
929 if (voice_active==NULL)
930 return NULL;
932 voiceLock();
933 for (v=voice_active; v!=NULL; v=v->next)
934 if (v->id == id)
935 break;
936 voiceUnlock();
938 return v;