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.
This commit is contained in:
Odin Omdal Hørthe 2011-09-13 02:42:23 +02:00 committed by Ichthyostega
parent 37384f1b68
commit 29394345af
2 changed files with 175 additions and 0 deletions

148
src/tool/alsa.c Normal file
View file

@ -0,0 +1,148 @@
#include <err.h>
#include <math.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
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);
}

27
src/tool/main.c Normal file
View file

@ -0,0 +1,27 @@
#include <unistd.h>
#include <stdint.h>
#include "alsa.c"
#define SAMPLE_RATE 44100
int16_t quiet[SAMPLE_RATE], noisy[SAMPLE_RATE];
void main () {
for (int i=0; i<SAMPLE_RATE; i++)
{
quiet[i] = 0;
noisy[i] = i%30000;
}
audio_start(44100, 2);
for (int i=0; i<10; i++)
{
audio_write(noisy, SAMPLE_RATE);
printf("=================================\n");
audio_write(quiet, SAMPLE_RATE);
printf("\n");
}
audio_stop();
}