12 #include <shairplay/dnssd.h>
13 #include <shairplay/raop.h>
25 char ao_devicename
[56];
27 } shairplay_options_t
;
37 } shairplay_session_t
;
44 audio_open_device(shairplay_options_t
*opt
, int bits
, int channels
, int samplerate
)
46 ao_device
*device
= NULL
;
47 ao_option
*ao_options
= NULL
;
48 ao_sample_format format
;
51 /* Get the libao driver ID */
52 if (strlen(opt
->ao_driver
)) {
53 driver_id
= ao_driver_id(opt
->ao_driver
);
55 driver_id
= ao_default_driver_id();
58 /* Add all available libao options */
59 ao_append_option(&ao_options
, "client_name", opt
->apname
);
60 if (strlen(opt
->ao_devicename
)) {
61 ao_append_option(&ao_options
, "dev", opt
->ao_devicename
);
63 if (strlen(opt
->ao_deviceid
)) {
64 ao_append_option(&ao_options
, "id", opt
->ao_deviceid
);
67 /* Set audio format */
68 memset(&format
, 0, sizeof(format
));
70 format
.channels
= channels
;
71 format
.rate
= samplerate
;
72 format
.byte_format
= AO_FMT_NATIVE
;
74 /* Try opening the actual device */
75 device
= ao_open_live(driver_id
, &format
, ao_options
);
76 ao_free_options(ao_options
);
81 audio_init(void *cls
, int bits
, int channels
, int samplerate
)
83 shairplay_options_t
*options
= cls
;
84 shairplay_session_t
*session
;
86 session
= calloc(1, sizeof(shairplay_session_t
));
89 session
->device
= audio_open_device(options
, bits
, channels
, samplerate
);
90 if (session
->device
== NULL
) {
91 printf("Error opening device %d\n", errno
);
93 assert(session
->device
);
95 session
->buffering
= 1;
96 session
->volume
= 1.0f
;
101 audio_output(shairplay_session_t
*session
, const void *buffer
, int buflen
)
107 tmpbuflen
= (buflen
> sizeof(tmpbuf
)) ? sizeof(tmpbuf
) : buflen
;
108 memcpy(tmpbuf
, buffer
, tmpbuflen
);
109 if (ao_is_big_endian()) {
110 for (i
=0; i
<tmpbuflen
/2; i
++) {
111 char tmpch
= tmpbuf
[i
*2];
112 tmpbuf
[i
*2] = tmpbuf
[i
*2+1];
113 tmpbuf
[i
*2+1] = tmpch
;
116 shortbuf
= (short *)tmpbuf
;
117 for (i
=0; i
<tmpbuflen
/2; i
++) {
118 shortbuf
[i
] = shortbuf
[i
] * session
->volume
;
120 ao_play(session
->device
, tmpbuf
, tmpbuflen
);
125 audio_process(void *cls
, void *opaque
, const void *buffer
, int buflen
)
127 shairplay_session_t
*session
= opaque
;
130 if (session
->buffering
) {
131 printf("Buffering...\n");
132 if (session
->buflen
+buflen
< sizeof(session
->buffer
)) {
133 memcpy(session
->buffer
+session
->buflen
, buffer
, buflen
);
134 session
->buflen
+= buflen
;
137 session
->buffering
= 0;
138 printf("Finished buffering...\n");
141 while (processed
< session
->buflen
) {
142 processed
+= audio_output(session
,
143 session
->buffer
+processed
,
144 session
->buflen
-processed
);
150 while (processed
< buflen
) {
151 processed
+= audio_output(session
,
158 audio_destroy(void *cls
, void *opaque
)
160 shairplay_session_t
*session
= opaque
;
162 ao_close(session
->device
);
167 audio_set_volume(void *cls
, void *opaque
, float volume
)
169 shairplay_session_t
*session
= opaque
;
170 session
->volume
= pow(10.0, 0.05*volume
);
174 parse_options(shairplay_options_t
*opt
, int argc
, char *argv
[])
176 char *path
= argv
[0];
179 strcpy(opt
->apname
, "Shairplay");
182 while ((arg
= *++argv
)) {
183 if (!strcmp(arg
, "-a")) {
184 strncpy(opt
->apname
, *++argv
, sizeof(opt
->apname
)-1);
185 } else if (!strncmp(arg
, "--apname=", 9)) {
186 strncpy(opt
->apname
, arg
+9, sizeof(opt
->apname
)-1);
187 } else if (!strcmp(arg
, "-p")) {
188 strncpy(opt
->password
, *++argv
, sizeof(opt
->password
)-1);
189 } else if (!strncmp(arg
, "--password=", 11)) {
190 strncpy(opt
->password
, arg
+11, sizeof(opt
->password
)-1);
191 } else if (!strcmp(arg
, "-o")) {
192 opt
->port
= atoi(*++argv
);
193 } else if (!strncmp(arg
, "--server_port=", 14)) {
194 opt
->port
= atoi(arg
+14);
195 } else if (!strncmp(arg
, "--ao_driver=", 12)) {
196 strncpy(opt
->ao_driver
, arg
+12, sizeof(opt
->ao_driver
)-1);
197 } else if (!strncmp(arg
, "--ao_devicename=", 16)) {
198 strncpy(opt
->ao_devicename
, arg
+16, sizeof(opt
->ao_devicename
)-1);
199 } else if (!strncmp(arg
, "--ao_deviceid=", 14)) {
200 strncpy(opt
->ao_deviceid
, arg
+14, sizeof(opt
->ao_deviceid
)-1);
201 } else if (!strcmp(arg
, "-h") || !strcmp(arg
, "--help")) {
202 fprintf(stderr
, "Shairplay version %s\n", VERSION
);
203 fprintf(stderr
, "Usage: %s [OPTION...]\n", path
);
204 fprintf(stderr
, "\n");
205 fprintf(stderr
, " -a, --apname=AirPort Sets Airport name\n");
206 fprintf(stderr
, " -p, --password=secret Sets password\n");
207 fprintf(stderr
, " -o, --server_port=5000 Sets port for RAOP service\n");
208 fprintf(stderr
, " --ao_driver=driver Sets the ao driver (optional)\n");
209 fprintf(stderr
, " --ao_devicename=devicename Sets the ao device name (optional)\n");
210 fprintf(stderr
, " --ao_deviceid=id Sets the ao device id (optional)\n");
211 fprintf(stderr
, " -h, --help This help\n");
212 fprintf(stderr
, "\n");
217 /* Set default values for apname and port */
218 if (!strlen(opt
->apname
)) {
219 strncpy(opt
->apname
, "Shairplay", sizeof(opt
->apname
)-1);
228 main(int argc
, char *argv
[])
230 const char hwaddr
[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 };
232 shairplay_options_t options
;
233 ao_device
*device
= NULL
;
237 raop_callbacks_t raop_cbs
;
241 memset(&options
, 0, sizeof(options
));
242 if (parse_options(&options
, argc
, argv
)) {
248 device
= audio_open_device(&options
, 16, 2, 44100);
249 if (device
== NULL
) {
250 fprintf(stderr
, "Error opening audio device %d\n", errno
);
251 fprintf(stderr
, "Please check your libao settings and try again\n");
258 memset(&raop_cbs
, 0, sizeof(raop_cbs
));
259 raop_cbs
.cls
= &options
;
260 raop_cbs
.audio_init
= audio_init
;
261 raop_cbs
.audio_process
= audio_process
;
262 raop_cbs
.audio_destroy
= audio_destroy
;
263 raop_cbs
.audio_set_volume
= audio_set_volume
;
265 raop
= raop_init_from_keyfile(10, &raop_cbs
, "airport.key", NULL
);
267 fprintf(stderr
, "Could not initialize the RAOP service\n");
271 raop_set_log_level(raop
, RAOP_LOG_DEBUG
);
272 raop_start(raop
, &options
.port
, hwaddr
, sizeof(hwaddr
), NULL
);
275 dnssd
= dnssd_init(&error
);
277 fprintf(stderr
, "ERROR: Could not initialize dnssd library!\n");
278 fprintf(stderr
, "------------------------------------------\n");
279 fprintf(stderr
, "You could try the following resolutions based on your OS:\n");
280 fprintf(stderr
, "Windows: Try installing http://support.apple.com/kb/DL999\n");
281 fprintf(stderr
, "Debian/Ubuntu: Try installing libavahi-compat-libdnssd1 package\n");
286 dnssd_register_raop(dnssd
, options
.apname
, options
.port
, hwaddr
, sizeof(hwaddr
), 0);
287 dnssd_register_airplay(dnssd
, options
.apname
, 1234, hwaddr
, sizeof(hwaddr
));
298 dnssd_unregister_raop(dnssd
);
299 dnssd_destroy(dnssd
);