]> git.sesse.net Git - pitch/blobdiff - linux_audio.cpp
Make the ALSA reader handle and recover from underruns.
[pitch] / linux_audio.cpp
index b76909e7588fa2048bfa6b7ba8bf2e0abb257847..c07e6bb289a543419d608063796ada457157c66c 100644 (file)
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <linux/soundcard.h>
-
+#include <alsa/asoundlib.h>
 #include "linux_audio.h"
 
-int get_dsp_fd(int sample_rate, int fft_length, int overlap)
+snd_pcm_t *get_dsp_handle(int sample_rate)
 {
-       int fd = open("/dev/dsp", O_RDWR);
-       if (fd == -1) {
-               perror("/dev/dsp");
+       int err;
+       snd_pcm_t *handle;
+       snd_pcm_hw_params_t *hw_params;
+
+       if ((err = snd_pcm_open(&handle, "plughw:0,0", SND_PCM_STREAM_CAPTURE, 0)) < 0) {
+               fprintf(stderr, "cannot open audio device plughw:0,0: %s\n", 
+                       snd_strerror(err));
                exit(1);
        }
-       
-       ioctl(3, SNDCTL_DSP_RESET, 0);
-       
-       int fmt = AFMT_S16_LE;   // FIXME
-       ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
 
-       int chan = 1;
-       ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &chan);
-       
-       int rate = sample_rate;
-       ioctl(fd, SOUND_PCM_WRITE_RATE, &rate);
+       if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
+               fprintf(stderr, "cannot allocate hardware parameter structure: %s\n",
+                       snd_strerror(err));
+               exit(1);
+       }
 
-       int max_fragments = 2;
-       int frag_shift = ffs(fft_length / overlap) - 1;
-       int fragments = (max_fragments << 16) | frag_shift;
-        ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fragments);
-       
-       ioctl(3, SNDCTL_DSP_SYNC, 0);
-       
-       return fd;
+       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, SND_PCM_FORMAT_S16_LE)) < 0) {  // FIXME
+               fprintf(stderr, "cannot set sample format: %s\n",
+                       snd_strerror(err));
+               exit(1);
+       }
+
+       if ((err = snd_pcm_hw_params_set_rate(handle, hw_params, sample_rate, 0)) < 0) {
+               fprintf(stderr, "cannot set sample rate: %s\n",
+                       snd_strerror(err));
+               exit(1);
+       }
+
+       if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, 1)) < 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_prepare(handle)) < 0) {
+               fprintf(stderr, "cannot prepare audio interface for use: %s\n",
+                       snd_strerror(err));
+               exit(1);
+       }
+
+       return handle;
 }
 
 #if 1
-void read_chunk(int fd, short *in, unsigned num_samples)
+void read_chunk(snd_pcm_t *handle, short *in, unsigned num_samples)
 {
        int ret;
+       int samples_left = num_samples;
 
-       ret = read(fd, in, num_samples * sizeof(short));
-       if (ret == 0) {
-               printf("EOF\n");
-               exit(0);
-       }
-
-       if (ret != int(num_samples * sizeof(short))) {
-               // blah
-               perror("read");
-               exit(1);
+       while (samples_left > 0) {
+               ret = snd_pcm_readi(handle, in, samples_left);
+               if (ret == 0) {
+                       printf("EOF\n");
+                       exit(0);
+               }
+               if (ret == -EPIPE) {
+                       fprintf(stderr, "ALSA underrun\n");
+                       snd_pcm_prepare(handle);
+                       continue;
+               }
+               if (ret < 0) {
+                       fprintf(stderr, "snd_pcm_readi: %s\n", snd_strerror(ret));
+                       exit(1);
+               }
+               in += ret;
+               samples_left -= ret;
        }
 }
 #else
 // make a pure 440hz sine for testing
-void read_chunk(int fd, short *in, unsigned num_samples)
+void read_chunk(snd_pcm_t *handle, short *in, unsigned num_samples)
 {
        static double theta = 0.0;
        for (unsigned i = 0; i < num_samples; ++i) {