1 /*
2 * uVoice audio hardware adapt layer
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <string.h>
9
10 #include <uvoice_audio.h>
11
12 #ifndef UVOICE_PCM_NO_ALSA
13 #include <alsa/asoundlib.h>
14
alsa_pcm_open(struct pcm_device * pcm)15 static int alsa_pcm_open(struct pcm_device *pcm)
16 {
17 struct pcm_config *config;
18 snd_pcm_t *pcm_handle;
19 snd_pcm_stream_t stream;
20 snd_pcm_hw_params_t *hw_params;
21 snd_pcm_sw_params_t *sw_params;
22 snd_pcm_uframes_t period_size;
23 snd_pcm_uframes_t buffer_size;
24 snd_pcm_uframes_t start_threshold;
25 snd_pcm_access_t access;
26 snd_pcm_format_t format;
27 unsigned int sample_rate;
28 unsigned int channels;
29 unsigned int periods;
30 unsigned int buffer_time;
31 unsigned int period_time;
32 unsigned int period_count;
33 int dir = 0;
34 int ret;
35
36 config = &pcm->config;
37
38 if (pcm->dir == PCM_OUT)
39 stream = SND_PCM_STREAM_PLAYBACK;
40 else if (pcm->dir == PCM_IN)
41 stream = SND_PCM_STREAM_CAPTURE;
42 else
43 return -1;
44
45 ret = snd_pcm_open(&pcm_handle, "default", stream, 0);
46 if (ret < 0) {
47 snd_err("open pcm failed %d!\n", ret);
48 goto __exit;
49 }
50
51 ret = snd_pcm_hw_params_malloc(&hw_params);
52 if (ret < 0) {
53 snd_err("alloc hw params failed %d!\n", ret);
54 goto __exit1;
55 }
56
57 ret = snd_pcm_hw_params_any(pcm_handle, hw_params);
58 if (ret < 0) {
59 snd_err("init hw params failed %d!\n", ret);
60 goto __exit2;
61 }
62
63 access = SND_PCM_ACCESS_RW_INTERLEAVED;
64 ret = snd_pcm_hw_params_set_access(pcm_handle, hw_params, access);
65 if (ret < 0) {
66 snd_err("set access failed %d!\n", ret);
67 goto __exit2;
68 }
69
70 switch (config->format) {
71 case PCM_FORMAT_S16_LE:
72 format = SND_PCM_FORMAT_S16_LE;
73 break;
74 case PCM_FORMAT_S24_LE:
75 format = SND_PCM_FORMAT_S24_LE;
76 break;
77 case PCM_FORMAT_S24_3LE:
78 format = SND_PCM_FORMAT_S24_3LE;
79 break;
80 case PCM_FORMAT_S8:
81 format = SND_PCM_FORMAT_S8;
82 break;
83 default:
84 ret = -1;
85 snd_err("unsupport format %d\n", config->format);
86 goto __exit2;
87 }
88
89 ret = snd_pcm_hw_params_set_format(pcm_handle, hw_params, format);
90 if (ret < 0) {
91 snd_err("set format failed %d!\n", ret);
92 goto __exit2;
93 }
94
95 sample_rate = config->rate;
96 dir = 0;
97 ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, &sample_rate, &dir);
98 if (ret < 0) {
99 snd_err("set sample rate failed %d!\n", ret);
100 goto __exit2;
101 }
102
103 channels = config->channels;
104 ret = snd_pcm_hw_params_set_channels(pcm_handle, hw_params, channels);
105 if (ret < 0) {
106 snd_err("set channels failed %d!\n", ret);
107 goto __exit2;
108 }
109
110 period_size = config->period_size;
111 dir = 0;
112 ret = snd_pcm_hw_params_set_period_size_near(pcm_handle, hw_params, &period_size, &dir);
113 if (ret < 0) {
114 snd_err("set period size failed %d!\n", ret);
115 goto __exit2;
116 }
117
118 dir = 0;
119 ret = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
120 if (ret < 0) {
121 snd_err("get period size failed %d!\n", ret);
122 goto __exit2;
123 }
124 snd_info("period_size %lu\n", period_size);
125
126 periods = config->period_count;
127 buffer_size = period_size * periods;
128 ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hw_params, &buffer_size);
129 if (ret < 0) {
130 snd_err("set buffer size failed %d!\n", ret);
131 goto __exit2;
132 }
133
134 ret = snd_pcm_hw_params(pcm_handle, hw_params);
135 if (ret < 0) {
136 snd_err("set hw params failed %d!\n", ret);
137 goto __exit2;
138 }
139
140 ret = snd_pcm_hw_params_get_channels(hw_params, &channels);
141 if (ret < 0) {
142 snd_err("get channels failed %d!\n", ret);
143 goto __exit2;
144 }
145 snd_info("channels %u\n", channels);
146
147 dir = 0;
148 ret = snd_pcm_hw_params_get_rate(hw_params, &sample_rate, &dir);
149 if (ret < 0) {
150 snd_err("get rate failed %d!\n", ret);
151 goto __exit2;
152 }
153 config->rate = sample_rate;
154 snd_info("sample_rate %u\n", sample_rate);
155
156 ret = snd_pcm_hw_params_get_sbits(hw_params);
157 if (ret < 0) {
158 snd_err("get sbits failed !\n");
159 goto __exit2;
160 }
161 snd_info("sbits %d\n", ret);
162
163 ret = snd_pcm_hw_params_get_format(hw_params, &format);
164 if (ret < 0) {
165 snd_err("get format failed !\n");
166 goto __exit2;
167 }
168 snd_info("format %d\n", format);
169
170 ret = snd_pcm_hw_params_get_access(hw_params, &access);
171 if (ret < 0) {
172 snd_err("get access failed !\n");
173 goto __exit2;
174 }
175 snd_info("access %d\n", access);
176
177 periods = 0;
178 dir = 0;
179 ret = snd_pcm_hw_params_get_periods(hw_params, &periods, &dir);
180 if (ret < 0) {
181 snd_err("get periods failed %d!\n", ret);
182 goto __exit2;
183 }
184 config->period_count = periods;
185 snd_info("periods %u\n", periods);
186
187 ret = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
188 if (ret < 0) {
189 snd_err("get buffer size failed %d!\n", ret);
190 goto __exit2;
191 }
192 snd_info("buffer_size %lu\n", buffer_size);
193
194 ret = snd_pcm_sw_params_malloc(&sw_params);
195 if (ret < 0) {
196 snd_err("alloc sw params failed %d!\n", ret);
197 goto __exit1;
198 }
199
200 ret = snd_pcm_sw_params_current(pcm_handle, sw_params);
201 if (ret < 0) {
202 snd_err("get sw params failed %d!\n", ret);
203 goto __exit3;
204 }
205
206 start_threshold = buffer_size;
207 ret = snd_pcm_sw_params_set_start_threshold(pcm_handle, sw_params, start_threshold);
208 if (ret < 0) {
209 snd_err("set start threshold failed %d!\n", ret);
210 goto __exit3;
211 }
212
213 ret = snd_pcm_sw_params(pcm_handle, sw_params);
214 if (ret < 0) {
215 snd_err("set sw params failed %d!\n", ret);
216 goto __exit3;
217 }
218
219 ret = snd_pcm_sw_params_get_start_threshold(sw_params, &start_threshold);
220 if (ret < 0) {
221 snd_err("get start threshold failed %d!\n", ret);
222 goto __exit3;
223 }
224 snd_info("start_threshold %lu\n", start_threshold);
225
226 snd_pcm_sw_params_free(sw_params);
227 snd_pcm_hw_params_free(hw_params);
228 pcm->private_data = pcm_handle;
229 return 0;
230
231 __exit3:
232 snd_pcm_sw_params_free(sw_params);
233 __exit2:
234 snd_pcm_hw_params_free(hw_params);
235 __exit1:
236 snd_pcm_close(pcm_handle);
237 __exit:
238 return -1;
239 }
240
alsa_pcm_drain(struct pcm_device * pcm)241 static int alsa_pcm_drain(struct pcm_device *pcm)
242 {
243 snd_pcm_t *pcm_handle;
244
245 pcm_handle = (snd_pcm_t *)pcm->private_data;
246 if (!pcm_handle) {
247 snd_err("pcm_handle null !\n");
248 return -1;
249 }
250
251 return snd_pcm_drain(pcm_handle);
252 }
253
alsa_pcm_close(struct pcm_device * pcm)254 static int alsa_pcm_close(struct pcm_device *pcm)
255 {
256 snd_pcm_t *pcm_handle;
257
258 pcm_handle = (snd_pcm_t *)pcm->private_data;
259 if (!pcm_handle) {
260 snd_err("pcm_handle null !\n");
261 return -1;
262 }
263
264 return snd_pcm_close(pcm_handle);
265 }
266
alsa_pcm_read(struct pcm_device * pcm,uint8_t * buffer,int nbytes)267 static int alsa_pcm_read(struct pcm_device *pcm, uint8_t *buffer, int nbytes)
268 {
269 snd_pcm_t *pcm_handle;
270 snd_pcm_uframes_t frames;
271 int ret;
272
273 pcm_handle = (snd_pcm_t *)pcm->private_data;
274 if (!pcm_handle) {
275 snd_err("pcm_handle null !\n");
276 return -1;
277 }
278
279 frames = snd_pcm_bytes_to_frames(pcm_handle, nbytes);
280 while ((ret = snd_pcm_readi(pcm_handle, buffer, frames)) < 0) {
281 if (ret == -EPIPE) {
282 snd_err("overrun occurued\n");
283 snd_pcm_prepare(pcm_handle);
284 } else if (ret < 0) {
285 snd_err("writei error: %s\n", snd_strerror(ret));
286 }
287 }
288 return nbytes;
289 }
290
alsa_pcm_write(struct pcm_device * pcm,uint8_t * buffer,int nbytes)291 static int alsa_pcm_write(struct pcm_device *pcm, uint8_t *buffer, int nbytes)
292 {
293 snd_pcm_t *pcm_handle;
294 snd_pcm_uframes_t frames;
295 int ret;
296
297 pcm_handle = (snd_pcm_t *)pcm->private_data;
298 if (!pcm_handle) {
299 snd_err("pcm_handle null !\n");
300 return -1;
301 }
302
303 frames = snd_pcm_bytes_to_frames(pcm_handle, nbytes);
304
305 while ((ret = snd_pcm_writei(pcm_handle, buffer, frames)) < 0) {
306 if (ret == -EPIPE) {
307 snd_err("underrun occurued\n");
308 snd_pcm_prepare(pcm_handle);
309 } else if (ret < 0) {
310 snd_err("writei error: %s\n", snd_strerror(ret));
311 }
312 }
313 return nbytes;
314 }
315
316 #endif
317
uvoice_set_volume(snd_device_t device,int volume)318 int uvoice_set_volume(snd_device_t device, int volume)
319 {
320 return 0;
321 }
322
uvoice_set_path(struct pcm_device * pcm,snd_device_t device)323 int uvoice_set_path(struct pcm_device *pcm, snd_device_t device)
324 {
325 if (!pcm) {
326 snd_err("pcm null !\n");
327 return -1;
328 }
329
330 return 0;
331 }
332
uvoice_dev_mute(struct pcm_device * pcm,snd_device_t device,int mute)333 int uvoice_dev_mute(struct pcm_device *pcm, snd_device_t device, int mute)
334 {
335 if (!pcm) {
336 snd_err("pcm null !\n");
337 return -1;
338 }
339
340 return 0;
341 }
342
uvoice_pcm_setup(struct pcm_device * pcm)343 int uvoice_pcm_setup(struct pcm_device *pcm)
344 {
345 if (!pcm) {
346 snd_err("pcm null !\n");
347 return -1;
348 }
349
350 if (pcm->dir == PCM_OUT) {
351 pcm->config.period_count = 4;
352 pcm->config.period_size = 960;
353 // pcm->config.rate = 48000;
354 } else if (pcm->dir == PCM_IN) {
355 pcm->config.period_count = 4;
356 pcm->config.period_size = 640;
357 }
358
359 pcm->state = PCM_STATE_SETUP;
360 return 0;
361 }
362
uvoice_pcm_open(struct pcm_device * pcm)363 int uvoice_pcm_open(struct pcm_device *pcm)
364 {
365 #ifndef UVOICE_PCM_NO_ALSA
366 snd_pcm_t *pcm_handle;
367 struct pcm_config *config;
368 snd_pcm_hw_params_t *hw_params;
369 int ret;
370
371 if (!pcm) {
372 snd_err("pcm null !\n");
373 return -1;
374 }
375
376 if (pcm->state != PCM_STATE_SETUP) {
377 snd_err("pcm not setup !\n");
378 goto __exit;
379 }
380
381 config = &pcm->config;
382
383 snd_debug("rate %d channels %d format %d period_size %d period_count %d dir %u\n", config->rate, config->channels,
384 config->format, config->period_size, config->period_count, pcm->dir);
385
386 if (alsa_pcm_open(pcm)) {
387 snd_err("open alsa pcm failed !\n");
388 return -1;
389 }
390 #endif
391
392 pcm->state = PCM_STATE_OPEN;
393
394 __exit:
395 return 0;
396 }
397
uvoice_pcm_read(struct pcm_device * pcm,uint8_t * buffer,int nbytes)398 int uvoice_pcm_read(struct pcm_device *pcm, uint8_t *buffer, int nbytes)
399 {
400 int ret = 0;
401
402 if (!pcm) {
403 snd_err("pcm null !\n");
404 return -1;
405 }
406
407 if (pcm->state == PCM_STATE_OPEN)
408 pcm->state = PCM_STATE_RUNNING;
409
410 #ifndef UVOICE_PCM_NO_ALSA
411 if (pcm->state == PCM_STATE_RUNNING) {
412 ret = alsa_pcm_read(pcm, buffer, nbytes);
413 } else {
414 snd_err("pcm not running !\n");
415 return -1;
416 }
417 #endif
418
419 return ret;
420 }
421
uvoice_pcm_write(struct pcm_device * pcm,uint8_t * buffer,int nbytes)422 int uvoice_pcm_write(struct pcm_device *pcm, uint8_t *buffer, int nbytes)
423 {
424 int ret = 0;
425
426 if (!pcm) {
427 snd_err("pcm null !\n");
428 return -1;
429 }
430
431 if (pcm->state == PCM_STATE_OPEN)
432 pcm->state = PCM_STATE_RUNNING;
433
434 #ifndef UVOICE_PCM_NO_ALSA
435 if (pcm->state == PCM_STATE_RUNNING) {
436 ret = alsa_pcm_write(pcm, buffer, nbytes);
437 if (ret < 0) {
438 snd_err("write failed %d!\n", ret);
439 ret = -1;
440 }
441 } else {
442 snd_err("pcm not running !\n");
443 return -1;
444 }
445 #endif
446
447 return ret;
448 }
449
uvoice_pcm_silence(struct pcm_device * pcm)450 int uvoice_pcm_silence(struct pcm_device *pcm)
451 {
452 return 0;
453 }
454
uvoice_pcm_flush(struct pcm_device * pcm)455 int uvoice_pcm_flush(struct pcm_device *pcm)
456 {
457 if (!pcm) {
458 snd_err("pcm null !\n");
459 return -1;
460 }
461
462 #ifndef UVOICE_PCM_NO_ALSA
463
464 if (pcm->dir == PCM_OUT)
465 alsa_pcm_drain(pcm);
466 #endif
467 return 0;
468 }
469
uvoice_extpa_config(struct external_pa_info * info)470 int uvoice_extpa_config(struct external_pa_info *info)
471 {
472 return 0;
473 }
474
uvoice_pcm_init(void)475 int uvoice_pcm_init(void)
476 {
477 return 0;
478 }
479
uvoice_pcm_close(struct pcm_device * pcm)480 int uvoice_pcm_close(struct pcm_device *pcm)
481 {
482 if (!pcm) {
483 snd_err("pcm null !\n");
484 return -1;
485 }
486
487 if (pcm->state == PCM_STATE_CLOSED) {
488 snd_err("close already, ignore\n");
489 goto __exit;
490 }
491
492 #ifndef UVOICE_PCM_NO_ALSA
493 alsa_pcm_close(pcm);
494 #endif
495 pcm->state = PCM_STATE_CLOSED;
496 snd_debug("pcm close\n");
497
498 __exit:
499 return 0;
500 }