diff options
Diffstat (limited to 'uisimulator/sdl/sound.c')
-rw-r--r-- | uisimulator/sdl/sound.c | 304 |
1 files changed, 240 insertions, 64 deletions
diff --git a/uisimulator/sdl/sound.c b/uisimulator/sdl/sound.c index eec9b3e78b..3447112231 100644 --- a/uisimulator/sdl/sound.c +++ b/uisimulator/sdl/sound.c | |||
@@ -25,34 +25,71 @@ | |||
25 | #include "debug.h" | 25 | #include "debug.h" |
26 | #include "kernel.h" | 26 | #include "kernel.h" |
27 | #include "sound.h" | 27 | #include "sound.h" |
28 | #include "pcm_sampr.h" | ||
28 | #include "SDL.h" | 29 | #include "SDL.h" |
29 | 30 | ||
30 | static bool pcm_playing; | 31 | static bool pcm_playing; |
31 | static bool pcm_paused; | 32 | static bool pcm_paused; |
33 | static int cvt_status = -1; | ||
34 | static unsigned long pcm_frequency = SAMPR_44; | ||
35 | static unsigned long pcm_curr_frequency = SAMPR_44; | ||
32 | 36 | ||
33 | static Uint8* pcm_data; | 37 | static Uint8* pcm_data; |
34 | static size_t pcm_data_size; | 38 | static size_t pcm_data_size; |
39 | static size_t pcm_sample_bytes; | ||
40 | static size_t pcm_channel_bytes; | ||
41 | |||
42 | struct pcm_udata | ||
43 | { | ||
44 | Uint8 *stream; | ||
45 | Uint32 num_in; | ||
46 | Uint32 num_out; | ||
47 | FILE *debug; | ||
48 | } udata; | ||
35 | 49 | ||
36 | static SDL_AudioSpec obtained; | 50 | static SDL_AudioSpec obtained; |
37 | static SDL_AudioCVT cvt; | 51 | static SDL_AudioCVT cvt; |
38 | 52 | ||
39 | extern bool debug_audio; | 53 | extern bool debug_audio; |
40 | 54 | ||
41 | static void sdl_dma_start(const void *addr, size_t size) | 55 | #ifndef MIN |
56 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) | ||
57 | #endif | ||
58 | |||
59 | static void pcm_apply_settings_nolock(void) | ||
42 | { | 60 | { |
43 | pcm_playing = true; | 61 | cvt_status = SDL_BuildAudioCVT(&cvt, AUDIO_S16SYS, 2, pcm_frequency, |
62 | obtained.format, obtained.channels, obtained.freq); | ||
63 | |||
64 | pcm_curr_frequency = pcm_frequency; | ||
65 | |||
66 | if (cvt_status < 0) { | ||
67 | cvt.len_ratio = (double)obtained.freq / (double)pcm_curr_frequency; | ||
68 | } | ||
69 | } | ||
44 | 70 | ||
71 | void pcm_apply_settings(void) | ||
72 | { | ||
45 | SDL_LockAudio(); | 73 | SDL_LockAudio(); |
46 | 74 | pcm_apply_settings_nolock(); | |
75 | SDL_UnlockAudio(); | ||
76 | } | ||
77 | |||
78 | static void sdl_dma_start_nolock(const void *addr, size_t size) | ||
79 | { | ||
80 | pcm_playing = false; | ||
81 | |||
82 | pcm_apply_settings_nolock(); | ||
83 | |||
47 | pcm_data = (Uint8 *) addr; | 84 | pcm_data = (Uint8 *) addr; |
48 | pcm_data_size = size; | 85 | pcm_data_size = size; |
49 | 86 | ||
50 | SDL_UnlockAudio(); | 87 | pcm_playing = true; |
51 | 88 | ||
52 | SDL_PauseAudio(0); | 89 | SDL_PauseAudio(0); |
53 | } | 90 | } |
54 | 91 | ||
55 | static void sdl_dma_stop(void) | 92 | static void sdl_dma_stop_nolock(void) |
56 | { | 93 | { |
57 | pcm_playing = false; | 94 | pcm_playing = false; |
58 | 95 | ||
@@ -65,18 +102,20 @@ static void (*callback_for_more)(unsigned char**, size_t*) = NULL; | |||
65 | void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size), | 102 | void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size), |
66 | unsigned char* start, size_t size) | 103 | unsigned char* start, size_t size) |
67 | { | 104 | { |
105 | SDL_LockAudio(); | ||
106 | |||
68 | callback_for_more = get_more; | 107 | callback_for_more = get_more; |
69 | 108 | ||
70 | if (!(start && size)) { | 109 | if (!(start && size)) { |
71 | if (get_more) | 110 | if (get_more) |
72 | get_more(&start, &size); | 111 | get_more(&start, &size); |
73 | else | ||
74 | return; | ||
75 | } | 112 | } |
76 | 113 | ||
77 | if (start && size) { | 114 | if (start && size) { |
78 | sdl_dma_start(start, size); | 115 | sdl_dma_start_nolock(start, size); |
79 | } | 116 | } |
117 | |||
118 | SDL_UnlockAudio(); | ||
80 | } | 119 | } |
81 | 120 | ||
82 | size_t pcm_get_bytes_waiting(void) | 121 | size_t pcm_get_bytes_waiting(void) |
@@ -91,24 +130,29 @@ void pcm_mute(bool mute) | |||
91 | 130 | ||
92 | void pcm_play_stop(void) | 131 | void pcm_play_stop(void) |
93 | { | 132 | { |
133 | SDL_LockAudio(); | ||
94 | if (pcm_playing) { | 134 | if (pcm_playing) { |
95 | sdl_dma_stop(); | 135 | sdl_dma_stop_nolock(); |
96 | } | 136 | } |
137 | SDL_UnlockAudio(); | ||
97 | } | 138 | } |
98 | 139 | ||
99 | void pcm_play_pause(bool play) | 140 | void pcm_play_pause(bool play) |
100 | { | 141 | { |
101 | size_t next_size; | 142 | size_t next_size; |
102 | Uint8 *next_start; | 143 | Uint8 *next_start; |
144 | |||
145 | SDL_LockAudio(); | ||
103 | 146 | ||
104 | if (!pcm_playing) { | 147 | if (!pcm_playing) { |
148 | SDL_UnlockAudio(); | ||
105 | return; | 149 | return; |
106 | } | 150 | } |
107 | 151 | ||
108 | if(pcm_paused && play) { | 152 | if(pcm_paused && play) { |
109 | if (pcm_get_bytes_waiting()) { | 153 | if (pcm_get_bytes_waiting()) { |
110 | printf("unpause\n"); | 154 | printf("unpause\n"); |
111 | 155 | pcm_apply_settings_nolock(); | |
112 | SDL_PauseAudio(0); | 156 | SDL_PauseAudio(0); |
113 | } else { | 157 | } else { |
114 | printf("unpause, no data waiting\n"); | 158 | printf("unpause, no data waiting\n"); |
@@ -120,9 +164,9 @@ void pcm_play_pause(bool play) | |||
120 | } | 164 | } |
121 | 165 | ||
122 | if (next_start && next_size) { | 166 | if (next_start && next_size) { |
123 | sdl_dma_start(next_start, next_size); | 167 | sdl_dma_start_nolock(next_start, next_size); |
124 | } else { | 168 | } else { |
125 | sdl_dma_stop(); | 169 | sdl_dma_stop_nolock(); |
126 | printf("unpause attempted, no data\n"); | 170 | printf("unpause attempted, no data\n"); |
127 | } | 171 | } |
128 | } | 172 | } |
@@ -133,6 +177,8 @@ void pcm_play_pause(bool play) | |||
133 | } | 177 | } |
134 | 178 | ||
135 | pcm_paused = !play; | 179 | pcm_paused = !play; |
180 | |||
181 | SDL_UnlockAudio(); | ||
136 | } | 182 | } |
137 | 183 | ||
138 | bool pcm_is_paused(void) | 184 | bool pcm_is_paused(void) |
@@ -147,9 +193,26 @@ bool pcm_is_playing(void) | |||
147 | 193 | ||
148 | void pcm_set_frequency(unsigned int frequency) | 194 | void pcm_set_frequency(unsigned int frequency) |
149 | { | 195 | { |
150 | // FIXME: Check return values | 196 | switch (frequency) |
151 | SDL_BuildAudioCVT(&cvt, AUDIO_S16SYS, 2, frequency, | 197 | { |
152 | obtained.format, obtained.channels, obtained.freq); | 198 | HW_HAVE_8_( case SAMPR_8:) |
199 | HW_HAVE_11_(case SAMPR_11:) | ||
200 | HW_HAVE_12_(case SAMPR_12:) | ||
201 | HW_HAVE_16_(case SAMPR_16:) | ||
202 | HW_HAVE_22_(case SAMPR_22:) | ||
203 | HW_HAVE_24_(case SAMPR_24:) | ||
204 | HW_HAVE_32_(case SAMPR_32:) | ||
205 | /* 44100 implied */ | ||
206 | HW_HAVE_48_(case SAMPR_48:) | ||
207 | HW_HAVE_64_(case SAMPR_64:) | ||
208 | HW_HAVE_88_(case SAMPR_88:) | ||
209 | HW_HAVE_96_(case SAMPR_96:) | ||
210 | break; | ||
211 | default: | ||
212 | frequency = SAMPR_44; | ||
213 | } | ||
214 | |||
215 | pcm_frequency = frequency; | ||
153 | } | 216 | } |
154 | 217 | ||
155 | /* | 218 | /* |
@@ -216,83 +279,174 @@ void pcm_calculate_peaks(int *left, int *right) | |||
216 | } | 279 | } |
217 | } | 280 | } |
218 | 281 | ||
219 | static long write_to_soundcard(Uint8 *stream, int len, FILE *debug) { | 282 | void write_to_soundcard(struct pcm_udata *udata) { |
220 | Uint32 written = (((Uint32) len) > pcm_data_size) ? pcm_data_size : (Uint32) len; | ||
221 | |||
222 | if (cvt.needed) { | 283 | if (cvt.needed) { |
223 | cvt.buf = (Uint8 *) malloc(written * cvt.len_mult); | 284 | Uint32 rd = udata->num_in; |
224 | cvt.len = written; | 285 | Uint32 wr = (double)rd * cvt.len_ratio; |
225 | 286 | ||
226 | memcpy(cvt.buf, pcm_data, written); | 287 | if (wr > udata->num_out) { |
288 | wr = udata->num_out; | ||
289 | rd = (double)wr / cvt.len_ratio; | ||
227 | 290 | ||
228 | SDL_ConvertAudio(&cvt); | 291 | if (rd > udata->num_in) |
229 | 292 | { | |
230 | memcpy(stream, cvt.buf, cvt.len_cvt); | 293 | rd = udata->num_in; |
294 | wr = (double)rd * cvt.len_ratio; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | if (wr == 0 || rd == 0) | ||
299 | { | ||
300 | udata->num_out = udata->num_in = 0; | ||
301 | return; | ||
302 | } | ||
303 | |||
304 | if (cvt_status > 0) { | ||
305 | cvt.len = rd * pcm_sample_bytes; | ||
306 | cvt.buf = (Uint8 *) malloc(cvt.len * cvt.len_mult); | ||
307 | |||
308 | memcpy(cvt.buf, pcm_data, cvt.len); | ||
309 | |||
310 | SDL_ConvertAudio(&cvt); | ||
231 | 311 | ||
232 | if (debug != NULL) { | 312 | memcpy(udata->stream, cvt.buf, cvt.len_cvt); |
233 | fwrite(cvt.buf, sizeof(Uint8), cvt.len_cvt, debug); | 313 | |
314 | udata->num_in = cvt.len / pcm_sample_bytes; | ||
315 | udata->num_out = cvt.len_cvt / pcm_sample_bytes; | ||
316 | |||
317 | if (udata->debug != NULL) { | ||
318 | fwrite(cvt.buf, sizeof(Uint8), cvt.len_cvt, udata->debug); | ||
319 | } | ||
320 | |||
321 | free(cvt.buf); | ||
234 | } | 322 | } |
323 | else { | ||
324 | /* Convert is bad, so do silence */ | ||
325 | Uint32 num = wr*obtained.channels; | ||
326 | udata->num_in = rd; | ||
327 | udata->num_out = wr; | ||
328 | |||
329 | switch (pcm_channel_bytes) | ||
330 | { | ||
331 | case 1: | ||
332 | { | ||
333 | Uint8 *stream = udata->stream; | ||
334 | while (num-- > 0) | ||
335 | *stream++ = obtained.silence; | ||
336 | break; | ||
337 | } | ||
338 | case 2: | ||
339 | { | ||
340 | Uint16 *stream = (Uint16 *)udata->stream; | ||
341 | while (num-- > 0) | ||
342 | *stream++ = obtained.silence; | ||
343 | break; | ||
344 | } | ||
345 | } | ||
235 | 346 | ||
236 | free(cvt.buf); | 347 | if (udata->debug != NULL) { |
348 | fwrite(udata->stream, sizeof(Uint8), wr, udata->debug); | ||
349 | } | ||
350 | } | ||
237 | } else { | 351 | } else { |
238 | memcpy(stream, pcm_data, written); | 352 | udata->num_in = udata->num_out = MIN(udata->num_in, udata->num_out); |
353 | memcpy(udata->stream, pcm_data, udata->num_out * pcm_sample_bytes); | ||
239 | 354 | ||
240 | if (debug != NULL) { | 355 | if (udata->debug != NULL) { |
241 | fwrite(pcm_data, sizeof(Uint8), written, debug); | 356 | fwrite(pcm_data, sizeof(Uint8), udata->num_out * pcm_sample_bytes, |
357 | udata->debug); | ||
242 | } | 358 | } |
243 | } | 359 | } |
244 | |||
245 | return written; | ||
246 | } | 360 | } |
247 | 361 | ||
248 | void sdl_audio_callback(void *udata, Uint8 *stream, int len) | 362 | void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len) |
249 | { | 363 | { |
250 | Uint32 have_now; | 364 | udata->stream = stream; |
251 | FILE *debug = (FILE *) udata; | ||
252 | |||
253 | /* At all times we need to write a full 'len' bytes to stream. */ | ||
254 | 365 | ||
255 | /* Write what we have in the PCM buffer */ | 366 | /* Write what we have in the PCM buffer */ |
256 | if (pcm_data_size > 0) { | 367 | if (pcm_data_size > 0) |
257 | have_now = write_to_soundcard(stream, len, debug); | 368 | goto start; |
258 | |||
259 | stream += have_now; | ||
260 | len -= have_now; | ||
261 | pcm_data += have_now; | ||
262 | pcm_data_size -= have_now; | ||
263 | } | ||
264 | 369 | ||
265 | /* Audio card wants more? Get some more then. */ | 370 | /* Audio card wants more? Get some more then. */ |
266 | while (len > 0) { | 371 | while (len > 0) { |
267 | if (callback_for_more) { | 372 | if ((ssize_t)pcm_data_size <= 0) { |
268 | callback_for_more(&pcm_data, &pcm_data_size); | ||
269 | } else { | ||
270 | pcm_data = NULL; | ||
271 | pcm_data_size = 0; | 373 | pcm_data_size = 0; |
374 | |||
375 | if (callback_for_more) | ||
376 | callback_for_more(&pcm_data, &pcm_data_size); | ||
272 | } | 377 | } |
273 | 378 | ||
274 | if (pcm_data_size > 0) { | 379 | if (pcm_data_size > 0) { |
275 | have_now = write_to_soundcard(stream, len, debug); | 380 | start: |
276 | 381 | udata->num_in = pcm_data_size / pcm_sample_bytes; | |
277 | stream += have_now; | 382 | udata->num_out = len / pcm_sample_bytes; |
278 | len -= have_now; | 383 | |
279 | pcm_data += have_now; | 384 | write_to_soundcard(udata); |
280 | pcm_data_size -= have_now; | 385 | |
386 | udata->num_in *= pcm_sample_bytes; | ||
387 | udata->num_out *= pcm_sample_bytes; | ||
388 | |||
389 | pcm_data += udata->num_in; | ||
390 | pcm_data_size -= udata->num_in; | ||
391 | udata->stream += udata->num_out; | ||
392 | len -= udata->num_out; | ||
281 | } else { | 393 | } else { |
282 | DEBUGF("sdl_audio_callback: No Data.\n"); | 394 | DEBUGF("sdl_audio_callback: No Data.\n"); |
283 | sdl_dma_stop(); | 395 | sdl_dma_stop_nolock(); |
284 | break; | 396 | break; |
285 | } | 397 | } |
286 | } | 398 | } |
287 | } | 399 | } |
288 | 400 | ||
401 | #ifdef HAVE_RECORDING | ||
402 | void pcm_init_recording(void) | ||
403 | { | ||
404 | } | ||
405 | |||
406 | void pcm_close_recording(void) | ||
407 | { | ||
408 | } | ||
409 | |||
410 | void pcm_record_data(void (*more_ready)(void* start, size_t size), | ||
411 | void *start, size_t size) | ||
412 | { | ||
413 | (void)more_ready; | ||
414 | (void)start; | ||
415 | (void)size; | ||
416 | } | ||
417 | |||
418 | void pcm_stop_recording(void) | ||
419 | { | ||
420 | } | ||
421 | |||
422 | void pcm_record_more(void *start, size_t size) | ||
423 | { | ||
424 | (void)start; | ||
425 | (void)size; | ||
426 | } | ||
427 | |||
428 | void pcm_calculate_rec_peaks(int *left, int *right) | ||
429 | { | ||
430 | if (left) | ||
431 | *left = 0; | ||
432 | if (right) | ||
433 | *right = 0; | ||
434 | } | ||
435 | |||
436 | unsigned long pcm_rec_status(void) | ||
437 | { | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | #endif /* HAVE_RECORDING */ | ||
442 | |||
289 | int pcm_init(void) | 443 | int pcm_init(void) |
290 | { | 444 | { |
291 | SDL_AudioSpec wanted_spec; | 445 | SDL_AudioSpec wanted_spec; |
292 | FILE *debug = NULL; | 446 | udata.debug = NULL; |
293 | 447 | ||
294 | if (debug_audio) { | 448 | if (debug_audio) { |
295 | debug = fopen("audiodebug.raw", "wb"); | 449 | udata.debug = fopen("audiodebug.raw", "wb"); |
296 | } | 450 | } |
297 | 451 | ||
298 | /* Set 16-bit stereo audio at 44Khz */ | 452 | /* Set 16-bit stereo audio at 44Khz */ |
@@ -300,8 +454,10 @@ int pcm_init(void) | |||
300 | wanted_spec.format = AUDIO_S16SYS; | 454 | wanted_spec.format = AUDIO_S16SYS; |
301 | wanted_spec.channels = 2; | 455 | wanted_spec.channels = 2; |
302 | wanted_spec.samples = 2048; | 456 | wanted_spec.samples = 2048; |
303 | wanted_spec.callback = sdl_audio_callback; | 457 | wanted_spec.callback = |
304 | wanted_spec.userdata = debug; | 458 | (void (SDLCALL *)(void *userdata, |
459 | Uint8 *stream, int len))sdl_audio_callback; | ||
460 | wanted_spec.userdata = &udata; | ||
305 | 461 | ||
306 | /* Open the audio device and start playing sound! */ | 462 | /* Open the audio device and start playing sound! */ |
307 | if(SDL_OpenAudio(&wanted_spec, &obtained) < 0) { | 463 | if(SDL_OpenAudio(&wanted_spec, &obtained) < 0) { |
@@ -309,7 +465,29 @@ int pcm_init(void) | |||
309 | return -1; | 465 | return -1; |
310 | } | 466 | } |
311 | 467 | ||
312 | sdl_dma_stop(); | 468 | switch (obtained.format) |
469 | { | ||
470 | case AUDIO_U8: | ||
471 | case AUDIO_S8: | ||
472 | pcm_channel_bytes = 1; | ||
473 | break; | ||
474 | case AUDIO_U16LSB: | ||
475 | case AUDIO_S16LSB: | ||
476 | case AUDIO_U16MSB: | ||
477 | case AUDIO_S16MSB: | ||
478 | pcm_channel_bytes = 2; | ||
479 | break; | ||
480 | default: | ||
481 | fprintf(stderr, "Unknown sample format obtained: %u\n", | ||
482 | (unsigned)obtained.format); | ||
483 | return -1; | ||
484 | } | ||
485 | |||
486 | pcm_sample_bytes = obtained.channels * pcm_channel_bytes; | ||
487 | |||
488 | pcm_apply_settings_nolock(); | ||
489 | |||
490 | sdl_dma_stop_nolock(); | ||
313 | 491 | ||
314 | return 0; | 492 | return 0; |
315 | } | 493 | } |
@@ -317,5 +495,3 @@ int pcm_init(void) | |||
317 | void pcm_postinit(void) | 495 | void pcm_postinit(void) |
318 | { | 496 | { |
319 | } | 497 | } |
320 | |||
321 | |||