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