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); ... }