DEADSOFTWARE

sound: add openal sound driver
[flatwaifu.git] / src / openal / sound.c
1 #include "sound.h"
2 #include "music.h"
4 #include "files.h" // F_findres F_getreslen
5 #include "memory.h" // M_lock M_unlock
6 #include "misc.h" // int2host
7 #include "error.h" // logo
9 #include <OpenAL/al.h>
10 #include <OpenAL/alc.h>
11 #include <SDL.h> // SDL_BuildAudioCVT SDL_ConvertAudio
12 #include <assert.h>
13 #include <stdlib.h> // malloc
14 #include <string.h> // memcpy
16 #define TAG_OAL1 0x4F414C31
17 #define MAX_CHANNELS 8
19 #pragma pack(1)
20 typedef struct dmi {
21 dword len;
22 dword rate;
23 dword lstart;
24 dword llen;
25 byte data[];
26 } dmi;
27 #pragma pack()
29 typedef struct openal_snd {
30 snd_t base;
31 ALuint buffer;
32 } openal_snd;
34 typedef struct openal_channel {
35 ALuint source;
36 } openal_channel;
38 short snd_vol;
39 short mus_vol;
40 char music_random;
41 int music_time;
42 int music_fade;
44 static ALCdevice *device;
45 static ALCcontext *context;
46 static ALuint sources[MAX_CHANNELS];
48 /* Music */
50 void S_initmusic (void) {
52 }
54 void S_donemusic (void) {
56 }
58 void S_startmusic (int time) {
60 }
62 void S_stopmusic (void) {
64 }
66 void S_volumemusic (int v) {
68 }
70 void F_loadmus (char n[8]) {
72 }
74 void F_freemus (void) {
76 }
78 void S_updatemusic (void) {
80 }
82 /* Sound */
84 static void convert_this_ext (Uint32 src_format, int src_chan, int src_rate, Uint32 dst_format, int dst_chan, int dst_rate, const void *buf, int len, void **maxbuf, int *maxlen) {
85 SDL_AudioCVT cvt;
86 *maxlen = 0;
87 *maxbuf = NULL;
88 if (SDL_BuildAudioCVT(&cvt, src_format, src_chan, src_rate, dst_format, dst_chan, dst_rate) != -1) {
89 *maxlen = len * cvt.len_mult;
90 *maxbuf = malloc(*maxlen);
91 if (*maxbuf != NULL) {
92 memcpy(*maxbuf, buf, len);
93 cvt.buf = *maxbuf;
94 cvt.len = len;
95 if (SDL_ConvertAudio(&cvt) == 0) {
96 *maxlen = len * cvt.len_ratio;
97 } else {
98 free(*maxbuf);
99 *maxbuf = NULL;
100 *maxlen = 0;
106 static openal_snd *new_openal_snd (const void *data, dword len, dword rate, dword lstart, dword llen) {
107 assert(data);
108 ALuint buffer = 0;
109 openal_snd *snd = NULL;
110 void *newdata = NULL;
111 int newlen = 0;
112 // for some reason 8bit formats makes psshshshsh
113 // TODO do this without SDL
114 convert_this_ext(AUDIO_S8, 1, rate, AUDIO_S16SYS, 1, rate, data, len, &newdata, &newlen);
115 if (newdata != NULL) {
116 alGenBuffers(1, &buffer);
117 if (alGetError() == AL_NO_ERROR) {
118 alBufferData(buffer, AL_FORMAT_MONO16, newdata, newlen, rate);
119 if (alGetError() == AL_NO_ERROR) {
120 snd = malloc(sizeof(openal_snd));
121 if (snd != NULL) {
122 snd->base.tag = TAG_OAL1;
123 snd->buffer = buffer;
124 // TODO loops
125 } else {
126 alDeleteBuffers(1, &buffer);
128 } else {
129 alDeleteBuffers(1, &buffer);
132 free(newdata);
134 return snd;
137 snd_t *S_get (int id) {
138 openal_snd *snd = NULL;
139 void *handle = M_lock(id);
140 if (context != NULL && handle != NULL) {
141 byte *data = handle;
142 dword len = F_getreslen(id);
143 dword rate = 8000;
144 dword lstart = 0;
145 dword llen = 0;
146 if (len > 16) {
147 dmi *hdr = handle;
148 dword hdr_len = int2host(hdr->len);
149 dword hdr_rate = int2host(hdr->rate);
150 dword hdr_lstart = int2host(hdr->lstart);
151 dword hdr_llen = int2host(hdr->llen);
152 if (hdr_len <= len - 8 && hdr_lstart + hdr_llen <= len - 16) {
153 data = hdr->data;
154 len = hdr_len;
155 rate = hdr_rate;
156 lstart = hdr_lstart;
157 llen = hdr_llen;
160 snd = new_openal_snd(data, len, rate, lstart, llen);
161 M_unlock(handle);
163 return (snd_t*)snd;
166 snd_t *S_load (const char name[8]) {
167 return S_get(F_findres(name));
170 void S_init (void) {
171 assert(device == NULL && context == NULL);
172 const ALCint attrs[] = {ALC_MONO_SOURCES, MAX_CHANNELS, 0};
173 device = alcOpenDevice(NULL);
174 if (device != NULL) {
175 context = alcCreateContext(device, attrs);
176 if (context != NULL) {
177 if (alcMakeContextCurrent(context)) {
178 alGenSources(MAX_CHANNELS, sources);
179 if (alGetError() == AL_NO_ERROR) {
180 alListenerf(AL_GAIN, 1);
181 alListener3f(AL_POSITION, 0, 0, 0);
182 alListener3f(AL_VELOCITY, 0, 0, 0);
183 } else {
184 logo("S_init: unable to create OpenAL sources\n");
185 alcDestroyContext(context);
186 alcCloseDevice(device);
187 context = NULL;
188 device = NULL;
190 } else {
191 logo("S_init: unable to make OpenAL context current\n");
192 alcDestroyContext(context);
193 alcCloseDevice(device);
194 context = NULL;
195 device = NULL;
197 } else {
198 logo("S_init: unable to create OpenAL context\n");
199 alcCloseDevice(device);
200 device = NULL;
202 } else {
203 logo("S_init: unable to open OpenAL device\n");
207 void S_done (void) {
208 if (context != NULL) {
209 alcMakeContextCurrent(NULL);
210 alcDestroyContext(context);
211 alcCloseDevice(device);
213 context = NULL;
214 device = NULL;
217 short S_play (snd_t *s, short c, short v) {
218 assert(c >= 0 && c <= MAX_CHANNELS);
219 assert(v >= 0 && v < 256);
220 ALuint source;
221 ALint state;
222 int channel;
223 if (context != NULL && s != NULL) {
224 openal_snd *snd = (openal_snd*)s;
225 assert(snd->base.tag == TAG_OAL1);
226 if (c == 0) {
227 for (channel = 0; channel < MAX_CHANNELS; channel++) {
228 state = AL_PLAYING;
229 alGetSourcei(sources[channel], AL_SOURCE_STATE, &state);
230 if (state == AL_STOPPED || state == AL_INITIAL) {
231 break; // !!!
234 } else {
235 channel = c - 1;
237 if (channel < MAX_CHANNELS) {
238 source = sources[channel];
239 alSourceStop(source);
240 alSourcei(source, AL_BUFFER, snd->buffer);
241 alSourcef(source, AL_PITCH, 1);
242 alSourcef(source, AL_GAIN, v / 255.0);
243 alSource3f(source, AL_POSITION, 0, 0, 0);
244 alSource3f(source, AL_VELOCITY, 0, 0, 0);
245 alSourcei(source, AL_LOOPING, AL_FALSE);
246 alSourcePlay(source);
249 return 0;
252 void S_stop (short c) {
253 assert(c >= 0 && c <= MAX_CHANNELS);
254 if (context != NULL) {
255 if (c != 0) {
256 alSourceStop(sources[c - 1]);
261 void S_volume (int v) {
262 assert(v >= 0 && v <= 128);
263 snd_vol = v;
264 if (context != NULL) {
265 alListenerf(AL_GAIN, v / 128.0);
269 void S_wait (void) {
270 int i;
271 ALint state;
272 if (context != NULL) {
273 for (i = 0; i < MAX_CHANNELS; i++) {
274 do {
275 state = AL_STOPPED;
276 alGetSourcei(sources[i], AL_SOURCE_STATE, &state);
277 } while (state == AL_PLAYING);