1#include "sound.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <stdint.h>
6
7#include <al.h>
8#include <alc.h>
9
10#define MAX_BUFFERS 100 // how many different sounds we can keep in memory at once
11#define MAX_SOURCES 100 // how many different sounds we can have active at one time
12
13// SIGIL owes thanks to the following websites for their usefulness:
14// - for loading wav files without ALUT: http://www.dunsanyinteractive.com/blogs/oliver/?p=72
15// - a few changes had to be made to port this to pure C, including the use of portable types (uint32_t, etc.) instead of long, short, etc.
16// - for initializing/deinitializing OpenAL without ALUT: http://kcat.strangesoft.net/openal-tutorial.html
17
18static ALCdevice *sliDevice;
19static ALCcontext *sliContext;
20
21static int sliNextFreeBuffer = 0;
22static ALuint buffers[MAX_BUFFERS];
23
24static int sliNextFreeSource = 0;
25static ALuint sources[MAX_SOURCES];
26
27static void sliLoadWaveFormat(const char *filename, ALuint buffer);
28static int sliGetFreeSource(void);
29static int sliIsIndexValid(int sourceIndex);
30
31/*
32 * (From http://www.dunsanyinteractive.com/blogs/oliver/?p=72)
33 * Struct that holds the RIFF data of the Wave file.
34 * The RIFF data is the meta data information that holds,
35 * the ID, size and format of the wave file
36 */
37struct RIFF_Header
38{
39 int8_t chunkID[4];
40 int32_t chunkSize; // size not including chunkSize or chunkID; we use int32_t for guaranteed type sizes (original code uses long)
41 int8_t format[4];
42};
43
44/*
45 * (From http://www.dunsanyinteractive.com/blogs/oliver/?p=72)
46 * Struct to hold fmt subchunk data for WAVE files.
47 */
48struct WAVE_Format
49{
50 int8_t subChunkID[4];
51 int32_t subChunkSize;
52 int16_t audioFormat;
53 int16_t numChannels;
54 int32_t sampleRate;
55 int32_t byteRate;
56 int16_t blockAlign;
57 int16_t bitsPerSample;
58};
59
60/*
61 * (From http://www.dunsanyinteractive.com/blogs/oliver/?p=72)
62 * Struct to hold the data of the wave file
63 */
64struct WAVE_Data
65{
66 int8_t subChunkID[4]; // should contain the word data
67 int32_t subChunk2Size; // stores the size of the data block
68};
69
70// special thanks to http://kcat.strangesoft.net/openal-tutorial.html for this
71void sliSoundInit(void)
72{
73 ALCdevice *sliDevice;
74 ALCcontext *sliContext;
75
76 // open our audio device
77 sliDevice = alcOpenDevice(NULL);
78 if(!sliDevice)
79 {
80 fprintf(stderr, "sliSoundInit() could not open audio device\n");
81 exit(1);
82 }
83
84 // create an OpenAL context
85 sliContext = alcCreateContext(sliDevice, NULL);
86 alcMakeContextCurrent(sliContext);
87 if(!sliContext)
88 {
89 fprintf(stderr, "sliSoundInit() could not create audio context\n");
90 exit(1);
91 }
92
93 // enable support for up to MAX_BUFFERS different sounds
94 alGenBuffers(MAX_BUFFERS, buffers);
95 alGenSources(MAX_SOURCES, sources);
96}
97
98void sliSoundDestroy()
99{
100 // delete our buffers and sources
101 alDeleteSources(MAX_BUFFERS, buffers);
102 alDeleteBuffers(MAX_BUFFERS, buffers);
103
104 // shut down our context and close the audio device (thanks http://kcat.strangesoft.net/openal-tutorial.html)
105 alcMakeContextCurrent(NULL);
106 alcDestroyContext(sliContext);
107 alcCloseDevice(sliDevice);
108}
109
110int sliLoadWAV(const char *filename)
111{
112 if(sliNextFreeBuffer < MAX_BUFFERS)
113 {
114 sliLoadWaveFormat(filename, buffers[sliNextFreeBuffer++]);
115 }
116 else
117 {
118 fprintf(stderr, "sliLoadWAV() has already reached the maximum number of OpenAL buffers, %d\n", MAX_BUFFERS);
119 fprintf(stderr, "\t(Please email Geoff at geoff@libsigil.com and tell him that he stupidly thought %d buffers would be enough!)\n", MAX_BUFFERS);
120 exit(1);
121 }
122
123 return sliNextFreeBuffer - 1;
124}
125
126int sliSoundPlay(int sound)
127{
128 int freeSourceIndex;
129
130 if(sound >= 0 && sound < MAX_SOURCES)
131 {
132 // find a source that is not being used
133 freeSourceIndex = sliGetFreeSource();
134 if(freeSourceIndex >= 0)
135 {
136 alSourcei(sources[freeSourceIndex], AL_BUFFER, buffers[sound]);
137 alSourcei(sources[freeSourceIndex], AL_LOOPING, AL_FALSE);
138 alSourcePlay(sources[freeSourceIndex]);
139 }
140 }
141 else
142 {
143 fprintf(stderr, "sliSoundPlay(): sound index %d is out of range [0, %d)\n", sound, (MAX_SOURCES - 1));
144 exit(1);
145 }
146
147 return freeSourceIndex;
148}
149
150int sliSoundLoop(int sound)
151{
152 int freeSourceIndex;
153
154 if(sound >= 0 && sound < MAX_SOURCES)
155 {
156 // find a source that is not being used
157 freeSourceIndex = sliGetFreeSource();
158 if(freeSourceIndex >= 0)
159 {
160 alSourcei(sources[freeSourceIndex], AL_BUFFER, buffers[sound]);
161 alSourcei(sources[freeSourceIndex], AL_LOOPING, AL_TRUE);
162 alSourcePlay(sources[freeSourceIndex]);
163 }
164 }
165 else
166 {
167 fprintf(stderr, "sliSoundLoop(): sound index %d is out of range [0, %d)\n", sound, MAX_SOURCES - 1);
168 exit(1);
169 }
170
171 return freeSourceIndex;
172}
173
174void sliSoundPause(int sound)
175{
176 if(sliIsIndexValid(sound))
177 {
178 alSourcePause(sources[sound]);
179 }
180 else
181 {
182 fprintf(stderr, "sliSoundPause(): sound index %d is out of range [0, %d)\n", sound, MAX_SOURCES - 1);
183 exit(1);
184 }
185}
186
187void sliSoundStop(int sound)
188{
189 if(sliIsIndexValid(sound))
190 {
191 alSourceStop(sources[sound]);
192 }
193 else
194 {
195 fprintf(stderr, "sliSoundStop(): sound index %d is out of range [0, %d)\n", sound, MAX_SOURCES - 1);
196 exit(1);
197 }
198}
199
200void sliSoundPauseAll()
201{
202 int i;
203 int state;
204
205 // pause all sounds that are playing (this includes sounds that are looping)
206 for(i = 0; i < MAX_SOURCES; i ++)
207 {
208 alGetSourcei(sources[i], AL_SOURCE_STATE, &state);
209 if(state == AL_PLAYING)
210 {
211 alSourcePause(sources[i]);
212 }
213 }
214}
215
216void sliSoundStopAll()
217{
218 int i;
219 int state;
220
221 // stop all sounds that are playing (this includes sounds that are looping or paused)
222 for(i = 0; i < MAX_SOURCES; i ++)
223 {
224 alGetSourcei(sources[i], AL_SOURCE_STATE, &state);
225 if(state == AL_PLAYING || state == AL_PAUSED)
226 {
227 alSourceStop(sources[i]);
228 }
229 }
230}
231
232void sliSoundResumeAll()
233{
234 int i;
235 int state;
236
237 // resumes all sounds that are paused
238 for(i = 0; i < MAX_SOURCES; i ++)
239 {
240 alGetSourcei(sources[i], AL_SOURCE_STATE, &state);
241 if(state == AL_PAUSED)
242 {
243 alSourcePlay(sources[i]);
244 }
245 }
246}
247
248int sliSoundPlaying(int sound)
249{
250 int state;
251 int result = 0;
252
253 if(sliIsIndexValid(sound))
254 {
255 alGetSourcei(sources[sound], AL_SOURCE_STATE, &state);
256 result = state == AL_PLAYING;
257 }
258 else
259 {
260 fprintf(stderr, "sliSoundIsPlaying(): sound index %d is out of range [0, %d)\n", sound, MAX_SOURCES - 1);
261 exit(1);
262 }
263
264 return result;
265}
266
267int sliSoundLooping(int sound)
268{
269 int state;
270 ALint looping;
271 int result = 0;
272
273 if(sliIsIndexValid(sound))
274 {
275 alGetSourcei(sources[sound], AL_SOURCE_STATE, &state);
276 alGetSourcei(sources[sound], AL_LOOPING, &looping);
277 result = state == AL_PLAYING && looping == AL_TRUE;
278 }
279 else
280 {
281 fprintf(stderr, "sliSoundIsLooping(): sound index %d is out of range [0, %d)\n", sound, MAX_SOURCES - 1);
282 exit(1);
283 }
284
285 return result;
286}
287
288// special thanks to http://www.dunsanyinteractive.com/blogs/oliver/?p=72 for this
289void sliLoadWaveFormat(const char *filename, ALuint buffer)
290{
291 FILE *soundFile = NULL;
292 struct WAVE_Format wave_format;
293 struct RIFF_Header riff_header;
294 struct WAVE_Data wave_data;
295 uint8_t *data;
296
297 ALsizei size;
298 ALsizei frequency;
299 ALenum format = 0;
300 ALenum error;
301
302 // attempt to open the file to see if it exists and is readable
303 soundFile = fopen(filename, "rb");
304 if (!soundFile)
305 {
306 fprintf(stderr, "sliLoadWaveFormat() could not load %s---does the file exist?\n", filename);
307 exit(1);
308 }
309
310 // read in the first chunk into the struct
311 fread(&riff_header, sizeof(struct RIFF_Header), 1, soundFile);
312
313 // check for RIFF and WAVE tag in memeory
314 if ((riff_header.chunkID[0] != 'R' || riff_header.chunkID[1] != 'I' || riff_header.chunkID[2] != 'F' || riff_header.chunkID[3] != 'F') ||
315 (riff_header.format[0] != 'W' || riff_header.format[1] != 'A' || riff_header.format[2] != 'V' || riff_header.format[3] != 'E'))
316 {
317 fprintf(stderr, "sliLoadWaveFormat() did not detect the RIFF/WAVE header combination in %s\n", filename);
318 exit(1);
319 }
320
321 // read in the 2nd chunk for the wave info
322 fread(&wave_format, sizeof(struct WAVE_Format), 1, soundFile);
323
324 // make sure the wave format is something we can work with
325 if (wave_format.subChunkID[0] != 'f' || wave_format.subChunkID[1] != 'm' || wave_format.subChunkID[2] != 't' || wave_format.subChunkID[3] != ' ')
326 {
327 fprintf(stderr, "sliLoadWaveFormat() detected invalid wave format in %s\n", filename);
328 exit(1);
329 }
330
331 // check for extra parameters
332 if (wave_format.subChunkSize > 16)
333 {
334 fseek(soundFile, sizeof(int16_t), SEEK_CUR);
335 }
336
337 // read in the the last byte of data before the sound file
338 fread(&wave_data, sizeof(struct WAVE_Data), 1, soundFile);
339
340 // check for data tag in memory
341 if (wave_data.subChunkID[0] != 'd' || wave_data.subChunkID[1] != 'a' || wave_data.subChunkID[2] != 't' || wave_data.subChunkID[3] != 'a')
342 {
343 fprintf(stderr, "sliLoadWaveFormat() detected invalid data header in %s\n", filename);
344 exit(1);
345 }
346
347 // allocate memory for wave data
348 data = (uint8_t*)malloc(wave_data.subChunk2Size);
349
350 // read in the sound data into the soundData variable
351 if (!fread(data, wave_data.subChunk2Size, 1, soundFile))
352 {
353 fprintf(stderr, "sliLoadWaveFormat() could not load wave data in %s into struct\n", filename);
354 exit(1);
355 }
356
357 // now we set the variables that we passed in with the data from the structs
358 size = wave_data.subChunk2Size;
359 frequency = wave_format.sampleRate;
360
361 // the format is worked out by looking at the number of channels and the bits per sample.
362 if (wave_format.numChannels == 1)
363 {
364 if (wave_format.bitsPerSample == 8 )
365 format = AL_FORMAT_MONO8;
366 else if (wave_format.bitsPerSample == 16)
367 format = AL_FORMAT_MONO16;
368 }
369 else if (wave_format.numChannels == 2)
370 {
371 if (wave_format.bitsPerSample == 8 )
372 format = AL_FORMAT_STEREO8;
373 else if (wave_format.bitsPerSample == 16)
374 format = AL_FORMAT_STEREO16;
375 }
376
377 // now we put our data into the OpenAL buffer and check for an error
378 alBufferData(buffer, format, (void*)data, size, frequency);
379 error = alGetError();
380 if(error != AL_NO_ERROR)
381 {
382 fprintf(stderr, "sliLoadWaveFormat() encountered AL error %d when buffering %s\n", (int)error, filename);
383 exit(1);
384 }
385
386 // clean up
387 fclose(soundFile);
388}
389
390int sliGetFreeSource()
391{
392 int i = sliNextFreeSource;
393 int state;
394 int result = -1;
395
396 // start at an index that is an educated guess for a sound that might be free
397 do
398 {
399 // get the state of the next source that might be available
400 alGetSourcei(sources[i], AL_SOURCE_STATE, &state);
401 if(state == AL_STOPPED || state == AL_INITIAL)
402 {
403 result = i;
404 sliNextFreeSource = (i + 1) % MAX_SOURCES;
405 }
406 else
407 {
408 i = (i + 1) % MAX_SOURCES;
409 }
410 }
411 while(result == -1 && i != sliNextFreeSource);
412
413 // return either -1 or the index of a source that can be used
414 return result;
415}
416
417int sliIsIndexValid(int sourceIndex)
418{
419 return sourceIndex >= 0 && sourceIndex < MAX_SOURCES;
420}
421