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