Codebase list squeezelite / ec4180b
0.9beta7 add support for forcing alsa sample size to 32, 24, 24_3 or 16 bits extend -a alsa option to combine all alsa options including mmap and bitdepth fix big endian support try to recover better from writei errors ensure squeezelite quits on ^C during server discovery Adrian Smith 11 years ago
4 changed file(s) with 89 addition(s) and 43 deletion(s). Raw diff Collapse all Expand all
3030 " -o <output device>\tSpecify output device, default \"default\"\n"
3131 " -l \t\t\tList output devices\n"
3232 #if ALSA
33 " -a <time>:<count>\tSpecify ALSA buffer_time (ms) and period_count, default 20:4\n"
33 " -a <b>:<c>:<f>:<m>\tSpecify ALSA params to open output device, b = buffer time in ms, c = period count, f sample format (16|24|24_3|32), m = use mmap (0|1)\n"
3434 #endif
3535 #if PORTAUDIO
3636 " -a <latency>\t\tSpecify output target latency in ms\n"
3737 #endif
3838 " -b <stream>:<output>\tSpecify internal Stream and Output buffer sizes in Kbytes\n"
39 " -c <codec1>,<codec2>\tRestrict codecs those specified, otherwise loads all available codecs; known codecs: flac,pcm,mp3,ogg,aac\n"
39 " -c <codec1>,<codec2>\tRestrict codecs those specified, otherwise loads all available codecs; known codecs: flac,pcm,mp3,ogg,aac (mad,mpg for specific mp3 codec)\n"
4040 " -d <log>=<level>\tSet logging level, logs: all|slimproto|stream|decode|output, level: info|debug|sdebug\n"
4141 " -f <logfile>\t\tWrite debug to logfile\n"
4242 " -m <mac addr>\t\tSet mac address, format: ab:cd:ef:12:34:56\n"
4343 " -n <name>\t\tSet the player name\n"
4444 " -r <rate>\t\tMax sample rate for output device, enables output device to be off when squeezelite is started\n"
45 #if ALSA
46 " -w \t\t\tDisable ASLA mmap output\n"
47 #endif
4845 #if LINUX
4946 " -z \t\t\tDaemonize\n"
5047 #endif
6865 );
6966 }
7067
71 void sighandler(int signum) {
68 static void sighandler(int signum) {
7269 slimproto_stop();
70 }
71
72 static char *next_param(char *src, char c) {
73 static char *str = NULL;
74 char *ptr, *ret;
75 if (src) str = src;
76 if (str && (ptr = strchr(str, c))) {
77 ret = str;
78 *ptr = '\0';
79 str = ptr + 1;
80 return ret[0] ? ret : NULL;
81 } else {
82 ret = str;
83 str = NULL;
84 return ret;
85 }
7386 }
7487
7588 int main(int argc, char **argv) {
88101 #if ALSA
89102 unsigned alsa_buffer_time = ALSA_BUFFER_TIME;
90103 unsigned alsa_period_count = ALSA_PERIOD_COUNT;
91 unsigned alsa_mmap = true;
104 char *alsa_sample_fmt = NULL;
105 bool alsa_mmap = true;
92106 #endif
93107 #if PORTAUDIO
94108 unsigned pa_latency = 0;
124138 case 'a':
125139 {
126140 #if ALSA
127 char *t = strtok(optarg, ":");
128 char *c = strtok(NULL, ":");
141 char *t = next_param(optarg, ':');
142 char *c = next_param(NULL, ':');
143 char *s = next_param(NULL, ':');
144 char *m = next_param(NULL, ':');
129145 if (t) alsa_buffer_time = atoi(t) * 1000;
130146 if (c) alsa_period_count = atoi(c);
147 if (s) alsa_sample_fmt = s;
148 if (m) alsa_mmap = atoi(m);
131149 #endif
132150 #if PORTAUDIO
133151 pa_latency = atoi(optarg);
136154 break;
137155 case 'b':
138156 {
139 char *s = strtok(optarg, ":");
140 char *o = strtok(NULL, ":");
157 char *s = next_param(optarg, ':');
158 char *o = next_param(NULL, ':');
141159 if (s) stream_buf_size = atoi(s) * 1024;
142160 if (o) output_buf_size = atoi(o) * 1024;
143161 }
188206 list_devices();
189207 exit(0);
190208 break;
191 #if ALSA
192 case 'w':
193 alsa_mmap = false;
194 break;
195 #endif
196209 #if LINUX
197210 case 'z':
198211 daemonize = true;
239252 #endif
240253
241254 #if ALSA
242 output_init(log_output, output_device, output_buf_size, alsa_buffer_time, alsa_period_count, alsa_mmap, max_rate);
255 output_init(log_output, output_device, output_buf_size, alsa_buffer_time, alsa_period_count, alsa_sample_fmt, alsa_mmap, max_rate);
243256 #endif
244257 #if PORTAUDIO
245258 output_init(log_output, output_device, output_buf_size, pa_latency, max_rate);
2222 // - PortAudio is the output output supported on other platforms and also builds on linux for test purposes
2323
2424 #include "squeezelite.h"
25
2625 #if ALSA
27
2826 #include <alsa/asoundlib.h>
27 #endif
28 #if PORTAUDIO
29 #include <portaudio.h>
30 #if OSX
31 #include <pa_mac_core.h>
32 #endif
33 #endif
34
35 #if defined LITTLE_ENDIAN
36 #undef LITTLE_ENDIAN
37 #endif
38 #define LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
39
40 #if ALSA
2941
3042 #define MAX_SILENCE_FRAMES 1024
3143
3244 static snd_pcm_format_t fmts[] = { SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S16_LE,
3345 SND_PCM_FORMAT_UNKNOWN };
46 #if LITTLE_ENDIAN
47 #define NATIVE_FORMAT SND_PCM_FORMAT_S32_LE
48 #else
49 #define NATIVE_FORMAT SND_PCM_FORMAT_S32_BE
50 #endif
3451
3552 // ouput device
3653 static struct {
4764 #endif // ALSA
4865
4966 #if PORTAUDIO
50
51 #include <portaudio.h>
52 #if OSX
53 #include <pa_mac_core.h>
54 #endif
5567
5668 #define MAX_SILENCE_FRAMES 102400 // silencebuf not used in pa case so set large
5769
189201 alsa.device = NULL;
190202 alsa.rate = 0;
191203 alsa.period_size = 0;
192 alsa.format = SND_PCM_FORMAT_UNKNOWN;
193204 alsa.device = malloc(strlen(device) + 1 + 4); // extra space for changing to plug
194205 strcpy(alsa.device, device);
195206
239250 }
240251
241252 // set the sample format
242 snd_pcm_format_t *fmt = (snd_pcm_format_t *)fmts;
243 while (*fmt != SND_PCM_FORMAT_UNKNOWN) {
253 snd_pcm_format_t *fmt = alsa.format ? &alsa.format : (snd_pcm_format_t *)fmts;
254 do {
244255 if (snd_pcm_hw_params_set_format(*pcmp, hw_params, *fmt) >= 0) {
245256 LOG_INFO("opened device %s using format: %s sample rate: %u mmap: %u", alsa.device, snd_pcm_format_name(*fmt), sample_rate, alsa.mmap);
246257 alsa.format = *fmt;
247258 break;
248259 }
260 if (alsa.format) {
261 LOG_ERROR("unable to open audio device requested format: %s", snd_pcm_format_name(alsa.format));
262 return -1;
263 }
249264 ++fmt;
250265 if (*fmt == SND_PCM_FORMAT_UNKNOWN) {
251266 LOG_ERROR("unable to open audio device with any supported format");
252267 return -1;
253268 }
254 }
269 } while (*fmt != SND_PCM_FORMAT_UNKNOWN);
255270
256271 // set channels
257272 if ((err = snd_pcm_hw_params_set_channels (*pcmp, hw_params, 2)) < 0) {
288303
289304 LOG_INFO("buffer time: %u period count: %u buffer size: %u period size: %u", time, count, buffer_size, alsa.period_size);
290305
291 // create an intermediate buffer for non mmap case for all but S32_LE
306 // create an intermediate buffer for non mmap case for all but NATIVE_FORMAT
292307 // this is used to pack samples into the output format before calling writei
293 if (!alsa.mmap && !alsa.write_buf && alsa.format != SND_PCM_FORMAT_S32_LE) {
308 if (!alsa.mmap && !alsa.write_buf && alsa.format != NATIVE_FORMAT) {
294309 alsa.write_buf = malloc(alsa.period_size * BYTES_PER_FRAME);
295310 if (!alsa.write_buf) {
296311 LOG_ERROR("unable to malloc write_buf");
830845 out_frames = !silence ? min(size, cont_frames) : size;
831846
832847 #if ALSA
833 if (alsa.mmap || alsa.format != SND_PCM_FORMAT_S32_LE) {
834
835 // in all alsa cases except SND_PCM_FORMAT_S32_LE non mmap we take this path:
848 if (alsa.mmap || alsa.format != NATIVE_FORMAT) {
849
850 // in all alsa cases except NATIVE_FORMAT non mmap we take this path:
836851 // - mmap: scale and pack to output format, write direct into mmap region
837852 // - non mmap: scale and pack into alsa.write_buf, which is the used with writei to send to alsa
838853 const snd_pcm_channel_area_t *areas;
10711086 } else {
10721087 snd_pcm_sframes_t w = snd_pcm_writei(pcmp, alsa.write_buf, out_frames);
10731088 if (w < 0) {
1074 LOG_WARN("writei error: %d", w);
1089 if (w != -EAGAIN && (err = snd_pcm_recover(pcmp, w, 1)) < 0) {
1090 LOG_WARN("recover failed: %s", snd_strerror(err));
1091 }
10751092 break;
10761093 } else {
10771094 if (w != out_frames) {
11151132 }
11161133 }
11171134 #if ALSA
1118 // only used in S32_LE non mmap case, write the 32 samples straight with writei, no need for intermediate buffer
1135 // only used in S32_LE non mmap LE case, write the 32 samples straight with writei, no need for intermediate buffer
11191136 snd_pcm_sframes_t w = snd_pcm_writei(pcmp, silence ? silencebuf : outputbuf->readp, out_frames);
11201137 if (w < 0) {
1121 LOG_WARN("writei error: %d", w);
1138 if (w != -EAGAIN && (err = snd_pcm_recover(pcmp, w, 1)) < 0) {
1139 LOG_WARN("recover failed: %s", snd_strerror(err));
1140 }
11221141 break;
11231142 } else {
11241143 if (w != out_frames) {
12331252
12341253 #if ALSA
12351254 static pthread_t thread;
1236 void output_init(log_level level, const char *device, unsigned output_buf_size, unsigned buffer_time, unsigned period_count, bool mmap, unsigned max_rate) {
1255 void output_init(log_level level, const char *device, unsigned output_buf_size, unsigned buffer_time, unsigned period_count,
1256 const char *alsa_sample_fmt, bool mmap, unsigned max_rate) {
12371257 #endif
12381258 #if PORTAUDIO
12391259 void output_init(log_level level, const char *device, unsigned output_buf_size, unsigned latency, unsigned max_rate) {
12621282 #if ALSA
12631283 alsa.mmap = mmap;
12641284 alsa.write_buf = NULL;
1285 alsa.format = 0;
12651286 output.buffer_time = buffer_time;
12661287 output.period_count = period_count;
12671288
1268 LOG_INFO("requested buffer_time: %u period_count: %u mmap: %u", output.buffer_time, output.period_count, alsa.mmap);
1289 if (alsa_sample_fmt) {
1290 if (!strcmp(alsa_sample_fmt, "32")) alsa.format = SND_PCM_FORMAT_S32_LE;
1291 if (!strcmp(alsa_sample_fmt, "24")) alsa.format = SND_PCM_FORMAT_S24_LE;
1292 if (!strcmp(alsa_sample_fmt, "24_3")) alsa.format = SND_PCM_FORMAT_S24_3LE;
1293 if (!strcmp(alsa_sample_fmt, "16")) alsa.format = SND_PCM_FORMAT_S16_LE;
1294 }
1295
1296 LOG_INFO("requested buffer_time: %u period_count: %u format: %s mmap: %u", output.buffer_time, output.period_count,
1297 alsa_sample_fmt ? alsa_sample_fmt : "any", alsa.mmap);
12691298 #endif
12701299
12711300 #if PORTAUDIO
610610 LOG_INFO("error sending disovery");
611611 }
612612
613 if (poll(&pollinfo, 1, 5000)) {
613 if (poll(&pollinfo, 1, 5000) && running) {
614614 char readbuf[10];
615615 socklen_t slen = sizeof(s);
616616 recvfrom(disc_sock, readbuf, 10, 0, (struct sockaddr *)&s, &slen);
617617 LOG_INFO("got response from: %s:%d", inet_ntoa(s.sin_addr), ntohs(s.sin_port));
618618 }
619619
620 } while (s.sin_addr.s_addr == 0);
620 } while (s.sin_addr.s_addr == 0 && running);
621621
622622 closesocket(disc_sock);
623623
633633 wake_create(wake_e);
634634
635635 loglevel = level;
636 running = true;
637
636638 slimproto_ip = addr ? inet_addr(addr) : discover_server();
639
640 if (!running) return;
637641
638642 LOCK_O;
639643 sprintf(fixed_cap, ",MaxSampleRate=%u", output.max_sample_rate);
653657
654658 LOG_INFO("connecting to %s:%d", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
655659
656 running = true;
657660 new_server = 0;
658661
659662 while (running) {
721724 }
722725
723726 void slimproto_stop(void) {
727 LOG_INFO("slimproto stop");
724728 running = false;
725729 }
1717 *
1818 */
1919
20 #define VERSION "v0.9beta6"
20 #define VERSION "v0.9beta7"
2121
2222 // build detection
2323 #if defined(linux)
401401
402402 void list_devices(void);
403403 #if ALSA
404 void output_init(log_level level, const char *device, unsigned output_buf_size, unsigned buffer_time, unsigned period_count, bool mmap, unsigned max_rate);
404 void output_init(log_level level, const char *device, unsigned output_buf_size, unsigned buffer_time, unsigned period_count, const char *alsa_sample_fmt, bool mmap, unsigned max_rate);
405405 #endif
406406 #if PORTAUDIO
407407 void output_init(log_level level, const char *device, unsigned output_buf_size, unsigned latency, unsigned max_rate);