summaryrefslogtreecommitdiff
path: root/uisimulator
diff options
context:
space:
mode:
Diffstat (limited to 'uisimulator')
-rw-r--r--uisimulator/sdl/sound.c304
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
30static bool pcm_playing; 31static bool pcm_playing;
31static bool pcm_paused; 32static bool pcm_paused;
33static int cvt_status = -1;
34static unsigned long pcm_frequency = SAMPR_44;
35static unsigned long pcm_curr_frequency = SAMPR_44;
32 36
33static Uint8* pcm_data; 37static Uint8* pcm_data;
34static size_t pcm_data_size; 38static size_t pcm_data_size;
39static size_t pcm_sample_bytes;
40static size_t pcm_channel_bytes;
41
42struct pcm_udata
43{
44 Uint8 *stream;
45 Uint32 num_in;
46 Uint32 num_out;
47 FILE *debug;
48} udata;
35 49
36static SDL_AudioSpec obtained; 50static SDL_AudioSpec obtained;
37static SDL_AudioCVT cvt; 51static SDL_AudioCVT cvt;
38 52
39extern bool debug_audio; 53extern bool debug_audio;
40 54
41static 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
59static 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
71void pcm_apply_settings(void)
72{
45 SDL_LockAudio(); 73 SDL_LockAudio();
46 74 pcm_apply_settings_nolock();
75 SDL_UnlockAudio();
76}
77
78static 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
55static void sdl_dma_stop(void) 92static 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;
65void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size), 102void 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
82size_t pcm_get_bytes_waiting(void) 121size_t pcm_get_bytes_waiting(void)
@@ -91,24 +130,29 @@ void pcm_mute(bool mute)
91 130
92void pcm_play_stop(void) 131void 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
99void pcm_play_pause(bool play) 140void 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
138bool pcm_is_paused(void) 184bool pcm_is_paused(void)
@@ -147,9 +193,26 @@ bool pcm_is_playing(void)
147 193
148void pcm_set_frequency(unsigned int frequency) 194void 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
219static long write_to_soundcard(Uint8 *stream, int len, FILE *debug) { 282void 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
248void sdl_audio_callback(void *udata, Uint8 *stream, int len) 362void 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
402void pcm_init_recording(void)
403{
404}
405
406void pcm_close_recording(void)
407{
408}
409
410void 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
418void pcm_stop_recording(void)
419{
420}
421
422void pcm_record_more(void *start, size_t size)
423{
424 (void)start;
425 (void)size;
426}
427
428void pcm_calculate_rec_peaks(int *left, int *right)
429{
430 if (left)
431 *left = 0;
432 if (right)
433 *right = 0;
434}
435
436unsigned long pcm_rec_status(void)
437{
438 return 0;
439}
440
441#endif /* HAVE_RECORDING */
442
289int pcm_init(void) 443int 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)
317void pcm_postinit(void) 495void pcm_postinit(void)
318{ 496{
319} 497}
320
321