src/osd/sdl/sound.c
| r907 | r908 | |
| 37 | 37 | //============================================================ |
| 38 | 38 | |
| 39 | 39 | // number of samples per SDL callback |
| 40 | | #define SDL_XFER_SAMPLES 512 |
| 40 | static int sdl_xfer_samples = 512; |
| 41 | static int stream_in_initialized = 0; |
| 42 | static int stream_loop = 0; |
| 41 | 43 | |
| 42 | 44 | // maximum audio latency |
| 43 | 45 | #define MAX_AUDIO_LATENCY 10 |
| r907 | r908 | |
| 56 | 58 | |
| 57 | 59 | static UINT32 stream_buffer_size; |
| 58 | 60 | static UINT32 stream_buffer_in; |
| 59 | | static volatile UINT32 stream_write_cursor; |
| 60 | 61 | |
| 61 | 62 | // buffer over/underflow counts |
| 62 | 63 | static int buffer_underflows; |
| r907 | r908 | |
| 143 | 144 | if (video_get_throttle()) |
| 144 | 145 | { |
| 145 | 146 | pstart = stream_playpos; |
| 146 | | pend = (pstart + SDL_XFER_SAMPLES); |
| 147 | pend = (pstart + sdl_xfer_samples); |
| 147 | 148 | lstart = offset; |
| 148 | 149 | lend = lstart+size; |
| 149 | 150 | while (((pstart >= lstart) && (pstart <= lend)) || |
| r907 | r908 | |
| 156 | 157 | #endif*/ |
| 157 | 158 | |
| 158 | 159 | pstart = stream_playpos; |
| 159 | | pend = pstart + SDL_XFER_SAMPLES; |
| 160 | pend = pstart + sdl_xfer_samples; |
| 160 | 161 | } |
| 161 | 162 | } |
| 162 | 163 | |
| r907 | r908 | |
| 182 | 183 | *buffer1 = &stream_buffer[offset]; |
| 183 | 184 | } |
| 184 | 185 | |
| 185 | | #if LOG_SOUND |
| 186 | | // fprintf(sound_log, "locking %ld bytes at offset %ld (total %d, playpos %d): len1 %ld len2 %ld\n", |
| 187 | | // size, offset, stream_buffer_size, stream_playpos, *length1, *length2); |
| 188 | | #endif |
| 186 | #if LOG_SOUND |
| 187 | fprintf(sound_log, "locking %ld bytes at offset %ld (total %d, playpos %d): len1 %ld len2 %ld\n", |
| 188 | size, offset, stream_buffer_size, stream_playpos, *length1, *length2); |
| 189 | #endif |
| 189 | 190 | |
| 190 | 191 | return 0; |
| 191 | 192 | } |
| r907 | r908 | |
| 199 | 200 | if (!buf_locked) |
| 200 | 201 | SDL_UnlockAudio(); |
| 201 | 202 | |
| 202 | | #if LOG_SOUND |
| 203 | | // fprintf(sound_log, "unlocking\n"); |
| 204 | | #endif |
| 203 | #if LOG_SOUND |
| 204 | fprintf(sound_log, "unlocking\n"); |
| 205 | #endif |
| 205 | 206 | } |
| 206 | 207 | |
| 207 | 208 | //============================================================ |
| r907 | r908 | |
| 238 | 239 | } |
| 239 | 240 | |
| 240 | 241 | // adjust the input pointer |
| 241 | | stream_buffer_in = (stream_buffer_in + bytes_to_copy) % stream_buffer_size; |
| 242 | stream_buffer_in += bytes_to_copy; |
| 243 | if (stream_buffer_in >= stream_buffer_size) |
| 244 | { |
| 245 | stream_buffer_in -= stream_buffer_size; |
| 246 | stream_loop = 1; |
| 247 | #if LOG_SOUND |
| 248 | fprintf(sound_log, "stream_loop set to 1 (stream_buffer_in looped)\n"); |
| 249 | #endif |
| 250 | } |
| 242 | 251 | |
| 243 | 252 | // copy the first chunk |
| 244 | 253 | cur_bytes = (bytes_to_copy > length1) ? length1 : bytes_to_copy; |
| r907 | r908 | |
| 270 | 279 | if (Machine->sample_rate != 0 && stream_buffer) |
| 271 | 280 | { |
| 272 | 281 | int bytes_this_frame = samples_this_frame * sizeof(INT16) * 2; |
| 273 | | int play_position, write_position, stream_in; |
| 282 | int play_position, write_position, orig_write, stream_in; |
| 274 | 283 | |
| 275 | 284 | play_position = stream_playpos; |
| 276 | 285 | |
| 277 | 286 | write_position = stream_playpos + ((Machine->sample_rate / 50) * sizeof(INT16) * 2); |
| 287 | orig_write = write_position; |
| 278 | 288 | |
| 279 | | // normalize the write position so it is always after the play position |
| 280 | | if (write_position < play_position) |
| 281 | | write_position += stream_buffer_size; |
| 289 | if (!stream_in_initialized) |
| 290 | { |
| 291 | stream_in = stream_buffer_in = (write_position + stream_buffer_size) / 2; |
| 282 | 292 | |
| 283 | | // normalize the stream in position so it is always after the write position |
| 284 | | stream_in = stream_buffer_in; |
| 285 | | if (stream_in < write_position) |
| 286 | | stream_in += stream_buffer_size; |
| 293 | #if LOG_SOUND |
| 294 | fprintf(sound_log, "stream_in = %d\n", (int)stream_in); |
| 295 | fprintf(sound_log, "stream_playpos = %d\n", (int)stream_playpos); |
| 296 | fprintf(sound_log, "write_position = %d\n", (int)write_position); |
| 297 | #endif |
| 287 | 298 | |
| 288 | | // now we should have, in order: |
| 289 | | // <------pp---wp---si---------------> |
| 299 | // start playing |
| 300 | SDL_PauseAudio(0); |
| 290 | 301 | |
| 291 | | // if we're between play and write positions, then bump forward, but only in full chunks |
| 292 | | while (stream_in < write_position) |
| 302 | stream_in_initialized = 1; |
| 303 | } |
| 304 | else |
| 293 | 305 | { |
| 294 | | // printf("Underflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame); |
| 295 | | buffer_underflows++; |
| 296 | | stream_in += samples_this_frame; |
| 306 | // normalize the stream in position |
| 307 | stream_in = stream_buffer_in; |
| 308 | if (stream_in < write_position && stream_loop == 1) |
| 309 | stream_in += stream_buffer_size; |
| 310 | |
| 311 | // now we should have, in order: |
| 312 | // <------pp---wp---si---------------> |
| 313 | |
| 314 | // if we're between play and write positions, then bump forward, but only in full chunks |
| 315 | while (stream_in < write_position) |
| 316 | { |
| 317 | #if LOG_SOUND |
| 318 | fprintf(sound_log, "Underflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame); |
| 319 | #endif |
| 320 | buffer_underflows++; |
| 321 | stream_in += samples_this_frame; |
| 322 | } |
| 323 | |
| 324 | // if we're going to overlap the play position, just skip this chunk |
| 325 | if (stream_in + bytes_this_frame > play_position + stream_buffer_size) |
| 326 | { |
| 327 | #if LOG_SOUND |
| 328 | fprintf(sound_log, "Overflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame); |
| 329 | #endif |
| 330 | buffer_overflows++; |
| 331 | return; |
| 332 | } |
| 297 | 333 | } |
| 298 | 334 | |
| 299 | | // if we're going to overlap the play position, just skip this chunk |
| 300 | | if (stream_in + bytes_this_frame > play_position + stream_buffer_size) |
| 335 | if (stream_in >= stream_buffer_size) |
| 301 | 336 | { |
| 302 | | // printf("Overflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame); |
| 303 | | buffer_overflows++; |
| 304 | | return; |
| 337 | stream_in -= stream_buffer_size; |
| 338 | stream_loop = 1; |
| 339 | #if LOG_SOUND |
| 340 | fprintf(sound_log, "stream_loop set to 1 (stream_in looped)\n"); |
| 341 | #endif |
| 305 | 342 | } |
| 306 | 343 | |
| 307 | 344 | // now we know where to copy; let's do it |
| 308 | | stream_buffer_in = stream_in % stream_buffer_size; |
| 345 | stream_buffer_in = stream_in; |
| 309 | 346 | copy_sample_data(buffer, bytes_this_frame); |
| 310 | 347 | } |
| 311 | 348 | } |
| r907 | r908 | |
| 356 | 393 | { |
| 357 | 394 | int len1, len2; |
| 358 | 395 | |
| 359 | | if ((stream_playpos+len) > stream_buffer_size) |
| 396 | if (stream_in_initialized == 1 && stream_loop == 0 |
| 397 | && (stream_playpos+len) > stream_buffer_in) |
| 360 | 398 | { |
| 399 | #if LOG_SOUND |
| 400 | // I solved a crash by comment out a printf at sdl_callback. so |
| 401 | // I think this fprintf could cause a crash too (well, it works |
| 402 | // fine to me) if output is slow and sdl_callback is called |
| 403 | // again before exit the function (could be possible?). |
| 404 | //fprintf(sound_log, "Underflow at sdl_callback: SPP=%d SBI=%d Len=%d\n", (int)stream_playpos, (int)stream_buffer_in, (int)len); |
| 405 | #endif |
| 406 | len1 = stream_buffer_in - stream_playpos; |
| 407 | len2 = 0; |
| 408 | } |
| 409 | else if ((stream_playpos+len) > stream_buffer_size) |
| 410 | { |
| 361 | 411 | len1 = stream_buffer_size - stream_playpos; |
| 362 | 412 | len2 = len - len1; |
| 363 | 413 | } |
| r907 | r908 | |
| 384 | 434 | } |
| 385 | 435 | |
| 386 | 436 | // move the play cursor |
| 387 | | stream_playpos += len; |
| 388 | | stream_playpos %= stream_buffer_size; |
| 437 | stream_playpos += len1 + len2; |
| 438 | if (stream_playpos >= stream_buffer_size) |
| 439 | { |
| 440 | stream_playpos -= stream_buffer_size; |
| 441 | stream_loop = 0; |
| 442 | #if LOG_SOUND |
| 443 | fprintf(sound_log, "stream_loop set to 0 (stream_playpos looped)\n"); |
| 444 | #endif |
| 445 | } |
| 389 | 446 | |
| 390 | | // always keep the write cursor just ahead of the play position |
| 391 | | stream_write_cursor = stream_playpos + len; |
| 392 | | stream_write_cursor %= stream_buffer_size; |
| 393 | | |
| 394 | | #if LOG_SOUND |
| 395 | | // fprintf(sound_log, "callback: xfer len1 %d len2 %d, playpos %d, wcursor %d\n", |
| 396 | | // len1, len2, stream_playpos, stream_write_cursor); |
| 397 | | #endif |
| 447 | #if LOG_SOUND |
| 448 | fprintf(sound_log, "callback: xfer len1 %d len2 %d, playpos %d\n", |
| 449 | len1, len2, stream_playpos); |
| 450 | #endif |
| 398 | 451 | } |
| 399 | 452 | |
| 400 | 453 | |
| r907 | r908 | |
| 405 | 458 | { |
| 406 | 459 | int n_channels = 2; |
| 407 | 460 | int audio_latency; |
| 408 | | SDL_AudioSpec aspec; |
| 461 | SDL_AudioSpec aspec, obtained; |
| 462 | char audio_driver[16]; |
| 409 | 463 | |
| 410 | 464 | initialized_audio = 0; |
| 411 | 465 | |
| r907 | r908 | |
| 413 | 467 | aspec.freq = Machine->sample_rate; |
| 414 | 468 | aspec.format = AUDIO_S16SYS; // keep endian independant |
| 415 | 469 | aspec.channels = n_channels; |
| 416 | | aspec.samples = SDL_XFER_SAMPLES; |
| 470 | aspec.samples = sdl_xfer_samples; |
| 417 | 471 | aspec.callback = sdl_callback; |
| 418 | 472 | aspec.userdata = 0; |
| 419 | 473 | |
| 420 | | if (SDL_OpenAudio(&aspec, NULL) < 0) |
| 474 | if (SDL_OpenAudio(&aspec, &obtained) < 0) |
| 421 | 475 | goto cant_start_audio; |
| 422 | 476 | |
| 423 | 477 | initialized_audio = 1; |
| 424 | 478 | snd_enabled = 1; |
| 425 | 479 | |
| 480 | SDL_AudioDriverName(audio_driver, sizeof(audio_driver)); |
| 481 | mame_printf_verbose("Audio initialized - driver: %s, frequency: %d, " |
| 482 | "channels: %d, samples: %d\n", audio_driver, |
| 483 | obtained.freq, obtained.channels, obtained.samples); |
| 484 | |
| 485 | sdl_xfer_samples = obtained.samples; |
| 486 | |
| 426 | 487 | audio_latency = options_get_int(mame_options(), SDLOPTION_AUDIO_LATENCY); |
| 427 | 488 | |
| 428 | 489 | // pin audio latency |
| r907 | r908 | |
| 441 | 502 | if (stream_buffer_size < 1024) |
| 442 | 503 | stream_buffer_size = 1024; |
| 443 | 504 | |
| 444 | | #if LOG_SOUND |
| 445 | | fprintf(sound_log, "stream_buffer_size = %d\n", stream_buffer_size); |
| 446 | | #endif |
| 447 | | |
| 448 | 505 | // create the buffers |
| 449 | 506 | if (sdl_create_buffers()) |
| 450 | 507 | goto cant_create_buffers; |
| 451 | 508 | |
| 452 | | // start playing |
| 453 | | SDL_PauseAudio(0); |
| 454 | 509 | return 0; |
| 455 | 510 | |
| 456 | 511 | // error handling |
| r907 | r908 | |
| 479 | 534 | |
| 480 | 535 | static int sdl_create_buffers(void) |
| 481 | 536 | { |
| 482 | | // printf("sdl_create_buffers: creating stream buffer of %x bytes\n", stream_buffer_size); |
| 537 | #if LOG_SOUND |
| 538 | fprintf(sound_log, "sdl_create_buffers: creating stream buffer of %d bytes\n", stream_buffer_size); |
| 539 | #endif |
| 483 | 540 | stream_buffer = (INT8 *)malloc(stream_buffer_size); |
| 484 | 541 | memset(stream_buffer, 0, stream_buffer_size); |
| 485 | 542 | stream_playpos = 0; |
| 486 | | stream_write_cursor = SDL_XFER_SAMPLES*2; |
| 487 | 543 | buf_locked = 0; |
| 488 | 544 | return 0; |
| 489 | 545 | } |