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 }