DEADSOFTWARE

menu: remove data field from menu_t
[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 = { sound_menu_handler };
119 return &m;
122 const cfg_t *S_args (void) {
123 static const cfg_t args[] = {
124 { "sndvol", &snd_vol, Y_WORD },
125 { NULL, NULL, 0 }
126 };
127 return args;
130 const cfg_t *S_conf (void) {
131 static const cfg_t conf[] = {
132 { "sound_volume", &snd_vol, Y_WORD },
133 { NULL, NULL, 0 }
134 };
135 return conf;
138 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) {
139 SDL_AudioCVT cvt;
140 *maxlen = 0;
141 *maxbuf = NULL;
142 if (SDL_BuildAudioCVT(&cvt, src_format, src_chan, src_rate, dst_format, dst_chan, dst_rate) != -1) {
143 *maxlen = len * cvt.len_mult;
144 *maxbuf = malloc(*maxlen);
145 if (*maxbuf != NULL) {
146 memcpy(*maxbuf, buf, len);
147 cvt.buf = *maxbuf;
148 cvt.len = len;
149 if (SDL_ConvertAudio(&cvt) == 0) {
150 *maxlen = len * cvt.len_ratio;
151 } else {
152 free(*maxbuf);
153 *maxbuf = NULL;
154 *maxlen = 0;
160 static openal_snd *new_openal_snd (const void *data, dword len, dword rate, dword lstart, dword llen, int sign) {
161 assert(data);
162 ALuint buffer = 0;
163 openal_snd *snd = NULL;
164 void *newdata = NULL;
165 int newlen = 0;
166 // for some reason 8bit formats makes psshshshsh
167 // TODO do this without SDL
168 convert_this_ext(sign ? AUDIO_S8 : AUDIO_U8, 1, rate, AUDIO_S16SYS, 1, rate, data, len, &newdata, &newlen);
169 if (newdata != NULL) {
170 alGenBuffers(1, &buffer);
171 if (alGetError() == AL_NO_ERROR) {
172 alBufferData(buffer, AL_FORMAT_MONO16, newdata, newlen, rate);
173 if (alGetError() == AL_NO_ERROR) {
174 snd = malloc(sizeof(openal_snd));
175 if (snd != NULL) {
176 snd->base.tag = TAG_OAL1;
177 snd->buffer = buffer;
178 // TODO loops
179 } else {
180 alDeleteBuffers(1, &buffer);
182 } else {
183 alDeleteBuffers(1, &buffer);
186 free(newdata);
188 return snd;
191 snd_t *S_get (int id) {
192 void *handle;
193 openal_snd *snd = NULL;
194 if (context != NULL) {
195 handle = M_lock(id);
196 if (handle != NULL) {
197 void *data = handle;
198 dword len = F_getreslen(id);
199 dword rate = 11025;
200 dword lstart = 0;
201 dword llen = 0;
202 int sign = 0;
203 if (len > 16) {
204 dmi *hdr = handle;
205 dword hdr_len = int2host(hdr->len);
206 dword hdr_rate = int2host(hdr->rate);
207 dword hdr_lstart = int2host(hdr->lstart);
208 dword hdr_llen = int2host(hdr->llen);
209 if (hdr_len <= len - 8 && hdr_lstart + hdr_llen <= len - 16) {
210 data = hdr->data;
211 len = hdr_len;
212 rate = hdr_rate;
213 lstart = hdr_lstart;
214 llen = hdr_llen;
215 sign = 1;
218 snd = new_openal_snd(data, len, rate, lstart, llen, sign);
219 M_unlock(handle);
222 return (snd_t*)snd;
225 snd_t *S_load (const char name[8]) {
226 return S_get(F_findres(name));
229 void S_free (snd_t *s) {
230 int i;
231 ALint h;
232 openal_snd *snd = (openal_snd*)s;
233 if (snd != NULL) {
234 assert(snd->base.tag == TAG_OAL1);
235 if (context != NULL) {
236 for (i = 0; i < MAX_CHANNELS; i++) {
237 alGetSourcei(sources[i], AL_BUFFER, &h);
238 if (h == snd->buffer) {
239 alSourceStop(sources[i]);
240 alSourcei(sources[i], AL_BUFFER, 0);
243 alDeleteBuffers(1, &snd->buffer);
244 assert(alGetError() == AL_NO_ERROR);
246 snd->base.tag = 0;
247 free(s);
251 void S_init (void) {
252 assert(device == NULL && context == NULL);
253 const ALCint attrs[] = {ALC_MONO_SOURCES, MAX_CHANNELS, 0};
254 device = alcOpenDevice(NULL);
255 if (device != NULL) {
256 context = alcCreateContext(device, attrs);
257 if (context != NULL) {
258 if (alcMakeContextCurrent(context)) {
259 alGenSources(MAX_CHANNELS, sources);
260 if (alGetError() == AL_NO_ERROR) {
261 alListenerf(AL_GAIN, 1);
262 alListener3f(AL_POSITION, 0, 0, 0);
263 alListener3f(AL_VELOCITY, 0, 0, 0);
264 } else {
265 logo("S_init: unable to create OpenAL sources\n");
266 alcDestroyContext(context);
267 alcCloseDevice(device);
268 context = NULL;
269 device = NULL;
271 } else {
272 logo("S_init: unable to make OpenAL context current\n");
273 alcDestroyContext(context);
274 alcCloseDevice(device);
275 context = NULL;
276 device = NULL;
278 } else {
279 logo("S_init: unable to create OpenAL context\n");
280 alcCloseDevice(device);
281 device = NULL;
283 } else {
284 logo("S_init: unable to open OpenAL device\n");
288 void S_done (void) {
289 if (context != NULL) {
290 alcMakeContextCurrent(NULL);
291 alcDestroyContext(context);
292 alcCloseDevice(device);
294 context = NULL;
295 device = NULL;
298 short S_play (snd_t *s, short c, short v) {
299 assert(c >= 0 && c <= MAX_CHANNELS);
300 assert(v >= 0 && v < 256);
301 ALuint source;
302 ALint state;
303 int channel;
304 if (context != NULL && s != NULL) {
305 openal_snd *snd = (openal_snd*)s;
306 assert(snd->base.tag == TAG_OAL1);
307 if (c == 0) {
308 for (channel = 0; channel < MAX_CHANNELS; channel++) {
309 state = AL_PLAYING;
310 alGetSourcei(sources[channel], AL_SOURCE_STATE, &state);
311 if (state == AL_STOPPED || state == AL_INITIAL) {
312 break; // !!!
315 } else {
316 channel = c - 1;
318 if (channel < MAX_CHANNELS) {
319 source = sources[channel];
320 alSourceStop(source);
321 alSourcei(source, AL_BUFFER, snd->buffer);
322 alSourcef(source, AL_PITCH, 1);
323 alSourcef(source, AL_GAIN, v / 255.0);
324 alSource3f(source, AL_POSITION, 0, 0, 0);
325 alSource3f(source, AL_VELOCITY, 0, 0, 0);
326 alSourcei(source, AL_LOOPING, AL_FALSE);
327 alSourcePlay(source);
330 return 0;
333 void S_stop (short c) {
334 assert(c >= 0 && c <= MAX_CHANNELS);
335 if (context != NULL) {
336 if (c != 0) {
337 alSourceStop(sources[c - 1]);
342 void S_volume (int v) {
343 assert(v >= 0 && v <= 128);
344 snd_vol = v;
345 if (context != NULL) {
346 alListenerf(AL_GAIN, v / 128.0);
350 void S_wait (void) {
351 int i;
352 ALint state;
353 if (context != NULL) {
354 for (i = 0; i < MAX_CHANNELS; i++) {
355 do {
356 state = AL_STOPPED;
357 alGetSourcei(sources[i], AL_SOURCE_STATE, &state);
358 } while (state == AL_PLAYING);