• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * lws-minimal-raw-audio
3  *
4  * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  * This demonstrates adopting and managing audio device file descriptors in the
10  * event loop.
11  */
12 
13 #include <libwebsockets.h>
14 #include <string.h>
15 #include <signal.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 
20 #include <alsa/asoundlib.h>
21 
22 static unsigned int sample_rate = 16000;
23 
24 struct raw_vhd {
25 	uint8_t simplebuf[32768 * 2];
26 	snd_pcm_t *pcm_capture;
27 	snd_pcm_t *pcm_playback;
28 	snd_pcm_hw_params_t *params;
29 	snd_pcm_uframes_t frames;
30 	int filefd;
31 	int rpos;
32 	int wpos;
33 	int times;
34 };
35 
36 static int
set_hw_params(struct lws_vhost * vh,snd_pcm_t ** pcm,int type)37 set_hw_params(struct lws_vhost *vh, snd_pcm_t **pcm, int type)
38 {
39 	unsigned int rate = sample_rate;
40 	snd_pcm_hw_params_t *params;
41 	lws_sock_file_fd_type u;
42 	struct pollfd pfd;
43 	struct lws *wsi1;
44 	int n;
45 
46 	n = snd_pcm_open(pcm, "default", type, SND_PCM_NONBLOCK);
47 	if (n < 0) {
48 		lwsl_err("%s: Can't open default for playback: %s\n",
49 			 __func__, snd_strerror(n));
50 
51 		return -1;
52 	}
53 
54 	if (snd_pcm_poll_descriptors(*pcm, &pfd, 1) != 1) {
55 		lwsl_err("%s: failed to get playback desc\n", __func__);
56 		return -1;
57 	}
58 
59 	u.filefd = (lws_filefd_type)(long long)pfd.fd;
60 	wsi1 = lws_adopt_descriptor_vhost(vh, LWS_ADOPT_RAW_FILE_DESC, u,
61 					  "lws-audio-test", NULL);
62 	if (!wsi1) {
63 		lwsl_err("%s: Failed to adopt playback desc\n", __func__);
64 		goto bail;
65 	}
66 	if (type == SND_PCM_STREAM_PLAYBACK)
67 		lws_rx_flow_control(wsi1, 0); /* no POLLIN */
68 
69 	snd_pcm_hw_params_malloc(&params);
70 	snd_pcm_hw_params_any(*pcm, params);
71 
72 	n = snd_pcm_hw_params_set_access(*pcm, params,
73 					 SND_PCM_ACCESS_RW_INTERLEAVED);
74 	if (n < 0)
75 		goto bail1;
76 
77 	n = snd_pcm_hw_params_set_format(*pcm, params, SND_PCM_FORMAT_S16_LE);
78 	if (n < 0)
79 		goto bail1;
80 
81 	n = snd_pcm_hw_params_set_channels(*pcm, params, 1);
82 	if (n < 0)
83 		goto bail1;
84 
85 	n = snd_pcm_hw_params_set_rate_near(*pcm, params, &rate, 0);
86 	if (n < 0)
87 		goto bail1;
88 
89 	n = snd_pcm_hw_params(*pcm, params);
90 	snd_pcm_hw_params_free(params);
91 	if (n < 0)
92 		goto bail;
93 
94 	return 0;
95 
96 bail1:
97 	snd_pcm_hw_params_free(params);
98 bail:
99 	lwsl_err("%s: Set hw params failed: %s\n", __func__, snd_strerror(n));
100 
101 	return -1;
102 }
103 
104 static int
callback_raw_test(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)105 callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
106 			void *user, void *in, size_t len)
107 {
108 	struct raw_vhd *vhd = (struct raw_vhd *)lws_protocol_vh_priv_get(
109 				     lws_get_vhost(wsi), lws_get_protocol(wsi));
110 	int n;
111 
112 	switch (reason) {
113 	case LWS_CALLBACK_PROTOCOL_INIT:
114 		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
115 				lws_get_protocol(wsi), sizeof(struct raw_vhd));
116 
117 		if (set_hw_params(lws_get_vhost(wsi), &vhd->pcm_playback,
118 				  SND_PCM_STREAM_PLAYBACK))  {
119 			lwsl_err("%s: Can't open default for playback\n",
120 				 __func__);
121 
122 			return -1;
123 		}
124 
125 		if (set_hw_params(lws_get_vhost(wsi), &vhd->pcm_capture,
126 				  SND_PCM_STREAM_CAPTURE))  {
127 			lwsl_err("%s: Can't open default for capture\n",
128 				 __func__);
129 
130 			return -1;
131 		}
132 		break;
133 
134 	case LWS_CALLBACK_PROTOCOL_DESTROY:
135 		lwsl_notice("LWS_CALLBACK_PROTOCOL_DESTROY\n");
136 		if (vhd && vhd->pcm_playback) {
137 			snd_pcm_drain(vhd->pcm_playback);
138 			snd_pcm_close(vhd->pcm_playback);
139 			vhd->pcm_playback = NULL;
140 		}
141 		if (vhd && vhd->pcm_capture) {
142 			snd_pcm_close(vhd->pcm_capture);
143 			vhd->pcm_capture = NULL;
144 		}
145 		break;
146 
147 	case LWS_CALLBACK_RAW_RX_FILE:
148 		if (vhd->times >= 6) {  /* delay amount decided by this */
149 			n = snd_pcm_writei(vhd->pcm_playback,
150 					   &vhd->simplebuf[vhd->rpos],
151 					   ((vhd->wpos - vhd->rpos) &
152 					    (sizeof(vhd->simplebuf) - 1)) / 2);
153 			vhd->rpos =  (vhd->rpos + (n * 2)) &
154 					(sizeof(vhd->simplebuf) - 1);
155 		}
156 
157 		n = snd_pcm_readi(vhd->pcm_capture, &vhd->simplebuf[vhd->wpos],
158 				  (sizeof(vhd->simplebuf) - vhd->wpos) / 2);
159 		lwsl_notice("LWS_CALLBACK_RAW_RX_FILE: %d samples\n", n);
160 		vhd->times++;
161 
162 		vhd->wpos = (vhd->wpos + (n * 2)) & (sizeof(vhd->simplebuf) - 1);
163 		break;
164 
165 	default:
166 		break;
167 	}
168 
169 	return 0;
170 }
171 
172 static struct lws_protocols protocols[] = {
173 	{ "lws-audio-test", callback_raw_test, 0, 0 },
174 	{ NULL, NULL, 0, 0 } /* terminator */
175 };
176 
177 static int interrupted;
178 
sigint_handler(int sig)179 void sigint_handler(int sig)
180 {
181 	interrupted = 1;
182 }
183 
main(int argc,const char ** argv)184 int main(int argc, const char **argv)
185 {
186 	struct lws_context_creation_info info;
187 	struct lws_context *context;
188 	int n = 0;
189 
190 	signal(SIGINT, sigint_handler);
191 	memset(&info, 0, sizeof info);
192 	lws_cmdline_option_handle_builtin(argc, argv, &info);
193 
194 	lwsl_user("LWS minimal raw audio\n");
195 
196 	info.port = CONTEXT_PORT_NO_LISTEN_SERVER;
197 	info.protocols = protocols;
198 
199 	context = lws_create_context(&info);
200 	if (!context) {
201 		lwsl_err("lws init failed\n");
202 		return 1;
203 	}
204 
205 	while (n >= 0 && !interrupted)
206 		n = lws_service(context, 0);
207 
208 	lws_context_destroy(context);
209 
210 	return 0;
211 }
212