DEADSOFTWARE

menu: remove data field from menu_t
[flatwaifu.git] / src / sdlmixer / sound.c
1 #include "glob.h"
2 #include "sound.h"
3 #include "music.h"
4 #include "misc.h" // int2host
5 #include "memory.h" // M_lock M_unlock
6 #include "files.h" // F_findres
7 #include "error.h"
9 #include "SDL.h"
10 #include "SDL_mixer.h"
11 #include <assert.h>
12 #include <string.h>
14 #define TAG_MIX1 0x4d495831
16 #pragma pack(1)
17 typedef struct dmi {
18 Uint32 len; // length [bytes]
19 Uint32 rate; // freq [Hz]
20 Uint32 lstart; // loop start offset [bytes]
21 Uint32 llen; // loop length [bytes]
22 Uint8 data[]; // sound data
23 } dmi;
24 #pragma pack()
26 typedef struct sdlmixer_snd {
27 snd_t base;
28 Mix_Chunk *c;
29 } sdlmixer_snd;
31 static short snd_vol;
33 static int devfreq = MIX_DEFAULT_FREQUENCY;
34 static Uint32 devformat = AUDIO_S16SYS; // MIX_DEFAULT_FORMAT
35 static int devchannels = 1; // MIX_DEFAULT_CHANNELS;
36 static int devchunksize = 1024;
37 static int devchunkchannels = 8;
38 static int devinit;
40 /* music */
42 const cfg_t *MUS_args (void) {
43 return NULL;
44 }
46 const cfg_t *MUS_conf (void) {
47 return NULL;
48 }
50 const menu_t *MUS_menu (void) {
51 return NULL;
52 }
54 void MUS_init (void) {
56 }
58 void MUS_done (void) {
60 }
62 void MUS_start (int time) {
64 }
66 void MUS_stop (void) {
68 }
70 void MUS_volume (int v) {
72 }
74 void MUS_load (char n[8]) {
76 }
78 void MUS_free (void) {
80 }
82 void MUS_update (void) {
84 }
86 /* Sound */
88 static int sound_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
89 static int cur;
90 enum { VOLUME, __NUM__ };
91 static const simple_menu_t sm = {
92 GM_BIG, "Sound", NULL,
93 {
94 { "Volume", NULL },
95 }
96 };
97 if (i == VOLUME) {
98 switch (msg->type) {
99 case GM_GETENTRY: return GM_init_int0(msg, GM_SCROLLER, 0, 0, 0);
100 case GM_GETINT: return GM_init_int(msg, snd_vol, 0, 128, 8);
101 case GM_SETINT: S_volume(msg->integer.i); return 1;
104 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
107 const menu_t *S_menu (void) {
108 static const menu_t m = { sound_menu_handler };
109 return &m;
112 const cfg_t *S_args (void) {
113 static const cfg_t args[] = {
114 { "sndvol", &snd_vol, Y_WORD },
115 { NULL, NULL, 0 }
116 };
117 return args;
120 const cfg_t *S_conf (void) {
121 static const cfg_t conf[] = {
122 { "sound_volume", &snd_vol, Y_WORD },
123 { NULL, NULL, 0 }
124 };
125 return conf;
128 void S_init (void) {
129 assert(devinit == 0);
130 logo("S_init: initialize sound\n");
131 if (SDL_InitSubSystem(SDL_INIT_AUDIO) == 0) {
132 if (Mix_OpenAudio(devfreq, devformat, devchannels, devchunksize) == 0) {
133 Mix_AllocateChannels(devchunkchannels);
134 devinit = 1;
135 } else {
136 logo("S_init: Mix_OpenAudio: %s\n", Mix_GetError());
137 SDL_QuitSubSystem(SDL_INIT_AUDIO);
139 } else {
140 logo("S_init: SDL_InitSubSytem: %s\n", SDL_GetError());
144 static Mix_Chunk *convert_this (int rate, int sign, const Uint8 *buf, int len) {
145 SDL_AudioCVT cvt;
146 Mix_Chunk *c = NULL;
147 if (SDL_BuildAudioCVT(&cvt, sign ? AUDIO_S8 : AUDIO_U8, 1, rate, devformat, devchannels, devfreq) != -1) {
148 int maxlen = len * cvt.len_mult;
149 Uint8 *maxbuf = malloc(maxlen);
150 if (maxbuf != NULL) {
151 memcpy(maxbuf, buf, len);
152 cvt.buf = maxbuf;
153 cvt.len = len;
154 if (SDL_ConvertAudio(&cvt) == 0) {
155 c = malloc(sizeof(Mix_Chunk));
156 if (c != NULL) {
157 c->allocated = 0;
158 c->abuf = maxbuf;
159 c->alen = len * cvt.len_ratio;
160 c->volume = MIX_MAX_VOLUME;
161 } else {
162 free(maxbuf);
164 } else {
165 free(maxbuf);
169 return c;
172 static sdlmixer_snd *new_sdlmixer_snd (const void *data, dword len, dword rate, dword lstart, dword llen, int sign) {
173 Mix_Chunk *c = NULL;
174 sdlmixer_snd *snd = NULL;
175 c = convert_this(rate, sign, data, len);
176 if (c != NULL) {
177 snd = malloc(sizeof(sdlmixer_snd));
178 if (snd != NULL) {
179 snd->base.tag = TAG_MIX1;
180 snd->c = c;
181 } else {
182 free(c->abuf);
183 free(c);
186 return snd;
189 snd_t *S_get (int id) {
190 void *handle;
191 sdlmixer_snd *snd = NULL;
192 if (devinit) {
193 handle = M_lock(id);
194 if (handle != NULL) {
195 void *data = handle;
196 dword len = F_getreslen(id);
197 dword rate = 11025;
198 dword lstart = 0;
199 dword llen = 0;
200 int sign = 0;
201 if (len > 16) {
202 dmi *hdr = handle;
203 dword hdr_len = int2host(hdr->len);
204 dword hdr_rate = int2host(hdr->rate);
205 dword hdr_lstart = int2host(hdr->lstart);
206 dword hdr_llen = int2host(hdr->llen);
207 if (hdr_len <= len - 8 && hdr_lstart + hdr_llen <= len - 16) {
208 data = hdr->data;
209 len = hdr_len;
210 rate = hdr_rate;
211 lstart = hdr_lstart;
212 llen = hdr_llen;
213 sign = 1;
216 snd = new_sdlmixer_snd(data, len, rate, lstart, llen, sign);
217 M_unlock(handle);
220 return (snd_t*)snd;
223 snd_t *S_load (const char name[8]) {
224 int id = F_findres(name);
225 return S_get(id);
228 void S_free (snd_t *s) {
229 int i;
230 sdlmixer_snd *snd = (sdlmixer_snd*)s;
231 if (snd != NULL) {
232 assert(snd->base.tag == TAG_MIX1);
233 if (devinit) {
234 for (i = 0; i < devchunkchannels; i++) {
235 if (Mix_GetChunk(i) == snd->c) {
236 Mix_HaltChannel(i);
240 free(snd->c->abuf);
241 free(snd->c);
242 free(snd);
246 short S_play (snd_t *s, short c, short v) {
247 short channel = 0;
248 sdlmixer_snd *snd = (sdlmixer_snd*)s;
249 assert(c >= 0 && c <= 8);
250 assert(v >= 0 && v <= 255);
251 if (devinit && snd != NULL) {
252 assert(snd->base.tag == TAG_MIX1);
253 // TODO care about global volume level
254 snd->c->volume = v * MIX_MAX_VOLUME / 255;
255 channel = Mix_PlayChannel(c <= 0 ? -1 : c - 1, snd->c, 0);
256 channel = channel == -1 ? 0 : channel + 1;
258 return channel;
261 void S_stop (short c) {
262 assert(c >= 0 && c <= 8);
263 if (devinit && c > 0) {
264 Mix_HaltChannel(c - 1);
268 void S_volume (int v) {
269 snd_vol = min(max(v, 0), 128);
270 if (devinit) {
271 // TODO change relativelly for every channel
272 Mix_Volume(-1, v * MIX_MAX_VOLUME / 128);
276 void S_wait (void) {
277 if (devinit) {
278 while (Mix_Playing(-1) > 0) {
279 SDL_Delay(10);
284 void S_done (void) {
285 if (devinit) {
286 // TODO free memory
287 Mix_AllocateChannels(0);
288 Mix_CloseAudio();
289 SDL_QuitSubSystem(SDL_INIT_AUDIO);