1 /*  DreamChess
2 **
3 **  DreamChess is the legal property of its developers, whose names are too
4 **  numerous to list here. Please refer to the AUTHORS.txt file distributed
5 **  with this source distribution.
6 **
7 **  This program is free software: you can redistribute it and/or modify
8 **  it under the terms of the GNU General Public License as published by
9 **  the Free Software Foundation, either version 3 of the License, or
10 **  (at your option) any later version.
11 **
12 **  This program is distributed in the hope that it will be useful,
13 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 **  GNU General Public License for more details.
16 **
17 **  You should have received a copy of the GNU General Public License
18 **  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "audio.h"
22 
23 #ifdef _WIN32
24 #include <direct.h>
25 #else
26 #include <unistd.h>
27 #endif
28 
29 #include <SDL.h>
30 #include <SDL_mixer.h>
31 
32 #include "debug.h"
33 #include "playlist.h"
34 #include "system_config.h"
35 #include "theme.h"
36 
37 static sound_t sounds[AUDIO_SOUNDS] = {{AUDIO_MOVE, "move1.wav"}};
38 
39 static audio_music_callback_t music_callback = NULL;
40 static Mix_Music *music = NULL;
41 static playlist_t *playlist;
42 static int next_song;
43 static int have_songs;
44 static playlist_entry_t *current_song;
45 static int have_audio = 0;
46 
47 static Mix_Chunk *wav_data[AUDIO_SOUNDS];
48 
49 static int sound_volume, music_volume;
50 
load_sounds(void)51 static void load_sounds(void) {
52 	int i;
53 
54 	for (i = 0; i < AUDIO_SOUNDS; i++) {
55 		DBG_LOG("Loading %s", sounds[i].filename);
56 		wav_data[sounds[i].id] = Mix_LoadWAV(sounds[i].filename);
57 		if (!wav_data[sounds[i].id]) {
58 			DBG_ERROR("Failed to load %s", sounds[i].filename);
59 			exit(1);
60 		}
61 	}
62 }
63 
free_sounds(void)64 static void free_sounds(void) {
65 	int i;
66 
67 	for (i = 0; i < AUDIO_SOUNDS; i++)
68 		Mix_FreeChunk(wav_data[i]);
69 }
70 
music_finished(void)71 static void music_finished(void) {
72 	next_song = 1;
73 }
74 
audio_init(void)75 void audio_init(void) {
76 	music_packs_t *music_packs;
77 	int audio_rate = 44100;
78 	Uint16 audio_format = AUDIO_S16;
79 	int audio_channels = 2;
80 	int audio_buffers = 4096;
81 	music_pack_t *music_pack;
82 	option_t *option;
83 
84 	music_packs = theme_get_music_packs();
85 
86 	if (SDL_Init(SDL_INIT_AUDIO) != 0) {
87 		DBG_ERROR("SDL audio initialization failed: %s", SDL_GetError());
88 		return;
89 	}
90 
91 	if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers)) {
92 		DBG_ERROR("Unable to open audio");
93 		return;
94 	}
95 
96 	have_audio = 1;
97 
98 	chdir("sounds");
99 	load_sounds();
100 	chdir("..");
101 
102 	Mix_HookMusicFinished(music_finished);
103 
104 	option = config_get_option("music_volume");
105 	audio_set_music_volume(option->selected->index);
106 	option = config_get_option("sound_volume");
107 	audio_set_sound_volume(option->selected->index);
108 
109 	playlist = playlist_create();
110 	TAILQ_FOREACH(music_pack, music_packs, entries)
111 	playlist_add_tracks(playlist, music_pack->dir);
112 
113 	/* Check for at least two songs */
114 	if ((TAILQ_FIRST(playlist) != TAILQ_LAST(playlist, playlist))) {
115 		current_song = TAILQ_LAST(playlist, playlist);
116 		have_songs = 1;
117 		next_song = 1;
118 	} else
119 		have_songs = 0;
120 }
121 
audio_exit(void)122 void audio_exit(void) {
123 	if (!have_audio)
124 		return;
125 
126 	Mix_CloseAudio();
127 	free_sounds();
128 	playlist_destroy(playlist);
129 }
130 
audio_poll(int title)131 void audio_poll(int title) {
132 	if (!have_audio)
133 		return;
134 
135 	/* Less than two songs or volume off, abort. */
136 	if (!have_songs || !music_volume)
137 		return;
138 
139 	/* Start a new song when the previous one is finished. Is also
140 	** triggered when going from the title-screen to in-game and back
141 	*/
142 	if ((next_song == 1) || (!title && (current_song == TAILQ_FIRST(playlist))) ||
143 		(title && (current_song != TAILQ_FIRST(playlist)))) {
144 		if (title)
145 			current_song = TAILQ_FIRST(playlist);
146 		else {
147 			current_song = TAILQ_NEXT(current_song, entries);
148 			if (!current_song) {
149 				/* Go to song 2 */
150 				current_song = TAILQ_FIRST(playlist);
151 				current_song = TAILQ_NEXT(current_song, entries);
152 			}
153 		}
154 
155 		next_song = 0;
156 
157 		if (music)
158 			Mix_FreeMusic(music);
159 
160 		music = Mix_LoadMUS(current_song->filename);
161 
162 		if (music_callback)
163 			music_callback(current_song->title, current_song->artist, current_song->album);
164 
165 		DBG_LOG("Playing %s", current_song->filename);
166 		Mix_PlayMusic(music, 0);
167 	}
168 }
169 
audio_set_music_callback(audio_music_callback_t callback)170 void audio_set_music_callback(audio_music_callback_t callback) {
171 	music_callback = callback;
172 }
173 
audio_play_sound(int id)174 void audio_play_sound(int id) {
175 	if (!have_audio)
176 		return;
177 
178 	if (sound_volume == 0)
179 		return;
180 
181 	if (Mix_PlayChannel(0, wav_data[id], 0) == -1)
182 		DBG_WARN("Failed to play sound %i", id);
183 }
184 
audio_set_sound_volume(int vol)185 void audio_set_sound_volume(int vol) {
186 	if (!have_audio)
187 		return;
188 
189 	sound_volume = vol * MIX_MAX_VOLUME / AUDIO_MAX_VOL;
190 	Mix_Volume(0, sound_volume);
191 }
192 
audio_set_music_volume(int vol)193 void audio_set_music_volume(int vol) {
194 	if (!have_audio)
195 		return;
196 
197 	int restart = vol && (music_volume == 0);
198 	music_volume = vol * MIX_MAX_VOLUME / AUDIO_MAX_VOL;
199 	Mix_VolumeMusic(music_volume);
200 
201 	if (music_volume == 0) {
202 		Mix_HaltMusic();
203 	} else if (restart) {
204 		next_song = 1;
205 	}
206 }
207