1*1c60b9acSAndroid Build Coastguard Worker /*
2*1c60b9acSAndroid Build Coastguard Worker * alsa audio handling
3*1c60b9acSAndroid Build Coastguard Worker *
4*1c60b9acSAndroid Build Coastguard Worker * Written in 2010-2020 by Andy Green <[email protected]>
5*1c60b9acSAndroid Build Coastguard Worker *
6*1c60b9acSAndroid Build Coastguard Worker * This file is made available under the Creative Commons CC0 1.0
7*1c60b9acSAndroid Build Coastguard Worker * Universal Public Domain Dedication.
8*1c60b9acSAndroid Build Coastguard Worker */
9*1c60b9acSAndroid Build Coastguard Worker
10*1c60b9acSAndroid Build Coastguard Worker #include <libwebsockets.h>
11*1c60b9acSAndroid Build Coastguard Worker #include <string.h>
12*1c60b9acSAndroid Build Coastguard Worker #include <signal.h>
13*1c60b9acSAndroid Build Coastguard Worker #include <sys/types.h>
14*1c60b9acSAndroid Build Coastguard Worker #include <sys/stat.h>
15*1c60b9acSAndroid Build Coastguard Worker #include <fcntl.h>
16*1c60b9acSAndroid Build Coastguard Worker
17*1c60b9acSAndroid Build Coastguard Worker #include <alsa/asoundlib.h>
18*1c60b9acSAndroid Build Coastguard Worker #include <pv_porcupine.h>
19*1c60b9acSAndroid Build Coastguard Worker
20*1c60b9acSAndroid Build Coastguard Worker #include <mpg123.h>
21*1c60b9acSAndroid Build Coastguard Worker
22*1c60b9acSAndroid Build Coastguard Worker #include "private.h"
23*1c60b9acSAndroid Build Coastguard Worker
24*1c60b9acSAndroid Build Coastguard Worker extern struct lws_ss_handle *hss_avs_event, *hss_avs_sync;
25*1c60b9acSAndroid Build Coastguard Worker
26*1c60b9acSAndroid Build Coastguard Worker int
27*1c60b9acSAndroid Build Coastguard Worker avs_query_start(struct lws_context *context);
28*1c60b9acSAndroid Build Coastguard Worker
29*1c60b9acSAndroid Build Coastguard Worker enum {
30*1c60b9acSAndroid Build Coastguard Worker MODE_IDLE,
31*1c60b9acSAndroid Build Coastguard Worker MODE_CAPTURING,
32*1c60b9acSAndroid Build Coastguard Worker MODE_PLAYING
33*1c60b9acSAndroid Build Coastguard Worker };
34*1c60b9acSAndroid Build Coastguard Worker
35*1c60b9acSAndroid Build Coastguard Worker struct raw_vhd {
36*1c60b9acSAndroid Build Coastguard Worker int16_t p[8 * 1024]; /* 500ms at 16kHz 16-bit PCM */
37*1c60b9acSAndroid Build Coastguard Worker pv_porcupine_object_t *porc;
38*1c60b9acSAndroid Build Coastguard Worker snd_pcm_t *pcm_capture;
39*1c60b9acSAndroid Build Coastguard Worker snd_pcm_t *pcm_playback;
40*1c60b9acSAndroid Build Coastguard Worker snd_pcm_hw_params_t *params;
41*1c60b9acSAndroid Build Coastguard Worker snd_pcm_uframes_t frames;
42*1c60b9acSAndroid Build Coastguard Worker int16_t *porcbuf;
43*1c60b9acSAndroid Build Coastguard Worker
44*1c60b9acSAndroid Build Coastguard Worker mpg123_handle *mh;
45*1c60b9acSAndroid Build Coastguard Worker
46*1c60b9acSAndroid Build Coastguard Worker mp3_done_cb done_cb;
47*1c60b9acSAndroid Build Coastguard Worker void *opaque;
48*1c60b9acSAndroid Build Coastguard Worker
49*1c60b9acSAndroid Build Coastguard Worker int mode;
50*1c60b9acSAndroid Build Coastguard Worker int rate;
51*1c60b9acSAndroid Build Coastguard Worker
52*1c60b9acSAndroid Build Coastguard Worker int porc_spf;
53*1c60b9acSAndroid Build Coastguard Worker int filefd;
54*1c60b9acSAndroid Build Coastguard Worker int rpos;
55*1c60b9acSAndroid Build Coastguard Worker int wpos;
56*1c60b9acSAndroid Build Coastguard Worker int porcpos;
57*1c60b9acSAndroid Build Coastguard Worker int npos;
58*1c60b9acSAndroid Build Coastguard Worker int times;
59*1c60b9acSAndroid Build Coastguard Worker int quietcount;
60*1c60b9acSAndroid Build Coastguard Worker int anycount;
61*1c60b9acSAndroid Build Coastguard Worker
62*1c60b9acSAndroid Build Coastguard Worker int wplay;
63*1c60b9acSAndroid Build Coastguard Worker int rplay;
64*1c60b9acSAndroid Build Coastguard Worker
65*1c60b9acSAndroid Build Coastguard Worker char last_wake_detect;
66*1c60b9acSAndroid Build Coastguard Worker char destroy_mh_on_drain;
67*1c60b9acSAndroid Build Coastguard Worker };
68*1c60b9acSAndroid Build Coastguard Worker
69*1c60b9acSAndroid Build Coastguard Worker static struct raw_vhd *avhd;
70*1c60b9acSAndroid Build Coastguard Worker
71*1c60b9acSAndroid Build Coastguard Worker /*
72*1c60b9acSAndroid Build Coastguard Worker * called from alexa.c to grab the next chunk of audio capture buffer
73*1c60b9acSAndroid Build Coastguard Worker * for upload
74*1c60b9acSAndroid Build Coastguard Worker */
75*1c60b9acSAndroid Build Coastguard Worker
76*1c60b9acSAndroid Build Coastguard Worker int
spool_capture(uint8_t * buf,size_t len)77*1c60b9acSAndroid Build Coastguard Worker spool_capture(uint8_t *buf, size_t len)
78*1c60b9acSAndroid Build Coastguard Worker {
79*1c60b9acSAndroid Build Coastguard Worker int16_t *sam = (int16_t *)buf;
80*1c60b9acSAndroid Build Coastguard Worker size_t s, os;
81*1c60b9acSAndroid Build Coastguard Worker
82*1c60b9acSAndroid Build Coastguard Worker if (avhd->mode != MODE_CAPTURING)
83*1c60b9acSAndroid Build Coastguard Worker return -1;
84*1c60b9acSAndroid Build Coastguard Worker
85*1c60b9acSAndroid Build Coastguard Worker os = s = len / 2;
86*1c60b9acSAndroid Build Coastguard Worker
87*1c60b9acSAndroid Build Coastguard Worker while (s && avhd->wpos != avhd->npos) {
88*1c60b9acSAndroid Build Coastguard Worker *sam++ = avhd->p[avhd->npos];
89*1c60b9acSAndroid Build Coastguard Worker avhd->npos = (avhd->npos + 1) % LWS_ARRAY_SIZE(avhd->p);
90*1c60b9acSAndroid Build Coastguard Worker s--;
91*1c60b9acSAndroid Build Coastguard Worker }
92*1c60b9acSAndroid Build Coastguard Worker
93*1c60b9acSAndroid Build Coastguard Worker lwsl_info("Copied %d samples (%d %d)\n", (int)(os - s),
94*1c60b9acSAndroid Build Coastguard Worker avhd->wpos, avhd->npos);
95*1c60b9acSAndroid Build Coastguard Worker
96*1c60b9acSAndroid Build Coastguard Worker return (os - s) * 2;
97*1c60b9acSAndroid Build Coastguard Worker }
98*1c60b9acSAndroid Build Coastguard Worker
99*1c60b9acSAndroid Build Coastguard Worker /*
100*1c60b9acSAndroid Build Coastguard Worker * Called from alexa.c to control when the mp3 playback should begin and end
101*1c60b9acSAndroid Build Coastguard Worker */
102*1c60b9acSAndroid Build Coastguard Worker
103*1c60b9acSAndroid Build Coastguard Worker int
play_mp3(mpg123_handle * mh,mp3_done_cb cb,void * opaque)104*1c60b9acSAndroid Build Coastguard Worker play_mp3(mpg123_handle *mh, mp3_done_cb cb, void *opaque)
105*1c60b9acSAndroid Build Coastguard Worker {
106*1c60b9acSAndroid Build Coastguard Worker if (mh) {
107*1c60b9acSAndroid Build Coastguard Worker avhd->mh = mh;
108*1c60b9acSAndroid Build Coastguard Worker avhd->mode = MODE_PLAYING;
109*1c60b9acSAndroid Build Coastguard Worker snd_pcm_prepare(avhd->pcm_playback);
110*1c60b9acSAndroid Build Coastguard Worker
111*1c60b9acSAndroid Build Coastguard Worker return 0;
112*1c60b9acSAndroid Build Coastguard Worker }
113*1c60b9acSAndroid Build Coastguard Worker
114*1c60b9acSAndroid Build Coastguard Worker avhd->destroy_mh_on_drain = 1;
115*1c60b9acSAndroid Build Coastguard Worker avhd->done_cb = cb;
116*1c60b9acSAndroid Build Coastguard Worker avhd->opaque = opaque;
117*1c60b9acSAndroid Build Coastguard Worker
118*1c60b9acSAndroid Build Coastguard Worker return 0;
119*1c60b9acSAndroid Build Coastguard Worker }
120*1c60b9acSAndroid Build Coastguard Worker
121*1c60b9acSAndroid Build Coastguard Worker /*
122*1c60b9acSAndroid Build Coastguard Worker * Helper used to set alsa hwparams on both capture and playback channels
123*1c60b9acSAndroid Build Coastguard Worker */
124*1c60b9acSAndroid Build Coastguard Worker
125*1c60b9acSAndroid Build Coastguard Worker static int
set_hw_params(struct lws_vhost * vh,snd_pcm_t ** pcm,int type)126*1c60b9acSAndroid Build Coastguard Worker set_hw_params(struct lws_vhost *vh, snd_pcm_t **pcm, int type)
127*1c60b9acSAndroid Build Coastguard Worker {
128*1c60b9acSAndroid Build Coastguard Worker unsigned int rate = pv_sample_rate(); /* it's 16kHz */
129*1c60b9acSAndroid Build Coastguard Worker snd_pcm_hw_params_t *params;
130*1c60b9acSAndroid Build Coastguard Worker lws_sock_file_fd_type u;
131*1c60b9acSAndroid Build Coastguard Worker struct pollfd pfd;
132*1c60b9acSAndroid Build Coastguard Worker struct lws *wsi1;
133*1c60b9acSAndroid Build Coastguard Worker int n;
134*1c60b9acSAndroid Build Coastguard Worker
135*1c60b9acSAndroid Build Coastguard Worker n = snd_pcm_open(pcm, "default", type, SND_PCM_NONBLOCK);
136*1c60b9acSAndroid Build Coastguard Worker if (n < 0) {
137*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: Can't open default for playback: %s\n",
138*1c60b9acSAndroid Build Coastguard Worker __func__, snd_strerror(n));
139*1c60b9acSAndroid Build Coastguard Worker
140*1c60b9acSAndroid Build Coastguard Worker return -1;
141*1c60b9acSAndroid Build Coastguard Worker }
142*1c60b9acSAndroid Build Coastguard Worker
143*1c60b9acSAndroid Build Coastguard Worker if (snd_pcm_poll_descriptors(*pcm, &pfd, 1) != 1) {
144*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: failed to get playback desc\n", __func__);
145*1c60b9acSAndroid Build Coastguard Worker return -1;
146*1c60b9acSAndroid Build Coastguard Worker }
147*1c60b9acSAndroid Build Coastguard Worker
148*1c60b9acSAndroid Build Coastguard Worker u.filefd = (lws_filefd_type)(long long)pfd.fd;
149*1c60b9acSAndroid Build Coastguard Worker wsi1 = lws_adopt_descriptor_vhost(vh, LWS_ADOPT_RAW_FILE_DESC, u,
150*1c60b9acSAndroid Build Coastguard Worker "lws-audio-test", NULL);
151*1c60b9acSAndroid Build Coastguard Worker if (!wsi1) {
152*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: Failed to adopt playback desc\n", __func__);
153*1c60b9acSAndroid Build Coastguard Worker goto bail;
154*1c60b9acSAndroid Build Coastguard Worker }
155*1c60b9acSAndroid Build Coastguard Worker if (type == SND_PCM_STREAM_PLAYBACK)
156*1c60b9acSAndroid Build Coastguard Worker lws_rx_flow_control(wsi1, 0); /* no POLLIN */
157*1c60b9acSAndroid Build Coastguard Worker
158*1c60b9acSAndroid Build Coastguard Worker snd_pcm_hw_params_malloc(¶ms);
159*1c60b9acSAndroid Build Coastguard Worker snd_pcm_hw_params_any(*pcm, params);
160*1c60b9acSAndroid Build Coastguard Worker
161*1c60b9acSAndroid Build Coastguard Worker n = snd_pcm_hw_params_set_access(*pcm, params,
162*1c60b9acSAndroid Build Coastguard Worker SND_PCM_ACCESS_RW_INTERLEAVED);
163*1c60b9acSAndroid Build Coastguard Worker if (n < 0)
164*1c60b9acSAndroid Build Coastguard Worker goto bail1;
165*1c60b9acSAndroid Build Coastguard Worker
166*1c60b9acSAndroid Build Coastguard Worker n = snd_pcm_hw_params_set_format(*pcm, params, SND_PCM_FORMAT_S16_LE);
167*1c60b9acSAndroid Build Coastguard Worker if (n < 0)
168*1c60b9acSAndroid Build Coastguard Worker goto bail1;
169*1c60b9acSAndroid Build Coastguard Worker
170*1c60b9acSAndroid Build Coastguard Worker n = snd_pcm_hw_params_set_channels(*pcm, params, 1);
171*1c60b9acSAndroid Build Coastguard Worker if (n < 0)
172*1c60b9acSAndroid Build Coastguard Worker goto bail1;
173*1c60b9acSAndroid Build Coastguard Worker
174*1c60b9acSAndroid Build Coastguard Worker n = snd_pcm_hw_params_set_rate_near(*pcm, params, &rate, 0);
175*1c60b9acSAndroid Build Coastguard Worker if (n < 0)
176*1c60b9acSAndroid Build Coastguard Worker goto bail1;
177*1c60b9acSAndroid Build Coastguard Worker
178*1c60b9acSAndroid Build Coastguard Worker lwsl_notice("%s: %s rate %d\n", __func__,
179*1c60b9acSAndroid Build Coastguard Worker type == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture", rate);
180*1c60b9acSAndroid Build Coastguard Worker
181*1c60b9acSAndroid Build Coastguard Worker n = snd_pcm_hw_params(*pcm, params);
182*1c60b9acSAndroid Build Coastguard Worker snd_pcm_hw_params_free(params);
183*1c60b9acSAndroid Build Coastguard Worker if (n < 0)
184*1c60b9acSAndroid Build Coastguard Worker goto bail;
185*1c60b9acSAndroid Build Coastguard Worker
186*1c60b9acSAndroid Build Coastguard Worker return 0;
187*1c60b9acSAndroid Build Coastguard Worker
188*1c60b9acSAndroid Build Coastguard Worker bail1:
189*1c60b9acSAndroid Build Coastguard Worker snd_pcm_hw_params_free(params);
190*1c60b9acSAndroid Build Coastguard Worker bail:
191*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: Set hw params failed: %s\n", __func__, snd_strerror(n));
192*1c60b9acSAndroid Build Coastguard Worker
193*1c60b9acSAndroid Build Coastguard Worker return -1;
194*1c60b9acSAndroid Build Coastguard Worker }
195*1c60b9acSAndroid Build Coastguard Worker
196*1c60b9acSAndroid Build Coastguard Worker /*
197*1c60b9acSAndroid Build Coastguard Worker * The lws RAW file protocol handler that wraps ALSA.
198*1c60b9acSAndroid Build Coastguard Worker *
199*1c60b9acSAndroid Build Coastguard Worker * The timing is coming from ALSA capture channel... since they are both set to
200*1c60b9acSAndroid Build Coastguard Worker * 16kHz, it's enough just to have the one.
201*1c60b9acSAndroid Build Coastguard Worker */
202*1c60b9acSAndroid Build Coastguard Worker
203*1c60b9acSAndroid Build Coastguard Worker static int
callback_audio(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)204*1c60b9acSAndroid Build Coastguard Worker callback_audio(struct lws *wsi, enum lws_callback_reasons reason, void *user,
205*1c60b9acSAndroid Build Coastguard Worker void *in, size_t len)
206*1c60b9acSAndroid Build Coastguard Worker {
207*1c60b9acSAndroid Build Coastguard Worker struct raw_vhd *vhd = (struct raw_vhd *)lws_protocol_vh_priv_get(
208*1c60b9acSAndroid Build Coastguard Worker lws_get_vhost(wsi), lws_get_protocol(wsi));
209*1c60b9acSAndroid Build Coastguard Worker uint16_t rands[50];
210*1c60b9acSAndroid Build Coastguard Worker int16_t temp[256];
211*1c60b9acSAndroid Build Coastguard Worker bool det;
212*1c60b9acSAndroid Build Coastguard Worker long avg;
213*1c60b9acSAndroid Build Coastguard Worker int n, s;
214*1c60b9acSAndroid Build Coastguard Worker
215*1c60b9acSAndroid Build Coastguard Worker switch (reason) {
216*1c60b9acSAndroid Build Coastguard Worker case LWS_CALLBACK_PROTOCOL_INIT:
217*1c60b9acSAndroid Build Coastguard Worker
218*1c60b9acSAndroid Build Coastguard Worker if (avhd) /* just on one vhost */
219*1c60b9acSAndroid Build Coastguard Worker return 0;
220*1c60b9acSAndroid Build Coastguard Worker
221*1c60b9acSAndroid Build Coastguard Worker avhd = vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
222*1c60b9acSAndroid Build Coastguard Worker lws_get_protocol(wsi), sizeof(struct raw_vhd));
223*1c60b9acSAndroid Build Coastguard Worker
224*1c60b9acSAndroid Build Coastguard Worker /*
225*1c60b9acSAndroid Build Coastguard Worker * Set up the wakeword library
226*1c60b9acSAndroid Build Coastguard Worker */
227*1c60b9acSAndroid Build Coastguard Worker
228*1c60b9acSAndroid Build Coastguard Worker n = pv_porcupine_init("porcupine_params.pv", "alexa_linux.ppn",
229*1c60b9acSAndroid Build Coastguard Worker 1.0, &vhd->porc);
230*1c60b9acSAndroid Build Coastguard Worker if (n) {
231*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: porcupine init fail %d\n", __func__, n);
232*1c60b9acSAndroid Build Coastguard Worker
233*1c60b9acSAndroid Build Coastguard Worker return -1;
234*1c60b9acSAndroid Build Coastguard Worker }
235*1c60b9acSAndroid Build Coastguard Worker vhd->porc_spf = pv_porcupine_frame_length();
236*1c60b9acSAndroid Build Coastguard Worker vhd->porcbuf = malloc(vhd->porc_spf * 2);
237*1c60b9acSAndroid Build Coastguard Worker lwsl_info("%s: %s porc frame length is %d samples\n", __func__,
238*1c60b9acSAndroid Build Coastguard Worker lws_get_vhost_name(lws_get_vhost(wsi)),
239*1c60b9acSAndroid Build Coastguard Worker vhd->porc_spf);
240*1c60b9acSAndroid Build Coastguard Worker
241*1c60b9acSAndroid Build Coastguard Worker vhd->rate = pv_sample_rate(); /* 16kHz */
242*1c60b9acSAndroid Build Coastguard Worker
243*1c60b9acSAndroid Build Coastguard Worker /* set up alsa */
244*1c60b9acSAndroid Build Coastguard Worker
245*1c60b9acSAndroid Build Coastguard Worker if (set_hw_params(lws_get_vhost(wsi), &vhd->pcm_playback,
246*1c60b9acSAndroid Build Coastguard Worker SND_PCM_STREAM_PLAYBACK)) {
247*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: Can't open default for playback\n",
248*1c60b9acSAndroid Build Coastguard Worker __func__);
249*1c60b9acSAndroid Build Coastguard Worker
250*1c60b9acSAndroid Build Coastguard Worker return -1;
251*1c60b9acSAndroid Build Coastguard Worker }
252*1c60b9acSAndroid Build Coastguard Worker
253*1c60b9acSAndroid Build Coastguard Worker if (set_hw_params(lws_get_vhost(wsi), &vhd->pcm_capture,
254*1c60b9acSAndroid Build Coastguard Worker SND_PCM_STREAM_CAPTURE)) {
255*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: Can't open default for capture\n",
256*1c60b9acSAndroid Build Coastguard Worker __func__);
257*1c60b9acSAndroid Build Coastguard Worker
258*1c60b9acSAndroid Build Coastguard Worker return -1;
259*1c60b9acSAndroid Build Coastguard Worker }
260*1c60b9acSAndroid Build Coastguard Worker
261*1c60b9acSAndroid Build Coastguard Worker snd_config_update_free_global();
262*1c60b9acSAndroid Build Coastguard Worker
263*1c60b9acSAndroid Build Coastguard Worker break;
264*1c60b9acSAndroid Build Coastguard Worker
265*1c60b9acSAndroid Build Coastguard Worker case LWS_CALLBACK_PROTOCOL_DESTROY:
266*1c60b9acSAndroid Build Coastguard Worker lwsl_info("%s: LWS_CALLBACK_PROTOCOL_DESTROY\n", __func__);
267*1c60b9acSAndroid Build Coastguard Worker if (!vhd)
268*1c60b9acSAndroid Build Coastguard Worker break;
269*1c60b9acSAndroid Build Coastguard Worker
270*1c60b9acSAndroid Build Coastguard Worker if (vhd->porcbuf) {
271*1c60b9acSAndroid Build Coastguard Worker free(vhd->porcbuf);
272*1c60b9acSAndroid Build Coastguard Worker vhd->porcbuf = NULL;
273*1c60b9acSAndroid Build Coastguard Worker }
274*1c60b9acSAndroid Build Coastguard Worker if (vhd->pcm_playback) {
275*1c60b9acSAndroid Build Coastguard Worker snd_pcm_drop(vhd->pcm_playback);
276*1c60b9acSAndroid Build Coastguard Worker snd_pcm_close(vhd->pcm_playback);
277*1c60b9acSAndroid Build Coastguard Worker vhd->pcm_playback = NULL;
278*1c60b9acSAndroid Build Coastguard Worker }
279*1c60b9acSAndroid Build Coastguard Worker if (vhd->pcm_capture) {
280*1c60b9acSAndroid Build Coastguard Worker snd_pcm_drop(vhd->pcm_capture);
281*1c60b9acSAndroid Build Coastguard Worker snd_pcm_close(vhd->pcm_capture);
282*1c60b9acSAndroid Build Coastguard Worker vhd->pcm_capture = NULL;
283*1c60b9acSAndroid Build Coastguard Worker }
284*1c60b9acSAndroid Build Coastguard Worker if (vhd->porc) {
285*1c60b9acSAndroid Build Coastguard Worker pv_porcupine_delete(vhd->porc);
286*1c60b9acSAndroid Build Coastguard Worker vhd->porc = NULL;
287*1c60b9acSAndroid Build Coastguard Worker }
288*1c60b9acSAndroid Build Coastguard Worker
289*1c60b9acSAndroid Build Coastguard Worker /* avoid most of the valgrind mess from alsa */
290*1c60b9acSAndroid Build Coastguard Worker snd_config_update_free_global();
291*1c60b9acSAndroid Build Coastguard Worker
292*1c60b9acSAndroid Build Coastguard Worker break;
293*1c60b9acSAndroid Build Coastguard Worker
294*1c60b9acSAndroid Build Coastguard Worker case LWS_CALLBACK_RAW_CLOSE_FILE:
295*1c60b9acSAndroid Build Coastguard Worker lwsl_info("%s: closed\n", __func__);
296*1c60b9acSAndroid Build Coastguard Worker break;
297*1c60b9acSAndroid Build Coastguard Worker
298*1c60b9acSAndroid Build Coastguard Worker case LWS_CALLBACK_RAW_RX_FILE:
299*1c60b9acSAndroid Build Coastguard Worker /* we come here about every 250ms */
300*1c60b9acSAndroid Build Coastguard Worker
301*1c60b9acSAndroid Build Coastguard Worker /*
302*1c60b9acSAndroid Build Coastguard Worker * Playing back the mp3?
303*1c60b9acSAndroid Build Coastguard Worker */
304*1c60b9acSAndroid Build Coastguard Worker if (vhd->mode == MODE_PLAYING && vhd->mh) {
305*1c60b9acSAndroid Build Coastguard Worker size_t amt, try;
306*1c60b9acSAndroid Build Coastguard Worker
307*1c60b9acSAndroid Build Coastguard Worker do {
308*1c60b9acSAndroid Build Coastguard Worker try = snd_pcm_avail(vhd->pcm_playback);
309*1c60b9acSAndroid Build Coastguard Worker if (try > LWS_ARRAY_SIZE(vhd->p))
310*1c60b9acSAndroid Build Coastguard Worker try = LWS_ARRAY_SIZE(vhd->p);
311*1c60b9acSAndroid Build Coastguard Worker
312*1c60b9acSAndroid Build Coastguard Worker n = mpg123_read(vhd->mh, (uint8_t *)vhd->p,
313*1c60b9acSAndroid Build Coastguard Worker try * 2, &amt);
314*1c60b9acSAndroid Build Coastguard Worker lwsl_info("%s: PLAYING: mpg123 read %d, n %d\n",
315*1c60b9acSAndroid Build Coastguard Worker __func__, (int)amt, n);
316*1c60b9acSAndroid Build Coastguard Worker if (n == MPG123_NEW_FORMAT) {
317*1c60b9acSAndroid Build Coastguard Worker snd_pcm_start(vhd->pcm_playback);
318*1c60b9acSAndroid Build Coastguard Worker memset(vhd->p, 0, try);
319*1c60b9acSAndroid Build Coastguard Worker snd_pcm_writei(vhd->pcm_playback,
320*1c60b9acSAndroid Build Coastguard Worker vhd->p, try / 2);
321*1c60b9acSAndroid Build Coastguard Worker snd_pcm_prepare(vhd->pcm_playback);
322*1c60b9acSAndroid Build Coastguard Worker }
323*1c60b9acSAndroid Build Coastguard Worker } while (n == MPG123_NEW_FORMAT);
324*1c60b9acSAndroid Build Coastguard Worker
325*1c60b9acSAndroid Build Coastguard Worker if (amt) {
326*1c60b9acSAndroid Build Coastguard Worker n = snd_pcm_writei(vhd->pcm_playback,
327*1c60b9acSAndroid Build Coastguard Worker vhd->p, amt / 2);
328*1c60b9acSAndroid Build Coastguard Worker if (n < 0)
329*1c60b9acSAndroid Build Coastguard Worker lwsl_notice("%s: snd_pcm_writei: %d %s\n",
330*1c60b9acSAndroid Build Coastguard Worker __func__, n, snd_strerror(n));
331*1c60b9acSAndroid Build Coastguard Worker if (n == -EPIPE) {
332*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: did EPIPE prep\n", __func__);
333*1c60b9acSAndroid Build Coastguard Worker snd_pcm_prepare(vhd->pcm_playback);
334*1c60b9acSAndroid Build Coastguard Worker }
335*1c60b9acSAndroid Build Coastguard Worker } else
336*1c60b9acSAndroid Build Coastguard Worker if (vhd->destroy_mh_on_drain &&
337*1c60b9acSAndroid Build Coastguard Worker n != MPG123_NEW_FORMAT) {
338*1c60b9acSAndroid Build Coastguard Worker snd_pcm_drain(vhd->pcm_playback);
339*1c60b9acSAndroid Build Coastguard Worker vhd->destroy_mh_on_drain = 0;
340*1c60b9acSAndroid Build Coastguard Worker lwsl_notice("%s: mp3 destroyed\n",
341*1c60b9acSAndroid Build Coastguard Worker __func__);
342*1c60b9acSAndroid Build Coastguard Worker mpg123_close(vhd->mh);
343*1c60b9acSAndroid Build Coastguard Worker mpg123_delete(vhd->mh);
344*1c60b9acSAndroid Build Coastguard Worker vhd->mh = NULL;
345*1c60b9acSAndroid Build Coastguard Worker vhd->mode = MODE_IDLE;
346*1c60b9acSAndroid Build Coastguard Worker
347*1c60b9acSAndroid Build Coastguard Worker if (vhd->done_cb)
348*1c60b9acSAndroid Build Coastguard Worker vhd->done_cb(vhd->opaque);
349*1c60b9acSAndroid Build Coastguard Worker }
350*1c60b9acSAndroid Build Coastguard Worker }
351*1c60b9acSAndroid Build Coastguard Worker
352*1c60b9acSAndroid Build Coastguard Worker /*
353*1c60b9acSAndroid Build Coastguard Worker * Get the capture data
354*1c60b9acSAndroid Build Coastguard Worker */
355*1c60b9acSAndroid Build Coastguard Worker
356*1c60b9acSAndroid Build Coastguard Worker n = snd_pcm_readi(vhd->pcm_capture, temp, LWS_ARRAY_SIZE(temp));
357*1c60b9acSAndroid Build Coastguard Worker s = 0;
358*1c60b9acSAndroid Build Coastguard Worker while (s < n) {
359*1c60b9acSAndroid Build Coastguard Worker vhd->p[(vhd->wpos + s) % LWS_ARRAY_SIZE(vhd->p)] = temp[s];
360*1c60b9acSAndroid Build Coastguard Worker s++;
361*1c60b9acSAndroid Build Coastguard Worker }
362*1c60b9acSAndroid Build Coastguard Worker
363*1c60b9acSAndroid Build Coastguard Worker if (vhd->mode == MODE_CAPTURING) {
364*1c60b9acSAndroid Build Coastguard Worker
365*1c60b9acSAndroid Build Coastguard Worker /*
366*1c60b9acSAndroid Build Coastguard Worker * We are recording an utterance.
367*1c60b9acSAndroid Build Coastguard Worker *
368*1c60b9acSAndroid Build Coastguard Worker * Estimate the sound density in the frame by picking 50
369*1c60b9acSAndroid Build Coastguard Worker * samples at random and averaging the sampled
370*1c60b9acSAndroid Build Coastguard Worker * [abs()^2] / 10000 to create a Figure of Merit.
371*1c60b9acSAndroid Build Coastguard Worker *
372*1c60b9acSAndroid Build Coastguard Worker * Speaking on my laptop gets us 1000 - 5000, silence
373*1c60b9acSAndroid Build Coastguard Worker * is typ under 30. The wakeword tells us there was
374*1c60b9acSAndroid Build Coastguard Worker * speech at the start, end the capture when there's
375*1c60b9acSAndroid Build Coastguard Worker * ~750ms (12000 samples) under 125 FOM.
376*1c60b9acSAndroid Build Coastguard Worker */
377*1c60b9acSAndroid Build Coastguard Worker
378*1c60b9acSAndroid Build Coastguard Worker #define SILENCE_THRESH 125
379*1c60b9acSAndroid Build Coastguard Worker
380*1c60b9acSAndroid Build Coastguard Worker avg = 0;
381*1c60b9acSAndroid Build Coastguard Worker lws_get_random(lws_get_context(wsi), rands, sizeof(rands));
382*1c60b9acSAndroid Build Coastguard Worker for (s = 0; s < (int)LWS_ARRAY_SIZE(rands); s++) {
383*1c60b9acSAndroid Build Coastguard Worker long q;
384*1c60b9acSAndroid Build Coastguard Worker
385*1c60b9acSAndroid Build Coastguard Worker q = temp[rands[s] % n];
386*1c60b9acSAndroid Build Coastguard Worker
387*1c60b9acSAndroid Build Coastguard Worker avg += (q * q);
388*1c60b9acSAndroid Build Coastguard Worker }
389*1c60b9acSAndroid Build Coastguard Worker avg = (avg / (int)LWS_ARRAY_SIZE(rands)) / 10000;
390*1c60b9acSAndroid Build Coastguard Worker
391*1c60b9acSAndroid Build Coastguard Worker lwsl_notice("est audio energy: %ld %d\n", avg, vhd->mode);
392*1c60b9acSAndroid Build Coastguard Worker
393*1c60b9acSAndroid Build Coastguard Worker /*
394*1c60b9acSAndroid Build Coastguard Worker * Only start looking for "silence" after 1.5s, in case
395*1c60b9acSAndroid Build Coastguard Worker * he does a long pause after the wakeword
396*1c60b9acSAndroid Build Coastguard Worker */
397*1c60b9acSAndroid Build Coastguard Worker
398*1c60b9acSAndroid Build Coastguard Worker if (vhd->anycount < (3 *vhd->rate) / 2 &&
399*1c60b9acSAndroid Build Coastguard Worker avg < SILENCE_THRESH) {
400*1c60b9acSAndroid Build Coastguard Worker vhd->quietcount += n;
401*1c60b9acSAndroid Build Coastguard Worker /* then 500ms of "silence" does it for us */
402*1c60b9acSAndroid Build Coastguard Worker if (vhd->quietcount >= ((vhd->rate * 3) / 4)) {
403*1c60b9acSAndroid Build Coastguard Worker lwsl_warn("%s: ended capture\n", __func__);
404*1c60b9acSAndroid Build Coastguard Worker vhd->mode = MODE_IDLE;
405*1c60b9acSAndroid Build Coastguard Worker vhd->quietcount = 0;
406*1c60b9acSAndroid Build Coastguard Worker }
407*1c60b9acSAndroid Build Coastguard Worker }
408*1c60b9acSAndroid Build Coastguard Worker
409*1c60b9acSAndroid Build Coastguard Worker /* if we're not "silent", reset the count */
410*1c60b9acSAndroid Build Coastguard Worker if (avg > SILENCE_THRESH * 2)
411*1c60b9acSAndroid Build Coastguard Worker vhd->quietcount = 0;
412*1c60b9acSAndroid Build Coastguard Worker
413*1c60b9acSAndroid Build Coastguard Worker /*
414*1c60b9acSAndroid Build Coastguard Worker * Since we are in capturing mode, we have something
415*1c60b9acSAndroid Build Coastguard Worker * new to send now.
416*1c60b9acSAndroid Build Coastguard Worker *
417*1c60b9acSAndroid Build Coastguard Worker * We must send an extra one at the end so we can finish
418*1c60b9acSAndroid Build Coastguard Worker * the tx.
419*1c60b9acSAndroid Build Coastguard Worker */
420*1c60b9acSAndroid Build Coastguard Worker lws_ss_request_tx(hss_avs_sync);
421*1c60b9acSAndroid Build Coastguard Worker }
422*1c60b9acSAndroid Build Coastguard Worker
423*1c60b9acSAndroid Build Coastguard Worker /*
424*1c60b9acSAndroid Build Coastguard Worker * Just waiting for a wakeword
425*1c60b9acSAndroid Build Coastguard Worker */
426*1c60b9acSAndroid Build Coastguard Worker
427*1c60b9acSAndroid Build Coastguard Worker while (vhd->mode == MODE_IDLE) {
428*1c60b9acSAndroid Build Coastguard Worker int m = 0, ppold = vhd->porcpos;
429*1c60b9acSAndroid Build Coastguard Worker
430*1c60b9acSAndroid Build Coastguard Worker s = (vhd->wpos - vhd->porcpos) % LWS_ARRAY_SIZE(vhd->p);
431*1c60b9acSAndroid Build Coastguard Worker if (s < vhd->porc_spf)
432*1c60b9acSAndroid Build Coastguard Worker goto eol;
433*1c60b9acSAndroid Build Coastguard Worker
434*1c60b9acSAndroid Build Coastguard Worker while (m < vhd->porc_spf) {
435*1c60b9acSAndroid Build Coastguard Worker vhd->porcbuf[m++] = avhd->p[vhd->porcpos];
436*1c60b9acSAndroid Build Coastguard Worker vhd->porcpos = (vhd->porcpos + 1) %
437*1c60b9acSAndroid Build Coastguard Worker LWS_ARRAY_SIZE(vhd->p);
438*1c60b9acSAndroid Build Coastguard Worker }
439*1c60b9acSAndroid Build Coastguard Worker
440*1c60b9acSAndroid Build Coastguard Worker if (pv_porcupine_process(vhd->porc, vhd->porcbuf, &det))
441*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: porc_process failed\n", __func__);
442*1c60b9acSAndroid Build Coastguard Worker
443*1c60b9acSAndroid Build Coastguard Worker if (!det && vhd->last_wake_detect &&
444*1c60b9acSAndroid Build Coastguard Worker vhd->mode == MODE_IDLE) {
445*1c60b9acSAndroid Build Coastguard Worker lwsl_warn("************* Wakeword\n");
446*1c60b9acSAndroid Build Coastguard Worker if (!avs_query_start(lws_get_context(wsi))) {
447*1c60b9acSAndroid Build Coastguard Worker vhd->mode = MODE_CAPTURING;
448*1c60b9acSAndroid Build Coastguard Worker vhd->quietcount = 0;
449*1c60b9acSAndroid Build Coastguard Worker vhd->last_wake_detect = det;
450*1c60b9acSAndroid Build Coastguard Worker vhd->npos = ppold;
451*1c60b9acSAndroid Build Coastguard Worker break;
452*1c60b9acSAndroid Build Coastguard Worker }
453*1c60b9acSAndroid Build Coastguard Worker }
454*1c60b9acSAndroid Build Coastguard Worker vhd->last_wake_detect = det;
455*1c60b9acSAndroid Build Coastguard Worker }
456*1c60b9acSAndroid Build Coastguard Worker
457*1c60b9acSAndroid Build Coastguard Worker eol:
458*1c60b9acSAndroid Build Coastguard Worker vhd->wpos = (vhd->wpos + n) % LWS_ARRAY_SIZE(vhd->p);
459*1c60b9acSAndroid Build Coastguard Worker break;
460*1c60b9acSAndroid Build Coastguard Worker
461*1c60b9acSAndroid Build Coastguard Worker default:
462*1c60b9acSAndroid Build Coastguard Worker break;
463*1c60b9acSAndroid Build Coastguard Worker }
464*1c60b9acSAndroid Build Coastguard Worker
465*1c60b9acSAndroid Build Coastguard Worker return 0;
466*1c60b9acSAndroid Build Coastguard Worker }
467*1c60b9acSAndroid Build Coastguard Worker
468*1c60b9acSAndroid Build Coastguard Worker struct lws_protocols protocol_audio_test =
469*1c60b9acSAndroid Build Coastguard Worker { "lws-audio-test", callback_audio, 0, 0 };
470