DEADSOFTWARE

files: add abstract streams
[flatwaifu.git] / src / openal / sound.c
1 /* Copyright (C) 2020 SovietPony
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 */
16 #include "sound.h"
17 #include "music.h"
19 #include "files.h" // F_findres F_getreslen
20 #include "memory.h" // M_lock M_unlock
21 #include "misc.h" // int2host
22 #include "error.h" // logo
24 #ifdef __APPLE__
25 # include <OpenAL/al.h>
26 # include <OpenAL/alc.h>
27 #else
28 # include <AL/al.h>
29 # include <AL/alc.h>
30 #endif
32 #include "SDL.h" // SDL_BuildAudioCVT SDL_ConvertAudio
33 #include <assert.h>
34 #include <stdlib.h> // malloc
35 #include <string.h> // memcpy
37 #define TAG_OAL1 0x4F414C31
38 #define MAX_CHANNELS 8
40 #pragma pack(1)
41 typedef struct dmi {
42 dword len;
43 dword rate;
44 dword lstart;
45 dword llen;
46 byte data[];
47 } dmi;
48 #pragma pack()
50 typedef struct openal_snd {
51 snd_t base;
52 ALuint buffer;
53 } openal_snd;
55 typedef struct openal_channel {
56 ALuint source;
57 } openal_channel;
59 static short snd_vol;
61 static ALCdevice *device;
62 static ALCcontext *context;
63 static ALuint sources[MAX_CHANNELS];
65 /* Music */
67 const cfg_t *MUS_args (void) {
68 return NULL;
69 }
71 const cfg_t *MUS_conf (void) {
72 return NULL;
73 }
75 const menu_t *MUS_menu (void) {
76 return NULL;
77 }
79 void MUS_init (void) {
81 }
83 void MUS_done (void) {
85 }
87 void MUS_start (int time) {
89 }
91 void MUS_stop (void) {
93 }
95 void MUS_volume (int v) {
97 }
99 void MUS_load (char n[8]) {
103 void MUS_free (void) {
107 void MUS_update (void) {
111 /* Sound */
113 static int sound_menu_handler (menu_msg_t *msg, const menu_t *m, int i) {
114 static int cur;
115 enum { VOLUME, __NUM__ };
116 static const simple_menu_t sm = {
117 GM_BIG, "Sound", NULL,
119 { "Volume", NULL },
121 };
122 if (i == VOLUME) {
123 switch (msg->type) {
124 case GM_GETENTRY: return GM_init_int0(msg, GM_SCROLLER, 0, 0, 0);
125 case GM_GETINT: return GM_init_int(msg, snd_vol, 0, 128, 8);
126 case GM_SETINT: S_volume(msg->integer.i); return 1;
129 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
132 const menu_t *S_menu (void) {
133 static const menu_t m = { &sound_menu_handler };
134 return &m;
137 const cfg_t *S_args (void) {
138 static const cfg_t args[] = {
139 { "sndvol", &snd_vol, Y_WORD },
140 { NULL, NULL, 0 }
141 };
142 return args;
145 const cfg_t *S_conf (void) {
146 static const cfg_t conf[] = {
147 { "sound_volume", &snd_vol, Y_WORD },
148 { NULL, NULL, 0 }
149 };
150 return conf;
153 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) {
154 SDL_AudioCVT cvt;
155 *maxlen = 0;
156 *maxbuf = NULL;
157 if (SDL_BuildAudioCVT(&cvt, src_format, src_chan, src_rate, dst_format, dst_chan, dst_rate) != -1) {
158 *maxlen = len * cvt.len_mult;
159 *maxbuf = malloc(*maxlen);
160 if (*maxbuf != NULL) {
161 memcpy(*maxbuf, buf, len);
162 cvt.buf = *maxbuf;
163 cvt.len = len;
164 if (SDL_ConvertAudio(&cvt) == 0) {
165 *maxlen = len * cvt.len_ratio;
166 } else {
167 free(*maxbuf);
168 *maxbuf = NULL;
169 *maxlen = 0;
175 static openal_snd *new_openal_snd (const void *data, dword len, dword rate, dword lstart, dword llen, int sign) {
176 assert(data);
177 ALuint buffer = 0;
178 openal_snd *snd = NULL;
179 void *newdata = NULL;
180 int newlen = 0;
181 // for some reason 8bit formats makes psshshshsh
182 // TODO do this without SDL
183 convert_this_ext(sign ? AUDIO_S8 : AUDIO_U8, 1, rate, AUDIO_S16SYS, 1, rate, data, len, &newdata, &newlen);
184 if (newdata != NULL) {
185 alGenBuffers(1, &buffer);
186 if (alGetError() == AL_NO_ERROR) {
187 alBufferData(buffer, AL_FORMAT_MONO16, newdata, newlen, rate);
188 if (alGetError() == AL_NO_ERROR) {
189 snd = malloc(sizeof(openal_snd));
190 if (snd != NULL) {
191 snd->base.tag = TAG_OAL1;
192 snd->buffer = buffer;
193 // TODO loops
194 } else {
195 alDeleteBuffers(1, &buffer);
197 } else {
198 alDeleteBuffers(1, &buffer);
201 free(newdata);
203 return snd;
206 snd_t *S_get (int id) {
207 void *handle;
208 openal_snd *snd = NULL;
209 if (context != NULL) {
210 handle = M_lock(id);
211 if (handle != NULL) {
212 void *data = handle;
213 dword len = F_getreslen(id);
214 dword rate = 11025;
215 dword lstart = 0;
216 dword llen = 0;
217 int sign = 0;
218 if (len > 16) {
219 dmi *hdr = handle;
220 dword hdr_len = int2host(hdr->len);
221 dword hdr_rate = int2host(hdr->rate);
222 dword hdr_lstart = int2host(hdr->lstart);
223 dword hdr_llen = int2host(hdr->llen);
224 if (hdr_len <= len - 8 && hdr_lstart + hdr_llen <= len - 16) {
225 data = hdr->data;
226 len = hdr_len;
227 rate = hdr_rate;
228 lstart = hdr_lstart;
229 llen = hdr_llen;
230 sign = 1;
233 snd = new_openal_snd(data, len, rate, lstart, llen, sign);
234 M_unlock(handle);
237 return (snd_t*)snd;
240 snd_t *S_load (const char name[8]) {
241 return S_get(F_findres(name));
244 void S_free (snd_t *s) {
245 int i;
246 ALint h;
247 openal_snd *snd = (openal_snd*)s;
248 if (snd != NULL) {
249 assert(snd->base.tag == TAG_OAL1);
250 if (context != NULL) {
251 for (i = 0; i < MAX_CHANNELS; i++) {
252 alGetSourcei(sources[i], AL_BUFFER, &h);
253 if (h == snd->buffer) {
254 alSourceStop(sources[i]);
255 alSourcei(sources[i], AL_BUFFER, 0);
258 alDeleteBuffers(1, &snd->buffer);
259 assert(alGetError() == AL_NO_ERROR);
261 snd->base.tag = 0;
262 free(s);
266 void S_init (void) {
267 assert(device == NULL && context == NULL);
268 const ALCint attrs[] = {ALC_MONO_SOURCES, MAX_CHANNELS, 0};
269 device = alcOpenDevice(NULL);
270 if (device != NULL) {
271 context = alcCreateContext(device, attrs);
272 if (context != NULL) {
273 if (alcMakeContextCurrent(context)) {
274 alGenSources(MAX_CHANNELS, sources);
275 if (alGetError() == AL_NO_ERROR) {
276 alListenerf(AL_GAIN, 1);
277 alListener3f(AL_POSITION, 0, 0, 0);
278 alListener3f(AL_VELOCITY, 0, 0, 0);
279 } else {
280 logo("S_init: unable to create OpenAL sources\n");
281 alcDestroyContext(context);
282 alcCloseDevice(device);
283 context = NULL;
284 device = NULL;
286 } else {
287 logo("S_init: unable to make OpenAL context current\n");
288 alcDestroyContext(context);
289 alcCloseDevice(device);
290 context = NULL;
291 device = NULL;
293 } else {
294 logo("S_init: unable to create OpenAL context\n");
295 alcCloseDevice(device);
296 device = NULL;
298 } else {
299 logo("S_init: unable to open OpenAL device\n");
303 void S_done (void) {
304 if (context != NULL) {
305 alcMakeContextCurrent(NULL);
306 alcDestroyContext(context);
307 alcCloseDevice(device);
309 context = NULL;
310 device = NULL;
313 short S_play (snd_t *s, short c, short v) {
314 assert(c >= 0 && c <= MAX_CHANNELS);
315 assert(v >= 0 && v < 256);
316 ALuint source;
317 ALint state;
318 int channel;
319 if (context != NULL && s != NULL) {
320 openal_snd *snd = (openal_snd*)s;
321 assert(snd->base.tag == TAG_OAL1);
322 if (c == 0) {
323 for (channel = 0; channel < MAX_CHANNELS; channel++) {
324 state = AL_PLAYING;
325 alGetSourcei(sources[channel], AL_SOURCE_STATE, &state);
326 if (state == AL_STOPPED || state == AL_INITIAL) {
327 break; // !!!
330 } else {
331 channel = c - 1;
333 if (channel < MAX_CHANNELS) {
334 source = sources[channel];
335 alSourceStop(source);
336 alSourcei(source, AL_BUFFER, snd->buffer);
337 alSourcef(source, AL_PITCH, 1);
338 alSourcef(source, AL_GAIN, v / 255.0);
339 alSource3f(source, AL_POSITION, 0, 0, 0);
340 alSource3f(source, AL_VELOCITY, 0, 0, 0);
341 alSourcei(source, AL_LOOPING, AL_FALSE);
342 alSourcePlay(source);
345 return 0;
348 void S_stop (short c) {
349 assert(c >= 0 && c <= MAX_CHANNELS);
350 if (context != NULL) {
351 if (c != 0) {
352 alSourceStop(sources[c - 1]);
357 void S_volume (int v) {
358 assert(v >= 0 && v <= 128);
359 snd_vol = v;
360 if (context != NULL) {
361 alListenerf(AL_GAIN, v / 128.0);
365 void S_wait (void) {
366 int i;
367 ALint state;
368 if (context != NULL) {
369 for (i = 0; i < MAX_CHANNELS; i++) {
370 do {
371 state = AL_STOPPED;
372 alGetSourcei(sources[i], AL_SOURCE_STATE, &state);
373 } while (state == AL_PLAYING);