7bb60a0933b0145b63a7d4a70054e06f4db5af75
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>
33 #include "SDL.h" // SDL_BuildAudioCVT SDL_ConvertAudio
35 #include <stdlib.h> // malloc
36 #include <string.h> // memcpy
38 #define TAG_OAL1 0x4F414C31
39 #define MAX_CHANNELS 8
51 typedef struct openal_snd
{
56 typedef struct openal_channel
{
62 static ALCdevice
*device
;
63 static ALCcontext
*context
;
64 static ALuint sources
[MAX_CHANNELS
];
68 const cfg_t
*MUS_args (void) {
72 const cfg_t
*MUS_conf (void) {
76 const menu_t
*MUS_menu (void) {
80 void MUS_init (void) {
84 void MUS_done (void) {
88 void MUS_start (int time
) {
92 void MUS_stop (void) {
96 void MUS_volume (int v
) {
100 void MUS_load (char n
[8]) {
104 void MUS_free (void) {
108 void MUS_update (void) {
114 static int sound_menu_handler (menu_msg_t
*msg
, const menu_t
*m
, int i
) {
116 enum { VOLUME
, __NUM__
};
117 static const simple_menu_t sm
= {
118 GM_BIG
, "Sound", NULL
,
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
};
138 const cfg_t
*S_args (void) {
139 static const cfg_t args
[] = {
140 { "sndvol", &snd_vol
, Y_WORD
},
146 const cfg_t
*S_conf (void) {
147 static const cfg_t conf
[] = {
148 { "sound_volume", &snd_vol
, Y_WORD
},
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
) {
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
);
165 if (SDL_ConvertAudio(&cvt
) == 0) {
166 *maxlen
= len
* cvt
.len_ratio
;
176 static openal_snd
*new_openal_snd (const void *data
, dword len
, dword rate
, dword lstart
, dword llen
, int sign
) {
179 openal_snd
*snd
= NULL
;
180 void *newdata
= NULL
;
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
));
192 snd
->base
.tag
= TAG_OAL1
;
193 snd
->buffer
= buffer
;
196 alDeleteBuffers(1, &buffer
);
199 alDeleteBuffers(1, &buffer
);
207 snd_t
*S_get (int id
) {
209 openal_snd
*snd
= NULL
;
210 if (context
!= NULL
) {
212 if (handle
!= NULL
) {
214 dword len
= F_getreslen(id
);
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) {
234 snd
= new_openal_snd(data
, len
, rate
, lstart
, llen
, sign
);
241 snd_t
*S_load (const char name
[8]) {
242 return S_get(F_findres(name
));
245 void S_free (snd_t
*s
) {
248 openal_snd
*snd
= (openal_snd
*)s
;
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
);
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);
281 logo("S_init: unable to create OpenAL sources\n");
282 alcDestroyContext(context
);
283 alcCloseDevice(device
);
288 logo("S_init: unable to make OpenAL context current\n");
289 alcDestroyContext(context
);
290 alcCloseDevice(device
);
295 logo("S_init: unable to create OpenAL context\n");
296 alcCloseDevice(device
);
300 logo("S_init: unable to open OpenAL device\n");
305 if (context
!= NULL
) {
306 alcMakeContextCurrent(NULL
);
307 alcDestroyContext(context
);
308 alcCloseDevice(device
);
314 short S_play (snd_t
*s
, short c
, short v
) {
315 assert(c
>= 0 && c
<= MAX_CHANNELS
);
316 assert(v
>= 0 && v
< 256);
320 if (context
!= NULL
&& s
!= NULL
) {
321 openal_snd
*snd
= (openal_snd
*)s
;
322 assert(snd
->base
.tag
== TAG_OAL1
);
324 for (channel
= 0; channel
< MAX_CHANNELS
; channel
++) {
326 alGetSourcei(sources
[channel
], AL_SOURCE_STATE
, &state
);
327 if (state
== AL_STOPPED
|| state
== AL_INITIAL
) {
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
);
349 void S_stop (short c
) {
350 assert(c
>= 0 && c
<= MAX_CHANNELS
);
351 if (context
!= NULL
) {
353 alSourceStop(sources
[c
- 1]);
358 void S_volume (int v
) {
359 assert(v
>= 0 && v
<= 128);
361 if (context
!= NULL
) {
362 alListenerf(AL_GAIN
, v
/ 128.0);
369 if (context
!= NULL
) {
370 for (i
= 0; i
< MAX_CHANNELS
; i
++) {
373 alGetSourcei(sources
[i
], AL_SOURCE_STATE
, &state
);
374 } while (state
== AL_PLAYING
);