From 29394345af5d16bc87903a2a131ba96e2ea06b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odin=20Omdal=20H=C3=B8rthe?= Date: Tue, 13 Sep 2011 02:42:23 +0200 Subject: [PATCH] ALSA audio output experiment I think it's smart to rather use ALSA directly instead of PortAudio. ALSA is push AFAIK, and talking about it here at the hackspace, seems like the better choice. It's a bit lower level, but anyway everything speaks ALSA anyway. It's not like there's any reason to use PortAudio at all. It's just an extra abstraction. Coding for ALSA it'll also work with Pulseaudio and esd. Do people really use other sound systems than Pulseaudio, esd or plain ALSA? I can't think of it. I really the idea about building a small tool first. I'll do that. Also thought about making a small blikning cursor/text output, and syncing a BEEP-sound to that, so that I can test around with throwing in lots and lots of latency between "me" and the video, and try to sync it anyway. I should be able to read back from the sound card (or pulse audio underneath, it will just work with alsa as the abstraction) how long it takes for the bytes I'm pushing to reach the speakers, and do some buffer tuning on that. --- src/tool/alsa.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++ src/tool/main.c | 27 +++++++++ 2 files changed, 175 insertions(+) create mode 100644 src/tool/alsa.c create mode 100644 src/tool/main.c diff --git a/src/tool/alsa.c b/src/tool/alsa.c new file mode 100644 index 000000000..e6c4afbba --- /dev/null +++ b/src/tool/alsa.c @@ -0,0 +1,148 @@ +#include +#include +#include + +#include + +static snd_pcm_t* playback_handle = 0; +static snd_pcm_sw_params_t* sw_params = 0; +static snd_pcm_hw_params_t* hw_params = 0; +static snd_pcm_sframes_t buffer_size = 0; + +static snd_pcm_sframes_t written = 0; +static snd_pcm_sframes_t delay = 0; + +static unsigned int rate = 44100; + +static int audio_initialized = 0; + +size_t +audio_offset() +{ + snd_pcm_delay(playback_handle, &delay); + + return written - delay; +} + +void +audio_init() +{ + unsigned int buffer_time = 50000; + const char* device; + int err; + + if(audio_initialized) + return; + + audio_initialized = 1; + + device = getenv("ALSA_DEVICE"); + + if(!device) + device = "default"; + + if(0 > (err = snd_pcm_open(&playback_handle, device, + SND_PCM_STREAM_PLAYBACK, 0/*SND_PCM_NONBLOCK*/))) + errx(EXIT_FAILURE, "Audio: Cannot open device %s: %s", device, snd_strerror(err)); + + if(0 > (err = snd_pcm_sw_params_malloc(&sw_params))) + errx(EXIT_FAILURE, "Audio: Could not allocate software parameter structure: %s", + snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_malloc(&hw_params))) + errx(EXIT_FAILURE, "Audio: Could not allocate hardware parameter structure: %s", + snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_any(playback_handle, hw_params))) + errx(EXIT_FAILURE, "Audio: Could not initializa hardware parameters: %s", + snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_set_access(playback_handle, hw_params, + SND_PCM_ACCESS_RW_INTERLEAVED))) + errx(EXIT_FAILURE, "Audio: Could not set access type: %s", snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_set_format(playback_handle, hw_params, + SND_PCM_FORMAT_S16))) + errx(EXIT_FAILURE, "Audio: Could not set sample format to signed 16 bit " + "native endian: %s", snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, + &rate, 0))) + errx(EXIT_FAILURE, "Audio: Could not set sample rate %uHz: %s", rate, + snd_strerror(err)); + + if(0 > (err = snd_pcm_hw_params_set_channels(playback_handle, hw_params, 2))) + errx(EXIT_FAILURE, "Audio: Could not set channel count to %u: %s", + 2, snd_strerror(err)); + + snd_pcm_hw_params_set_buffer_time_near(playback_handle, hw_params, &buffer_time, 0); + + if(0 > (err = snd_pcm_hw_params(playback_handle, hw_params))) + errx(EXIT_FAILURE, "Audio: Could not set hardware parameters: %s", snd_strerror(err)); + + fprintf(stderr, "Buffer time is %.3f seconds\n", buffer_time / 1.0e6); + + if(0 > (err = snd_pcm_sw_params_current(playback_handle, sw_params))) + errx(EXIT_FAILURE, "Audio: Could not initialize software parameters: %s", + snd_strerror(err)); + + snd_pcm_sw_params_set_start_threshold(playback_handle, sw_params, 0); + snd_pcm_sw_params_set_avail_min(playback_handle, sw_params, 1024); + + snd_pcm_uframes_t min; + snd_pcm_sw_params_get_avail_min(sw_params, &min); + fprintf(stderr, "Minimum %u\n", (unsigned) min); + + if(0 > (err = snd_pcm_sw_params(playback_handle, sw_params))) + errx(EXIT_FAILURE, "Audio: Could not set software parameters: %s", + snd_strerror(err)); + + buffer_size = snd_pcm_avail_update(playback_handle); +} + +size_t +audio_write(const void* data, size_t amount) +{ + int err; + + amount /= 4; + + for(;;) + { + err = snd_pcm_writei(playback_handle, data, amount); + + if(err == -EAGAIN) + return 0; + + if(err < 0) + { + err = snd_pcm_recover(playback_handle, err, 0); + + if(err < 0) + errx(EXIT_FAILURE, "Audio playback failed: %s", strerror(-err)); + } + + + break; + } + + written += err; + + err *= 4; + + return err; +} + +void +audio_start(unsigned int rate, unsigned int channel_count) +{ + audio_init(); + + snd_pcm_prepare(playback_handle); +} + +void +audio_stop() +{ + snd_pcm_drain(playback_handle); +} diff --git a/src/tool/main.c b/src/tool/main.c new file mode 100644 index 000000000..4e6b1adc6 --- /dev/null +++ b/src/tool/main.c @@ -0,0 +1,27 @@ +#include +#include +#include "alsa.c" + +#define SAMPLE_RATE 44100 + +int16_t quiet[SAMPLE_RATE], noisy[SAMPLE_RATE]; + +void main () { + + for (int i=0; i