ec27e45f373387512c6e2422220d4c5866ee63a6
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 "misc.h" // int2host
20 #include "memory.h" // M_lock M_unlock
21 #include "files.h" // F_findres
25 #include "SDL_mixer.h"
29 #define TAG_MIX1 0x4d495831
33 Uint32 len
; // length [bytes]
34 Uint32 rate
; // freq [Hz]
35 Uint32 lstart
; // loop start offset [bytes]
36 Uint32 llen
; // loop length [bytes]
37 Uint8 data
[]; // sound data
41 typedef struct sdlmixer_snd
{
48 static int devfreq
= MIX_DEFAULT_FREQUENCY
;
49 static Uint32 devformat
= AUDIO_S16SYS
; // MIX_DEFAULT_FORMAT
50 static int devchannels
= 1; // MIX_DEFAULT_CHANNELS;
51 static int devchunksize
= 1024;
52 static int devchunkchannels
= 8;
57 const cfg_t
*MUS_args (void) {
61 const cfg_t
*MUS_conf (void) {
65 const menu_t
*MUS_menu (void) {
69 void MUS_init (void) {
73 void MUS_done (void) {
77 void MUS_start (int time
) {
81 void MUS_stop (void) {
85 void MUS_volume (int v
) {
89 void MUS_load (char n
[8]) {
93 void MUS_free (void) {
97 void MUS_update (void) {
103 static int sound_menu_handler (menu_msg_t
*msg
, const menu_t
*m
, void *data
, int i
) {
105 enum { VOLUME
, __NUM__
};
106 static const simple_menu_t sm
= {
107 GM_BIG
, "Sound", NULL
,
114 case GM_GETENTRY
: return GM_init_int0(msg
, GM_SCROLLER
, 0, 0, 0);
115 case GM_GETINT
: return GM_init_int(msg
, snd_vol
, 0, 128, 8);
116 case GM_SETINT
: S_volume(msg
->integer
.i
); return 1;
119 return simple_menu_handler(msg
, i
, __NUM__
, &sm
, &cur
);
122 const menu_t
*S_menu (void) {
123 static const menu_t m
= { sound_menu_handler
};
127 const cfg_t
*S_args (void) {
128 static const cfg_t args
[] = {
129 { "sndvol", &snd_vol
, Y_WORD
},
135 const cfg_t
*S_conf (void) {
136 static const cfg_t conf
[] = {
137 { "sound_volume", &snd_vol
, Y_WORD
},
144 assert(devinit
== 0);
145 logo("S_init: initialize sound\n");
146 if (SDL_InitSubSystem(SDL_INIT_AUDIO
) == 0) {
147 if (Mix_OpenAudio(devfreq
, devformat
, devchannels
, devchunksize
) == 0) {
148 Mix_AllocateChannels(devchunkchannels
);
151 logo("S_init: Mix_OpenAudio: %s\n", Mix_GetError());
152 SDL_QuitSubSystem(SDL_INIT_AUDIO
);
155 logo("S_init: SDL_InitSubSytem: %s\n", SDL_GetError());
159 static Mix_Chunk
*convert_this (int rate
, int sign
, const Uint8
*buf
, int len
) {
162 if (SDL_BuildAudioCVT(&cvt
, sign
? AUDIO_S8
: AUDIO_U8
, 1, rate
, devformat
, devchannels
, devfreq
) != -1) {
163 int maxlen
= len
* cvt
.len_mult
;
164 Uint8
*maxbuf
= malloc(maxlen
);
165 if (maxbuf
!= NULL
) {
166 memcpy(maxbuf
, buf
, len
);
169 if (SDL_ConvertAudio(&cvt
) == 0) {
170 c
= malloc(sizeof(Mix_Chunk
));
174 c
->alen
= len
* cvt
.len_ratio
;
175 c
->volume
= MIX_MAX_VOLUME
;
187 static sdlmixer_snd
*new_sdlmixer_snd (const void *data
, dword len
, dword rate
, dword lstart
, dword llen
, int sign
) {
189 sdlmixer_snd
*snd
= NULL
;
190 c
= convert_this(rate
, sign
, data
, len
);
192 snd
= malloc(sizeof(sdlmixer_snd
));
194 snd
->base
.tag
= TAG_MIX1
;
204 snd_t
*S_get (int id
) {
206 sdlmixer_snd
*snd
= NULL
;
209 if (handle
!= NULL
) {
211 dword len
= F_getreslen(id
);
218 dword hdr_len
= int2host(hdr
->len
);
219 dword hdr_rate
= int2host(hdr
->rate
);
220 dword hdr_lstart
= int2host(hdr
->lstart
);
221 dword hdr_llen
= int2host(hdr
->llen
);
222 if (hdr_len
<= len
- 8 && hdr_lstart
+ hdr_llen
<= len
- 16) {
231 snd
= new_sdlmixer_snd(data
, len
, rate
, lstart
, llen
, sign
);
238 snd_t
*S_load (const char name
[8]) {
239 int id
= F_findres(name
);
243 void S_free (snd_t
*s
) {
245 sdlmixer_snd
*snd
= (sdlmixer_snd
*)s
;
247 assert(snd
->base
.tag
== TAG_MIX1
);
249 for (i
= 0; i
< devchunkchannels
; i
++) {
250 if (Mix_GetChunk(i
) == snd
->c
) {
261 short S_play (snd_t
*s
, short c
, short v
) {
263 sdlmixer_snd
*snd
= (sdlmixer_snd
*)s
;
264 assert(c
>= 0 && c
<= 8);
265 assert(v
>= 0 && v
<= 255);
266 if (devinit
&& snd
!= NULL
) {
267 assert(snd
->base
.tag
== TAG_MIX1
);
268 // TODO care about global volume level
269 snd
->c
->volume
= v
* MIX_MAX_VOLUME
/ 255;
270 channel
= Mix_PlayChannel(c
<= 0 ? -1 : c
- 1, snd
->c
, 0);
271 channel
= channel
== -1 ? 0 : channel
+ 1;
276 void S_stop (short c
) {
277 assert(c
>= 0 && c
<= 8);
278 if (devinit
&& c
> 0) {
279 Mix_HaltChannel(c
- 1);
283 void S_volume (int v
) {
284 snd_vol
= min(max(v
, 0), 128);
286 // TODO change relativelly for every channel
287 Mix_Volume(-1, v
* MIX_MAX_VOLUME
/ 128);
293 while (Mix_Playing(-1) > 0) {
302 Mix_AllocateChannels(0);
304 SDL_QuitSubSystem(SDL_INIT_AUDIO
);