1 /**
2  * @file handler_audio.cc
3  * @brief Handler of the sound and music
4  * @created 2004-03-22
5  * @date 2014-08-19
6  * @copyright 1991-2014 TLK Games
7  * @author Bruno Ethvignot
8  * @version $Revision: 23 $
9  */
10 /*
11  * copyright (c) 1991-2014 TLK Games all rights reserved
12  * $Id: handler_audio.cc 23 2014-08-16 20:13:07Z bruno.ethvignot@gmail.com $
13  *
14  * TecnoballZ is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * TecnoballZ is distributed in the hope that it will be useful, but
20  * WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27  * MA  02110-1301, USA.
28  */
29 #include "../include/handler_audio.h"
30 #ifndef SOUNDISOFF
31 #include "../include/handler_resources.h"
32 #include "../include/handler_keyboard.h"
33 
34 handler_audio *
35 handler_audio::audio_singleton = NULL;
36 bool
37 handler_audio::is_audio_enable = true;
38 
39 /** Positions in music modules */
40 const
41 musics_pos
42 handler_audio::ptMusicpos[] =
43 {
44   {
45     /* first music of a bricks level */
46     0,
47     /* restart first music */
48     2,
49     /* second music of a bricks level */
50     11,
51     /* restart second music */
52     11,
53     /* "bricks level" completed */
54     23,
55     /* lost ball in "bricks level" */
56     24,
57     /* shop music */
58     25
59   },
60   {0, 0, 15, 15, 22, 23, 24},
61   {0, 0, 15, 15, 28, 29, 30},
62   {0, 0, 11, 11, 18, 19, 20},
63   {0, 0, 15, 15, 30, 31, 32}
64 };
65 
66 /** Pointers of all sound effects */
67 Mix_Chunk *
68 handler_audio::sound_list[NUM_OF_SOUNDS];
69 char
70 handler_audio::sounds_play[NUM_OF_SOUNDS];
71 
72 /**
73  * Create the handler_audoi singleton object, and clear members
74  */
handler_audio()75 handler_audio::handler_audio ()
76 {
77   object_init ();
78   initialize ();
79   area_number = 0;
80   level_number = 0;
81   song_module = NULL;
82   is_only_music = true;
83   is_music_enable = true;
84   is_sound_enable = true;
85 }
86 
87 /**
88  * Release the handler_keyboard singleton object
89  */
~handler_audio()90 handler_audio::~handler_audio ()
91 {
92   if (NULL != song_module)
93     {
94       Mix_HaltMusic ();
95       Mix_FreeMusic (current_music);
96       song_module = NULL;
97     }
98 
99   /* release the samples */
100   for (Sint32 i = 0; i < NUM_OF_SOUNDS; i++)
101     {
102       if (sound_list[i])
103         {
104           Mix_FreeChunk (sound_list[i]);
105           sound_list[i] = NULL;
106         }
107       sounds_play[i] = false;
108     }
109 
110   if (is_audio_enable)
111     {
112       Mix_CloseAudio ();
113       Uint32 subsystem_init = SDL_WasInit (SDL_INIT_AUDIO);
114       if (subsystem_init & SDL_INIT_AUDIO)
115         {
116           if (is_verbose)
117             {
118               std::cout << ">handler_audio::~handler_audio()" <<
119                         " SDL_QuitSubSystem (SDL_INIT_AUDIO)" << std::endl;
120             }
121           SDL_QuitSubSystem (SDL_INIT_AUDIO);
122         }
123     }
124   audio_singleton = NULL;
125 }
126 
127 /**
128  * Get the object instance
129  * handler_audio is a singleton
130  * @return the handler_audoi object
131  */
132 handler_audio *
get_instance()133 handler_audio::get_instance ()
134 {
135   if (NULL == audio_singleton)
136     {
137       audio_singleton = new handler_audio ();
138     }
139   return audio_singleton;
140 }
141 
142 /**
143  *  Initialize SDL audio and load files waves in memory
144  */
145 void
initialize()146 handler_audio::initialize ()
147 {
148   if (!is_audio_enable)
149     {
150       if (is_verbose)
151         {
152           std::cout << "handler_audio::initialize() " <<
153                     "audio disable!" << std::endl;
154         }
155       return;
156     }
157   if (SDL_InitSubSystem (SDL_INIT_AUDIO) < 0)
158     {
159       std::cerr << "handler_audio::initialize() " <<
160                 "SDL_Init() return " << SDL_GetError () << std::endl;
161       is_audio_enable = false;
162       return;
163     }
164 
165   Sint32 audio_rate, audio_buffers;
166 #ifdef TECNOBALLZ_GP2X
167   /* we need a reduced audio rate for the GP2X to make sure sound
168    * doesn't lag */
169   audio_rate = 22050;
170   audio_buffers = 64;
171 #else
172   audio_rate = 44100;
173   audio_buffers = 4096;
174 #endif
175   if (Mix_OpenAudio (audio_rate, AUDIO_S16, 2, audio_buffers))
176     {
177       std::cerr << "(!)handler_audio::initialize() " <<
178                 "Mix_OpenAudio() return " << SDL_GetError () << std::endl;
179       is_audio_enable = false;
180       return;
181     }
182 
183   if (is_verbose)
184     {
185       query_spec ();
186     }
187   /* get the current volume music setting */
188   music_volume = Mix_VolumeMusic (-1);
189   Mix_AllocateChannels (8);
190   /* get the current volume channels setting */
191   channels_volume = Mix_Volume (-1, -1);
192 
193   /* load files waves in memory */
194   for (Sint32 i = 0; i < NUM_OF_SOUNDS; i++)
195     {
196       sound_list[i] = NULL;
197     }
198   waves_size = 0;
199   for (Sint32 i = 0; i < NUM_OF_SOUNDS; i++)
200     {
201       char *pathname = resources->get_sound_filename (i);
202       if (NULL == pathname)
203         {
204           std::cerr << "handler_audio::initialize() " <<
205                     "(!)handler_audio::initialize() file " << i <<
206                     "not found" << std::endl;
207           is_audio_enable = false;
208           return;
209         }
210 
211       Mix_Chunk *ptWav = Mix_LoadWAV (pathname);
212       if (NULL == ptWav)
213         {
214           std::cerr << "(!)handler_audio::initialize() " <<
215                     "Mix_LoadWAV(" << pathname << ") return " <<
216                     SDL_GetError () << std::endl;
217           is_audio_enable = false;
218           return;
219         }
220       Mix_VolumeChunk (ptWav, 32);
221       sound_list[i] = ptWav;
222       waves_size += ptWav->alen;
223     }
224   is_audio_enable = true;
225   is_only_music = false;
226   if (is_verbose)
227     {
228       std::cout << "handler_audio::initialize() initialize succeded"
229                 << std::endl;
230     }
231 }
232 
233 /**
234  * Display some infos
235  */
236 void
query_spec()237 handler_audio::query_spec ()
238 {
239   Sint32 result, frequency, channels;
240   Uint16 format_id;
241   result = Mix_QuerySpec (&frequency, &format_id, &channels);
242   if (result == 0)
243     {
244       std::cerr << "handler_audio::query_spec() " <<
245                 "Mix_QuerySpec return " << Mix_GetError () << std::endl;
246       return;
247     }
248   const char *format = "Unknown";
249   switch (format_id)
250     {
251     case AUDIO_U8:
252       format = "U8";
253       break;
254     case AUDIO_S8:
255       format = "S8";
256       break;
257     case AUDIO_U16LSB:
258       format = "U16LSB";
259       break;
260     case AUDIO_S16LSB:
261       format = "S16LSB";
262       break;
263     case AUDIO_U16MSB:
264       format = "U16MSB";
265       break;
266     case AUDIO_S16MSB:
267       format = "S16MSB";
268       break;
269     }
270   std::cout << "handler_audio::query_spec()" <<
271             " times frequencyency: " << frequency <<
272             " format: " << format << " channels: " << channels << std::endl;
273 }
274 
275 /**
276  * Play the music of a bricks level
277  * @param area_num area number 1 to 5
278  * @param level level number 1 to 12
279  * @return error code, 0 if no error
280  */
281 void
play_level_music(Uint32 area_num,Uint32 level)282 handler_audio::play_level_music (Uint32 area_num, Uint32 level)
283 {
284   if (!is_audio_enable)
285     {
286       return;
287     }
288   area_number = area_num;
289   level_number = level;
290   Uint32 music = area_music (area_num);
291   Uint32 paire = level & 0x1;
292   if ((level <= 5 && paire) || (level > 5 && !paire))
293     {
294       restart_position = ptMusicpos[music].music_1_loop;
295       music_1_position = ptMusicpos[music].music_1;
296       music_2_position = ptMusicpos[music].music_2 - 1;
297     }
298   else
299     {
300       restart_position = ptMusicpos[music].music_2_loop;
301       music_1_position = ptMusicpos[music].music_2;
302       music_2_position = ptMusicpos[music].level_completed - 1;
303     }
304   play_music (music);
305   Mix_SetMusicPosition (music_1_position);
306   current_portion_music = GAME_PORTION;
307   Player_Stop ();
308 }
309 
310 /**
311  * Play the music of the shop
312  * @param area_num area number
313  */
314 void
play_shop_music(Uint32 area_num)315 handler_audio::play_shop_music (Uint32 area_num)
316 {
317   if (!is_audio_enable || NULL == song_module)
318     {
319       return;
320     }
321   area_number = area_num;
322   Uint32 music = area_music (area_num);
323   music_1_position = ptMusicpos[music].shop_music;
324   music_2_position = song_module->numpos - 1;
325   restart_position = music_1_position;
326   play_music (music);
327   Mix_SetMusicPosition (music_1_position);
328   current_portion_music = SHOP_PORTION;
329 }
330 
331 /**
332  * Play the music of the victory, when the player finished a level
333  */
334 void
play_win_music()335 handler_audio::play_win_music ()
336 {
337   if (!is_audio_enable)
338     {
339       return;
340     }
341   Uint32 music = area_music (area_number);
342   music_1_position = ptMusicpos[music].level_completed;
343   music_2_position = ptMusicpos[music].pos_losing - 1;
344   restart_position = music_1_position;
345   Mix_SetMusicPosition (music_1_position);
346   current_portion_music = WIN_PORTION;
347 }
348 
349 /**
350  * Play the music of the defeat, when the player loses a life
351  */
352 void
play_lost_music()353 handler_audio::play_lost_music ()
354 {
355   if (!is_audio_enable || current_portion_music == LOST_PORTION)
356     {
357       return;
358     }
359   Uint32 music = area_music (area_number);
360   music_1_position = ptMusicpos[music].pos_losing;
361   music_2_position = ptMusicpos[music].shop_music - 1;
362   restart_position = music_1_position;
363   Mix_SetMusicPosition (music_1_position);
364   current_portion_music = LOST_PORTION;
365 }
366 
367 /**
368  * Stop the music of the defeat, when the player loses a life
369  */
370 void
stop_lost_music()371 handler_audio::stop_lost_music ()
372 {
373   if (!is_audio_enable || current_portion_music != LOST_PORTION)
374     {
375       return;
376     }
377   play_level_music (area_number, level_number);
378 }
379 
380 /**
381  * Check if the music of the victory is finished
382  * @return true if the music of the victory is finished
383  */
384 bool
is_win_music_finished()385 handler_audio::is_win_music_finished ()
386 {
387   if (!is_audio_enable || NULL == song_module)
388     {
389       return true;
390     }
391   if (WIN_PORTION == current_portion_music)
392     {
393       return false;
394     }
395   else
396     {
397       return true;
398     }
399 }
400 
401 /**
402  * If a music is played, it is stopped
403  */
404 void
stop_music()405 handler_audio::stop_music ()
406 {
407   if (!is_audio_enable || NULL == song_module)
408     {
409       return;
410     }
411   Mix_HaltMusic ();
412   Mix_FreeMusic (current_music);
413   song_module = NULL;
414   current_music_id = -1;
415 }
416 
417 /**
418  * Play a music module
419  * @param music_id resource identifier of the music module
420  */
421 void
play_music(Uint32 music_id)422 handler_audio::play_music (Uint32 music_id)
423 {
424   if (!is_audio_enable)
425     {
426       return;
427     }
428 
429   /* if a music is played, it is stopped */
430   if (NULL != song_module)
431     {
432       if (current_music_id == (Sint32) music_id)
433         {
434           return;
435         }
436       else
437         {
438           stop_music ();
439         }
440     }
441   char *pathname = resources->get_music_filename (music_id);
442   if (is_verbose)
443     {
444       std::cout << "handler_audio::play_music() " <<
445                 "try load pathname '" << pathname << "'" << std::endl;
446     }
447 
448   /* load the music in memory */
449   current_music = Mix_LoadMUS (pathname);
450   if (NULL == current_music)
451     {
452       std::cerr << "handler_audio::play_music() " <<
453                 "Mix_LoadMUS return " << SDL_GetError () << std::endl;
454       return;
455     }
456   /* Ugly way to access sdl-mixer's internal structures */
457   union
458   {
459     int i;
460     void *p;
461   } dummy;
462   memcpy (&song_module, (Uint8 *) current_music + sizeof (dummy),
463           sizeof (void *));
464 
465   /* start the music */
466   if (Mix_PlayMusic (current_music, -1) == -1)
467     {
468       std::cerr << "(!)handler_audio::play_music() " <<
469                 SDL_GetError () << std::endl;
470       return;
471     }
472 
473   current_portion_music = MUSIC_UNDIVIDED;
474   song_pos = -1;
475   current_music_id = music_id;
476   if (is_verbose)
477     {
478       std::cout << "handler_audio::play_music() module " <<
479                 current_music_id << " playing!" << std::endl;
480     }
481   if (is_music_enable)
482     {
483       Mix_VolumeMusic (music_volume);
484     }
485   else
486     {
487       Mix_VolumeMusic (0);
488     }
489   return;
490 }
491 
492 /**
493  * Return the portion of the music currently played
494  * @return identifier of the portion played
495  */
496 Uint32
get_portion_music_played()497 handler_audio::get_portion_music_played ()
498 {
499   return current_portion_music;
500 }
501 
502 /**
503  * Volume control
504  */
505 void
sound_volume_ctrl(void)506 handler_audio::sound_volume_ctrl (void)
507 {
508   Uint32 mvol = music_volume;
509   Uint32 cvol = channels_volume;
510 
511   /* volume up */
512   if (keyboard->command_is_pressed (handler_keyboard::VOLUME_UP))
513     {
514       if (is_music_enable)
515         {
516           music_volume =
517             (music_volume + VOLUME_INC >
518              MIX_MAX_VOLUME) ? MIX_MAX_VOLUME : music_volume + VOLUME_INC;
519         }
520       if (is_sound_enable)
521         {
522           channels_volume =
523             (channels_volume + VOLUME_INC >
524              MIX_MAX_VOLUME) ? MIX_MAX_VOLUME : channels_volume + VOLUME_INC;
525         }
526     }
527 
528   /* volume down */
529   if (keyboard->command_is_pressed (handler_keyboard::VOLUME_DOWN))
530     {
531       if (is_music_enable)
532         {
533           music_volume = (music_volume <= VOLUME_INC)
534                          ? 0 : music_volume - VOLUME_INC;
535         }
536       if (is_sound_enable)
537         {
538           channels_volume = (channels_volume <= VOLUME_INC)
539                             ? 0 : channels_volume - VOLUME_INC;
540         }
541     }
542 
543   if (mvol != music_volume)
544     {
545       Mix_VolumeMusic (music_volume);
546     }
547   if (cvol != channels_volume)
548     {
549       /* set the volume of all channels */
550       Mix_Volume (-1, channels_volume);
551     }
552 
553 }
554 
555 /**
556  * Handler of toggle keys, played sounds, and portions
557  * of the music played
558  */
559 void
run()560 handler_audio::run ()
561 {
562   if (!is_audio_enable)
563     {
564       return;
565     }
566 
567   /*
568    * [Ctrl] + [S]: enable/disable audio (sound effects and musics)
569    */
570   if (keyboard->command_is_pressed (handler_keyboard::TOGGLE_AUDIO, true))
571     {
572       if (!is_music_enable || !is_sound_enable)
573         {
574           is_music_enable = true;
575           is_sound_enable = true;
576           Mix_VolumeMusic (music_volume);
577         }
578       else
579         {
580           is_music_enable = false;
581           is_sound_enable = false;
582           Mix_VolumeMusic (0);
583         }
584     }
585   else
586     {
587       /* [Ctrl] + [F]: enable/disable sound effects */
588       if (keyboard->command_is_pressed (handler_keyboard::TOGGLE_SOUND, true))
589         {
590           is_sound_enable = is_sound_enable ? false : true;
591           if (!is_sound_enable)
592             {
593               for (Sint32 i = 0; i < NUM_OF_SOUNDS; i++)
594                 {
595                   sounds_play[i] = false;
596                 }
597             }
598         }
599       /* [Ctrl] + [D]: enable/disable musics */
600       else
601         {
602           if (keyboard->command_is_pressed
603               (handler_keyboard::TOGGLE_MUSIC, true))
604             {
605               is_music_enable = is_music_enable ? false : true;
606               if (is_music_enable)
607                 {
608                   Mix_VolumeMusic (music_volume);
609                 }
610               else
611                 {
612                   Mix_VolumeMusic (0);
613                 }
614             }
615         }
616     }
617   sound_volume_ctrl ();
618   control_music_position ();
619   play_requested_sounds ();
620 }
621 
622 /**
623  * Play all requested sounds
624  */
625 void
play_requested_sounds()626 handler_audio::play_requested_sounds ()
627 {
628   if (is_only_music || !is_sound_enable)
629     {
630       return;
631     }
632   for (Sint32 i = 0; i < NUM_OF_SOUNDS; i++)
633     {
634       if (sounds_play[i])
635         {
636           sounds_play[i] = false;
637           Mix_PlayChannel (-1, sound_list[i], 0);
638         }
639     }
640 }
641 
642 /**
643  * Request to play sound effect
644  * @param sound_num sound number
645  */
646 void
play_sound(Uint32 sound_num)647 handler_audio::play_sound (Uint32 sound_num)
648 {
649   if (!is_only_music)
650     {
651       sounds_play[sound_num] = true;
652     }
653 }
654 
655 /**
656  * Control of the portion of the played musics
657  */
658 void
control_music_position()659 handler_audio::control_music_position ()
660 {
661   if (NULL == song_module)
662     {
663       return;
664     }
665   if (song_pos != song_module->sngpos)
666     {
667       song_pos = song_module->sngpos;
668     }
669   Sint32 posgo = -1;
670   switch (current_portion_music)
671     {
672     case GAME_PORTION:
673     case SHOP_PORTION:
674       if (song_module->sngpos < music_1_position)
675         {
676           posgo = music_1_position;
677         }
678       if (song_module->sngpos > music_2_position)
679         {
680           posgo = restart_position;
681         }
682       if (song_module->patpos >= 63
683           && song_module->sngpos == music_2_position)
684         {
685           posgo = restart_position;
686         }
687       if (posgo >= 0)
688         {
689           if (is_verbose)
690             {
691               std::cout << "handler_audio::execution1() " <<
692                         " - Player_SetPosition(" << posgo << ")" << std::endl;
693             }
694           Mix_SetMusicPosition (posgo);
695         }
696       break;
697 
698     case LOST_PORTION:
699       if (song_module->sngpos < music_1_position ||
700           song_module->sngpos > music_2_position ||
701           (song_module->patpos >= 63
702            && song_module->sngpos == music_2_position))
703         {
704           play_level_music (area_number, level_number);
705         }
706       break;
707 
708     case WIN_PORTION:
709       if (song_module->sngpos < music_1_position ||
710           song_module->sngpos > music_2_position ||
711           (song_module->patpos >= 63
712            && song_module->sngpos == music_2_position))
713         {
714           if (!Player_Paused ())
715             {
716               Player_TogglePause ();
717             }
718           current_portion_music = MUSIC_IS_OFF;
719         }
720       break;
721     }
722 }
723 
724 /**
725   * Return music area identifier
726   * @param narea area number, form 1 to 5
727   * @return music area identifier
728 */
729 Uint32
area_music(Uint32 narea)730 handler_audio::area_music (Uint32 narea)
731 {
732   switch (narea)
733     {
734     case 2:
735       return MUSICAREA2;
736       break;
737     case 3:
738       return MUSICAREA3;
739       break;
740     case 4:
741       return MUSICAREA4;
742       break;
743     case 5:
744       return MUSICAREA5;
745       break;
746     default:
747       return MUSICAREA1;
748       break;
749     }
750   return MUSICAREA1;
751 }
752 
753 /**
754  * Disable the sound effect
755  */
756 void
disable_sound()757 handler_audio::disable_sound ()
758 {
759   is_only_music = true;
760   for (Sint32 i = 0; i < NUM_OF_SOUNDS; i++)
761     {
762       sounds_play[i] = false;
763     }
764 }
765 
766 /**
767  * Enable the sound effect
768  */
769 void
enable_sound()770 handler_audio::enable_sound ()
771 {
772   for (Sint32 i = 0; i < NUM_OF_SOUNDS; i++)
773     {
774       sounds_play[i] = false;
775     }
776   is_only_music = false;
777 }
778 
779 #endif
780