2eeebe7fee43ada37b260e9384414643eaac0d1c
[neverball] / share / audio.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
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.
8  *
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.
13  */
14
15 #include <SDL.h>
16
17 #include <vorbis/codec.h>
18 #include <vorbis/vorbisfile.h>
19
20 #include <string.h>
21 #include <stdlib.h>
22
23 #include "config.h"
24 #include "audio.h"
25 #include "common.h"
26 #include "fs.h"
27 #include "fs_ov.h"
28
29 /*---------------------------------------------------------------------------*/
30
31 #define AUDIO_RATE 44100
32 #define AUDIO_CHAN 2
33
34 struct voice
35 {
36     OggVorbis_File  vf;
37     float          amp;
38     float         damp;
39     int           chan;
40     int           play;
41     int           loop;
42     char         *name;
43     struct voice *next;
44 };
45
46 static int   audio_state = 0;
47 static float sound_vol   = 1.0f;
48 static float music_vol   = 1.0f;
49
50 static SDL_AudioSpec spec;
51
52 static struct voice *music  = NULL;
53 static struct voice *queue  = NULL;
54 static struct voice *voices = NULL;
55 static short        *buffer = NULL;
56
57 static ov_callbacks callbacks = {
58     fs_ov_read, fs_ov_seek, fs_ov_close, fs_ov_tell
59 };
60
61 /*---------------------------------------------------------------------------*/
62
63 #define MIX(d, s) {                           \
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; \
68     }
69
70 static int voice_step(struct voice *V, float volume, Uint8 *stream, int length)
71 {
72 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
73     int order = 1;
74 #else
75     int order = 0;
76 #endif
77
78     short *obuf = (short *) stream;
79     char  *ibuf = (char  *) buffer;
80
81     int i, b = 0, n = 1, c = 0, r = 0;
82
83     /* Compute the total request size for the current stream. */
84
85     if (V->chan == 1) r = length / 2;
86     if (V->chan == 2) r = length    ;
87
88     /* While data is coming in and data is still needed... */
89
90     while (n > 0 && r > 0)
91     {
92         /* Read audio from the stream. */
93
94         if ((n = (int) ov_read(&V->vf, ibuf, r, order, 2, 1, &b)) > 0)
95         {
96             /* Mix mono audio. */
97
98             if (V->chan == 1)
99                 for (i = 0; i < n / 2; i += 1)
100                 {
101                     short M = (short) (V->amp * volume * buffer[i]);
102
103                     MIX(obuf[c], M); c++;
104                     MIX(obuf[c], M); c++;
105
106                     V->amp += V->damp;
107
108                     if (V->amp < 0.0) V->amp = 0.0;
109                     if (V->amp > 1.0) V->amp = 1.0;
110                 }
111
112             /* Mix stereo audio. */
113
114             if (V->chan == 2)
115                 for (i = 0; i < n / 2; i += 2)
116                 {
117                     short L = (short) (V->amp * volume * buffer[i + 0]);
118                     short R = (short) (V->amp * volume * buffer[i + 1]);
119
120                     MIX(obuf[c], L); c++;
121                     MIX(obuf[c], R); c++;
122
123                     V->amp += V->damp;
124
125                     if (V->amp < 0.0) V->amp = 0.0;
126                     if (V->amp > 1.0) V->amp = 1.0;
127                 }
128
129             r -= n;
130         }
131         else
132         {
133             /* We're at EOF.  Loop or end the voice. */
134
135             if (V->loop)
136             {
137                 ov_raw_seek(&V->vf, 0);
138                 n = 1;
139             }
140             else return 1;
141         }
142     }
143     return 0;
144 }
145
146 static struct voice *voice_init(const char *filename, float a)
147 {
148     struct voice *V;
149     fs_file      fp;
150
151     /* Allocate and initialize a new voice structure. */
152
153     if ((V = (struct voice *) calloc(1, sizeof (struct voice))))
154     {
155         /* Note the name. */
156
157         V->name = strdup(filename);
158
159         /* Attempt to open the named Ogg stream. */
160
161         if ((fp = fs_open(filename, "r")))
162         {
163             if (ov_open_callbacks(fp, &V->vf, NULL, 0, callbacks) == 0)
164             {
165                 vorbis_info *info = ov_info(&V->vf, -1);
166
167                 /* On success, configure the voice. */
168
169                 V->amp  = a;
170                 V->damp = 0;
171                 V->chan = info->channels;
172                 V->play = 1;
173                 V->loop = 0;
174
175                 if (V->amp > 1.0) V->amp = 1.0;
176                 if (V->amp < 0.0) V->amp = 0.0;
177
178                 /* The file will be closed when the Ogg is cleared. */
179             }
180             else fs_close(fp);
181         }
182     }
183     return V;
184 }
185
186 static void voice_free(struct voice *V)
187 {
188     ov_clear(&V->vf);
189
190     free(V->name);
191     free(V);
192 }
193
194 /*---------------------------------------------------------------------------*/
195
196 static void audio_step(void *data, Uint8 *stream, int length)
197 {
198     struct voice *V = voices;
199     struct voice *P = NULL;
200
201     /* Zero the output buffer. */
202
203     memset(stream, 0, length);
204
205     /* Mix the background music. */
206
207     if (music)
208     {
209         voice_step(music, music_vol, stream, length);
210
211         /* If the track has faded out, move to a queued track. */
212
213         if (music->amp <= 0.0f && music->damp < 0.0f && queue)
214         {
215             voice_free(music);
216             music = queue;
217             queue = NULL;
218         }
219     }
220
221     /* Iterate over all active voices. */
222
223     while (V)
224     {
225         /* Mix this voice. */
226
227         if (V->play && voice_step(V, sound_vol, stream, length))
228         {
229             /* Delete a finished voice... */
230
231             struct voice *T = V;
232
233             if (P)
234                 V = P->next = V->next;
235             else
236                 V = voices  = V->next;
237
238             voice_free(T);
239         }
240         else
241         {
242             /* ... or continue to the next. */
243
244             P = V;
245             V = V->next;
246         }
247     }
248 }
249
250 /*---------------------------------------------------------------------------*/
251
252 void audio_init(void)
253 {
254     audio_state = 0;
255
256     /* Configure the audio. */
257
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;
263
264     /* Allocate an input buffer. */
265
266     if ((buffer = (short *) malloc(spec.samples * 4)))
267     {
268         /* Start the audio thread. */
269
270         if (SDL_OpenAudio(&spec, NULL) == 0)
271         {
272             audio_state = 1;
273             SDL_PauseAudio(0);
274         }
275         else fprintf(stderr, "%s\n", SDL_GetError());
276     }
277
278     /* Set the initial volumes. */
279
280     audio_volume(config_get_d(CONFIG_SOUND_VOLUME),
281                  config_get_d(CONFIG_MUSIC_VOLUME));
282 }
283
284 void audio_free(void)
285 {
286     /* Halt the audio thread. */
287
288     SDL_CloseAudio();
289
290     /* Release the input buffer. */
291
292     free(buffer);
293
294     /* Ogg streams and voice structure remain open to allow quality setting. */
295 }
296
297 void audio_play(const char *filename, float a)
298 {
299     if (audio_state)
300     {
301         struct voice *V;
302
303         /* If we're already playing this sound, preempt the running copy. */
304
305         SDL_LockAudio();
306         {
307             for (V = voices; V; V = V->next)
308                 if (strcmp(V->name, filename) == 0)
309                 {
310                     ov_raw_seek(&V->vf, 0);
311
312                     V->amp = a;
313
314                     if (V->amp > 1.0) V->amp = 1.0;
315                     if (V->amp < 0.0) V->amp = 0.0;
316
317                     SDL_UnlockAudio();
318                     return;
319                 }
320         }
321         SDL_UnlockAudio();
322
323         /* Create a new voice structure. */
324
325         V = voice_init(filename, a);
326
327         /* Add it to the list of sounding voices. */
328
329         SDL_LockAudio();
330         {
331             V->next = voices;
332             voices  = V;
333         }
334         SDL_UnlockAudio();
335     }
336 }
337
338 /*---------------------------------------------------------------------------*/
339
340 void audio_music_play(const char *filename)
341 {
342     if (audio_state)
343     {
344         audio_music_stop();
345
346         SDL_LockAudio();
347         {
348             if ((music = voice_init(filename, 0.0f)))
349             {
350                 music->loop = 1;
351             }
352         }
353         SDL_UnlockAudio();
354     }
355 }
356
357 void audio_music_queue(const char *filename, float t)
358 {
359     if (audio_state)
360     {
361         SDL_LockAudio();
362         {
363             if ((queue = voice_init(filename, 0.0f)))
364             {
365                 queue->loop = 1;
366
367                 if (t > 0.0f)
368                     queue->damp = +1.0f / (AUDIO_RATE * t);
369             }
370         }
371         SDL_UnlockAudio();
372     }
373 }
374
375 void audio_music_stop(void)
376 {
377     if (audio_state)
378     {
379         SDL_LockAudio();
380         {
381             if (music)
382             {
383                 voice_free(music);
384             }
385             music = NULL;
386         }
387         SDL_UnlockAudio();
388     }
389 }
390
391 /*---------------------------------------------------------------------------*/
392
393 void audio_music_fade_out(float t)
394 {
395     SDL_LockAudio();
396     {
397         if (music) music->damp = -1.0f / (AUDIO_RATE * t);
398     }
399     SDL_UnlockAudio();
400 }
401
402 void audio_music_fade_in(float t)
403 {
404     SDL_LockAudio();
405     {
406         if (music) music->damp = +1.0f / (AUDIO_RATE * t);
407     }
408     SDL_UnlockAudio();
409 }
410
411 void audio_music_fade_to(float t, const char *filename)
412 {
413     if (music)
414     {
415         if (strcmp(filename, music->name) != 0)
416         {
417             audio_music_fade_out(t);
418             audio_music_queue(filename, t);
419         }
420         else
421         {
422             /*
423              * We're fading to the current track.  Chances are,
424              * whatever track is still in the queue, we don't want to
425              * hear it anymore.
426              */
427
428             if (queue)
429             {
430                 voice_free(queue);
431                 queue = NULL;
432             }
433
434             audio_music_fade_in(t);
435         }
436     }
437     else
438     {
439         audio_music_play(filename);
440         audio_music_fade_in(t);
441     }
442 }
443
444 void audio_volume(int s, int m)
445 {
446     sound_vol = (float) s / 10.0f;
447     music_vol = (float) m / 10.0f;
448 }
449
450 /*---------------------------------------------------------------------------*/