DEADSOFTWARE

portability: avoid errors on some compilers
[flatwaifu.git] / src / sdlmixer / 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 "glob.h"
17 #include "sound.h"
18 #include "music.h"
19 #include "memory.h" // M_lock M_unlock
20 #include "files.h" // F_findres
21 #include "error.h"
23 #include "common/endianness.h"
25 #include "SDL.h"
26 #include "SDL_mixer.h"
27 #include <assert.h>
28 #include <string.h>
30 #define TAG_MIX1 0x4d495831
32 #pragma pack(1)
33 typedef struct dmi {
34 word len; // length [bytes]
35 word rate; // freq [Hz]
36 word lstart; // loop start offset [bytes]
37 word llen; // loop length [bytes]
38 byte data[]; // sound data
39 } dmi;
40 #pragma pack()
42 typedef struct sdlmixer_snd {
43 snd_t base;
44 Mix_Chunk *c;
45 } sdlmixer_snd;
47 static short snd_vol;
49 static int devfreq = MIX_DEFAULT_FREQUENCY;
50 static Uint32 devformat = AUDIO_S16SYS; // MIX_DEFAULT_FORMAT
51 static int devchannels = 1; // MIX_DEFAULT_CHANNELS;
52 static int devchunksize = 1024;
53 static int devchunkchannels = 8;
54 static int devinit;
56 /* music */
58 const cfg_t *MUS_args (void) {
59 return NULL;
60 }
62 const cfg_t *MUS_conf (void) {
63 return NULL;
64 }
66 const menu_t *MUS_menu (void) {
67 return NULL;
68 }
70 void MUS_init (void) {
72 }
74 void MUS_done (void) {
76 }
78 void MUS_start (int time) {
80 }
82 void MUS_stop (void) {
84 }
86 void MUS_volume (int v) {
88 }
90 void MUS_load (char n[8]) {
92 }
94 void MUS_free (void) {
96 }
98 void MUS_update (void) {
102 /* Sound */
104 static int sound_menu_handler (menu_msg_t *msg, const menu_t *m, int i) {
105 static int cur;
106 enum { VOLUME, __NUM__ };
107 static const simple_menu_t sm = {
108 GM_BIG, "Sound", NULL,
110 { "Volume", NULL },
112 };
113 if (i == VOLUME) {
114 switch (msg->type) {
115 case GM_GETENTRY: return GM_init_int0(msg, GM_SCROLLER, 0, 0, 0);
116 case GM_GETINT: return GM_init_int(msg, snd_vol, 0, 128, 8);
117 case GM_SETINT: S_volume(msg->integer.i); return 1;
120 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
123 const menu_t *S_menu (void) {
124 static const menu_t m = { sound_menu_handler };
125 return &m;
128 const cfg_t *S_args (void) {
129 static const cfg_t args[] = {
130 { "sndvol", &snd_vol, Y_WORD },
131 { NULL, NULL, 0 }
132 };
133 return args;
136 const cfg_t *S_conf (void) {
137 static const cfg_t conf[] = {
138 { "sound_volume", &snd_vol, Y_WORD },
139 { NULL, NULL, 0 }
140 };
141 return conf;
144 void S_init (void) {
145 assert(devinit == 0);
146 logo("S_init: initialize sound\n");
147 if (SDL_InitSubSystem(SDL_INIT_AUDIO) == 0) {
148 if (Mix_OpenAudio(devfreq, devformat, devchannels, devchunksize) == 0) {
149 Mix_AllocateChannels(devchunkchannels);
150 devinit = 1;
151 } else {
152 logo("S_init: Mix_OpenAudio: %s\n", Mix_GetError());
153 SDL_QuitSubSystem(SDL_INIT_AUDIO);
155 } else {
156 logo("S_init: SDL_InitSubSytem: %s\n", SDL_GetError());
160 static Mix_Chunk *convert_this (int rate, int sign, const Uint8 *buf, int len) {
161 SDL_AudioCVT cvt;
162 Mix_Chunk *c = NULL;
163 if (SDL_BuildAudioCVT(&cvt, sign ? AUDIO_S8 : AUDIO_U8, 1, rate, devformat, devchannels, devfreq) != -1) {
164 int maxlen = len * cvt.len_mult;
165 Uint8 *maxbuf = malloc(maxlen);
166 if (maxbuf != NULL) {
167 memcpy(maxbuf, buf, len);
168 cvt.buf = maxbuf;
169 cvt.len = len;
170 if (SDL_ConvertAudio(&cvt) == 0) {
171 c = malloc(sizeof(Mix_Chunk));
172 if (c != NULL) {
173 c->allocated = 0;
174 c->abuf = maxbuf;
175 c->alen = len * cvt.len_ratio;
176 c->volume = MIX_MAX_VOLUME;
177 } else {
178 free(maxbuf);
180 } else {
181 free(maxbuf);
185 return c;
188 static sdlmixer_snd *new_sdlmixer_snd (const void *data, dword len, dword rate, dword lstart, dword llen, int sign) {
189 Mix_Chunk *c = NULL;
190 sdlmixer_snd *snd = NULL;
191 c = convert_this(rate, sign, data, len);
192 if (c != NULL) {
193 snd = malloc(sizeof(sdlmixer_snd));
194 if (snd != NULL) {
195 snd->base.tag = TAG_MIX1;
196 snd->c = c;
197 } else {
198 free(c->abuf);
199 free(c);
202 return snd;
205 snd_t *S_get (int id) {
206 void *handle;
207 sdlmixer_snd *snd = NULL;
208 if (devinit) {
209 handle = M_lock(id);
210 if (handle != NULL) {
211 void *data = handle;
212 word len = F_getreslen(id);
213 word rate = 11025;
214 word lstart = 0;
215 word llen = 0;
216 int sign = 0;
217 if (len > 8) {
218 dmi *hdr = handle;
219 word hdr_len = short2host(hdr->len);
220 word hdr_rate = short2host(hdr->rate);
221 word hdr_lstart = short2host(hdr->lstart);
222 word hdr_llen = short2host(hdr->llen);
223 if (hdr_len <= len - 4 && hdr_lstart + hdr_llen <= len - 8) {
224 data = hdr->data;
225 len = hdr_len;
226 rate = hdr_rate;
227 lstart = hdr_lstart;
228 llen = hdr_llen;
229 sign = 1;
232 snd = new_sdlmixer_snd(data, len, rate, lstart, llen, sign);
233 M_unlock(handle);
236 return (snd_t*)snd;
239 snd_t *S_load (const char name[8]) {
240 int id = F_findres(name);
241 return S_get(id);
244 void S_free (snd_t *s) {
245 int i;
246 sdlmixer_snd *snd = (sdlmixer_snd*)s;
247 if (snd != NULL) {
248 assert(snd->base.tag == TAG_MIX1);
249 if (devinit) {
250 for (i = 0; i < devchunkchannels; i++) {
251 if (Mix_GetChunk(i) == snd->c) {
252 Mix_HaltChannel(i);
256 free(snd->c->abuf);
257 free(snd->c);
258 free(snd);
262 short S_play (snd_t *s, short c, short v) {
263 short channel = 0;
264 sdlmixer_snd *snd = (sdlmixer_snd*)s;
265 assert(c >= 0 && c <= 8);
266 assert(v >= 0 && v <= 255);
267 if (devinit && snd != NULL) {
268 assert(snd->base.tag == TAG_MIX1);
269 // TODO care about global volume level
270 snd->c->volume = v * MIX_MAX_VOLUME / 255;
271 channel = Mix_PlayChannel(c <= 0 ? -1 : c - 1, snd->c, 0);
272 channel = channel == -1 ? 0 : channel + 1;
274 return channel;
277 void S_stop (short c) {
278 assert(c >= 0 && c <= 8);
279 if (devinit && c > 0) {
280 Mix_HaltChannel(c - 1);
284 void S_volume (int v) {
285 snd_vol = min(max(v, 0), 128);
286 if (devinit) {
287 // TODO change relativelly for every channel
288 Mix_Volume(-1, v * MIX_MAX_VOLUME / 128);
292 void S_wait (void) {
293 if (devinit) {
294 while (Mix_Playing(-1) > 0) {
295 SDL_Delay(10);
300 void S_done (void) {
301 if (devinit) {
302 // TODO free memory
303 Mix_AllocateChannels(0);
304 Mix_CloseAudio();
305 SDL_QuitSubSystem(SDL_INIT_AUDIO);