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