25 | 25 |
#if ALSA
|
26 | 26 |
#include <alsa/asoundlib.h>
|
27 | 27 |
#include <sys/mman.h>
|
|
28 |
#include <malloc.h>
|
28 | 29 |
#endif
|
29 | 30 |
#if PORTAUDIO
|
30 | 31 |
#include <portaudio.h>
|
|
41 | 42 |
#if ALSA
|
42 | 43 |
|
43 | 44 |
#define MAX_SILENCE_FRAMES 1024
|
|
45 |
#define MAX_DEVICE_LEN 128
|
44 | 46 |
|
45 | 47 |
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,
|
46 | 48 |
SND_PCM_FORMAT_UNKNOWN };
|
|
52 | 54 |
|
53 | 55 |
// ouput device
|
54 | 56 |
static struct {
|
55 | |
char *device;
|
|
57 |
char device[MAX_DEVICE_LEN + 1];
|
56 | 58 |
snd_pcm_format_t format;
|
57 | 59 |
snd_pcm_uframes_t period_size;
|
58 | 60 |
unsigned rate;
|
|
198 | 200 |
if (*pcmp) alsa_close(*pcmp);
|
199 | 201 |
|
200 | 202 |
// reset params
|
201 | |
if (alsa.device) free(alsa.device);
|
202 | |
alsa.device = NULL;
|
203 | 203 |
alsa.rate = 0;
|
204 | 204 |
alsa.period_size = 0;
|
205 | |
alsa.device = malloc(strlen(device) + 1 + 4); // extra space for changing to plug
|
206 | 205 |
strcpy(alsa.device, device);
|
|
206 |
|
|
207 |
if (strlen(device) > MAX_DEVICE_LEN - 4 - 1) {
|
|
208 |
LOG_ERROR("device name too long: %s", device);
|
|
209 |
return -1;
|
|
210 |
}
|
207 | 211 |
|
208 | 212 |
bool retry;
|
209 | 213 |
do {
|
|
482 | 486 |
outputParameters.hostApiSpecificStreamInfo = NULL;
|
483 | 487 |
|
484 | 488 |
#if OSX
|
485 | |
// enable pro mode which aims to avoid resampling if possible
|
|
489 |
// enable pro mode which aims to avoid resampling if possible for non built in devices
|
|
490 |
// see http://code.google.com/p/squeezelite/issues/detail?id=11 for reason for not doing with built in device
|
486 | 491 |
PaMacCoreStreamInfo macInfo;
|
487 | |
PaMacCore_SetupStreamInfo(&macInfo, paMacCorePro);
|
|
492 |
unsigned long streamInfoFlags;
|
|
493 |
if (!strcmp(Pa_GetDeviceInfo(outputParameters.device)->name, "Built-in Output")) {
|
|
494 |
LOG_INFO("opening device in PlayNice mode");
|
|
495 |
streamInfoFlags = paMacCorePlayNice;
|
|
496 |
} else {
|
|
497 |
LOG_INFO("opening device in Pro mode");
|
|
498 |
streamInfoFlags = paMacCorePro;
|
|
499 |
}
|
|
500 |
PaMacCore_SetupStreamInfo(&macInfo, streamInfoFlags);
|
488 | 501 |
outputParameters.hostApiSpecificStreamInfo = &macInfo;
|
489 | 502 |
#endif
|
490 | 503 |
}
|
|
840 | 853 |
cross_ptr = (s32_t *)(output.fade_end + cur_f * BYTES_PER_FRAME);
|
841 | 854 |
} else {
|
842 | 855 |
LOG_INFO("unable to continue crossfade - too few samples");
|
843 | |
output.fade = FADE_NONE;
|
|
856 |
output.fade = FADE_INACTIVE;
|
844 | 857 |
}
|
845 | 858 |
}
|
846 | 859 |
}
|
|
1251 | 1264 |
// if default setting used and nothing in buffer attempt to resize to provide full crossfade support
|
1252 | 1265 |
LOG_INFO("resize outputbuf for crossfade");
|
1253 | 1266 |
_buf_resize(outputbuf, OUTPUTBUF_SIZE_CROSSFADE);
|
|
1267 |
#if LINUX
|
|
1268 |
touch_memory(outputbuf->buf, outputbuf->size);
|
|
1269 |
#endif
|
1254 | 1270 |
}
|
1255 | 1271 |
}
|
1256 | 1272 |
}
|
|
1279 | 1295 |
|
1280 | 1296 |
LOCK;
|
1281 | 1297 |
|
1282 | |
output.state = STOPPED;
|
|
1298 |
output.state = OUTPUT_STOPPED;
|
1283 | 1299 |
output.current_sample_rate = 44100;
|
1284 | 1300 |
output.device = device;
|
1285 | 1301 |
output.fade = FADE_INACTIVE;
|
|
1326 | 1342 |
LOG_INFO("output: %s maxrate: %u", output.device, output.max_sample_rate);
|
1327 | 1343 |
|
1328 | 1344 |
#if ALSA
|
|
1345 |
|
|
1346 |
#if LINUX
|
|
1347 |
// RT linux - aim to avoid pagefaults by locking memory:
|
|
1348 |
// https://rt.wiki.kernel.org/index.php/Threaded_RT-application_with_memory_locking_and_stack_handling_example
|
|
1349 |
if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
|
|
1350 |
LOG_INFO("unable to lock memory: %s", strerror(errno));
|
|
1351 |
} else {
|
|
1352 |
LOG_INFO("memory locked");
|
|
1353 |
}
|
|
1354 |
|
|
1355 |
mallopt(M_TRIM_THRESHOLD, -1);
|
|
1356 |
mallopt(M_MMAP_MAX, 0);
|
|
1357 |
|
|
1358 |
touch_memory(silencebuf, MAX_SILENCE_FRAMES * BYTES_PER_FRAME);
|
|
1359 |
touch_memory(outputbuf->buf, outputbuf->size);
|
|
1360 |
#endif
|
|
1361 |
|
|
1362 |
// start output thread
|
1329 | 1363 |
pthread_attr_t attr;
|
1330 | 1364 |
pthread_attr_init(&attr);
|
1331 | |
pthread_attr_setstacksize(&attr, STREAM_THREAD_STACK_SIZE);
|
|
1365 |
pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE);
|
1332 | 1366 |
pthread_create(&thread, &attr, output_thread, max_rate ? "probe" : NULL);
|
1333 | 1367 |
pthread_attr_destroy(&attr);
|
1334 | 1368 |
|
|
1340 | 1374 |
} else {
|
1341 | 1375 |
LOG_DEBUG("set output sched fifo rt: %u", param.sched_priority);
|
1342 | 1376 |
}
|
1343 | |
|
1344 | |
// try to lock memory into RAM to avoid delay on page faults, only works as root or if user has permission
|
1345 | |
if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
|
1346 | |
LOG_INFO("unable to lock memory: %s", strerror(errno));
|
1347 | |
} else {
|
1348 | |
LOG_INFO("memory locked");
|
1349 | |
}
|
1350 | 1377 |
#endif
|
1351 | 1378 |
|
1352 | 1379 |
#if PORTAUDIO
|