1 /* Copyright (C) 2020 SovietPony
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.
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.
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/>.
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"
26 # include <OpenAL/al.h>
27 # include <OpenAL/alc.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
52 typedef struct openal_snd
{
57 typedef struct openal_channel
{
63 static ALCdevice
*device
;
64 static ALCcontext
*context
;
65 static ALuint sources
[MAX_CHANNELS
];
69 const cfg_t
*MUS_args (void) {
73 const cfg_t
*MUS_conf (void) {
77 const menu_t
*MUS_menu (void) {
81 void MUS_init (void) {
85 void MUS_done (void) {
89 void MUS_start (int time
) {
93 void MUS_stop (void) {
97 void MUS_volume (int v
) {
101 void MUS_load (char n
[8]) {
105 void MUS_free (void) {
109 void MUS_update (void) {
115 static int sound_menu_handler (menu_msg_t
*msg
, const menu_t
*m
, int i
) {
117 enum { VOLUME
, __NUM__
};
118 static const simple_menu_t sm
= {
119 GM_BIG
, "Sound", NULL
,
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
};
139 const cfg_t
*S_args (void) {
140 static const cfg_t args
[] = {
141 { "sndvol", &snd_vol
, Y_WORD
},
147 const cfg_t
*S_conf (void) {
148 static const cfg_t conf
[] = {
149 { "sound_volume", &snd_vol
, Y_WORD
},
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
) {
164 case AL_FORMAT_MONO8
:
166 int8_t *y
= malloc(len
);
168 memcpy(y
, data
, len
);
170 for (size_t i
= 0; i
< len
; i
++) {
179 case AL_FORMAT_MONO16
:
181 int16_t *y
= malloc(2 * len
);
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;
192 case AL_FORMAT_STEREO8
:
194 int8_t *y
= malloc(2 * len
);
197 for (size_t i
= 0; i
< len
; i
++) {
198 int8_t xx
= ((int8_t*)data
)[i
] ^ (sign
? 0x80 : 0x00);
207 case AL_FORMAT_STEREO16
:
209 int16_t *y
= malloc(2 * 2 * len
);
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;
220 *outlen
= 2 * 2* len
;
226 static openal_snd
*new_openal_snd (const void *data
, dword len
, dword rate
, dword lstart
, dword llen
, int sign
) {
229 openal_snd
*snd
= NULL
;
230 void *newdata
= NULL
;
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
));
240 snd
->base
.tag
= TAG_OAL1
;
241 snd
->buffer
= buffer
;
244 alDeleteBuffers(1, &buffer
);
247 alDeleteBuffers(1, &buffer
);
255 snd_t
*S_get (int id
) {
257 openal_snd
*snd
= NULL
;
258 if (context
!= NULL
) {
260 if (handle
!= NULL
) {
262 word len
= F_getreslen(id
);
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) {
282 snd
= new_openal_snd(data
, len
, rate
, lstart
, llen
, sign
);
289 snd_t
*S_load (const char name
[8]) {
290 return S_get(F_findres(name
));
293 void S_free (snd_t
*s
) {
296 openal_snd
*snd
= (openal_snd
*)s
;
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
);
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);
329 logo("S_init: unable to create OpenAL sources\n");
330 alcDestroyContext(context
);
331 alcCloseDevice(device
);
336 logo("S_init: unable to make OpenAL context current\n");
337 alcDestroyContext(context
);
338 alcCloseDevice(device
);
343 logo("S_init: unable to create OpenAL context\n");
344 alcCloseDevice(device
);
348 logo("S_init: unable to open OpenAL device\n");
353 if (context
!= NULL
) {
354 alcMakeContextCurrent(NULL
);
355 alcDestroyContext(context
);
356 alcCloseDevice(device
);
362 short S_play (snd_t
*s
, short c
, short v
) {
363 assert(c
>= 0 && c
<= MAX_CHANNELS
);
364 assert(v
>= 0 && v
< 256);
368 if (context
!= NULL
&& s
!= NULL
) {
369 openal_snd
*snd
= (openal_snd
*)s
;
370 assert(snd
->base
.tag
== TAG_OAL1
);
372 for (channel
= 0; channel
< MAX_CHANNELS
; channel
++) {
374 alGetSourcei(sources
[channel
], AL_SOURCE_STATE
, &state
);
375 if (state
== AL_STOPPED
|| state
== AL_INITIAL
) {
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
);
397 void S_stop (short c
) {
398 assert(c
>= 0 && c
<= MAX_CHANNELS
);
399 if (context
!= NULL
) {
401 alSourceStop(sources
[c
- 1]);
406 void S_volume (int v
) {
407 assert(v
>= 0 && v
<= 128);
409 if (context
!= NULL
) {
410 alListenerf(AL_GAIN
, v
/ 128.0);
417 if (context
!= NULL
) {
418 for (i
= 0; i
< MAX_CHANNELS
; i
++) {
421 alGetSourcei(sources
[i
], AL_SOURCE_STATE
, &state
);
422 } while (state
== AL_PLAYING
);