]> git.sesse.net Git - pitch/blob - linux_audio.cpp
Make the ALSA reader handle and recover from underruns.
[pitch] / linux_audio.cpp
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <math.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <sys/ioctl.h>
8 #include <linux/soundcard.h>
9 #include <alsa/asoundlib.h>
10 #include "linux_audio.h"
11
12 snd_pcm_t *get_dsp_handle(int sample_rate)
13 {
14         int err;
15         snd_pcm_t *handle;
16         snd_pcm_hw_params_t *hw_params;
17
18         if ((err = snd_pcm_open(&handle, "plughw:0,0", SND_PCM_STREAM_CAPTURE, 0)) < 0) {
19                 fprintf(stderr, "cannot open audio device plughw:0,0: %s\n", 
20                         snd_strerror(err));
21                 exit(1);
22         }
23
24         if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
25                 fprintf(stderr, "cannot allocate hardware parameter structure: %s\n",
26                         snd_strerror(err));
27                 exit(1);
28         }
29
30         if ((err = snd_pcm_hw_params_any(handle, hw_params)) < 0) {
31                 fprintf(stderr, "cannot initialize hardware parameter structure: %s\n",
32                         snd_strerror(err));
33                 exit(1);
34         }
35
36         if ((err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
37                 fprintf(stderr, "cannot set access type: %s\n",
38                         snd_strerror(err));
39                 exit(1);
40         }
41
42         if ((err = snd_pcm_hw_params_set_format(handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {  // FIXME
43                 fprintf(stderr, "cannot set sample format: %s\n",
44                         snd_strerror(err));
45                 exit(1);
46         }
47
48         if ((err = snd_pcm_hw_params_set_rate(handle, hw_params, sample_rate, 0)) < 0) {
49                 fprintf(stderr, "cannot set sample rate: %s\n",
50                         snd_strerror(err));
51                 exit(1);
52         }
53
54         if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, 1)) < 0) {
55                 fprintf(stderr, "cannot set channel count: %s\n",
56                         snd_strerror(err));
57                 exit(1);
58         }
59
60         if ((err = snd_pcm_hw_params(handle, hw_params)) < 0) {
61                 fprintf(stderr, "cannot set parameters: %s\n",
62                         snd_strerror(err));
63                 exit(1);
64         }
65
66         snd_pcm_hw_params_free(hw_params);
67
68         if ((err = snd_pcm_prepare(handle)) < 0) {
69                 fprintf(stderr, "cannot prepare audio interface for use: %s\n",
70                         snd_strerror(err));
71                 exit(1);
72         }
73
74         return handle;
75 }
76
77 #if 1
78 void read_chunk(snd_pcm_t *handle, short *in, unsigned num_samples)
79 {
80         int ret;
81         int samples_left = num_samples;
82
83         while (samples_left > 0) {
84                 ret = snd_pcm_readi(handle, in, samples_left);
85                 if (ret == 0) {
86                         printf("EOF\n");
87                         exit(0);
88                 }
89                 if (ret == -EPIPE) {
90                         fprintf(stderr, "ALSA underrun\n");
91                         snd_pcm_prepare(handle);
92                         continue;
93                 }
94                 if (ret < 0) {
95                         fprintf(stderr, "snd_pcm_readi: %s\n", snd_strerror(ret));
96                         exit(1);
97                 }
98                 in += ret;
99                 samples_left -= ret;
100         }
101 }
102 #else
103 // make a pure 440hz sine for testing
104 void read_chunk(snd_pcm_t *handle, short *in, unsigned num_samples)
105 {
106         static double theta = 0.0;
107         for (unsigned i = 0; i < num_samples; ++i) {
108                 in[i] = 32768.0 * cos(theta);
109                 theta += 2.0 * M_PI * 440.0 / double(SAMPLE_RATE);
110         }
111 }
112 #endif
113
114 void write_sine(int dsp_fd, double freq, unsigned num_samples, unsigned sample_rate) 
115 {
116         static double theta = 0.0;
117         short buf[num_samples];
118         
119         for (unsigned i = 0; i < num_samples; ++i) {
120                 buf[i] = short(cos(theta) * 16384.0);
121                 theta += 2.0 * M_PI * freq / double(sample_rate);
122         }
123
124         write(dsp_fd, buf, num_samples * sizeof(short));
125 }
126