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(&params);
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