12 #include <shairplay/dnssd.h>
13 #include <shairplay/raop.h>
25 char ao_devicename
[56];
27 } shairplay_options_t
;
37 } shairplay_session_t
;
46 signal_handler(int sig
)
58 struct sigaction sigact
;
60 sigact
.sa_handler
= signal_handler
;
61 sigemptyset(&sigact
.sa_mask
);
63 sigaction(SIGINT
, &sigact
, NULL
);
64 sigaction(SIGTERM
, &sigact
, NULL
);
71 audio_open_device(shairplay_options_t
*opt
, int bits
, int channels
, int samplerate
)
73 ao_device
*device
= NULL
;
74 ao_option
*ao_options
= NULL
;
75 ao_sample_format format
;
78 /* Get the libao driver ID */
79 if (strlen(opt
->ao_driver
)) {
80 driver_id
= ao_driver_id(opt
->ao_driver
);
82 driver_id
= ao_default_driver_id();
85 /* Add all available libao options */
86 ao_append_option(&ao_options
, "client_name", opt
->apname
);
87 if (strlen(opt
->ao_devicename
)) {
88 ao_append_option(&ao_options
, "dev", opt
->ao_devicename
);
90 if (strlen(opt
->ao_deviceid
)) {
91 ao_append_option(&ao_options
, "id", opt
->ao_deviceid
);
94 /* Set audio format */
95 memset(&format
, 0, sizeof(format
));
97 format
.channels
= channels
;
98 format
.rate
= samplerate
;
99 format
.byte_format
= AO_FMT_NATIVE
;
101 /* Try opening the actual device */
102 device
= ao_open_live(driver_id
, &format
, ao_options
);
103 ao_free_options(ao_options
);
108 audio_init(void *cls
, int bits
, int channels
, int samplerate
)
110 shairplay_options_t
*options
= cls
;
111 shairplay_session_t
*session
;
113 session
= calloc(1, sizeof(shairplay_session_t
));
116 session
->device
= audio_open_device(options
, bits
, channels
, samplerate
);
117 if (session
->device
== NULL
) {
118 printf("Error opening device %d\n", errno
);
120 assert(session
->device
);
122 session
->buffering
= 1;
123 session
->volume
= 1.0f
;
128 audio_output(shairplay_session_t
*session
, const void *buffer
, int buflen
)
134 tmpbuflen
= (buflen
> sizeof(tmpbuf
)) ? sizeof(tmpbuf
) : buflen
;
135 memcpy(tmpbuf
, buffer
, tmpbuflen
);
136 if (ao_is_big_endian()) {
137 for (i
=0; i
<tmpbuflen
/2; i
++) {
138 char tmpch
= tmpbuf
[i
*2];
139 tmpbuf
[i
*2] = tmpbuf
[i
*2+1];
140 tmpbuf
[i
*2+1] = tmpch
;
143 shortbuf
= (short *)tmpbuf
;
144 for (i
=0; i
<tmpbuflen
/2; i
++) {
145 shortbuf
[i
] = shortbuf
[i
] * session
->volume
;
147 ao_play(session
->device
, tmpbuf
, tmpbuflen
);
152 audio_process(void *cls
, void *opaque
, const void *buffer
, int buflen
)
154 shairplay_session_t
*session
= opaque
;
157 if (session
->buffering
) {
158 printf("Buffering...\n");
159 if (session
->buflen
+buflen
< sizeof(session
->buffer
)) {
160 memcpy(session
->buffer
+session
->buflen
, buffer
, buflen
);
161 session
->buflen
+= buflen
;
164 session
->buffering
= 0;
165 printf("Finished buffering...\n");
168 while (processed
< session
->buflen
) {
169 processed
+= audio_output(session
,
170 session
->buffer
+processed
,
171 session
->buflen
-processed
);
177 while (processed
< buflen
) {
178 processed
+= audio_output(session
,
185 audio_destroy(void *cls
, void *opaque
)
187 shairplay_session_t
*session
= opaque
;
189 ao_close(session
->device
);
194 audio_set_volume(void *cls
, void *opaque
, float volume
)
196 shairplay_session_t
*session
= opaque
;
197 session
->volume
= pow(10.0, 0.05*volume
);
201 parse_options(shairplay_options_t
*opt
, int argc
, char *argv
[])
203 char *path
= argv
[0];
206 strcpy(opt
->apname
, "Shairplay");
209 while ((arg
= *++argv
)) {
210 if (!strcmp(arg
, "-a")) {
211 strncpy(opt
->apname
, *++argv
, sizeof(opt
->apname
)-1);
212 } else if (!strncmp(arg
, "--apname=", 9)) {
213 strncpy(opt
->apname
, arg
+9, sizeof(opt
->apname
)-1);
214 } else if (!strcmp(arg
, "-p")) {
215 strncpy(opt
->password
, *++argv
, sizeof(opt
->password
)-1);
216 } else if (!strncmp(arg
, "--password=", 11)) {
217 strncpy(opt
->password
, arg
+11, sizeof(opt
->password
)-1);
218 } else if (!strcmp(arg
, "-o")) {
219 opt
->port
= atoi(*++argv
);
220 } else if (!strncmp(arg
, "--server_port=", 14)) {
221 opt
->port
= atoi(arg
+14);
222 } else if (!strncmp(arg
, "--ao_driver=", 12)) {
223 strncpy(opt
->ao_driver
, arg
+12, sizeof(opt
->ao_driver
)-1);
224 } else if (!strncmp(arg
, "--ao_devicename=", 16)) {
225 strncpy(opt
->ao_devicename
, arg
+16, sizeof(opt
->ao_devicename
)-1);
226 } else if (!strncmp(arg
, "--ao_deviceid=", 14)) {
227 strncpy(opt
->ao_deviceid
, arg
+14, sizeof(opt
->ao_deviceid
)-1);
228 } else if (!strcmp(arg
, "-h") || !strcmp(arg
, "--help")) {
229 fprintf(stderr
, "Shairplay version %s\n", VERSION
);
230 fprintf(stderr
, "Usage: %s [OPTION...]\n", path
);
231 fprintf(stderr
, "\n");
232 fprintf(stderr
, " -a, --apname=AirPort Sets Airport name\n");
233 fprintf(stderr
, " -p, --password=secret Sets password\n");
234 fprintf(stderr
, " -o, --server_port=5000 Sets port for RAOP service\n");
235 fprintf(stderr
, " --ao_driver=driver Sets the ao driver (optional)\n");
236 fprintf(stderr
, " --ao_devicename=devicename Sets the ao device name (optional)\n");
237 fprintf(stderr
, " --ao_deviceid=id Sets the ao device id (optional)\n");
238 fprintf(stderr
, " -h, --help This help\n");
239 fprintf(stderr
, "\n");
244 /* Set default values for apname and port */
245 if (!strlen(opt
->apname
)) {
246 strncpy(opt
->apname
, "Shairplay", sizeof(opt
->apname
)-1);
255 main(int argc
, char *argv
[])
257 const char hwaddr
[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 };
259 shairplay_options_t options
;
260 ao_device
*device
= NULL
;
264 raop_callbacks_t raop_cbs
;
272 memset(&options
, 0, sizeof(options
));
273 if (parse_options(&options
, argc
, argv
)) {
279 device
= audio_open_device(&options
, 16, 2, 44100);
280 if (device
== NULL
) {
281 fprintf(stderr
, "Error opening audio device %d\n", errno
);
282 fprintf(stderr
, "Please check your libao settings and try again\n");
289 memset(&raop_cbs
, 0, sizeof(raop_cbs
));
290 raop_cbs
.cls
= &options
;
291 raop_cbs
.audio_init
= audio_init
;
292 raop_cbs
.audio_process
= audio_process
;
293 raop_cbs
.audio_destroy
= audio_destroy
;
294 raop_cbs
.audio_set_volume
= audio_set_volume
;
296 raop
= raop_init_from_keyfile(10, &raop_cbs
, "airport.key", NULL
);
298 fprintf(stderr
, "Could not initialize the RAOP service\n");
302 raop_set_log_level(raop
, RAOP_LOG_DEBUG
);
303 raop_start(raop
, &options
.port
, hwaddr
, sizeof(hwaddr
), NULL
);
306 dnssd
= dnssd_init(&error
);
308 fprintf(stderr
, "ERROR: Could not initialize dnssd library!\n");
309 fprintf(stderr
, "------------------------------------------\n");
310 fprintf(stderr
, "You could try the following resolutions based on your OS:\n");
311 fprintf(stderr
, "Windows: Try installing http://support.apple.com/kb/DL999\n");
312 fprintf(stderr
, "Debian/Ubuntu: Try installing libavahi-compat-libdnssd1 package\n");
317 dnssd_register_raop(dnssd
, options
.apname
, options
.port
, hwaddr
, sizeof(hwaddr
), 0);
328 dnssd_unregister_raop(dnssd
);
329 dnssd_destroy(dnssd
);