Add Alsa polling examples (simple + complex usecase)
This commit is contained in:
parent
4b8204cd93
commit
d31864be6e
1 changed files with 326 additions and 2 deletions
328
alsa_info.md
328
alsa_info.md
|
|
@ -658,10 +658,79 @@ Oh you naive fool....
|
|||
|
||||
|||
|
||||
#### Polling
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#define AUDIO_SAMPLERATE 16000
|
||||
|
||||
#define AUDIO_NR_OF_SAMPLES 128u
|
||||
#define AUDIO_BYTES_PER_SAMPLE 2u
|
||||
#define AUDIO_OUTPUT_NR_CHANNELS 2u
|
||||
#define SAMPLE_BUFFER_SIZE (AUDIO_NR_OF_SAMPLES * AUDIO_OUTPUT_NR_CHANNELS * AUDIO_BYTES_PER_SAMPLE)
|
||||
|
||||
#define PERIOD_SIZE_US 8000u
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
//TODO
|
||||
int error = 0;
|
||||
struct pollfd *ufds = NULL;
|
||||
snd_pcm_t *pcm_handle = NULL;
|
||||
int poll_fd_count = 0;
|
||||
char my_samples[SAMPLE_BUFFER_SIZE] = { 0 };
|
||||
int revents = 0;
|
||||
|
||||
snd_pcm_open(&pcm_handle, "default:CARD=MyAwesomeAudioCard", SND_PCM_STREAM_PLAYBACK, 0);
|
||||
|
||||
// default setup stuff here....
|
||||
|
||||
// Set start value
|
||||
if (err < snd_pcm_sw_params_set_avail_min(handle, swparams, period_size)) {
|
||||
printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
// Enable poll event
|
||||
err = snd_pcm_sw_params_set_period_event(handle, swparams, 1);
|
||||
if (err < 0) {
|
||||
printf("Unable to set period event: %s\n", snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
// Set SW params
|
||||
if ((err = snd_pcm_sw_params(handle, swparams)) < 0) {
|
||||
printf("ERROR: Can't set software parameters. %s\n", snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
// Get number of file descriptors (can be more than 1, depends on driver)
|
||||
poll_fd_count = snd_pcm_poll_descriptors_count(pcm_handle);
|
||||
|
||||
// Get the actual file descriptors
|
||||
ufds = malloc(sizeof(*ufds) * poll_fd_count);
|
||||
snd_pcm_poll_descriptors(pcm_handle, ufds, poll_fd_count);
|
||||
|
||||
// start device
|
||||
snd_pcm_start(pcm_handle);
|
||||
|
||||
while (1) {
|
||||
poll(ufds, poll_fd_count, -1);
|
||||
snd_pcm_poll_descriptors_revents(pcm_handle, ufds, poll_fd_count, &revents);
|
||||
if (revents & POLLERR) {
|
||||
// All hope is lost...
|
||||
} else if (revents & POLLOUT) {
|
||||
error = snd_pcm_writei(pcm_handle, my_samples, AUDIO_NR_OF_SAMPLES);
|
||||
if (error < 0) {
|
||||
// xrun recovery
|
||||
}
|
||||
}
|
||||
|
||||
// Throttle our thread...
|
||||
usleep(PERIOD_SIZE_US / 2)
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -707,10 +776,265 @@ Extrapolate the blocking usecase
|
|||
|
||||
|||
|
||||
#### Polling
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <alloca.h>
|
||||
|
||||
#define AUDIO_NR_OF_SAMPLES 128u
|
||||
#define AUDIO_BYTES_PER_SAMPLE 2u
|
||||
#define AUDIO_NR_OF_CHANNELS 2u
|
||||
#define SAMPLE_BUFFER_SIZE (AUDIO_NR_OF_SAMPLES * AUDIO_NR_OF_CHANNELS * AUDIO_BYTES_PER_SAMPLE)
|
||||
|
||||
static char *device_names[] = {
|
||||
"sysdefault:CARD=AudioBoard00",
|
||||
"sysdefault:CARD=AudioBoard01",
|
||||
"sysdefault:CARD=AudioBoard02",
|
||||
"sysdefault:CARD=AudioBoard03",
|
||||
"sysdefault:CARD=AudioBoard04",
|
||||
"sysdefault:CARD=AudioBoard05",
|
||||
"sysdefault:CARD=AudioBoard06",
|
||||
"sysdefault:CARD=AudioBoard07",
|
||||
};
|
||||
|
||||
#define NR_OF_DEVICES (sizeof(device_names)/sizeof(*device_names))
|
||||
|
||||
static void *read_thread_func(void *cookie)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
struct pollfd *ufds = NULL;
|
||||
snd_pcm_t *pcm_handles[NR_OF_DEVICES];
|
||||
int poll_fd_count[NR_OF_DEVICES] = { 0 };
|
||||
int total_poll_fd_count = 0;
|
||||
int poll_fd_count_offset[NR_OF_DEVICES] = { 0 };
|
||||
|
||||
for (int i = 0; i< NR_OF_DEVICES; ++i) {
|
||||
if (snd_pcm_open(&pcm_handles[i], device_names[i], SND_PCM_STREAM_CAPTURE, 0) < 0) {
|
||||
printf("Failed to open device '%s'\n", device_names[i]);
|
||||
goto _free_resources;
|
||||
}
|
||||
|
||||
// setup hw/sw params of alsa device...
|
||||
|
||||
poll_fd_count[i] = snd_pcm_poll_descriptors_count(pcm_handles[i]);
|
||||
if (poll_fd_count[i] <= 0) {
|
||||
printf("Invalid poll descriptors count for device '%s'\n", device_names[i]);
|
||||
goto _free_resources;
|
||||
}
|
||||
|
||||
total_poll_fd_count += poll_fd_count[i];
|
||||
}
|
||||
|
||||
ufds = malloc(sizeof(*ufds) * total_poll_fd_count);
|
||||
if (ufds == NULL) {
|
||||
printf("Not enough memory\n");
|
||||
goto _free_resources;
|
||||
}
|
||||
memset(ufds, 0, sizeof(*ufds) * total_poll_fd_count);
|
||||
|
||||
for (int i = 1; i < NR_OF_DEVICES; ++i) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
poll_fd_count_offset[i] += poll_fd_count[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* Get poll file descriptors */
|
||||
for (int i = 0; i< NR_OF_DEVICES; ++i) {
|
||||
if ((error = snd_pcm_poll_descriptors(pcm_handles[i], ufds + poll_fd_count_offset[i], poll_fd_count[i])) < 0) {
|
||||
printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(error));
|
||||
goto _free_resources;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start readers */
|
||||
for (int i = 0; i < NR_OF_DEVICES; i++) {
|
||||
if (snd_pcm_start(pcm_handles[i]) < 0) {
|
||||
printf("Failed to start pcm '%s'", device_names[i]);
|
||||
goto _free_resources;
|
||||
}
|
||||
}
|
||||
|
||||
char black_hole[SAMPLE_BUFFER_SIZE] = {0};
|
||||
|
||||
while (1) {
|
||||
unsigned short revents = 0;
|
||||
|
||||
poll(ufds, total_poll_fd_count, -1);
|
||||
|
||||
for (int i = 0; i < NR_OF_DEVICES; i++) {
|
||||
revents = 0;
|
||||
snd_pcm_poll_descriptors_revents(pcm_handles[i], ufds + poll_fd_count_offset[i], poll_fd_count[i], &revents);
|
||||
if (revents & POLLERR) {
|
||||
printf("ERROR:Failed to poll device %s\n", device_names[i]);
|
||||
break;
|
||||
} else if (revents & POLLIN) {
|
||||
error = snd_pcm_readi(pcm_handles[i], black_hole, AUDIO_NR_OF_SAMPLES);
|
||||
if (error < 0) {
|
||||
// xrun recovery
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("Reader done...\n");
|
||||
|
||||
for (int i = 0; i < NR_OF_DEVICES; ++i) {
|
||||
snd_pcm_drop(pcm_handles[i]);
|
||||
snd_pcm_close(pcm_handles[i]);
|
||||
}
|
||||
|
||||
_free_resources:
|
||||
// clean up your shit
|
||||
}
|
||||
|
||||
static void *write_thread_func(void *cookie)
|
||||
{
|
||||
int error = 0;
|
||||
struct pollfd *ufds = NULL;
|
||||
snd_pcm_t *pcm_handles[NR_OF_DEVICES];
|
||||
int poll_fd_count[NR_OF_DEVICES] = { 0 };
|
||||
int total_poll_fd_count = 0;
|
||||
int poll_fd_count_offset[NR_OF_DEVICES] = { 0 };
|
||||
|
||||
for (int i = 0; i< NR_OF_DEVICES; ++i) {
|
||||
if (snd_pcm_open(&pcm_handles[i], device_names[i], SND_PCM_STREAM_PLAYBACK, 0) < 0) {
|
||||
printf("Failed to open device '%s'\n", device_names[i]);
|
||||
goto _free_resources;
|
||||
}
|
||||
|
||||
// setup hw/sw params
|
||||
|
||||
poll_fd_count[i] = snd_pcm_poll_descriptors_count(pcm_handles[i]);
|
||||
if (poll_fd_count[i] <= 0) {
|
||||
printf("Invalid poll descriptors count for device '%s'\n", device_names[i]);
|
||||
goto _free_resources;
|
||||
}
|
||||
|
||||
total_poll_fd_count += poll_fd_count[i];
|
||||
}
|
||||
|
||||
ufds = malloc(sizeof(*ufds) * total_poll_fd_count);
|
||||
if (ufds == NULL) {
|
||||
printf("Not enough memory\n");
|
||||
goto _free_resources;
|
||||
}
|
||||
memset(ufds, 0, sizeof(*ufds) * total_poll_fd_count);
|
||||
|
||||
for (int i = 1; i < NR_OF_DEVICES; ++i) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
poll_fd_count_offset[i] += poll_fd_count[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* Get poll file descriptors */
|
||||
for (int i = 0; i< NR_OF_DEVICES; ++i) {
|
||||
if ((error = snd_pcm_poll_descriptors(pcm_handles[i], ufds + poll_fd_count_offset[i], poll_fd_count[i])) < 0) {
|
||||
printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(error));
|
||||
goto _free_resources;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prefill */
|
||||
for (int i = 0; i < NR_OF_DEVICES; i++) {
|
||||
char mybuff[SAMPLE_BUFFER_SIZE * 2] = {0 };
|
||||
if (snd_pcm_writei(pcm_handles[i], mybuff, AUDIO_NR_OF_SAMPLES * 2) < 0 ) {
|
||||
printf("could not prefill\n");
|
||||
goto _free_resources;
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
unsigned short revents = 0;
|
||||
|
||||
poll(ufds, total_poll_fd_count, -1);
|
||||
|
||||
for (int i = 0; i < NR_OF_DEVICES; i++) {
|
||||
revents = 0;
|
||||
snd_pcm_poll_descriptors_revents(pcm_handles[i], ufds + poll_fd_count_offset[i], poll_fd_count[i], &revents);
|
||||
if (revents & POLLERR) {
|
||||
printf("ERROR:Failed to poll device '%s'\n", device_names[i]);
|
||||
|
||||
if (snd_pcm_state(pcm_handles[i]) == SND_PCM_STATE_XRUN ||
|
||||
snd_pcm_state(pcm_handles[i]) == SND_PCM_STATE_SUSPENDED) {
|
||||
error = snd_pcm_state(pcm_handles[i]) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
|
||||
if (xrun_recovery(pcm_handles[i], error) < 0) {
|
||||
printf("Write error: %s\n", snd_strerror(error));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("Wait for poll failed '%s'\n", device_names[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* _is_writer_done = true; */
|
||||
continue;
|
||||
} else if (revents & POLLOUT) {
|
||||
char *ptr = cbuffer_get_read_pointer(_loopback_buffers[i]);
|
||||
if (!ptr) {
|
||||
continue;
|
||||
}
|
||||
error = snd_pcm_writei(pcm_handles[i], ptr, AUDIO_NR_OF_SAMPLES);
|
||||
if (error < 0) {
|
||||
if (xrun_recovery(pcm_handles[i], error) < 0) {
|
||||
printf("Write error: %s\n", snd_strerror(error));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
/* printf("%d: Written %d samples in device %s\n", ++count, error, device_names[i]); */
|
||||
}
|
||||
cbuffer_signal_element_read(_loopback_buffers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("writer done...\n");
|
||||
|
||||
for (int i = 0; i < NR_OF_DEVICES; ++i) {
|
||||
snd_pcm_drain(pcm_handles[i]);
|
||||
snd_pcm_close(pcm_handles[i]);
|
||||
}
|
||||
|
||||
printf("free resources writer...\n");
|
||||
_free_resources:
|
||||
// clean up your shit
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
//TODO
|
||||
pthread_t write_thread;
|
||||
pthread_t read_thread;
|
||||
int error = 0;
|
||||
|
||||
printf("Start playback/record with following devices:\n");
|
||||
for (int i = 0; i < NR_OF_DEVICES; ++i) {
|
||||
printf("%s\n", device_names[i]);
|
||||
}
|
||||
|
||||
if (pthread_create(&read_thread, NULL, read_thread_func, NULL) < 0) {
|
||||
printf("Failed to create reader thread");
|
||||
goto _free_resources;
|
||||
}
|
||||
|
||||
if (pthread_create(&write_thread, NULL, write_thread_func, NULL) < 0) {
|
||||
printf("Failed to create reader thread");
|
||||
goto _free_resources;
|
||||
}
|
||||
|
||||
pthread_join(write_thread, NULL);
|
||||
pthread_join(read_thread, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue