2 * Copyright (C) 2003 Robert Kooima
4 * NEVERBALL is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
17 #include <vorbis/codec.h>
18 #include <vorbis/vorbisfile.h>
29 /*---------------------------------------------------------------------------*/
31 #define AUDIO_RATE 44100
46 static int audio_state = 0;
47 static float sound_vol = 1.0f;
48 static float music_vol = 1.0f;
50 static SDL_AudioSpec spec;
52 static struct voice *music = NULL;
53 static struct voice *queue = NULL;
54 static struct voice *voices = NULL;
55 static short *buffer = NULL;
57 static ov_callbacks callbacks = {
58 fs_ov_read, fs_ov_seek, fs_ov_close, fs_ov_tell
61 /*---------------------------------------------------------------------------*/
64 int n = (int) (d) + (int) (s); \
65 if (n > 32767) (d) = 32767; \
66 else if (n < -32768) (d) = -32768; \
67 else (d) = (short) n; \
70 static int voice_step(struct voice *V, float volume, Uint8 *stream, int length)
72 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
78 short *obuf = (short *) stream;
79 char *ibuf = (char *) buffer;
81 int i, b = 0, n = 1, c = 0, r = 0;
83 /* Compute the total request size for the current stream. */
85 if (V->chan == 1) r = length / 2;
86 if (V->chan == 2) r = length ;
88 /* While data is coming in and data is still needed... */
90 while (n > 0 && r > 0)
92 /* Read audio from the stream. */
94 if ((n = (int) ov_read(&V->vf, ibuf, r, order, 2, 1, &b)) > 0)
99 for (i = 0; i < n / 2; i += 1)
101 short M = (short) (V->amp * volume * buffer[i]);
103 MIX(obuf[c], M); c++;
104 MIX(obuf[c], M); c++;
108 if (V->amp < 0.0) V->amp = 0.0;
109 if (V->amp > 1.0) V->amp = 1.0;
112 /* Mix stereo audio. */
115 for (i = 0; i < n / 2; i += 2)
117 short L = (short) (V->amp * volume * buffer[i + 0]);
118 short R = (short) (V->amp * volume * buffer[i + 1]);
120 MIX(obuf[c], L); c++;
121 MIX(obuf[c], R); c++;
125 if (V->amp < 0.0) V->amp = 0.0;
126 if (V->amp > 1.0) V->amp = 1.0;
133 /* We're at EOF. Loop or end the voice. */
137 ov_raw_seek(&V->vf, 0);
146 static struct voice *voice_init(const char *filename, float a)
151 /* Allocate and initialize a new voice structure. */
153 if ((V = (struct voice *) calloc(1, sizeof (struct voice))))
157 V->name = strdup(filename);
159 /* Attempt to open the named Ogg stream. */
161 if ((fp = fs_open(filename, "r")))
163 if (ov_open_callbacks(fp, &V->vf, NULL, 0, callbacks) == 0)
165 vorbis_info *info = ov_info(&V->vf, -1);
167 /* On success, configure the voice. */
171 V->chan = info->channels;
175 if (V->amp > 1.0) V->amp = 1.0;
176 if (V->amp < 0.0) V->amp = 0.0;
178 /* The file will be closed when the Ogg is cleared. */
186 static void voice_free(struct voice *V)
194 /*---------------------------------------------------------------------------*/
196 static void audio_step(void *data, Uint8 *stream, int length)
198 struct voice *V = voices;
199 struct voice *P = NULL;
201 /* Zero the output buffer. */
203 memset(stream, 0, length);
205 /* Mix the background music. */
209 voice_step(music, music_vol, stream, length);
211 /* If the track has faded out, move to a queued track. */
213 if (music->amp <= 0.0f && music->damp < 0.0f && queue)
221 /* Iterate over all active voices. */
225 /* Mix this voice. */
227 if (V->play && voice_step(V, sound_vol, stream, length))
229 /* Delete a finished voice... */
234 V = P->next = V->next;
236 V = voices = V->next;
242 /* ... or continue to the next. */
250 /*---------------------------------------------------------------------------*/
252 void audio_init(void)
256 /* Configure the audio. */
258 spec.format = AUDIO_S16SYS;
259 spec.channels = AUDIO_CHAN;
260 spec.samples = config_get_d(CONFIG_AUDIO_BUFF);
261 spec.freq = AUDIO_RATE;
262 spec.callback = audio_step;
264 /* Allocate an input buffer. */
266 if ((buffer = (short *) malloc(spec.samples * 4)))
268 /* Start the audio thread. */
270 if (SDL_OpenAudio(&spec, NULL) == 0)
275 else fprintf(stderr, "%s\n", SDL_GetError());
278 /* Set the initial volumes. */
280 audio_volume(config_get_d(CONFIG_SOUND_VOLUME),
281 config_get_d(CONFIG_MUSIC_VOLUME));
284 void audio_free(void)
286 /* Halt the audio thread. */
290 /* Release the input buffer. */
294 /* Ogg streams and voice structure remain open to allow quality setting. */
297 void audio_play(const char *filename, float a)
303 /* If we're already playing this sound, preempt the running copy. */
307 for (V = voices; V; V = V->next)
308 if (strcmp(V->name, filename) == 0)
310 ov_raw_seek(&V->vf, 0);
314 if (V->amp > 1.0) V->amp = 1.0;
315 if (V->amp < 0.0) V->amp = 0.0;
323 /* Create a new voice structure. */
325 V = voice_init(filename, a);
327 /* Add it to the list of sounding voices. */
338 /*---------------------------------------------------------------------------*/
340 void audio_music_play(const char *filename)
348 if ((music = voice_init(filename, 0.0f)))
357 void audio_music_queue(const char *filename, float t)
363 if ((queue = voice_init(filename, 0.0f)))
368 queue->damp = +1.0f / (AUDIO_RATE * t);
375 void audio_music_stop(void)
391 /*---------------------------------------------------------------------------*/
393 void audio_music_fade_out(float t)
397 if (music) music->damp = -1.0f / (AUDIO_RATE * t);
402 void audio_music_fade_in(float t)
406 if (music) music->damp = +1.0f / (AUDIO_RATE * t);
411 void audio_music_fade_to(float t, const char *filename)
415 if (strcmp(filename, music->name) != 0)
417 audio_music_fade_out(t);
418 audio_music_queue(filename, t);
423 * We're fading to the current track. Chances are,
424 * whatever track is still in the queue, we don't want to
434 audio_music_fade_in(t);
439 audio_music_play(filename);
440 audio_music_fade_in(t);
444 void audio_volume(int s, int m)
446 sound_vol = (float) s / 10.0f;
447 music_vol = (float) m / 10.0f;
450 /*---------------------------------------------------------------------------*/