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