12 #include <shairplay/dnssd.h>
13 #include <shairplay/raop.h>
25 char ao_devicename
[56];
27 } shairplay_options_t
;
37 } shairplay_session_t
;
41 audio_open_device(shairplay_options_t
*opt
, int bits
, int channels
, int samplerate
)
43 ao_device
*device
= NULL
;
44 ao_option
*ao_options
= NULL
;
45 ao_sample_format format
;
48 /* Get the libao driver ID */
49 if (strlen(opt
->ao_driver
)) {
50 driver_id
= ao_driver_id(opt
->ao_driver
);
52 driver_id
= ao_default_driver_id();
55 /* Add all available libao options */
56 ao_append_option(&ao_options
, "client_name", opt
->apname
);
57 if (strlen(opt
->ao_devicename
)) {
58 ao_append_option(&ao_options
, "dev", opt
->ao_devicename
);
60 if (strlen(opt
->ao_deviceid
)) {
61 ao_append_option(&ao_options
, "id", opt
->ao_deviceid
);
64 /* Set audio format */
65 memset(&format
, 0, sizeof(format
));
67 format
.channels
= channels
;
68 format
.rate
= samplerate
;
69 format
.byte_format
= AO_FMT_NATIVE
;
71 /* Try opening the actual device */
72 device
= ao_open_live(driver_id
, &format
, ao_options
);
73 ao_free_options(ao_options
);
78 audio_init(void *cls
, int bits
, int channels
, int samplerate
)
80 shairplay_options_t
*options
= cls
;
81 shairplay_session_t
*session
;
83 session
= calloc(1, sizeof(shairplay_session_t
));
86 session
->device
= audio_open_device(options
, bits
, channels
, samplerate
);
87 if (session
->device
== NULL
) {
88 printf("Error opening device %d\n", errno
);
90 assert(session
->device
);
92 session
->buffering
= 1;
93 session
->volume
= 1.0f
;
98 audio_output(shairplay_session_t
*session
, const void *buffer
, int buflen
)
104 tmpbuflen
= (buflen
> sizeof(tmpbuf
)) ? sizeof(tmpbuf
) : buflen
;
105 memcpy(tmpbuf
, buffer
, tmpbuflen
);
106 if (ao_is_big_endian()) {
107 for (i
=0; i
<tmpbuflen
/2; i
++) {
108 char tmpch
= tmpbuf
[i
*2];
109 tmpbuf
[i
*2] = tmpbuf
[i
*2+1];
110 tmpbuf
[i
*2+1] = tmpch
;
113 shortbuf
= (short *)tmpbuf
;
114 for (i
=0; i
<tmpbuflen
/2; i
++) {
115 shortbuf
[i
] = shortbuf
[i
] * session
->volume
;
117 ao_play(session
->device
, tmpbuf
, tmpbuflen
);
122 audio_process(void *cls
, void *opaque
, const void *buffer
, int buflen
)
124 shairplay_session_t
*session
= opaque
;
127 if (session
->buffering
) {
128 printf("Buffering...\n");
129 if (session
->buflen
+buflen
< sizeof(session
->buffer
)) {
130 memcpy(session
->buffer
+session
->buflen
, buffer
, buflen
);
131 session
->buflen
+= buflen
;
134 session
->buffering
= 0;
135 printf("Finished buffering...\n");
138 while (processed
< session
->buflen
) {
139 processed
+= audio_output(session
,
140 session
->buffer
+processed
,
141 session
->buflen
-processed
);
147 while (processed
< buflen
) {
148 processed
+= audio_output(session
,
155 audio_destroy(void *cls
, void *opaque
)
157 shairplay_session_t
*session
= opaque
;
159 ao_close(session
->device
);
164 audio_set_volume(void *cls
, void *opaque
, float volume
)
166 shairplay_session_t
*session
= opaque
;
167 session
->volume
= pow(10.0, 0.05*volume
);
171 parse_options(shairplay_options_t
*opt
, int argc
, char *argv
[])
173 char *path
= argv
[0];
176 strcpy(opt
->apname
, "Shairplay");
179 while ((arg
= *++argv
)) {
180 if (!strcmp(arg
, "-a")) {
181 strncpy(opt
->apname
, *++argv
, sizeof(opt
->apname
)-1);
182 } else if (!strncmp(arg
, "--apname=", 9)) {
183 strncpy(opt
->apname
, arg
+9, sizeof(opt
->apname
)-1);
184 } else if (!strcmp(arg
, "-p")) {
185 strncpy(opt
->password
, *++argv
, sizeof(opt
->password
)-1);
186 } else if (!strncmp(arg
, "--password=", 11)) {
187 strncpy(opt
->password
, arg
+11, sizeof(opt
->password
)-1);
188 } else if (!strcmp(arg
, "-o")) {
189 opt
->port
= atoi(*++argv
);
190 } else if (!strncmp(arg
, "--server_port=", 14)) {
191 opt
->port
= atoi(arg
+14);
192 } else if (!strncmp(arg
, "--ao_driver=", 12)) {
193 strncpy(opt
->ao_driver
, arg
+12, sizeof(opt
->ao_driver
)-1);
194 } else if (!strncmp(arg
, "--ao_devicename=", 16)) {
195 strncpy(opt
->ao_devicename
, arg
+16, sizeof(opt
->ao_devicename
)-1);
196 } else if (!strncmp(arg
, "--ao_deviceid=", 14)) {
197 strncpy(opt
->ao_deviceid
, arg
+14, sizeof(opt
->ao_deviceid
)-1);
198 } else if (!strcmp(arg
, "-h") || !strcmp(arg
, "--help")) {
199 fprintf(stderr
, "Shairplay version %s\n", VERSION
);
200 fprintf(stderr
, "Usage: %s [OPTION...]\n", path
);
201 fprintf(stderr
, "\n");
202 fprintf(stderr
, " -a, --apname=AirPort Sets Airport name\n");
203 fprintf(stderr
, " -p, --password=secret Sets password\n");
204 fprintf(stderr
, " -o, --server_port=5000 Sets port for RAOP service\n");
205 fprintf(stderr
, " --ao_driver=driver Sets the ao driver (optional)\n");
206 fprintf(stderr
, " --ao_devicename=devicename Sets the ao device name (optional)\n");
207 fprintf(stderr
, " --ao_deviceid=id Sets the ao device id (optional)\n");
208 fprintf(stderr
, " -h, --help This help\n");
209 fprintf(stderr
, "\n");
214 /* Set default values for apname and port */
215 if (!strlen(opt
->apname
)) {
216 strncpy(opt
->apname
, "Shairplay", sizeof(opt
->apname
)-1);
225 main(int argc
, char *argv
[])
227 const char hwaddr
[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 };
229 shairplay_options_t options
;
230 ao_device
*device
= NULL
;
234 raop_callbacks_t raop_cbs
;
236 memset(&options
, 0, sizeof(options
));
237 if (parse_options(&options
, argc
, argv
)) {
243 device
= audio_open_device(&options
, 16, 2, 44100);
244 if (device
== NULL
) {
245 fprintf(stderr
, "Error opening audio device %d\n", errno
);
246 fprintf(stderr
, "Please check your libao settings and try again\n");
253 memset(&raop_cbs
, 0, sizeof(raop_cbs
));
254 raop_cbs
.cls
= &options
;
255 raop_cbs
.audio_init
= audio_init
;
256 raop_cbs
.audio_process
= audio_process
;
257 raop_cbs
.audio_destroy
= audio_destroy
;
258 raop_cbs
.audio_set_volume
= audio_set_volume
;
260 raop
= raop_init_from_keyfile(10, &raop_cbs
, "airport.key", NULL
);
262 fprintf(stderr
, "Could not initialize the RAOP service\n");
266 raop_set_log_level(raop
, RAOP_LOG_DEBUG
);
267 raop_start(raop
, &options
.port
, hwaddr
, sizeof(hwaddr
), NULL
);
269 dnssd
= dnssd_init(NULL
);
270 dnssd_register_raop(dnssd
, options
.apname
, options
.port
, hwaddr
, sizeof(hwaddr
), 0);
278 dnssd_unregister_raop(dnssd
);
279 dnssd_destroy(dnssd
);