DEADSOFTWARE

7bb60a0933b0145b63a7d4a70054e06f4db5af75
[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 "SDL.h" // SDL_BuildAudioCVT SDL_ConvertAudio
34 #include <assert.h>
35 #include <stdlib.h> // malloc
36 #include <string.h> // memcpy
38 #define TAG_OAL1 0x4F414C31
39 #define MAX_CHANNELS 8
41 #pragma pack(1)
42 typedef struct dmi {
43 dword len;
44 dword rate;
45 dword lstart;
46 dword llen;
47 byte data[];
48 } dmi;
49 #pragma pack()
51 typedef struct openal_snd {
52 snd_t base;
53 ALuint buffer;
54 } openal_snd;
56 typedef struct openal_channel {
57 ALuint source;
58 } openal_channel;
60 static short snd_vol;
62 static ALCdevice *device;
63 static ALCcontext *context;
64 static ALuint sources[MAX_CHANNELS];
66 /* Music */
68 const cfg_t *MUS_args (void) {
69 return NULL;
70 }
72 const cfg_t *MUS_conf (void) {
73 return NULL;
74 }
76 const menu_t *MUS_menu (void) {
77 return NULL;
78 }
80 void MUS_init (void) {
82 }
84 void MUS_done (void) {
86 }
88 void MUS_start (int time) {
90 }
92 void MUS_stop (void) {
94 }
96 void MUS_volume (int v) {
98 }
100 void MUS_load (char n[8]) {
104 void MUS_free (void) {
108 void MUS_update (void) {
112 /* Sound */
114 static int sound_menu_handler (menu_msg_t *msg, const menu_t *m, int i) {
115 static int cur;
116 enum { VOLUME, __NUM__ };
117 static const simple_menu_t sm = {
118 GM_BIG, "Sound", NULL,
120 { "Volume", NULL },
122 };
123 if (i == VOLUME) {
124 switch (msg->type) {
125 case GM_GETENTRY: return GM_init_int0(msg, GM_SCROLLER, 0, 0, 0);
126 case GM_GETINT: return GM_init_int(msg, snd_vol, 0, 128, 8);
127 case GM_SETINT: S_volume(msg->integer.i); return 1;
130 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
133 const menu_t *S_menu (void) {
134 static const menu_t m = { &sound_menu_handler };
135 return &m;
138 const cfg_t *S_args (void) {
139 static const cfg_t args[] = {
140 { "sndvol", &snd_vol, Y_WORD },
141 { NULL, NULL, 0 }
142 };
143 return args;
146 const cfg_t *S_conf (void) {
147 static const cfg_t conf[] = {
148 { "sound_volume", &snd_vol, Y_WORD },
149 { NULL, NULL, 0 }
150 };
151 return conf;
154 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) {
155 SDL_AudioCVT cvt;
156 *maxlen = 0;
157 *maxbuf = NULL;
158 if (SDL_BuildAudioCVT(&cvt, src_format, src_chan, src_rate, dst_format, dst_chan, dst_rate) != -1) {
159 *maxlen = len * cvt.len_mult;
160 *maxbuf = malloc(*maxlen);
161 if (*maxbuf != NULL) {
162 memcpy(*maxbuf, buf, len);
163 cvt.buf = *maxbuf;
164 cvt.len = len;
165 if (SDL_ConvertAudio(&cvt) == 0) {
166 *maxlen = len * cvt.len_ratio;
167 } else {
168 free(*maxbuf);
169 *maxbuf = NULL;
170 *maxlen = 0;
176 static openal_snd *new_openal_snd (const void *data, dword len, dword rate, dword lstart, dword llen, int sign) {
177 assert(data);
178 ALuint buffer = 0;
179 openal_snd *snd = NULL;
180 void *newdata = NULL;
181 int newlen = 0;
182 // for some reason 8bit formats makes psshshshsh
183 // TODO do this without SDL
184 convert_this_ext(sign ? AUDIO_S8 : AUDIO_U8, 1, rate, AUDIO_S16SYS, 1, rate, data, len, &newdata, &newlen);
185 if (newdata != NULL) {
186 alGenBuffers(1, &buffer);
187 if (alGetError() == AL_NO_ERROR) {
188 alBufferData(buffer, AL_FORMAT_MONO16, newdata, newlen, rate);
189 if (alGetError() == AL_NO_ERROR) {
190 snd = malloc(sizeof(openal_snd));
191 if (snd != NULL) {
192 snd->base.tag = TAG_OAL1;
193 snd->buffer = buffer;
194 // TODO loops
195 } else {
196 alDeleteBuffers(1, &buffer);
198 } else {
199 alDeleteBuffers(1, &buffer);
202 free(newdata);
204 return snd;
207 snd_t *S_get (int id) {
208 void *handle;
209 openal_snd *snd = NULL;
210 if (context != NULL) {
211 handle = M_lock(id);
212 if (handle != NULL) {
213 void *data = handle;
214 dword len = F_getreslen(id);
215 dword rate = 11025;
216 dword lstart = 0;
217 dword llen = 0;
218 int sign = 0;
219 if (len > 16) {
220 dmi *hdr = handle;
221 dword hdr_len = int2host(hdr->len);
222 dword hdr_rate = int2host(hdr->rate);
223 dword hdr_lstart = int2host(hdr->lstart);
224 dword hdr_llen = int2host(hdr->llen);
225 if (hdr_len <= len - 8 && hdr_lstart + hdr_llen <= len - 16) {
226 data = hdr->data;
227 len = hdr_len;
228 rate = hdr_rate;
229 lstart = hdr_lstart;
230 llen = hdr_llen;
231 sign = 1;
234 snd = new_openal_snd(data, len, rate, lstart, llen, sign);
235 M_unlock(handle);
238 return (snd_t*)snd;
241 snd_t *S_load (const char name[8]) {
242 return S_get(F_findres(name));
245 void S_free (snd_t *s) {
246 int i;
247 ALint h;
248 openal_snd *snd = (openal_snd*)s;
249 if (snd != NULL) {
250 assert(snd->base.tag == TAG_OAL1);
251 if (context != NULL) {
252 for (i = 0; i < MAX_CHANNELS; i++) {
253 alGetSourcei(sources[i], AL_BUFFER, &h);
254 if (h == snd->buffer) {
255 alSourceStop(sources[i]);
256 alSourcei(sources[i], AL_BUFFER, 0);
259 alDeleteBuffers(1, &snd->buffer);
260 assert(alGetError() == AL_NO_ERROR);
262 snd->base.tag = 0;
263 free(s);
267 void S_init (void) {
268 assert(device == NULL && context == NULL);
269 const ALCint attrs[] = {ALC_MONO_SOURCES, MAX_CHANNELS, 0};
270 device = alcOpenDevice(NULL);
271 if (device != NULL) {
272 context = alcCreateContext(device, attrs);
273 if (context != NULL) {
274 if (alcMakeContextCurrent(context)) {
275 alGenSources(MAX_CHANNELS, sources);
276 if (alGetError() == AL_NO_ERROR) {
277 alListenerf(AL_GAIN, 1);
278 alListener3f(AL_POSITION, 0, 0, 0);
279 alListener3f(AL_VELOCITY, 0, 0, 0);
280 } else {
281 logo("S_init: unable to create OpenAL sources\n");
282 alcDestroyContext(context);
283 alcCloseDevice(device);
284 context = NULL;
285 device = NULL;
287 } else {
288 logo("S_init: unable to make OpenAL context current\n");
289 alcDestroyContext(context);
290 alcCloseDevice(device);
291 context = NULL;
292 device = NULL;
294 } else {
295 logo("S_init: unable to create OpenAL context\n");
296 alcCloseDevice(device);
297 device = NULL;
299 } else {
300 logo("S_init: unable to open OpenAL device\n");
304 void S_done (void) {
305 if (context != NULL) {
306 alcMakeContextCurrent(NULL);
307 alcDestroyContext(context);
308 alcCloseDevice(device);
310 context = NULL;
311 device = NULL;
314 short S_play (snd_t *s, short c, short v) {
315 assert(c >= 0 && c <= MAX_CHANNELS);
316 assert(v >= 0 && v < 256);
317 ALuint source;
318 ALint state;
319 int channel;
320 if (context != NULL && s != NULL) {
321 openal_snd *snd = (openal_snd*)s;
322 assert(snd->base.tag == TAG_OAL1);
323 if (c == 0) {
324 for (channel = 0; channel < MAX_CHANNELS; channel++) {
325 state = AL_PLAYING;
326 alGetSourcei(sources[channel], AL_SOURCE_STATE, &state);
327 if (state == AL_STOPPED || state == AL_INITIAL) {
328 break; // !!!
331 } else {
332 channel = c - 1;
334 if (channel < MAX_CHANNELS) {
335 source = sources[channel];
336 alSourceStop(source);
337 alSourcei(source, AL_BUFFER, snd->buffer);
338 alSourcef(source, AL_PITCH, 1);
339 alSourcef(source, AL_GAIN, v / 255.0);
340 alSource3f(source, AL_POSITION, 0, 0, 0);
341 alSource3f(source, AL_VELOCITY, 0, 0, 0);
342 alSourcei(source, AL_LOOPING, AL_FALSE);
343 alSourcePlay(source);
346 return 0;
349 void S_stop (short c) {
350 assert(c >= 0 && c <= MAX_CHANNELS);
351 if (context != NULL) {
352 if (c != 0) {
353 alSourceStop(sources[c - 1]);
358 void S_volume (int v) {
359 assert(v >= 0 && v <= 128);
360 snd_vol = v;
361 if (context != NULL) {
362 alListenerf(AL_GAIN, v / 128.0);
366 void S_wait (void) {
367 int i;
368 ALint state;
369 if (context != NULL) {
370 for (i = 0; i < MAX_CHANNELS; i++) {
371 do {
372 state = AL_STOPPED;
373 alGetSourcei(sources[i], AL_SOURCE_STATE, &state);
374 } while (state == AL_PLAYING);