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 22static unsigned int sample_rate = 16000; 23 24struct 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 36static int 37set_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(¶ms); 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 96bail1: 97 snd_pcm_hw_params_free(params); 98bail: 99 lwsl_err("%s: Set hw params failed: %s\n", __func__, snd_strerror(n)); 100 101 return -1; 102} 103 104static int 105callback_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 172static struct lws_protocols protocols[] = { 173 { "lws-audio-test", callback_raw_test, 0, 0 }, 174 LWS_PROTOCOL_LIST_TERM 175}; 176 177static int interrupted; 178 179void sigint_handler(int sig) 180{ 181 interrupted = 1; 182} 183 184int 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