DEADSOFTWARE

portability: avoid errors on some compilers
[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 "error.h" // logo
23 #include "common/endianness.h"
25 #ifdef __APPLE__
26 # include <OpenAL/al.h>
27 # include <OpenAL/alc.h>
28 #else
29 # include <AL/al.h>
30 # include <AL/alc.h>
31 #endif
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 #define OAL_FORMAT AL_FORMAT_MONO8
42 #pragma pack(1)
43 typedef struct dmi {
44 word len;
45 word rate;
46 word lstart;
47 word llen;
48 byte data[];
49 } dmi;
50 #pragma pack()
52 typedef struct openal_snd {
53 snd_t base;
54 ALuint buffer;
55 } openal_snd;
57 typedef struct openal_channel {
58 ALuint source;
59 } openal_channel;
61 static short snd_vol;
63 static ALCdevice *device;
64 static ALCcontext *context;
65 static ALuint sources[MAX_CHANNELS];
67 /* Music */
69 const cfg_t *MUS_args (void) {
70 return NULL;
71 }
73 const cfg_t *MUS_conf (void) {
74 return NULL;
75 }
77 const menu_t *MUS_menu (void) {
78 return NULL;
79 }
81 void MUS_init (void) {
83 }
85 void MUS_done (void) {
87 }
89 void MUS_start (int time) {
91 }
93 void MUS_stop (void) {
95 }
97 void MUS_volume (int v) {
99 }
101 void MUS_load (char n[8]) {
105 void MUS_free (void) {
109 void MUS_update (void) {
113 /* Sound */
115 static int sound_menu_handler (menu_msg_t *msg, const menu_t *m, int i) {
116 static int cur;
117 enum { VOLUME, __NUM__ };
118 static const simple_menu_t sm = {
119 GM_BIG, "Sound", NULL,
121 { "Volume", NULL },
123 };
124 if (i == VOLUME) {
125 switch (msg->type) {
126 case GM_GETENTRY: return GM_init_int0(msg, GM_SCROLLER, 0, 0, 0);
127 case GM_GETINT: return GM_init_int(msg, snd_vol, 0, 128, 8);
128 case GM_SETINT: S_volume(msg->integer.i); return 1;
131 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
134 const menu_t *S_menu (void) {
135 static const menu_t m = { &sound_menu_handler };
136 return &m;
139 const cfg_t *S_args (void) {
140 static const cfg_t args[] = {
141 { "sndvol", &snd_vol, Y_WORD },
142 { NULL, NULL, 0 }
143 };
144 return args;
147 const cfg_t *S_conf (void) {
148 static const cfg_t conf[] = {
149 { "sound_volume", &snd_vol, Y_WORD },
150 { NULL, NULL, 0 }
151 };
152 return conf;
155 // Convert mono signed/unsigned 8bit sound to openal format
156 // AL_FORMAT_MONO8 -> mono unsigned 8bit
157 // AL_FORMAT_STEREO8 -> stereo unsigned 8bit
158 // AL_FORMAT_MONO16 -> mono signed 16bit
159 // AL_FORMAT_STEREO16 -> stereo signed 16bit
160 static void cvt_this (int sign, ALint format, const void *data, size_t len, void **outdata, size_t *outlen) {
161 *outdata = NULL;
162 *outlen = 0;
163 switch (format) {
164 case AL_FORMAT_MONO8:
166 int8_t *y = malloc(len);
167 if (y != NULL) {
168 memcpy(y, data, len);
169 if (sign) {
170 for (size_t i = 0; i < len; i++) {
171 y[i] ^= 0x80;
174 *outdata = y;
175 *outlen = len;
178 break;
179 case AL_FORMAT_MONO16:
181 int16_t *y = malloc(2 * len);
182 if (y != NULL) {
183 for (size_t i = 0; i < len; ++i) {
184 int32_t c = ((int8_t*)data)[i] ^ (sign ? 0x00 : 0x80);
185 y[i] = c >= 0 ? c * 32767 / 127 : c * -32767 / -128;
188 *outdata = y;
189 *outlen = 2 * len;
191 break;
192 case AL_FORMAT_STEREO8:
194 int8_t *y = malloc(2 * len);
195 if (y != NULL) {
196 int8_t *yy = y;
197 for (size_t i = 0; i < len; i++) {
198 int8_t xx = ((int8_t*)data)[i] ^ (sign ? 0x80 : 0x00);
199 *yy = xx; yy++;
200 *yy = xx; yy++;
202 *outdata = y;
203 *outlen = 2 * len;
206 break;
207 case AL_FORMAT_STEREO16:
209 int16_t *y = malloc(2 * 2 * len);
210 if (y != NULL) {
211 int16_t *yy = y;
212 for (size_t i = 0; i < len; ++i) {
213 int32_t x = ((int8_t*)data)[i] ^ (sign ? 0x00 : 0x80);
214 int16_t xx = x >= 0 ? x * 32767 / 127 : x * -32767 / -128;
215 *yy = xx; yy++;
216 *yy = xx; yy++;
219 *outdata = y;
220 *outlen = 2 * 2* len;
222 break;
226 static openal_snd *new_openal_snd (const void *data, dword len, dword rate, dword lstart, dword llen, int sign) {
227 assert(data);
228 ALuint buffer = 0;
229 openal_snd *snd = NULL;
230 void *newdata = NULL;
231 size_t newlen = 0;
232 cvt_this(sign, OAL_FORMAT, data, len, &newdata, &newlen);
233 if (newdata != NULL) {
234 alGenBuffers(1, &buffer);
235 if (alGetError() == AL_NO_ERROR) {
236 alBufferData(buffer, OAL_FORMAT, newdata, newlen, rate);
237 if (alGetError() == AL_NO_ERROR) {
238 snd = malloc(sizeof(openal_snd));
239 if (snd != NULL) {
240 snd->base.tag = TAG_OAL1;
241 snd->buffer = buffer;
242 // TODO loops
243 } else {
244 alDeleteBuffers(1, &buffer);
246 } else {
247 alDeleteBuffers(1, &buffer);
250 free(newdata);
252 return snd;
255 snd_t *S_get (int id) {
256 void *handle;
257 openal_snd *snd = NULL;
258 if (context != NULL) {
259 handle = M_lock(id);
260 if (handle != NULL) {
261 void *data = handle;
262 word len = F_getreslen(id);
263 word rate = 11025;
264 word lstart = 0;
265 word llen = 0;
266 int sign = 0;
267 if (len > 8) {
268 dmi *hdr = handle;
269 word hdr_len = short2host(hdr->len);
270 word hdr_rate = short2host(hdr->rate);
271 word hdr_lstart = short2host(hdr->lstart);
272 word hdr_llen = short2host(hdr->llen);
273 if (hdr_len <= len - 4 && hdr_lstart + hdr_llen <= len - 8) {
274 data = hdr->data;
275 len = hdr_len;
276 rate = hdr_rate;
277 lstart = hdr_lstart;
278 llen = hdr_llen;
279 sign = 1;
282 snd = new_openal_snd(data, len, rate, lstart, llen, sign);
283 M_unlock(handle);
286 return (snd_t*)snd;
289 snd_t *S_load (const char name[8]) {
290 return S_get(F_findres(name));
293 void S_free (snd_t *s) {
294 int i;
295 ALint h;
296 openal_snd *snd = (openal_snd*)s;
297 if (snd != NULL) {
298 assert(snd->base.tag == TAG_OAL1);
299 if (context != NULL) {
300 for (i = 0; i < MAX_CHANNELS; i++) {
301 alGetSourcei(sources[i], AL_BUFFER, &h);
302 if (h == snd->buffer) {
303 alSourceStop(sources[i]);
304 alSourcei(sources[i], AL_BUFFER, 0);
307 alDeleteBuffers(1, &snd->buffer);
308 assert(alGetError() == AL_NO_ERROR);
310 snd->base.tag = 0;
311 free(s);
315 void S_init (void) {
316 assert(device == NULL && context == NULL);
317 const ALCint attrs[] = {ALC_MONO_SOURCES, MAX_CHANNELS, 0};
318 device = alcOpenDevice(NULL);
319 if (device != NULL) {
320 context = alcCreateContext(device, attrs);
321 if (context != NULL) {
322 if (alcMakeContextCurrent(context)) {
323 alGenSources(MAX_CHANNELS, sources);
324 if (alGetError() == AL_NO_ERROR) {
325 alListenerf(AL_GAIN, 1);
326 alListener3f(AL_POSITION, 0, 0, 0);
327 alListener3f(AL_VELOCITY, 0, 0, 0);
328 } else {
329 logo("S_init: unable to create OpenAL sources\n");
330 alcDestroyContext(context);
331 alcCloseDevice(device);
332 context = NULL;
333 device = NULL;
335 } else {
336 logo("S_init: unable to make OpenAL context current\n");
337 alcDestroyContext(context);
338 alcCloseDevice(device);
339 context = NULL;
340 device = NULL;
342 } else {
343 logo("S_init: unable to create OpenAL context\n");
344 alcCloseDevice(device);
345 device = NULL;
347 } else {
348 logo("S_init: unable to open OpenAL device\n");
352 void S_done (void) {
353 if (context != NULL) {
354 alcMakeContextCurrent(NULL);
355 alcDestroyContext(context);
356 alcCloseDevice(device);
358 context = NULL;
359 device = NULL;
362 short S_play (snd_t *s, short c, short v) {
363 assert(c >= 0 && c <= MAX_CHANNELS);
364 assert(v >= 0 && v < 256);
365 ALuint source;
366 ALint state;
367 int channel;
368 if (context != NULL && s != NULL) {
369 openal_snd *snd = (openal_snd*)s;
370 assert(snd->base.tag == TAG_OAL1);
371 if (c == 0) {
372 for (channel = 0; channel < MAX_CHANNELS; channel++) {
373 state = AL_PLAYING;
374 alGetSourcei(sources[channel], AL_SOURCE_STATE, &state);
375 if (state == AL_STOPPED || state == AL_INITIAL) {
376 break; // !!!
379 } else {
380 channel = c - 1;
382 if (channel < MAX_CHANNELS) {
383 source = sources[channel];
384 alSourceStop(source);
385 alSourcei(source, AL_BUFFER, snd->buffer);
386 alSourcef(source, AL_PITCH, 1);
387 alSourcef(source, AL_GAIN, v / 255.0);
388 alSource3f(source, AL_POSITION, 0, 0, 0);
389 alSource3f(source, AL_VELOCITY, 0, 0, 0);
390 alSourcei(source, AL_LOOPING, AL_FALSE);
391 alSourcePlay(source);
394 return 0;
397 void S_stop (short c) {
398 assert(c >= 0 && c <= MAX_CHANNELS);
399 if (context != NULL) {
400 if (c != 0) {
401 alSourceStop(sources[c - 1]);
406 void S_volume (int v) {
407 assert(v >= 0 && v <= 128);
408 snd_vol = v;
409 if (context != NULL) {
410 alListenerf(AL_GAIN, v / 128.0);
414 void S_wait (void) {
415 int i;
416 ALint state;
417 if (context != NULL) {
418 for (i = 0; i < MAX_CHANNELS; i++) {
419 do {
420 state = AL_STOPPED;
421 alGetSourcei(sources[i], AL_SOURCE_STATE, &state);
422 } while (state == AL_PLAYING);