added summerhack
[summerhack] / src / sdlvf / sdlvf.c
1 /*
2  * sdlvf - the SDL/vorbisfile sound system
3  * Copyright (C) 2004  Vasilis Vasaitis <vvas@hal.csd.auth.gr>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <vorbis/codec.h>
21 #include <vorbis/vorbisfile.h>
22 #include <SDL.h>
23 #include <string.h>
24 #include "sdlvf.h"
25
26 #define SDL_SAMPLES 2048
27 #define VORBISFILE_BUFFER 4096
28
29 #define MIN(x, y) ((x) < (y) ? (x) : (y))
30
31 /*
32  * The error strings array, which must be kept in sync with the error
33  * codes in sdlvf.h.
34  */
35 static char *audio_errors[] = {
36         "playing audio",
37         "audio playback has been stopped",
38         "could not open input file for reading",
39         "input file does not appear to be an valid bitstream",
40         "unable to get information about the current logical bitstream",
41         "unable to open the audio device"
42 };
43
44 /*
45  * The static data shared by all SDL/vorbisfile functions. Note that
46  * there is never a need for more than one instance of the sound
47  * system, so this a reasonable thing to do.
48  */
49 static OggVorbis_File audio_vf;
50 static volatile int audio_stopped, audio_reopen;
51
52 /*
53  * This function is called by SDL when more audio data is needed.
54  */
55 static void audio_callback(void *userdata, Uint8 *stream, int len)
56 {
57         /* state information kept across calls */
58         static char buf[VORBISFILE_BUFFER];
59         static int buflen = 0;
60         static int curr = -1;
61         static int stopped = 0, reopen = 0;
62
63         /* local variables */
64         int filled = 0;
65
66         /* check for exceptional conditions */
67         if (stopped || reopen) {
68                 audio_stopped = stopped;
69                 audio_reopen = reopen;
70                 stopped = reopen = 0;
71         }
72         if (audio_stopped || audio_reopen) return;
73
74         /* check for leftovers in our buffer */
75         if (buflen != 0) {
76                 int copy = MIN(buflen, len);
77                 memcpy(stream, buf, copy);
78                 filled += copy;
79                 buflen -= copy;
80                 memmove(buf, buf + copy, buflen);
81         }
82
83         /* main loop */
84         while (filled < len) {
85                 int needed = MIN(VORBISFILE_BUFFER, len - filled), read;
86                 int prev = curr;
87                 do read = ov_read(&audio_vf, stream + filled, needed, 0, 2, 1, &curr);
88                 while (read < 0);
89                 if (read == 0) {
90                         stopped = 1;
91                         break;
92                 }
93                 if (curr != prev && prev != -1) {
94                         memcpy(buf, stream + filled, buflen = read);
95                         memset(stream + filled, 0, read);
96                         reopen = 1;
97                         break;
98                 }
99                 filled += read;
100         }
101 }
102
103 /*
104  * Opens the audio device based on the parameters of the current
105  * logical bitstream of the Ogg Vorbis file.
106  */
107 static int audio_open(void)
108 {
109         vorbis_info *vi;
110         SDL_AudioSpec as;
111
112         audio_stopped = 0;
113         audio_reopen = 0;
114
115         if ((vi = ov_info(&audio_vf, -1)) == NULL)
116                 return SDLVF_BADSTREAM;
117         as.freq = vi->rate;
118         as.format = AUDIO_S16;
119         as.channels = vi->channels;
120         as.samples = SDL_SAMPLES;
121         as.callback = audio_callback;
122         as.userdata = NULL;
123         if (SDL_OpenAudio(&as, NULL) == -1)
124                 return SDLVF_NOAUDIO;
125         SDL_PauseAudio(0);
126
127         return SDLVF_PLAYING;
128 }
129
130 /*
131  * Closes the audio device; provided for naming consistency.
132  */
133 static void audio_close(void)
134 {
135         SDL_CloseAudio();
136 }
137
138 /*
139  * Initialises SDL/vorbisfile and starts playing the Ogg Vorbis file
140  * <fname>. Returns zero (SDLVF_PLAYING) on success; any other value
141  * indicates an error.
142  */
143 int sdlvf_init(const char *fname)
144 {
145         FILE *f;
146         int result;
147         if ((f = fopen(fname, "rb")) == NULL)
148                 return SDLVF_BADFILE;
149         if (ov_open(f, &audio_vf, NULL, 0) != 0) {
150                 fclose(f);
151                 return SDLVF_BADOGG;
152         }
153         if ((result = audio_open()) != SDLVF_PLAYING)
154                 ov_clear(&audio_vf);
155         return result;
156 }
157
158 /*
159  * Checks the status of SDL/vorbisfile, reopening the audio device if
160  * necessary. It also reports whether audio playback has stopped.
161  */
162 int sdlvf_check(void)
163 {
164         if (audio_stopped)
165                 return SDLVF_STOPPED;
166         if (audio_reopen) {
167                 audio_close();
168                 return audio_open();
169         }
170         return SDLVF_PLAYING;
171 }
172
173 /*
174  * Seeks to the specified <time> in seconds, taking care of locking
175  * and avoiding clicks and pops.
176  */
177 int sdlvf_seek(double time)
178 {
179         int result;
180         SDL_LockAudio();
181         result = ov_time_seek_lap(&audio_vf, time);
182         SDL_UnlockAudio();
183         return result;
184 }
185
186 /*
187  * Shuts down SDL/vorbisfile, closing the audio device and freeing
188  * data structures.
189  */
190 void sdlvf_done(void)
191 {
192         audio_close();
193         ov_clear(&audio_vf);
194 }
195
196 /*
197  * Returns the equivalent error string for the supplied error code.
198  */
199 char *sdlvf_strerror(int error)
200 {
201         return audio_errors[error];
202 }