Makefile: build 2 more examples
[rofl0r-rocksock.git] / examples / micserver.c
blob6c173821ec331409e1317630237ab0b46c2a515c
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"
23 //RcB: CFLAGS "-std=c99 -D_GNU_SOURCE -DVERBOSE"
24 //RcB: LINK "-lasound -lpthread"
26 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
27 #define MICSERVER_NUMBUFFERS 20
29 #ifdef VERBOSE
30 #define DPRINTF(fd, ...) dprintf(fd, __VA_ARGS__)
31 #else
32 #define DPRINTF(fd, ...)
33 #endif
35 typedef struct client {
36 int fd;
37 unsigned char wave[MICSERVER_NUMBUFFERS][MIC_BUFFER_SIZE];
38 unsigned long long samples_recvd;
39 unsigned long long samples_consumed;
40 snd_pcm_t *playback_handle;
41 pthread_mutex_t mtx;
42 pthread_t thr;
43 int terminate;
44 } client;
46 typedef struct server_state {
47 rocksockserver srv;
48 const char* audiodevice;
49 } server;
51 static struct progstate {
52 server s;
53 client client;
54 } p;
56 static void disconnect_client(server* s, int fd) {
57 if(p.client.fd == fd) {
58 struct client* c = &p.client;
59 c->terminate = -1;
60 pthread_join(c->thr, 0);
61 snd_pcm_close(c->playback_handle);
62 c->fd = -1;
64 rocksockserver_disconnect_client(&s->srv, fd);
67 static int on_cdisconnect (void* userdata, int fd) {
68 server* s = userdata;
69 disconnect_client(s, fd);
70 return 0;
73 static const char* getstatus_string(snd_pcm_state_t status) {
74 switch (status) {
75 case SND_PCM_STATE_PAUSED:
76 return "SND_PCM_STATE_PAUSED";
77 case SND_PCM_STATE_SETUP:
78 return "SND_PCM_STATE_SETUP";
79 case SND_PCM_STATE_PREPARED:
80 return "SND_PCM_STATE_PREPARED";
81 case SND_PCM_STATE_SUSPENDED:
82 return "SND_PCM_STATE_SUSPENDED";
83 case SND_PCM_STATE_DISCONNECTED:
84 return "SND_PCM_STATE_DISCONNECTED";
85 case SND_PCM_STATE_XRUN:
86 return "SND_PCM_STATE_XRUN";
87 case SND_PCM_STATE_DRAINING:
88 return "SND_PCM_STATE_DRAINING";
89 case SND_PCM_STATE_RUNNING:
90 return "SND_PCM_STATE_RUNNING";
91 default:
92 return "UNKNOWN";
96 static void chk(const int err, const char* msg) {
97 if(err < 0) {
98 dprintf(2, msg, snd_strerror(err));
99 exit(1);
103 static snd_pcm_t* init_playback_device(const char* devname) {
104 snd_pcm_t *playback_handle;
105 snd_pcm_hw_params_t *hw_params;
106 chk(snd_pcm_open(&playback_handle, devname, SND_PCM_STREAM_PLAYBACK,0), "cannot open audio device (%s)\n");
107 chk(snd_pcm_hw_params_malloc(&hw_params), "cannot allocate hardware parameter structure (%s)\n");
108 chk(snd_pcm_hw_params_any(playback_handle, hw_params), "cannot initialize hardware parameter structure (%s)\n");
109 chk(snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "cannot set access type (%s)\n");
110 chk(snd_pcm_hw_params_set_format(playback_handle, hw_params, FORMAT), "cannot set sample format (%s)\n");
111 unsigned rate = BITRATE;
112 chk(snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &rate, 0), "cannot set sample rate (%s)\n");
113 chk(snd_pcm_hw_params_set_channels(playback_handle, hw_params, NUMCHANNELS),"cannot set channel count (%s)\n");
114 chk(snd_pcm_hw_params(playback_handle, hw_params), "cannot set parameters (%s)\n");
115 snd_pcm_hw_params_free(hw_params);
116 chk(snd_pcm_prepare(playback_handle), "cannot prepare audio interface for use (%s)\n");
117 return playback_handle;
120 static unsigned long long get_recvd(client *c) {
121 unsigned long long l;
122 pthread_mutex_lock(&c->mtx);
123 l = c->samples_recvd;
124 pthread_mutex_unlock(&c->mtx);
125 return l;
128 static void play(snd_pcm_t *playback_handle, const unsigned char* buf, size_t s) {
129 int ret = snd_pcm_writei(playback_handle, buf, s);
130 if (ret<0) {
131 DPRINTF(2, "write to audio interface failed (%s)\n", snd_strerror(ret));
132 snd_pcm_state_t state = snd_pcm_state(playback_handle);
133 DPRINTF(2, "%s\n", getstatus_string(state));
134 if(state == SND_PCM_STATE_XRUN) snd_pcm_recover(playback_handle, ret, 1);
138 static void *c_thread(void *userdata) {
139 client *c = userdata;
140 while(!c->terminate) {
141 if(get_recvd(c) - c->samples_consumed >= ARRAY_SIZE(c->wave)/2) {
142 do {
143 unsigned x = c->samples_consumed % ARRAY_SIZE(c->wave);
144 DPRINTF(2, "playing samplebuf %u\n", x);
145 play(c->playback_handle, c->wave[x], sizeof(c->wave[0]));
146 c->samples_consumed++;
147 } while(get_recvd(c) - c->samples_consumed > 0);
149 usleep(1);
151 return 0;
154 static int on_cconnect (void* userdata, struct sockaddr_storage* clientaddr, int fd) {
155 (void) clientaddr;
156 server* s = userdata;
157 if(p.client.fd != -1) {
158 disconnect_client(s, fd);
159 return -1;
161 struct client* c = &p.client;
162 c->fd = fd;
163 c->terminate = 0;
164 c->playback_handle = init_playback_device(s->audiodevice);
165 pthread_mutex_init(&c->mtx, 0);
166 pthread_create(&c->thr, 0, c_thread, c);
167 return 0;
170 static int on_cread (void* userdata, int fd, size_t dummy) {
171 (void) dummy;
172 server* s = userdata;
173 struct client *c = &p.client;
174 unsigned x = c->samples_recvd % ARRAY_SIZE(c->wave);
175 DPRINTF(2, "receiving samplebuf %u\n", x);
176 if(sizeof(c->wave[0]) != recv(fd, c->wave[x], sizeof(c->wave[0]), 0)) {
177 disconnect_client(s, fd);
178 return 0;
180 pthread_mutex_lock(&c->mtx);
181 c->samples_recvd++;
182 pthread_mutex_unlock(&c->mtx);
183 return 0;
186 static int on_cwantsdata (void* userdata, int fd) {
187 server* s = userdata;
188 return 0;
191 int main(int argc, char** argv) {
192 server *s = &p.s;
193 p.client.fd = -1;
194 s->audiodevice = argc > 1 ? argv[1] : "default";
195 const int port = 9999;
196 const char* listenip = "0.0.0.0";
197 if(rocksockserver_init(&s->srv, listenip, port, (void*) s)) return -1;
198 rocksockserver_set_sleeptime(&s->srv, BITRATE);
199 rocksockserver_set_perrorfunc(&s->srv, perror);
200 if(rocksockserver_loop(&s->srv, NULL, 0,
201 &on_cconnect, &on_cread,
202 &on_cwantsdata, &on_cdisconnect)) return -2;
203 return 0;