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
14 #include <sys/types.h>
15 #include <alsa/asoundlib.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
30 #define DPRINTF(fd, ...) dprintf(fd, __VA_ARGS__)
32 #define DPRINTF(fd, ...)
35 typedef struct client
{
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
;
46 typedef struct server_state
{
48 const char* audiodevice
;
51 static struct progstate
{
56 static void disconnect_client(server
* s
, int fd
) {
57 if(p
.client
.fd
== fd
) {
58 struct client
* c
= &p
.client
;
60 pthread_join(c
->thr
, 0);
61 snd_pcm_close(c
->playback_handle
);
64 rocksockserver_disconnect_client(&s
->srv
, fd
);
67 static int on_cdisconnect (void* userdata
, int fd
) {
69 disconnect_client(s
, fd
);
73 static const char* getstatus_string(snd_pcm_state_t 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";
96 static void chk(const int err
, const char* msg
) {
98 dprintf(2, msg
, snd_strerror(err
));
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
);
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
);
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) {
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);
154 static int on_cconnect (void* userdata
, struct sockaddr_storage
* clientaddr
, int fd
) {
156 server
* s
= userdata
;
157 if(p
.client
.fd
!= -1) {
158 disconnect_client(s
, fd
);
161 struct client
* c
= &p
.client
;
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
);
170 static int on_cread (void* userdata
, int fd
, size_t 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
);
180 pthread_mutex_lock(&c
->mtx
);
182 pthread_mutex_unlock(&c
->mtx
);
186 static int on_cwantsdata (void* userdata
, int fd
) {
187 server
* s
= userdata
;
191 int main(int argc
, char** argv
) {
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;