1 /*
2  * nazghul - an old-school RPG engine
3  * Copyright (C) 2002, 2003, 2004 Gordon McNutt
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Foundation, Inc., 59 Temple Place,
17  * Suite 330, Boston, MA 02111-1307 USA
18  *
19  *----------------------------------------------------------------------------
20  *
21  * This file implements the sound-playing library for the nazghul engine. It
22  * wraps the SDL sound library and provides a simple mixer. The code which
23  * interacts with the SDL sound library is borrowed straight from the SDL
24  * examples, which are in the public domain (see www.libsdl.org).
25  *
26  * Gordon McNutt
27  * gmcnutt@users.sourceforge.net
28  */
29 
30 #include "sound.h"
31 #include "debug.h"
32 #include "file.h"
33 #include "cfg.h"
34 
35 #include <assert.h>
36 #include <SDL.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <SDL_mixer.h>
40 
41 #define NUM_SOUNDS 64
42 #define SOUND_MAGIC 50043
43 
44 #define IS_SOUND(ptr) ((ptr)->magic == SOUND_MAGIC)
45 
46 
47 struct sound {
48         int magic;
49 	     char *tag;
50         Mix_Chunk *data;
51         int channel;
52         //char *fname;
53         int refcount;
54         bool ambient;
55         int volume;
56         int nextVolume;
57 };
58 
59 sound_t *sound_reverse_lookup[NUM_SOUNDS];
60 
61 int SOUND_MAX_VOLUME = SDL_MIX_MAXVOLUME;
62 
63 /* This indicates whether or not the player has turned sound on or off. */
64 static int sound_enabled = 1;
65 
66 /* This is 1 iff SDL_Audio() is initialized and ready for use. */
67 static int sound_activated = 0;
68 
config_to_soundvolume(const char * config)69 static int config_to_soundvolume(const char* config)
70 {
71 	//our input percentages have nicely unique initial characters
72 	const char* comp="O2571";
73 	int i;
74 	for (i=0;i<5;i++)
75 	{
76 		if (config[0] == comp[i])
77 		{
78 			break;
79 		}
80 	}
81 	if (i>4)
82 	{
83 		i=0;
84 	}
85 	return i;
86 }
87 
sound_unref(sound_t * sound)88 static void sound_unref(sound_t *sound)
89 {
90 	assert(sound->refcount > 0);
91 
92 	sound->refcount--;
93 	if (! sound->refcount)
94 	{
95 		// make sure it isnt being played
96 		if (sound->channel >= 0)
97 		{
98 			// it *shouldnt* be being played, so lets die if it is.
99 			assert(false);
100 			Mix_HaltChannel(sound->channel);
101 		}
102 		free(sound->tag);
103 		Mix_FreeChunk(sound->data);
104 		free(sound);
105 	}
106 
107 }
108 
sound_play(sound_t * sound,int volume,bool ambient)109 void sound_play(sound_t *sound, int volume, bool ambient)
110 {
111 
112 
113 	if (!sound_enabled || !sound_activated) {
114 		return;
115 	}
116 
117 	if (NULL_SOUND == sound)
118 	{
119 	    return;
120 	}
121 	assert(IS_SOUND(sound));
122 
123 	if (sound->channel < 0)
124 	{
125 		if (ambient)
126 		{
127 			sound->channel = Mix_PlayChannel(-1, sound->data,-1);
128 			Mix_VolumeChunk(sound->data,volume);
129 			sound->volume = volume;
130 			sound->ambient = true;
131 			sound->nextVolume = volume;
132 		}
133 		else
134 		{
135 			sound->channel = Mix_PlayChannel(-1, sound->data,0);
136 			Mix_VolumeChunk(sound->data,volume);
137 			sound->volume = volume;
138 			sound->ambient = false;
139 			sound->nextVolume = 0;
140 		}
141 		sound->refcount++;
142 		sound_reverse_lookup[sound->channel] = sound;
143 	}
144 	else if (ambient)
145 	{
146 		if (sound->volume < volume)
147 		{
148 			Mix_VolumeChunk(sound->data,volume);
149 			sound->volume = volume;
150 		}
151 		if (sound->nextVolume < volume)
152 		{
153 			sound->nextVolume = volume;
154 		}
155 	}
156 	else
157 	{
158 		if (sound->volume < volume)
159 		{
160 			sound->volume = volume;
161 			Mix_VolumeChunk(sound->data,volume);
162 		}
163 	}
164 }
165 
sound_played(int channel)166 void sound_played(int channel)
167 {
168 	sound_t *sound = sound_reverse_lookup[channel];
169 	sound->channel=-1;
170 	sound_reverse_lookup[channel] = NULL;
171 	sound->volume=0;
172 	sound_unref(sound);
173 }
174 
sound_flush_ambient()175 void sound_flush_ambient()
176 {
177 	unsigned int i;
178 	sound_t *active;
179 
180 	for (i = 0; i < NUM_SOUNDS; ++i)
181 	{
182 		active = sound_reverse_lookup[i];
183 
184 		// Skip idle or oneoff entries
185 		if (! active || !active->ambient)
186 			continue;
187 
188 		active->volume = active->nextVolume;
189 		active->nextVolume = 0;
190 		Mix_VolumeChunk(active->data,active->volume);
191 	}
192 }
193 
sound_del(sound_t * sound)194 void sound_del(sound_t *sound)
195 {
196         if (sound != NULL_SOUND)
197                 sound_unref(sound);
198 }
199 
sound_new(const char * tag,const char * file)200 sound_t *sound_new(const char *tag, const char *file)
201 {
202    sound_t *sound;
203 	char *fn;
204 	Mix_Chunk *wave = NULL;
205 
206 	if (!sound_activated) {
207 		return NULL_SOUND;
208 	}
209 
210         if (file == NULL)
211                 return NULL_SOUND;
212 
213 	fn = file_mkpath(cfg_get("include-dirname"), file);
214 	/* Load the sound file and convert it to 16-bit stereo at 22kHz */
215 
216 	wave = Mix_LoadWAV(fn?fn:file);
217 	if (!wave)
218 	{
219       warn("Mix_LoadWav:%s:%s", fn?fn:file, SDL_GetError());
220       	free(fn);
221 		return NULL_SOUND;
222 	}
223 	free(fn);
224 
225         /* Allocate the sound structure */
226         sound = (sound_t *)calloc(1, sizeof(*sound));
227         assert(sound);
228 
229         /* Copy the tag */
230         sound->tag = strdup(tag);
231         assert(sound->tag);
232 
233         /* Initialized defaults */
234         sound->magic = SOUND_MAGIC;
235         sound->refcount = 1;
236         sound->channel = -1;
237         sound->data = wave;
238 
239         return sound;
240 }
241 
sound_init(void)242 int sound_init(void)
243 {
244 
245         if (sound_activated)
246                 return 0;
247 
248         /* Init the active sound list */
249 
250 	/* Set 16-bit stereo audio at 22Khz */
251 	/* Open the audio device and start playing sound! */
252 	if (Mix_OpenAudio(22050,AUDIO_S16,2,1024) < 0) {
253           warn("Mix_OpenAudio: %s", SDL_GetError());
254 		return -1;
255 	}
256 
257         /* Create the mutex */
258 
259 
260 	atexit(Mix_CloseAudio);
261 
262 	sound_activated = 1;
263 
264 	for (int i=0;i<NUM_SOUNDS;i++)
265 	{
266 		sound_reverse_lookup[i]=NULL;
267 	}
268 	Mix_AllocateChannels(NUM_SOUNDS);
269 	Mix_ChannelFinished(sound_played);
270 
271         return 0;
272 }
273 
sound_exit(void)274 void sound_exit(void)
275 {
276         if (! sound_activated)
277                 return;
278 
279         sound_activated = 0;
280 
281         /* Does this invoke the mixer callback on all active sounds? If not
282          * then how will I unref active sounds? */
283         Mix_CloseAudio();
284 }
285 
sound_haltall()286 void sound_haltall()
287 {
288 	//Mix_HaltChannel(-1);
289 }
290 
sound_get_tag(sound_t * sound)291 const char *sound_get_tag(sound_t *sound)
292 {
293         return sound->tag;
294 }
295 
sound_on(void)296 void sound_on(void)
297 {
298         sound_enabled = 1;
299 }
300 
sound_off(void)301 void sound_off(void)
302 {
303         sound_enabled = 0;
304 }
305 
sound_is_activated(void)306 int sound_is_activated(void)
307 {
308         return sound_activated;
309 }
310 
311 //////////////////////////////////////////////////////////////////////////////
312 // Music API
313 
314 Mix_Music *music_track;
315 Mix_Music *prev_track = NULL;
316 
317 int music_volume=0;
318 bool music_needtrack=false;
319 
320 //setting must be one of Off 25% 50% 75% 100%
set_music_volume(const char * setting)321 void set_music_volume(const char *setting)
322 {
323 	music_volume = config_to_soundvolume(setting);
324 	fprintf(stderr, "vol: %s\n", setting);
325 	fprintf(stderr, "vol: %d\n", music_volume);
326 	int mvm = MIX_MAX_VOLUME * music_volume / 4;
327 	fprintf(stderr, "vol: %d\n", mvm);
328 	Mix_VolumeMusic(mvm);
329 	fprintf(stderr, "vol?: %d\n", Mix_VolumeMusic(-1));
330 }
331 
music_load_track(const char * file)332 void music_load_track(const char *file)
333 {
334 	// nasty hack:
335 	// SDL mixer seems to have some rather odd timing requirements.
336 	// For example, I cant stop a track and immediately dispose of it
337 	// so instead I will stop this track, and dispose of one I stopped earlier
338 	if (prev_track)
339 	{
340 		Mix_FreeMusic(prev_track);
341 		prev_track = NULL;
342 	}
343 	if (music_volume==0)
344 	{
345 		music_needtrack=false;
346 		return;
347 	}
348 	if (Mix_PlayingMusic())
349 	{
350 		Mix_HaltMusic();
351 		prev_track = music_track;
352 	}
353 	char *fn = NULL;
354 	if (!file_exists(file))
355 	{
356 		fn = file_mkpath(cfg_get("include-dirname"), file);
357 	}
358 	music_track=Mix_LoadMUS(fn?fn:file);
359 	if (music_track)
360 	{
361 		Mix_PlayMusic(music_track,1);
362 	}
363 	else
364 	{
365       warn("Mix_LoadMusic:%s:%s\n", fn?fn:file, SDL_GetError());
366 	}
367 	music_needtrack=false;
368 	free(fn);
369 }
370 
music_finished()371 void music_finished()
372 {
373 	music_needtrack=true;
374 }
375 
376 // SDL_Mixer has a music-has-finished hook, but  you arent allowed to do anything
377 // involving SDL_Mixer in that thread, so I am reduced to polling...
music_need_track()378 bool music_need_track()
379 {
380 	return music_needtrack;
381 }
382 
music_init()383 void music_init()
384 {
385 	Mix_HookMusicFinished(music_finished);
386 }
387 
sound_enable(int enable)388 void sound_enable(int enable)
389 {
390         if (enable) {
391                 sound_on();
392         } else {
393                 sound_off();
394         }
395 }
396