foostudio.cView |
---|
10 | 10 … | #include <unistd.h> |
11 | 11 … | #include "tune.h" |
12 | 12 … | #include "ccdl.h" |
13 | 13 … | |
| 14 … | +#define max(a,b) ((a) > (b) ? (a) : (b)) |
| 15 … | +#define min(a,b) ((a) < (b) ? (a) : (b)) |
| 16 … | + |
14 | 17 … | static unsigned int rate = 44100; |
15 | 18 … | static unsigned int channels = 1; |
16 | | -static unsigned int latency = 500000; |
| 19 … | +static unsigned int channels_in = 1; |
| 20 … | +static unsigned int latency = 50000; |
| 21 … | +static int resample = 1; |
| 22 … | + |
17 | 23 … | static snd_pcm_format_t format = SND_PCM_FORMAT_FLOAT; |
18 | 24 … | |
19 | 25 … | |
20 | 26 … | * Underrun and suspend recovery, from alsa-lib/test/pcm.c |
42 | 48 … | |
43 | 49 … | |
44 | 50 … | static void generate(struct tune *tune, |
45 | 51 … | const snd_pcm_channel_area_t *areas, |
46 | | - snd_pcm_uframes_t offset, |
47 | | - int count, double *_phase) |
| 52 … | + snd_pcm_uframes_t offset, int count, |
| 53 … | + float *frames_in, snd_pcm_uframes_t count_in, |
| 54 … | + double *_phase) |
48 | 55 … | { |
49 | 56 … | double phase = *_phase; |
50 | 57 … | double step = 1/(double)rate; |
51 | 58 … | unsigned char *samples[channels]; |
71 | 78 … | samples[chn] += offset * steps[chn]; |
72 | 79 … | } |
73 | 80 … | |
74 | 81 … | |
| 82 … | + size_t j = 0; |
75 | 83 … | while (count-- > 0) { |
76 | 84 … | union { |
77 | 85 … | float f; |
78 | 86 … | int i; |
79 | 87 … | } fval; |
80 | 88 … | int res, i; |
81 | 89 … | if (tune && tune->play) { |
82 | | - fval.f = tune->play(tune, phase); |
| 90 … | + float frame_in = j <= count_in ? frames_in[j] : 0; |
| 91 … | + fval.f = tune->play(tune, phase, frame_in); |
83 | 92 … | } else { |
84 | 93 … | fval.f = 0; |
85 | 94 … | } |
86 | 95 … | res = fval.i; |
101 | 110 … | |
102 | 111 … | if (phase >= max_phase) |
103 | 112 … | phase -= max_phase; |
104 | 113 … | */ |
| 114 … | + j++; |
105 | 115 … | } |
106 | 116 … | *_phase = phase; |
107 | 117 … | } |
108 | 118 … | |
| 119 … | +int pre_begin(snd_pcm_t *pcm, int *first, snd_pcm_uframes_t period_size) { |
| 120 … | + int err; |
| 121 … | + snd_pcm_state_t state = snd_pcm_state(pcm); |
| 122 … | + snd_pcm_sframes_t avail; |
109 | 123 … | |
| 124 … | + if (state == SND_PCM_STATE_XRUN) { |
| 125 … | + err = xrun_recovery(pcm, -EPIPE); |
| 126 … | + if (err < 0) { |
| 127 … | + errx(1, "XRUN recovery failed: %s", snd_strerror(err)); |
| 128 … | + } |
| 129 … | + *first = 1; |
| 130 … | + } else if (state == SND_PCM_STATE_SUSPENDED) { |
| 131 … | + err = xrun_recovery(pcm, -ESTRPIPE); |
| 132 … | + if (err < 0) { |
| 133 … | + errx(1, "SUSPEND recovery failed: %s", snd_strerror(err)); |
| 134 … | + } |
| 135 … | + } |
| 136 … | + avail = snd_pcm_avail_update(pcm); |
| 137 … | + if (avail < 0) { |
| 138 … | + err = xrun_recovery(pcm, avail); |
| 139 … | + if (err < 0) { |
| 140 … | + errx(1, "avail update failed: %s", snd_strerror(err)); |
| 141 … | + } |
| 142 … | + *first = 1; |
| 143 … | + return 1; |
| 144 … | + } |
| 145 … | + if ((snd_pcm_uframes_t)avail < period_size) { |
| 146 … | + if (*first) { |
| 147 … | + *first = 0; |
| 148 … | + err = snd_pcm_start(pcm); |
| 149 … | + if (err < 0) { |
| 150 … | + errx(1, "Start error: %s", snd_strerror(err)); |
| 151 … | + } |
| 152 … | + } else { |
| 153 … | + err = snd_pcm_wait(pcm, -1); |
| 154 … | + if (err < 0) { |
| 155 … | + if ((err = xrun_recovery(pcm, err)) < 0) { |
| 156 … | + errx(1, "snd_pcm_wait error: %s", snd_strerror(err)); |
| 157 … | + } |
| 158 … | + *first = 1; |
| 159 … | + } |
| 160 … | + } |
| 161 … | + return 1; |
| 162 … | + } |
| 163 … | + return 0; |
| 164 … | +} |
| 165 … | + |
110 | 166 … | int main(int argc, char *argv[]) |
111 | 167 … | { |
112 | 168 … | struct ccdl ccdl; |
113 | 169 … | int err; |
114 | | - snd_pcm_t *pcm_out; |
| 170 … | + snd_pcm_t *pcm_out, *pcm_in; |
115 | 171 … | const char *src_fname = "tune.c"; |
116 | 172 … | const char *device_out = "default"; |
| 173 … | + const char *device_in = "default"; |
117 | 174 … | |
118 | 175 … | if (argc > 1) src_fname = argv[1]; |
119 | 176 … | if (argc > 2) device_out = argv[2]; |
| 177 … | + if (argc > 3) device_in = argv[3]; |
120 | 178 … | |
121 | 179 … | ccdl_init(&ccdl, src_fname, "TUNE"); |
122 | 180 … | |
123 | 181 … | if (ccdl_watch(&ccdl)) { |
124 | 182 … | errx(1, "ccdl_watch"); |
125 | 183 … | } |
126 | 184 … | |
127 | | - if ((err = snd_pcm_open(&pcm_out, device_out, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { |
| 185 … | + err = snd_pcm_open(&pcm_out, device_out, SND_PCM_STREAM_PLAYBACK, 0); |
| 186 … | + if (err < 0) { |
128 | 187 … | warnx("Playback open error: %s", snd_strerror(err)); |
129 | 188 … | return 1; |
130 | 189 … | } |
131 | 190 … | |
| 191 … | + err = snd_pcm_open(&pcm_in, device_in, SND_PCM_STREAM_CAPTURE, 0); |
| 192 … | + if (err < 0) { |
| 193 … | + warnx("Capture open error: %s", snd_strerror(err)); |
| 194 … | + return 1; |
| 195 … | + } |
| 196 … | + |
132 | 197 … | if ((err = snd_pcm_set_params(pcm_out, |
133 | 198 … | format, |
134 | 199 … | SND_PCM_ACCESS_MMAP_INTERLEAVED, |
135 | 200 … | channels, |
136 | 201 … | rate, |
137 | | - 1, |
| 202 … | + resample, |
138 | 203 … | latency)) < 0) { |
139 | 204 … | warnx("Playback open error: %s", snd_strerror(err)); |
140 | 205 … | return 1; |
141 | 206 … | } |
142 | 207 … | |
143 | | - snd_pcm_uframes_t buffer_size; |
144 | | - snd_pcm_uframes_t period_size; |
145 | | - if ((err = snd_pcm_get_params(pcm_out, &buffer_size, &period_size)) < 0) { |
| 208 … | + if ((err = snd_pcm_set_params(pcm_in, |
| 209 … | + format, |
| 210 … | + SND_PCM_ACCESS_MMAP_INTERLEAVED, |
| 211 … | + channels_in, |
| 212 … | + rate, |
| 213 … | + resample, |
| 214 … | + latency)) < 0) { |
| 215 … | + warnx("Capture open error: %s", snd_strerror(err)); |
| 216 … | + return 1; |
| 217 … | + } |
| 218 … | + |
| 219 … | + snd_pcm_uframes_t buffer_size, buffer_size_in; |
| 220 … | + snd_pcm_uframes_t period_size, period_size_in, period_size_out; |
| 221 … | + if ((err = snd_pcm_get_params(pcm_out, &buffer_size, &period_size_out)) < 0) { |
146 | 222 … | warnx("Playback get params error: %s", snd_strerror(err)); |
147 | 223 … | return 1; |
148 | 224 … | } |
| 225 … | + if ((err = snd_pcm_get_params(pcm_in, &buffer_size_in, &period_size_in)) < 0) { |
| 226 … | + warnx("Capture get params error: %s", snd_strerror(err)); |
| 227 … | + return 1; |
| 228 … | + } |
| 229 … | + period_size = min(period_size_in, period_size_out); |
149 | 230 … | |
| 231 … | + |
| 232 … | + if (period_size != period_size_in) { |
| 233 … | + errx(1, "playback and capture have different period sizes: %u %u", (unsigned int)period_size, (unsigned int)period_size_in); |
| 234 … | + } |
| 235 … | + */ |
| 236 … | + |
150 | 237 … | double phase = 0; |
151 | 238 … | const snd_pcm_channel_area_t *my_areas; |
152 | 239 … | snd_pcm_uframes_t offset, frames, size; |
153 | | - snd_pcm_sframes_t avail, commitres; |
154 | | - snd_pcm_state_t state; |
155 | | - int first = 1; |
| 240 … | + snd_pcm_sframes_t commitres, frames_read; |
| 241 … | + int first = 1, first_in = 1; |
| 242 … | + float frames_in[period_size]; |
156 | 243 … | |
157 | 244 … | |
158 | 245 … | while (1) { |
159 | | - state = snd_pcm_state(pcm_out); |
160 | | - if (state == SND_PCM_STATE_XRUN) { |
161 | | - err = xrun_recovery(pcm_out, -EPIPE); |
162 | | - if (err < 0) { |
163 | | - printf("XRUN recovery failed: %s\n", snd_strerror(err)); |
164 | | - return err; |
| 246 … | + while (pre_begin(pcm_out, &first, period_size)); |
| 247 … | + while (pre_begin(pcm_in, &first_in, period_size)); |
| 248 … | + |
| 249 … | + frames_read = snd_pcm_mmap_readi(pcm_in, frames_in, period_size); |
| 250 … | + if (frames_read < 0) { |
| 251 … | + if ((err = xrun_recovery(pcm_in, err)) < 0) { |
| 252 … | + warnx("read error: %s", snd_strerror(err)); |
165 | 253 … | } |
166 | | - first = 1; |
167 | | - } else if (state == SND_PCM_STATE_SUSPENDED) { |
168 | | - err = xrun_recovery(pcm_out, -ESTRPIPE); |
169 | | - if (err < 0) { |
170 | | - printf("SUSPEND recovery failed: %s\n", snd_strerror(err)); |
171 | | - return err; |
172 | | - } |
| 254 … | + first_in = 1; |
173 | 255 … | } |
174 | | - avail = snd_pcm_avail_update(pcm_out); |
175 | | - if (avail < 0) { |
176 | | - err = xrun_recovery(pcm_out, avail); |
177 | | - if (err < 0) { |
178 | | - printf("avail update failed: %s\n", snd_strerror(err)); |
179 | | - return err; |
180 | | - } |
181 | | - first = 1; |
182 | | - continue; |
183 | | - } |
184 | | - if ((snd_pcm_uframes_t)avail < period_size) { |
185 | | - if (first) { |
186 | | - first = 0; |
187 | | - err = snd_pcm_start(pcm_out); |
188 | | - if (err < 0) { |
189 | | - printf("Start error: %s\n", snd_strerror(err)); |
190 | | - exit(EXIT_FAILURE); |
191 | | - } |
192 | | - } else { |
193 | | - err = snd_pcm_wait(pcm_out, -1); |
194 | | - if (err < 0) { |
195 | | - if ((err = xrun_recovery(pcm_out, err)) < 0) { |
196 | | - printf("snd_pcm_wait error: %s\n", snd_strerror(err)); |
197 | | - exit(EXIT_FAILURE); |
198 | | - } |
199 | | - first = 1; |
200 | | - } |
201 | | - } |
202 | | - continue; |
203 | | - } |
204 | | - size = period_size; |
205 | | - while (size > 0) { |
| 256 … | + |
| 257 … | + for (size = period_size; size > 0; size -= frames) { |
206 | 258 … | frames = size; |
| 259 … | + |
207 | 260 … | err = snd_pcm_mmap_begin(pcm_out, &my_areas, &offset, &frames); |
208 | 261 … | if (err < 0) { |
209 | 262 … | if ((err = xrun_recovery(pcm_out, err)) < 0) { |
210 | | - printf("MMAP begin avail error: %s\n", snd_strerror(err)); |
211 | | - exit(EXIT_FAILURE); |
| 263 … | + errx(1, "MMAP begin avail error: %s", snd_strerror(err)); |
|
212 | 264 … | } |
213 | 265 … | first = 1; |
214 | 266 … | } |
| 267 … | + |
| 268 … | + err = snd_pcm_mmap_begin(pcm_in, &my_areas_in, &offset, &frames); |
| 269 … | + if (err < 0) { |
| 270 … | + if ((err = xrun_recovery(pcm_in, err)) < 0) { |
| 271 … | + warnx("read error: %s", snd_strerror(err)); |
| 272 … | + } |
| 273 … | + first = 1; |
| 274 … | + } |
| 275 … | + */ |
| 276 … | + |
215 | 277 … | struct tune *tune = ccdl_get(&ccdl); |
216 | | - generate(tune, my_areas, offset, frames, &phase); |
| 278 … | + generate(tune, my_areas, offset, frames, frames_in, frames_read, &phase); |
217 | 279 … | commitres = snd_pcm_mmap_commit(pcm_out, offset, frames); |
218 | 280 … | if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { |
219 | 281 … | if ((err = xrun_recovery(pcm_out, commitres >= 0 ? -EPIPE : commitres)) < 0) { |
220 | | - printf("MMAP commit error: %s\n", snd_strerror(err)); |
221 | | - exit(EXIT_FAILURE); |
| 282 … | + errx(1, "MMAP commit error: %s", snd_strerror(err)); |
222 | 283 … | } |
223 | 284 … | first = 1; |
224 | 285 … | } |
225 | | - size -= frames; |
226 | 286 … | } |
227 | 287 … | |
228 | 288 … | } |
229 | 289 … | |