DEADSOFTWARE

ec27e45f373387512c6e2422220d4c5866ee63a6
[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 "misc.h" // int2host
20 #include "memory.h" // M_lock M_unlock
21 #include "files.h" // F_findres
22 #include "error.h"
24 #include "SDL.h"
25 #include "SDL_mixer.h"
26 #include <assert.h>
27 #include <string.h>
29 #define TAG_MIX1 0x4d495831
31 #pragma pack(1)
32 typedef struct dmi {
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
38 } dmi;
39 #pragma pack()
41 typedef struct sdlmixer_snd {
42 snd_t base;
43 Mix_Chunk *c;
44 } sdlmixer_snd;
46 static short snd_vol;
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;
53 static int devinit;
55 /* music */
57 const cfg_t *MUS_args (void) {
58 return NULL;
59 }
61 const cfg_t *MUS_conf (void) {
62 return NULL;
63 }
65 const menu_t *MUS_menu (void) {
66 return NULL;
67 }
69 void MUS_init (void) {
71 }
73 void MUS_done (void) {
75 }
77 void MUS_start (int time) {
79 }
81 void MUS_stop (void) {
83 }
85 void MUS_volume (int v) {
87 }
89 void MUS_load (char n[8]) {
91 }
93 void MUS_free (void) {
95 }
97 void MUS_update (void) {
99 }
101 /* Sound */
103 static int sound_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
104 static int cur;
105 enum { VOLUME, __NUM__ };
106 static const simple_menu_t sm = {
107 GM_BIG, "Sound", NULL,
109 { "Volume", NULL },
111 };
112 if (i == VOLUME) {
113 switch (msg->type) {
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 };
124 return &m;
127 const cfg_t *S_args (void) {
128 static const cfg_t args[] = {
129 { "sndvol", &snd_vol, Y_WORD },
130 { NULL, NULL, 0 }
131 };
132 return args;
135 const cfg_t *S_conf (void) {
136 static const cfg_t conf[] = {
137 { "sound_volume", &snd_vol, Y_WORD },
138 { NULL, NULL, 0 }
139 };
140 return conf;
143 void S_init (void) {
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);
149 devinit = 1;
150 } else {
151 logo("S_init: Mix_OpenAudio: %s\n", Mix_GetError());
152 SDL_QuitSubSystem(SDL_INIT_AUDIO);
154 } else {
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) {
160 SDL_AudioCVT cvt;
161 Mix_Chunk *c = NULL;
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);
167 cvt.buf = maxbuf;
168 cvt.len = len;
169 if (SDL_ConvertAudio(&cvt) == 0) {
170 c = malloc(sizeof(Mix_Chunk));
171 if (c != NULL) {
172 c->allocated = 0;
173 c->abuf = maxbuf;
174 c->alen = len * cvt.len_ratio;
175 c->volume = MIX_MAX_VOLUME;
176 } else {
177 free(maxbuf);
179 } else {
180 free(maxbuf);
184 return c;
187 static sdlmixer_snd *new_sdlmixer_snd (const void *data, dword len, dword rate, dword lstart, dword llen, int sign) {
188 Mix_Chunk *c = NULL;
189 sdlmixer_snd *snd = NULL;
190 c = convert_this(rate, sign, data, len);
191 if (c != NULL) {
192 snd = malloc(sizeof(sdlmixer_snd));
193 if (snd != NULL) {
194 snd->base.tag = TAG_MIX1;
195 snd->c = c;
196 } else {
197 free(c->abuf);
198 free(c);
201 return snd;
204 snd_t *S_get (int id) {
205 void *handle;
206 sdlmixer_snd *snd = NULL;
207 if (devinit) {
208 handle = M_lock(id);
209 if (handle != NULL) {
210 void *data = handle;
211 dword len = F_getreslen(id);
212 dword rate = 11025;
213 dword lstart = 0;
214 dword llen = 0;
215 int sign = 0;
216 if (len > 16) {
217 dmi *hdr = handle;
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) {
223 data = hdr->data;
224 len = hdr_len;
225 rate = hdr_rate;
226 lstart = hdr_lstart;
227 llen = hdr_llen;
228 sign = 1;
231 snd = new_sdlmixer_snd(data, len, rate, lstart, llen, sign);
232 M_unlock(handle);
235 return (snd_t*)snd;
238 snd_t *S_load (const char name[8]) {
239 int id = F_findres(name);
240 return S_get(id);
243 void S_free (snd_t *s) {
244 int i;
245 sdlmixer_snd *snd = (sdlmixer_snd*)s;
246 if (snd != NULL) {
247 assert(snd->base.tag == TAG_MIX1);
248 if (devinit) {
249 for (i = 0; i < devchunkchannels; i++) {
250 if (Mix_GetChunk(i) == snd->c) {
251 Mix_HaltChannel(i);
255 free(snd->c->abuf);
256 free(snd->c);
257 free(snd);
261 short S_play (snd_t *s, short c, short v) {
262 short channel = 0;
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;
273 return channel;
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);
285 if (devinit) {
286 // TODO change relativelly for every channel
287 Mix_Volume(-1, v * MIX_MAX_VOLUME / 128);
291 void S_wait (void) {
292 if (devinit) {
293 while (Mix_Playing(-1) > 0) {
294 SDL_Delay(10);
299 void S_done (void) {
300 if (devinit) {
301 // TODO free memory
302 Mix_AllocateChannels(0);
303 Mix_CloseAudio();
304 SDL_QuitSubSystem(SDL_INIT_AUDIO);