[alsa-devel] Underrun on Jade controller...
Sandulescu Bogdan
bogdan.sandulescu at yahoo.com
Fri Nov 23 11:35:46 CET 2012
Daniel Mack <zonque <at> gmail.com> writes:
>
> Without providing any code, I guess nobody on this list will be able to
> help you.
>
> Daniel
>
OK. Here is the essential part of my code - settings and write to ALSA:
PlayThread()
{
...
/********************************* SET PARAMS ******************/
/* Set HW Parameters, based on .wav header settings */
...
/* APLAY settings */
if (buffer_time == 0 && buffer_frames == 0)
{
iCheckErrorState =
snd_pcm_hw_params_get_buffer_time_max(pcmHwParameterCfg, &buffer_time, 0);
if (iCheckErrorState < 0)
{
iStatus = -1; /* set erroneous state */
}
if (buffer_time > 500000)
{
buffer_time = 500000;
}
}
if (period_time == 0 && period_frames == 0)
{
if (buffer_time > 0)
{
period_time = buffer_time / 4;
}
else
{
period_frames = buffer_frames / 4;
}
}
if (period_time > 0)
{
period_time = 500000; /* restrict /smaller period time */
iCheckErrorState =
snd_pcm_hw_params_set_period_time_near(pPcmPlayHandle, pcmHwParameterCfg,
&period_time, 0);
}
else
{
iCheckErrorState =
snd_pcm_hw_params_set_period_size_near(pPcmPlayHandle, pcmHwParameterCfg,
&period_frames, 0);
}
if (iCheckErrorState < 0)
{
iStatus = -1; /* set erroneous state */
}
if (buffer_time > 0)
{
iCheckErrorState =
snd_pcm_hw_params_set_buffer_time_near(pPcmPlayHandle, pcmHwParameterCfg,
&buffer_time, 0);
}
else
{
iCheckErrorState =
snd_pcm_hw_params_set_buffer_size_near(pPcmPlayHandle, pcmHwParameterCfg,
&buffer_frames);
}
if (iCheckErrorState < 0)
{
iStatus = -1; /* set erroneous state */
}
iCheckErrorState = snd_pcm_hw_params(pPcmPlayHandle, pcmHwParameterCfg);
if (iCheckErrorState != 0)
{
iStatus = -1;
}
snd_pcm_hw_params_get_period_size(pcmHwParameterCfg, &pcmPeriodSize, 0);
snd_pcm_hw_params_get_buffer_size(pcmHwParameterCfg, &pcmBufferSize);
fprintf(stderr,"Period size = %lu ; Buffer size = %lu \n",
pcmPeriodSize, pcmBufferSize);
if (pcmPeriodSize == pcmBufferSize)
{
fprintf(stderr,"Can't use period equal to buffer size (%lu == %lu)",
pcmPeriodSize, pcmBufferSize);
}
snd_pcm_sw_params_current(pPcmPlayHandle, pcmSWParameterCfg);
if (avail_min < 0)
{
n = pcmPeriodSize;
}
else
{
n = (double) rate * avail_min / 1000000;
}
iCheckErrorState = snd_pcm_sw_params_set_avail_min(pPcmPlayHandle,
pcmSWParameterCfg, n);
/* round up to closest transfer boundary */
n = pcmBufferSize;
if (start_delay <= 0)
{
start_threshold = n + (double) rate * start_delay / 1000000;
}
else
{
start_threshold = (double) rate * start_delay / 1000000;
}
if (start_threshold < 1)
{
start_threshold = 1;
}
if (start_threshold > n)
{
start_threshold = n;
}
iCheckErrorState =
snd_pcm_sw_params_set_start_threshold(pPcmPlayHandle, pcmSWParameterCfg,
start_threshold);
if (iCheckErrorState < 0)
{
iStatus = -1; /* set erroneous state */
}
fprintf(stderr,"Start threshold = %lu \n",start_threshold);
if (stop_delay <= 0)
{
stop_threshold = pcmBufferSize + (double) rate * stop_delay / 1000000;
}
else
{
stop_threshold = (double) rate * stop_delay / 1000000;
}
iCheckErrorState = snd_pcm_sw_params_set_stop_threshold(pPcmPlayHandle,
pcmSWParameterCfg, stop_threshold);
if (iCheckErrorState < 0)
{
iStatus = -1; /* set erroneous state */
}
if (snd_pcm_sw_params(pPcmPlayHandle, pcmSWParameterCfg) < 0)
{
fprintf(stderr,"Unable to install sw parameters, exit thread \n");
iStatus = -1; /* set erroneous state */
}
fprintf(stderr,"Play ThreadID [%lu]: after parmas ended\n",
pthread_self());
/* END APLAY settings */
/*********************************END SET PARAMS ******************/
/* Check if the Settings were correctly done or not */
if (iStatus < 0)
{
fprintf(stderr,"Could not SET correctly the wav settings! \n");
/* ... do something */
}
else
{
/* Finished operation with Settings, continue processing */
/*Now we've set up our device, but we still can't write to it.
There's one last step we have to take: prepare the device */
snd_pcm_prepare (pPcmPlayHandle);
/* NOTE: cannot use to malloc the entire wav file size, as this
leads to: frequent overrun (more data in output buffer),
* and the User-Space app runtime memory is increased with wav
file size; Hence, is better to use the ALSA pcm buffer sizes. */
iBufferSize = pcmBufferSize;
TimeStamp("Play ThreadID [%lu]: before malloc\n", pthread_self());
fprintf(stderr,"About to allocate %u buffer...\n", iBufferSize);
pu8WavDataBuf = (uint8*) malloc( (size_t) iBufferSize);
if (pu8WavDataBuf != NULL)
{
/* Data allocation successful continue with play... */
fprintf(stderr,"Done allocating the buffer, start execute ALSA
play sequence...\n");
size_t offset = 0;
int remaining = WavSoundSize;
Wavfp = fopen(WavPathAndName, "r");
if (Wavfp != NULL)
{
/* Wav FILE opened OK, continue reading the data and send it
to ALSA driver */
TimeStamp("Play ThreadID [%lu]: before reading/writing\n",
pthread_self());
while (remaining > 0)
{
/* READ DATA into local buffer */
/* Firstly, read a block of data into buffer */
/********* READ DATA ********/
/* Normal read from wav file, with a complete a buffer size */
iStatus = fread(pu8WavDataBuf, sizeof(uint8),
iBufferSize , Wavfp);
/********** END READ DATA ****************/
/* Secondly, output that buffer to ALSA driver */
iStatus = snd_pcm_writei(pPcmPlayHandle, pu8WavDataBuf,
iBufferSize / (stHWSettings.format * stHWSettings.channels) );
if (iStatus == -EAGAIN || (iStatus >= 0 && (
(size_t)iStatus < iBufferSize / (stHWSettings.format * stHWSettings.channels) ) ))
{
/* The ALSA ring buffer is full, wait some small time to
flush parts of data, so we can fill it with rest of .wav data */
fprintf(stderr,"The ALSA ring buffer is full, wait some
small time to flush parts of data \n");
snd_pcm_wait(pPcmPlayHandle, 10);
}
else
{
if (iStatus == -EPIPE)
{
/* In some cases, it might happen that this thread
does not feed new samples in time to alsa-lib (due to high CPU usage - thread
interrupted often -
* or large delay between consecutive writei()
calls). Therefore, we must prepare/recover the PCM handle for next usage.
*/
/* Print delay time */
snd_pcm_status_t *status;
snd_pcm_status_alloca(&status);
if ((snd_pcm_status(pPcmPlayHandle, status))<0) {
fprintf(stderr, "some error \n");
}
struct timespec now, diff, tstamp;
clock_gettime(CLOCK_MONOTONIC, &now);
snd_pcm_status_get_trigger_htstamp(status, &tstamp);
timermsub(&now, &tstamp, &diff);
fprintf(stderr,"underrun!!! (at least %.3f ms long)\n",
diff.tv_sec * 1000 + diff.tv_nsec / 10000000.0);
fprintf(stderr,"Underrun encountered: application
didn't sent enough data to the ALSA buffer, status = %d \n", iStatus);
iStatus = snd_pcm_recover(pPcmPlayHandle,iStatus,0 );
}
else
{
if (iStatus == -ESTRPIPE)
{
/* ignore it */
}
else
{
if (iStatus < 0)
{
/* ignore it */
}
else
{
/* Write performed OK */
}
}
}
}
/* Use the pcm_avail_update() only for checking buffer
playback fill level - not for SYNCHRONISATION */
snd_pcm_sframes_t pcmAvailBytes;
pcmAvailBytes = snd_pcm_avail_update(pPcmPlayHandle);
offset = offset + ( iStatus * (stHWSettings.format *
stHWSettings.channels));
remaining = remaining - ( iStatus * (stHWSettings.format *
stHWSettings.channels));
fprintf(stderr,"Writei() ended, bytes written = %d,
available bytes to be written = %lu\n", iStatus, pcmAvailBytes);
}
TimeStamp("Play ThreadID [%lu]: after reading/writing fully
ended\n", pthread_self());
/* Finished reading the wav file, now it's time to close it */
fclose(Wavfp);
/* Done the complete output, now drain/reset the ALSA device */
snd_pcm_nonblock(pPcmPlayHandle, 0);
snd_pcm_drain(pPcmPlayHandle);
snd_pcm_nonblock(pPcmPlayHandle, 0);
...
}
More information about the Alsa-devel
mailing list