Hi Ian, and folks,
I fixed the issue of sound latency in ALSA. I tried minimize the delay as well as possible. I have tested it on my Fedora Core with VMWare, and A-board.
- Minimum delay size depends on the sound buffer size (periods frames in ALSA term). - If underrun error happens, the delay is extended longer. - If nothing wrong, the delay becomes shorter. - sound_AvailableSpace() (#primSoundAvailableBytes) answers always the buffer size (periods frames) if the delay is short enough.
Yoshiki, could you test it on B-Board?
Thank you, - Takashi
Index: platforms/unix/vm-sound-ALSA/sqUnixSoundALSA.c =================================================================== --- platforms/unix/vm-sound-ALSA/sqUnixSoundALSA.c (revision 1592) +++ platforms/unix/vm-sound-ALSA/sqUnixSoundALSA.c (working copy) @@ -69,6 +69,7 @@ static int output_channels= 0; static int output_buffer_frames_size= 0; static int output_buffer_frames_available= 0; +static double max_delay_frames = 0;
static void output_callback(snd_async_handler_t *handler) { @@ -120,8 +121,9 @@ snd(pcm_sw_params_set_xfer_align(output_handle, swparams, 1), "sound_Start: snd_pcm_sw_params_set_xfer_align"); snd(pcm_sw_params(output_handle, swparams), "sound_Start: snd_pcm_sw_params");
- output_buffer_frames_size= frameCount; + output_buffer_frames_size= frames; output_buffer_frames_available= 1; + max_delay_frames = frames * 1.5; /* set initial delay frames */
snd(pcm_nonblock(output_handle, 1), "sound_Start: snd_pcm_nonblock"); snd(async_add_pcm_handler(&output_handler, output_handle, output_callback, 0), "soundStart: snd_add_pcm_handler"); @@ -152,17 +154,35 @@ return 1; }
+/* Answers periods frame size, or zero if the delay is over. */ + static sqInt sound_AvailableSpace(void) { - if (output_handle) - { - int count = snd_pcm_avail_update(output_handle); - if (count >= 0) - return count; - fprintf(stderr, "sound_AvailableSpace: snd_pcm_avail_update: %s\n", snd_strerror(count)); - snd_pcm_prepare(output_handle); - } - return 0; + snd_pcm_sframes_t delay; /* distance to playback point (in frames) */ + snd_pcm_state_t state; + sqInt avail = 0; + + if (!output_handle) return 0; + + snd_pcm_delay(output_handle, &delay); + state = snd_pcm_state (output_handle); + + /* if underrun causes, max delay is loosened */ + if (state == SND_PCM_STATE_XRUN) { + max_delay_frames = max_delay_frames * 1.5; + } + + /* if the state is not running, new sound is needed bacause nobody can + signal the semaphore. */ + if (delay < max_delay_frames || state != SND_PCM_STATE_RUNNING) { + avail = output_buffer_frames_size; + double new_delay = max_delay_frames * 0.9995; + max_delay_frames = new_delay > output_buffer_frames_size ? + new_delay : output_buffer_frames_size; + } + // fprintf(stderr, "delay=%i, avail=%i, state=%i, delay=%.1fms\n", + // (int) delay, avail, state, 1000 * max_delay_frames / 22050); + return avail * output_channels * 2; /* bytes */ }
static sqInt sound_InsertSamplesFromLeadTime(sqInt frameCount, sqInt srcBufPtr, sqInt samplesOfLeadTime) FAIL(frameCount)