Hi,
I'm still working with sound issue. There is not great progress though, I could find some direction how to deal with it. I guess latency is reduced almost from 0.5 ms to 0.3 ms with this patch. This is just current status. (you can use it to copy vm-sound-ALSA to /usr/lib/squeak/3.9-9/)
What I did is;
- Setting periods count to two. (more periods cause more latency). - Use "plughw:0,0" device instead of "default"
The defect is the sound quality becomes bad! So, I will try another way. I think mmap is good to the purpose. API document said snd_pcm_avail_update() is used with mmap.
http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html#g8bb836bd0c...
Cheers, - Takashi
Index: platforms/unix/vm-sound-ALSA/sqUnixSoundALSA.c =================================================================== --- platforms/unix/vm-sound-ALSA/sqUnixSoundALSA.c (revision 1586) +++ platforms/unix/vm-sound-ALSA/sqUnixSoundALSA.c (working copy) @@ -33,7 +33,7 @@ #include <errno.h>
-static char *sound_device = "default"; +static char *sound_device = "plughw:0,0"; static char *sound_playback = "Master"; static char *sound_capture = "Capture";
@@ -87,9 +87,10 @@ int err; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; - snd_pcm_uframes_t frames; unsigned int uval; int dir; + int periodsCount = 2; + snd_pcm_uframes_t bufferSize;
if (output_handle) sound_Stop();
@@ -104,10 +105,15 @@ snd_pcm_hw_params_set_channels(output_handle, hwparams, output_channels); uval= samplesPerSec; snd_pcm_hw_params_set_rate_near(output_handle, hwparams, &uval, &dir); - frames= frameCount; - snd_pcm_hw_params_set_period_size_near(output_handle, hwparams, &frames, &dir); + snd(pcm_hw_params_set_periods_near(output_handle, hwparams, &periodsCount, &dir), "pcm_hw_params_set_periods_near"); + bufferSize = frameCount * periodsCount; + snd(pcm_hw_params_set_buffer_size_near(output_handle, hwparams, &bufferSize), "pcm_hw_params_set_buffer_size_near"); + snd(pcm_hw_params(output_handle, hwparams), "sound_Start: snd_pcm_hw_params");
+ fprintf(stderr, "Request: frames=%i, periods=%i\n", frameCount, 2); + fprintf(stderr, "Result: frames=%i, periods=%i\n", bufferSize / periodsCount, periodsCount); + snd_pcm_sw_params_alloca(&swparams); snd(pcm_sw_params_current(output_handle, swparams), "sound_Start: snd_pcm_sw_params_current"); snd(pcm_sw_params_set_start_threshold(output_handle, swparams, frameCount * SQ_SND_PLAY_START_THRESHOLD), "sound_Start: snd_pcm_sw_params_set_start_threshold");
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)
Takashi,
The attached vm-sound-ALSA does shorten the delay when I play sound from PianoKeyboard, but for a longer sound, it doesn't work as expected. The buffer underrun seems to be happening a lot, and also I might guess that the index to fill the buffer may be wrong.
Now you have one, try
SampledSound stereoBachFugue play
and see (hear) how it sounds.
-- Yoshiki
Takashi Yamamiya wrote:
[1 <text/plain; ISO-2022-JP (7bit)>] 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
[2 alsa-fixDelay-tak.diff <text/plain (base64)>] 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) [3 vm-sound-ALSA.gz <application/gzip (base64)>]
[4 <text/plain; us-ascii (7bit)>] _______________________________________________ Etoys mailing list Etoys@laptop.org http://mailman.laptop.org/mailman/listinfo/etoys
Yoshiki,
Thank you, I'll try same test, too. - Takashi
Yoshiki Ohshima wrote:
Takashi,
The attached vm-sound-ALSA does shorten the delay when I play sound from PianoKeyboard, but for a longer sound, it doesn't work as expected. The buffer underrun seems to be happening a lot, and also I might guess that the index to fill the buffer may be wrong.
Now you have one, try
SampledSound stereoBachFugue play
and see (hear) how it sounds.
Hello Yoshiki,
I fixed this problem. The reason of this issue was the delay buffer was overflowed if under run happened repeatedly. I tested it on my machine. Please test it again.
Thank you, - Takashi
Yoshiki Ohshima wrote:
Takashi,
The attached vm-sound-ALSA does shorten the delay when I play sound from PianoKeyboard, but for a longer sound, it doesn't work as expected. The buffer underrun seems to be happening a lot, and also I might guess that the index to fill the buffer may be wrong.
Now you have one, try
SampledSound stereoBachFugue play
Index: platforms/unix/vm-sound-ALSA/sqUnixSoundALSA.c =================================================================== --- platforms/unix/vm-sound-ALSA/sqUnixSoundALSA.c (revision 1594) +++ platforms/unix/vm-sound-ALSA/sqUnixSoundALSA.c (working copy) @@ -56,6 +56,8 @@ static void sigio_save(void); static void sigio_restore(void);
+#define MIN(x,y) ((x) < (y)) ? (x) : (y) +#define MAX(x,y) ((x) > (y)) ? (x) : (y)
/* output */
@@ -67,8 +69,10 @@ static snd_async_handler_t *output_handler= 0; static int output_semaphore= 0; static int output_channels= 0; -static int output_buffer_frames_size= 0; static int output_buffer_frames_available= 0; +static snd_pcm_uframes_t output_buffer_period_size= 0; +static snd_pcm_uframes_t output_buffer_size= 0; +static double max_delay_frames= 0;
static void output_callback(snd_async_handler_t *handler) { @@ -92,7 +96,6 @@ int err; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; - snd_pcm_uframes_t frames; unsigned int uval; int dir;
@@ -109,8 +112,8 @@ snd_pcm_hw_params_set_channels(output_handle, hwparams, output_channels); uval= samplesPerSec; snd_pcm_hw_params_set_rate_near(output_handle, hwparams, &uval, &dir); - frames= frameCount; - snd_pcm_hw_params_set_period_size_near(output_handle, hwparams, &frames, &dir); + output_buffer_period_size= frameCount; + snd_pcm_hw_params_set_period_size_near(output_handle, hwparams, &output_buffer_period_size, &dir); snd(pcm_hw_params(output_handle, hwparams), "sound_Start: snd_pcm_hw_params");
snd_pcm_sw_params_alloca(&swparams); @@ -120,8 +123,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; + snd(pcm_hw_params_get_buffer_size(hwparams, &output_buffer_size), "sound_Start: pcm_hw_params_get_buffer_size"); output_buffer_frames_available= 1; + max_delay_frames= output_buffer_period_size * 2; /* 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 +156,34 @@ 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; /* current state of the stream */ + sqInt avail= 0; /* available space for the answer (in bytes) */ + + 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= + MIN(max_delay_frames * 1.5, output_buffer_size - output_buffer_period_size); + } + + /* 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_period_size; + max_delay_frames= MAX(max_delay_frames * 0.9995, output_buffer_period_size); + } + fprintf(stderr, "delay=%i, ans_avail=%i, state=%i, real_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) @@ -231,7 +252,6 @@ snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t frames; - unsigned int uval; int dir;
if (input_handle) sound_StopRecording(); @@ -261,6 +281,7 @@ snd(pcm_nonblock(input_handle, 1), "sound_StartRecording: snd_pcm_nonblock"); snd(async_add_pcm_handler(&input_handler, input_handle, input_callback, 0), "sound_StartRecording: snd_add_pcm_handler"); snd(pcm_start(input_handle), "sound_StartRecording: snd_pcm_start"); + return 1; }
static double sound_GetRecordingSampleRate(void)
etoys-dev@lists.squeakfoundation.org