DEADSOFTWARE

ef9afdab2b09c6d8fb26bda6d4f74b09947f55c0
[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 #ifdef __APPLE__
10 # include <OpenAL/al.h>
11 # include <OpenAL/alc.h>
12 #else
13 # include <AL/al.h>
14 # include <AL/alc.h>
15 #endif
17 #include "SDL.h" // SDL_BuildAudioCVT SDL_ConvertAudio
18 #include <assert.h>
19 #include <stdlib.h> // malloc
20 #include <string.h> // memcpy
22 #define TAG_OAL1 0x4F414C31
23 #define MAX_CHANNELS 8
25 #pragma pack(1)
26 typedef struct dmi {
27 dword len;
28 dword rate;
29 dword lstart;
30 dword llen;
31 byte data[];
32 } dmi;
33 #pragma pack()
35 typedef struct openal_snd {
36 snd_t base;
37 ALuint buffer;
38 } openal_snd;
40 typedef struct openal_channel {
41 ALuint source;
42 } openal_channel;
44 static short snd_vol;
46 static ALCdevice *device;
47 static ALCcontext *context;
48 static ALuint sources[MAX_CHANNELS];
50 /* Music */
52 const cfg_t *MUS_args (void) {
53 return NULL;
54 }
56 const cfg_t *MUS_conf (void) {
57 return NULL;
58 }
60 const menu_t *MUS_menu (void) {
61 return NULL;
62 }
64 void MUS_init (void) {
66 }
68 void MUS_done (void) {
70 }
72 void MUS_start (int time) {
74 }
76 void MUS_stop (void) {
78 }
80 void MUS_volume (int v) {
82 }
84 void MUS_load (char n[8]) {
86 }
88 void MUS_free (void) {
90 }
92 void MUS_update (void) {
94 }
96 /* Sound */
98 static int sound_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
99 static int cur;
100 enum { VOLUME, __NUM__ };
101 static const simple_menu_t sm = {
102 GM_BIG, "Sound", NULL,
104 { "Volume", NULL },
106 };
107 if (i == VOLUME) {
108 switch (msg->type) {
109 case GM_GETENTRY: return GM_init_int0(msg, GM_SCROLLER, 0, 0, 0);
110 case GM_GETINT: return GM_init_int(msg, snd_vol, 0, 128, 8);
111 case GM_SETINT: S_volume(msg->integer.i); return 1;
114 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
117 const menu_t *S_menu (void) {
118 static const menu_t m = {
119 NULL, &sound_menu_handler
120 };
121 return &m;
124 const cfg_t *S_args (void) {
125 static const cfg_t args[] = {
126 { "sndvol", &snd_vol, Y_WORD },
127 { NULL, NULL, 0 }
128 };
129 return args;
132 const cfg_t *S_conf (void) {
133 static const cfg_t conf[] = {
134 { "sound_volume", &snd_vol, Y_WORD },
135 { NULL, NULL, 0 }
136 };
137 return conf;
140 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) {
141 SDL_AudioCVT cvt;
142 *maxlen = 0;
143 *maxbuf = NULL;
144 if (SDL_BuildAudioCVT(&cvt, src_format, src_chan, src_rate, dst_format, dst_chan, dst_rate) != -1) {
145 *maxlen = len * cvt.len_mult;
146 *maxbuf = malloc(*maxlen);
147 if (*maxbuf != NULL) {
148 memcpy(*maxbuf, buf, len);
149 cvt.buf = *maxbuf;
150 cvt.len = len;
151 if (SDL_ConvertAudio(&cvt) == 0) {
152 *maxlen = len * cvt.len_ratio;
153 } else {
154 free(*maxbuf);
155 *maxbuf = NULL;
156 *maxlen = 0;
162 static openal_snd *new_openal_snd (const void *data, dword len, dword rate, dword lstart, dword llen, int sign) {
163 assert(data);
164 ALuint buffer = 0;
165 openal_snd *snd = NULL;
166 void *newdata = NULL;
167 int newlen = 0;
168 // for some reason 8bit formats makes psshshshsh
169 // TODO do this without SDL
170 convert_this_ext(sign ? AUDIO_S8 : AUDIO_U8, 1, rate, AUDIO_S16SYS, 1, rate, data, len, &newdata, &newlen);
171 if (newdata != NULL) {
172 alGenBuffers(1, &buffer);
173 if (alGetError() == AL_NO_ERROR) {
174 alBufferData(buffer, AL_FORMAT_MONO16, newdata, newlen, rate);
175 if (alGetError() == AL_NO_ERROR) {
176 snd = malloc(sizeof(openal_snd));
177 if (snd != NULL) {
178 snd->base.tag = TAG_OAL1;
179 snd->buffer = buffer;
180 // TODO loops
181 } else {
182 alDeleteBuffers(1, &buffer);
184 } else {
185 alDeleteBuffers(1, &buffer);
188 free(newdata);
190 return snd;
193 snd_t *S_get (int id) {
194 void *handle;
195 openal_snd *snd = NULL;
196 if (context != NULL) {
197 handle = M_lock(id);
198 if (handle != NULL) {
199 void *data = handle;
200 dword len = F_getreslen(id);
201 dword rate = 11025;
202 dword lstart = 0;
203 dword llen = 0;
204 int sign = 0;
205 if (len > 16) {
206 dmi *hdr = handle;
207 dword hdr_len = int2host(hdr->len);
208 dword hdr_rate = int2host(hdr->rate);
209 dword hdr_lstart = int2host(hdr->lstart);
210 dword hdr_llen = int2host(hdr->llen);
211 if (hdr_len <= len - 8 && hdr_lstart + hdr_llen <= len - 16) {
212 data = hdr->data;
213 len = hdr_len;
214 rate = hdr_rate;
215 lstart = hdr_lstart;
216 llen = hdr_llen;
217 sign = 1;
220 snd = new_openal_snd(data, len, rate, lstart, llen, sign);
221 M_unlock(handle);
224 return (snd_t*)snd;
227 snd_t *S_load (const char name[8]) {
228 return S_get(F_findres(name));
231 void S_free (snd_t *s) {
232 int i;
233 ALint h;
234 openal_snd *snd = (openal_snd*)s;
235 if (snd != NULL) {
236 assert(snd->base.tag == TAG_OAL1);
237 if (context != NULL) {
238 for (i = 0; i < MAX_CHANNELS; i++) {
239 alGetSourcei(sources[i], AL_BUFFER, &h);
240 if (h == snd->buffer) {
241 alSourceStop(sources[i]);
242 alSourcei(sources[i], AL_BUFFER, 0);
245 alDeleteBuffers(1, &snd->buffer);
246 assert(alGetError() == AL_NO_ERROR);
248 snd->base.tag = 0;
249 free(s);
253 void S_init (void) {
254 assert(device == NULL && context == NULL);
255 const ALCint attrs[] = {ALC_MONO_SOURCES, MAX_CHANNELS, 0};
256 device = alcOpenDevice(NULL);
257 if (device != NULL) {
258 context = alcCreateContext(device, attrs);
259 if (context != NULL) {
260 if (alcMakeContextCurrent(context)) {
261 alGenSources(MAX_CHANNELS, sources);
262 if (alGetError() == AL_NO_ERROR) {
263 alListenerf(AL_GAIN, 1);
264 alListener3f(AL_POSITION, 0, 0, 0);
265 alListener3f(AL_VELOCITY, 0, 0, 0);
266 } else {
267 logo("S_init: unable to create OpenAL sources\n");
268 alcDestroyContext(context);
269 alcCloseDevice(device);
270 context = NULL;
271 device = NULL;
273 } else {
274 logo("S_init: unable to make OpenAL context current\n");
275 alcDestroyContext(context);
276 alcCloseDevice(device);
277 context = NULL;
278 device = NULL;
280 } else {
281 logo("S_init: unable to create OpenAL context\n");
282 alcCloseDevice(device);
283 device = NULL;
285 } else {
286 logo("S_init: unable to open OpenAL device\n");
290 void S_done (void) {
291 if (context != NULL) {
292 alcMakeContextCurrent(NULL);
293 alcDestroyContext(context);
294 alcCloseDevice(device);
296 context = NULL;
297 device = NULL;
300 short S_play (snd_t *s, short c, short v) {
301 assert(c >= 0 && c <= MAX_CHANNELS);
302 assert(v >= 0 && v < 256);
303 ALuint source;
304 ALint state;
305 int channel;
306 if (context != NULL && s != NULL) {
307 openal_snd *snd = (openal_snd*)s;
308 assert(snd->base.tag == TAG_OAL1);
309 if (c == 0) {
310 for (channel = 0; channel < MAX_CHANNELS; channel++) {
311 state = AL_PLAYING;
312 alGetSourcei(sources[channel], AL_SOURCE_STATE, &state);
313 if (state == AL_STOPPED || state == AL_INITIAL) {
314 break; // !!!
317 } else {
318 channel = c - 1;
320 if (channel < MAX_CHANNELS) {
321 source = sources[channel];
322 alSourceStop(source);
323 alSourcei(source, AL_BUFFER, snd->buffer);
324 alSourcef(source, AL_PITCH, 1);
325 alSourcef(source, AL_GAIN, v / 255.0);
326 alSource3f(source, AL_POSITION, 0, 0, 0);
327 alSource3f(source, AL_VELOCITY, 0, 0, 0);
328 alSourcei(source, AL_LOOPING, AL_FALSE);
329 alSourcePlay(source);
332 return 0;
335 void S_stop (short c) {
336 assert(c >= 0 && c <= MAX_CHANNELS);
337 if (context != NULL) {
338 if (c != 0) {
339 alSourceStop(sources[c - 1]);
344 void S_volume (int v) {
345 assert(v >= 0 && v <= 128);
346 snd_vol = v;
347 if (context != NULL) {
348 alListenerf(AL_GAIN, v / 128.0);
352 void S_wait (void) {
353 int i;
354 ALint state;
355 if (context != NULL) {
356 for (i = 0; i < MAX_CHANNELS; i++) {
357 do {
358 state = AL_STOPPED;
359 alGetSourcei(sources[i], AL_SOURCE_STATE, &state);
360 } while (state == AL_PLAYING);