I'm trying to develop a simple audio program that runs on a ADSL router. I found two GPL sources for other routers that have the same chips and I'm able to build kernel and rootfs. I already add ALSA support to the kernel and successfully deployed to the router.
Now I'm trying to get my program to work. Using a usb sound card, the first thing that I'm doing is to passthrough capture signal to the playback and measure the latency.
I already remove all jitter that was present but I can't get a better latency that 1 second (it's a lot of latency). I'm initializing the device with 128 frames of buffer and 1 frame for period size, and latency is always 1 second. I'm using only 1 channel and a rate of 44100, but also tested with a rate of 8000hz and the latency is the same
I already tried setting another priority to the running thread and tested a lot of combinations of hw params, but never could get minor latency than that.
I checked the cpu and memory while my program is running and there is a lot of free resources. I removed many services of the system like telnet, samba and other programs that could interfere with my program.
An interesting point is that when my program only set the playback device and play some pcm file, there is no latency (right after I start the program, the sound is played correctly).
Linux kernel version is v2.6.22.15. Alsa driver is 1.0.14. I tried a cheap usb sound card, a Zoom h1 recorder (that works as usb card) and a Presonus 22vsl audio interface, always the same latency.
The CPU is a Ralink rt63365, it has 4 cores and it seems to run at 400hz aprox.
I don't know what else to try. What kind of test could I do to detect where the latency problem is generated?
Edit
I forget to mention that I run the same program with all the cards mentioned but in my PC (kernel version > 5, with the respective alsa version) and the program works perfectly, there is no notable latency.
Main functions of my program:
void setscheduler(void)
{
struct sched_param sched_param;
if (sched_getparam(0, &sched_param) < 0) {
printf("Scheduler getparam failed...\n");
return;
}
sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
if (!sched_setscheduler(0, SCHED_RR, &sched_param)) {
printf("Scheduler set to Round Robin with priority %i...\n", sched_param.sched_priority);
fflush(stdout);
return;
}
printf("!!!Scheduler set to Round Robin with priority %i FAILED!!!\n", sched_param.sched_priority);
}
int setup_alsa_handle(snd_pcm_t *handle, snd_pcm_stream_t stream)
{
int err = 0;
snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params;
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n",
snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params_any(handle, hw_params)) < 0)
{
fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n",
snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params_set_access(handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
fprintf(stderr, "cannot set access type (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params_set_format(handle, hw_params, AUDIO_FORMAT)) < 0)
{
fprintf(stderr, "cannot set sample format (%s)\n", snd_strerror(err));
exit(1);
}
unsigned int rate = SAMPLE_RATE;
if ((err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rate, 0)) <
0)
{
fprintf(stderr, "cannot set sample rate (%s)\n", snd_strerror(err));
exit(1);
}
int dir;
snd_pcm_uframes_t period_size = PERIOD_SIZE_IN_FRAMES;
if ((err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, &dir)) <
0)
{
fprintf(stderr, "cannot set period size (%s)\n", snd_strerror(err));
exit(1);
}
int channels;
if (stream == SND_PCM_STREAM_CAPTURE)
{
channels = CAPTURE_CHANNELS;
}
else
{
channels = PLAYBACK_CHANNELS;
}
if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, channels)) < 0)
{
fprintf(stderr, "cannot set channel count (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params(handle, hw_params)) < 0)
{
fprintf(stderr, "cannot set parameters (%s)\n", snd_strerror(err));
exit(1);
}
snd_pcm_hw_params_free(hw_params);
if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) {
fprintf(stderr, "cannot allocate software parameters structure(%s)\n",
snd_strerror(err));
return err;
}
if ((err = snd_pcm_sw_params_current(handle, sw_params)) < 0) {
fprintf(stderr, "cannot initialize software parameters structure(%s)\n",
snd_strerror(err));
return err;
}
if ((err = snd_pcm_sw_params_set_avail_min(handle, sw_params, FRAMES_PER_BUFFER)) < 0) {
fprintf(stderr, "cannot set minimum available count(%s)\n",
snd_strerror(err));
return err;
}
if ((err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, 0U)) < 0) {
fprintf(stderr, "cannot set start mode(%s)\n",
snd_strerror(err));
return err;
}
if ((err = snd_pcm_sw_params(handle, sw_params)) < 0) {
fprintf(stderr, "cannot set software parameters(%s)\n",
snd_strerror(err));
return err;
}
return 0;
}
void start_stream(snd_pcm_t *_playback_handle, snd_pcm_t *_capture_handle,
void (*stream_callback)(const void *, void *, unsigned long,
void *),
void (*controls_callback)(void *), void *data)
{
audio_sample *in_buffer;
audio_sample *out_buffer;
FILE *fout = NULL;
int in_result = 0;
int out_result = 0;
int temp_n = 0;
int buffer_frames = FRAMES_PER_BUFFER;
int frame_size_in_bytes = snd_pcm_format_width(AUDIO_FORMAT) / 8;
int in_buffer_frames = buffer_frames * CAPTURE_CHANNELS;
in_buffer = (audio_sample *)malloc(in_buffer_frames * frame_size_in_bytes);
int out_buffer_frames = buffer_frames * PLAYBACK_CHANNELS;
out_buffer = (audio_sample *)malloc(out_buffer_frames * frame_size_in_bytes);
memset(in_buffer, SAMPLE_SILENCE, in_buffer_frames * frame_size_in_bytes);
memset(out_buffer, SAMPLE_SILENCE, out_buffer_frames * frame_size_in_bytes);
int err;
while (1)
{
int avail;
if ((err = snd_pcm_wait(_playback_handle, 1000)) < 0) {
fprintf(stderr, "poll failed(%s)\n", strerror(errno));
break;
}
avail = snd_pcm_avail_update(_capture_handle);
fprintf(stderr, "1 avail (%d)\n", avail);
if (avail > 0) {
if (avail > FRAMES_PER_BUFFER)
avail = FRAMES_PER_BUFFER;
snd_pcm_readi(_capture_handle, in_buffer, avail);
}
avail = snd_pcm_avail_update(_playback_handle);
fprintf(stderr, "2 avail (%d)\n", avail);
if (avail > 0) {
if (avail > FRAMES_PER_BUFFER)
avail = FRAMES_PER_BUFFER;
snd_pcm_writei(_playback_handle, in_buffer, avail);
}
}
}
/**
* Main
*/
int main(int argc, char *argv[])
{
setscheduler();
if ((err = snd_pcm_open(&capture_handle, CAPTURE_AUDIO_DEVICE, SND_PCM_STREAM_CAPTURE, 0)) < 0)
{
fprintf(stderr, "cannot open audio device '%s'. Error: %s\n", CAPTURE_AUDIO_DEVICE, snd_strerror(err));
exit(1);
}
setup_alsa_handle(capture_handle, SND_PCM_STREAM_CAPTURE);
// Init playback device
if ((err = snd_pcm_open(&playback_handle, PLAYBACK_AUDIO_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
fprintf(stderr, "cannot open audio device '%s'. Error: %s\n", PLAYBACK_AUDIO_DEVICE, snd_strerror(err));
exit(1);
}
setup_alsa_handle(playback_handle, SND_PCM_STREAM_PLAYBACK);
if ((err = snd_pcm_start(capture_handle)) < 0) {
fprintf(stderr, "cannot prepare audio interface for use(%s)\n",
snd_strerror(err));
return err;
}
if ((err = snd_pcm_prepare(playback_handle)) < 0) {
fprintf(stderr, "cannot prepare audio interface for use(%s)\n",
snd_strerror(err));
return err;
}
// Start stream
start_stream(playback_handle, capture_handle, audio_processing_callback, controls_callback, &data, write_to_file, read_from_file);
return 0;
}