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 |
|
18 | static ALCdevice *sliDevice;
|
19 | static ALCcontext *sliContext;
|
20 |
|
21 | static int sliNextFreeBuffer = 0;
|
22 | static ALuint buffers[MAX_BUFFERS];
|
23 |
|
24 | static int sliNextFreeSource = 0;
|
25 | static ALuint sources[MAX_SOURCES];
|
26 |
|
27 | static void sliLoadWaveFormat(const char *filename, ALuint buffer);
|
28 | static int sliGetFreeSource(void);
|
29 | static 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 | */
|
37 | struct
|
38 | {
|
39 | int8_t [4];
|
40 | int32_t ; // size not including chunkSize or chunkID; we use int32_t for guaranteed type sizes (original code uses long)
|
41 | int8_t [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 | */
|
48 | struct 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 | */
|
64 | struct 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
|
71 | void 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 |
|
98 | void 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 |
|
110 | int 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 |
|
126 | int 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 |
|
150 | int 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 |
|
174 | void 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 |
|
187 | void 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 |
|
200 | void 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 |
|
216 | void 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 |
|
232 | void 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 |
|
248 | int 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 |
|
267 | int 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
|
289 | void sliLoadWaveFormat(const char *filename, ALuint buffer)
|
290 | {
|
291 | FILE *soundFile = NULL;
|
292 | struct WAVE_Format wave_format;
|
293 | struct 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 |
|
390 | int 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 |
|
417 | int sliIsIndexValid(int sourceIndex)
|
418 | {
|
419 | return sourceIndex >= 0 && sourceIndex < MAX_SOURCES;
|
420 | }
|
421 | |