add microphone server/client examples
[rofl0r-rocksock.git] / examples / micserver.c
blobb0588d074fbc0e5c39d798487657274036902fb0
1 /*
2 * author: rofl0r
4 * License: LGPL 2.1+ with static linking exception
6 * soundplay server: waits for a client to connect,
7 * then passes stream received to audio hardware.
8 * designed to stream microphone input from one computer to
9 * the other.
12 #include <string.h>
13 #include <stdio.h>
14 #include <sys/types.h>
15 #include <alsa/asoundlib.h>
16 #include <pthread.h>
18 #include "micbuffer.h"
20 #include "../rocksockserver.h"
21 #include "../../lib/include/sblist.h"
22 #include "../../lib/include/macros.h"
25 //RcB: CFLAGS "-std=c99 -D_GNU_SOURCE -DVERBOSE"
26 //RcB: LINK "-lasound -lpthread"
28 #ifdef VERBOSE
29 #define DPRINTF(fd, ...) dprintf(fd, __VA_ARGS__)
30 #else
31 #define DPRINTF(fd, ...)
32 #endif
34 typedef struct client {
35 int fd;
36 unsigned char wave[MICSERVER_NUMBUFFERS][MIC_BUFFER_SIZE];
37 unsigned long long samples_recvd;
38 unsigned long long samples_consumed;
39 snd_pcm_t *playback_handle;
40 pthread_mutex_t mtx;
41 pthread_t thr;
42 int terminate;
43 } client;
45 typedef struct server_state {
46 rocksockserver srv;
47 sblist *clients;
48 const char* audiodevice;
49 } server;
51 struct client *client_from_fd(sblist *clients, int fd) {
52 struct client* c;
53 sblist_iter(clients, c) {
54 if(c->fd == fd) return c;
56 return 0;
59 static void disconnect_client(server* s, int fd) {
60 struct client *c;
61 sblist_iter_counter(s->clients, i, c) {
62 if(c->fd == fd) {
63 c->terminate = -1;
64 pthread_join(c->thr, 0);
65 snd_pcm_close(c->playback_handle);
66 sblist_delete(s->clients, i);
67 break;
70 rocksockserver_disconnect_client(&s->srv, fd);
73 static int on_cdisconnect (void* userdata, int fd) {
74 server* s = userdata;
75 disconnect_client(s, fd);
76 return 0;
79 static const char* getstatus_string(snd_pcm_state_t status) {
80 switch (status) {
81 case SND_PCM_STATE_PAUSED:
82 return "SND_PCM_STATE_PAUSED";
83 case SND_PCM_STATE_SETUP:
84 return "SND_PCM_STATE_SETUP";
85 case SND_PCM_STATE_PREPARED:
86 return "SND_PCM_STATE_PREPARED";
87 case SND_PCM_STATE_SUSPENDED:
88 return "SND_PCM_STATE_SUSPENDED";
89 case SND_PCM_STATE_DISCONNECTED:
90 return "SND_PCM_STATE_DISCONNECTED";
91 case SND_PCM_STATE_XRUN:
92 return "SND_PCM_STATE_XRUN";
93 case SND_PCM_STATE_DRAINING:
94 return "SND_PCM_STATE_DRAINING";
95 case SND_PCM_STATE_RUNNING:
96 return "SND_PCM_STATE_RUNNING";
97 default:
98 return "UNKNOWN";
102 static void chk(const int err, const char* msg) {
103 if(err < 0) {
104 dprintf(2, msg, snd_strerror(err));
105 exit(1);
109 static snd_pcm_t* init_playback_device(const char* devname) {
110 snd_pcm_t *playback_handle;
111 snd_pcm_hw_params_t *hw_params;
112 chk(snd_pcm_open(&playback_handle, devname, SND_PCM_STREAM_PLAYBACK,0), "cannot open audio device (%s)\n");
113 chk(snd_pcm_hw_params_malloc(&hw_params), "cannot allocate hardware parameter structure (%s)\n");
114 chk(snd_pcm_hw_params_any(playback_handle, hw_params), "cannot initialize hardware parameter structure (%s)\n");
115 chk(snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "cannot set access type (%s)\n");
116 chk(snd_pcm_hw_params_set_format(playback_handle, hw_params, FORMAT), "cannot set sample format (%s)\n");
117 unsigned rate = BITRATE;
118 chk(snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &rate, 0), "cannot set sample rate (%s)\n");
119 chk(snd_pcm_hw_params_set_channels(playback_handle, hw_params, NUMCHANNELS),"cannot set channel count (%s)\n");
120 chk(snd_pcm_hw_params(playback_handle, hw_params), "cannot set parameters (%s)\n");
121 snd_pcm_hw_params_free(hw_params);
122 chk(snd_pcm_prepare(playback_handle), "cannot prepare audio interface for use (%s)\n");
123 return playback_handle;
126 static unsigned long long get_recvd(client *c) {
127 unsigned long long l;
128 pthread_mutex_lock(&c->mtx);
129 l = c->samples_recvd;
130 pthread_mutex_unlock(&c->mtx);
131 return l;
134 static void play(snd_pcm_t *playback_handle, const unsigned char* buf, size_t s) {
135 int ret = snd_pcm_writei(playback_handle, buf, s);
136 if (ret<0) {
137 DPRINTF(2, "write to audio interface failed (%s)\n", snd_strerror(ret));
138 snd_pcm_state_t state = snd_pcm_state(playback_handle);
139 DPRINTF(2, "%s\n", getstatus_string(state));
140 if(state == SND_PCM_STATE_XRUN) snd_pcm_recover(playback_handle, ret, 1);
144 static void *c_thread(void *userdata) {
145 client *c = userdata;
146 while(!c->terminate) {
147 if(get_recvd(c) - c->samples_consumed >= ARRAY_SIZE(c->wave)/2) {
148 do {
149 unsigned x = c->samples_consumed % ARRAY_SIZE(c->wave);
150 DPRINTF(2, "playing samplebuf %u\n", x);
151 play(c->playback_handle, c->wave[x], sizeof(c->wave[0]));
152 c->samples_consumed++;
153 } while(get_recvd(c) - c->samples_consumed > 0);
155 usleep(1);
157 return 0;
160 static int on_cconnect (void* userdata, struct sockaddr_storage* clientaddr, int fd) {
161 (void) clientaddr;
162 server* s = userdata;
163 struct client cb = {.fd = fd};
164 if(sblist_getsize(s->clients) || !sblist_add(s->clients, &cb)) {
165 disconnect_client(s, fd);
166 return -1;
168 struct client* c = client_from_fd(s->clients, fd);
169 c->playback_handle = init_playback_device(s->audiodevice);
170 pthread_mutex_init(&c->mtx, 0);
171 pthread_create(&c->thr, 0, c_thread, c);
172 return 0;
175 static int on_cread (void* userdata, int fd, size_t dummy) {
176 (void) dummy;
177 server* s = userdata;
178 struct client *c;
179 if(!(c = client_from_fd(s->clients, fd))) return -1;
180 unsigned x = c->samples_recvd % ARRAY_SIZE(c->wave);
181 DPRINTF(2, "receiving samplebuf %u\n", x);
182 if(sizeof(c->wave[0]) != recv(fd, c->wave[x], sizeof(c->wave[0]), 0)) {
183 disconnect_client(s, fd);
184 return 0;
186 pthread_mutex_lock(&c->mtx);
187 c->samples_recvd++;
188 pthread_mutex_unlock(&c->mtx);
189 return 0;
192 static int on_cwantsdata (void* userdata, int fd) {
193 server* s = userdata;
194 struct client *c;
195 if(!(c = client_from_fd(s->clients, fd))) return -1;
196 return 0;
199 int main(int argc, char** argv) {
200 server sv, *s = &sv;
201 s->clients = sblist_new(sizeof(struct client), 32);
202 s->audiodevice = argc > 1 ? argv[1] : "default";
203 const int port = 9999;
204 const char* listenip = "0.0.0.0";
205 if(rocksockserver_init(&s->srv, listenip, port, (void*) s)) return -1;
206 rocksockserver_set_sleeptime(&s->srv, BITRATE);
207 if(rocksockserver_loop(&s->srv, NULL, 0,
208 &on_cconnect, &on_cread,
209 &on_cwantsdata, &on_cdisconnect)) return -2;
210 return 0;