33 | 33 |
#include <pa_mac_core.h>
|
34 | 34 |
#endif
|
35 | 35 |
#endif
|
|
36 |
#if VISEXPORT && !ALSA
|
|
37 |
#include <sys/mman.h>
|
|
38 |
#include <fcntl.h>
|
|
39 |
#endif
|
36 | 40 |
|
37 | 41 |
#if ALSA
|
38 | 42 |
|
|
73 | 77 |
} pa;
|
74 | 78 |
|
75 | 79 |
#endif // PORTAUDIO
|
|
80 |
|
|
81 |
#if VISEXPORT
|
|
82 |
|
|
83 |
#define VIS_BUF_SIZE 16384
|
|
84 |
#define VIS_LOCK_NS 1000000 // ns to wait for vis wrlock
|
|
85 |
|
|
86 |
static struct vis_t {
|
|
87 |
pthread_rwlock_t rwlock;
|
|
88 |
u32_t buf_size;
|
|
89 |
u32_t buf_index;
|
|
90 |
bool running;
|
|
91 |
u32_t rate;
|
|
92 |
time_t updated;
|
|
93 |
s16_t buffer[VIS_BUF_SIZE];
|
|
94 |
} *vis_mmap = NULL;
|
|
95 |
|
|
96 |
static char vis_shm_path[40];
|
|
97 |
static int vis_fd = -1;
|
|
98 |
|
|
99 |
#endif // VISEXPORT
|
76 | 100 |
|
77 | 101 |
static log_level loglevel;
|
78 | 102 |
|
|
734 | 758 |
pcmp = NULL;
|
735 | 759 |
output_off = true;
|
736 | 760 |
LOG_INFO("disabling output");
|
|
761 |
#if VISEXPORT
|
|
762 |
if (vis_mmap) {
|
|
763 |
pthread_rwlock_wrlock(&vis_mmap->rwlock);
|
|
764 |
vis_mmap->running = false;
|
|
765 |
pthread_rwlock_unlock(&vis_mmap->rwlock);
|
|
766 |
}
|
|
767 |
#endif
|
737 | 768 |
continue;
|
738 | 769 |
}
|
739 | 770 |
|
|
1285 | 1316 |
}
|
1286 | 1317 |
|
1287 | 1318 |
size -= out_frames;
|
|
1319 |
|
|
1320 |
#if VISEXPORT
|
|
1321 |
// attempt to write audio to vis_mmap but do not wait more than VIS_LOCK_NS to get wrlock
|
|
1322 |
// this can result in missing audio export to the mmap region, but this is preferable dropping audio
|
|
1323 |
if (vis_mmap) {
|
|
1324 |
int err;
|
|
1325 |
err = pthread_rwlock_trywrlock(&vis_mmap->rwlock);
|
|
1326 |
if (err) {
|
|
1327 |
struct timespec ts;
|
|
1328 |
clock_gettime(CLOCK_REALTIME, &ts);
|
|
1329 |
ts.tv_nsec += VIS_LOCK_NS;
|
|
1330 |
if (ts.tv_nsec > 1000000000) {
|
|
1331 |
ts.tv_sec += 1;
|
|
1332 |
ts.tv_nsec -= 1000000000;
|
|
1333 |
}
|
|
1334 |
err = pthread_rwlock_timedwrlock(&vis_mmap->rwlock, &ts);
|
|
1335 |
}
|
|
1336 |
|
|
1337 |
if (err) {
|
|
1338 |
LOG_DEBUG("failed to get wrlock - skipping visulizer export");
|
|
1339 |
|
|
1340 |
} else {
|
|
1341 |
|
|
1342 |
if (silence) {
|
|
1343 |
vis_mmap->running = false;
|
|
1344 |
} else {
|
|
1345 |
frames_t vis_cnt = out_frames;
|
|
1346 |
s32_t *ptr = (s32_t *) outputbuf->readp;
|
|
1347 |
unsigned i = vis_mmap->buf_index;
|
|
1348 |
|
|
1349 |
if (!output.current_replay_gain) {
|
|
1350 |
while (vis_cnt--) {
|
|
1351 |
vis_mmap->buffer[i++] = *(ptr++) >> 16;
|
|
1352 |
vis_mmap->buffer[i++] = *(ptr++) >> 16;
|
|
1353 |
if (i == VIS_BUF_SIZE) i = 0;
|
|
1354 |
}
|
|
1355 |
} else {
|
|
1356 |
while (vis_cnt--) {
|
|
1357 |
vis_mmap->buffer[i++] = gain(*(ptr++), output.current_replay_gain) >> 16;
|
|
1358 |
vis_mmap->buffer[i++] = gain(*(ptr++), output.current_replay_gain) >> 16;
|
|
1359 |
if (i == VIS_BUF_SIZE) i = 0;
|
|
1360 |
}
|
|
1361 |
}
|
|
1362 |
|
|
1363 |
vis_mmap->updated = time(NULL);
|
|
1364 |
vis_mmap->running = true;
|
|
1365 |
vis_mmap->buf_index = i;
|
|
1366 |
vis_mmap->rate = output.current_sample_rate;
|
|
1367 |
}
|
|
1368 |
|
|
1369 |
pthread_rwlock_unlock(&vis_mmap->rwlock);
|
|
1370 |
}
|
|
1371 |
}
|
|
1372 |
#endif
|
1288 | 1373 |
|
1289 | 1374 |
if (!silence) {
|
1290 | 1375 |
_buf_inc_readp(outputbuf, out_frames * BYTES_PER_FRAME);
|
|
1390 | 1475 |
const char *alsa_sample_fmt, bool mmap, unsigned max_rate, unsigned rt_priority) {
|
1391 | 1476 |
#endif
|
1392 | 1477 |
#if PORTAUDIO
|
1393 | |
void output_init(log_level level, const char *device, unsigned output_buf_size, unsigned latency, int osx_playnice, unsigned max_rate) {
|
|
1478 |
void output_init(log_level level, const char *device, unsigned output_buf_size, unsigned latency, int osx_playnice, unsigned max_rate) {
|
1394 | 1479 |
PaError err;
|
1395 | 1480 |
#endif
|
1396 | 1481 |
loglevel = level;
|
|
1503 | 1588 |
UNLOCK;
|
1504 | 1589 |
}
|
1505 | 1590 |
|
|
1591 |
#if VISEXPORT
|
|
1592 |
void output_vis_init(u8_t *mac) {
|
|
1593 |
sprintf(vis_shm_path, "/squeezelite-%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
1594 |
|
|
1595 |
LOCK;
|
|
1596 |
vis_fd = shm_open(vis_shm_path, O_CREAT | O_RDWR, 0666);
|
|
1597 |
if (vis_fd != -1) {
|
|
1598 |
if (ftruncate(vis_fd, sizeof(struct vis_t)) == 0) {
|
|
1599 |
vis_mmap = (struct vis_t *)mmap(NULL, sizeof(struct vis_t), PROT_READ | PROT_WRITE, MAP_SHARED, vis_fd, 0);
|
|
1600 |
}
|
|
1601 |
}
|
|
1602 |
|
|
1603 |
if (vis_mmap > 0) {
|
|
1604 |
pthread_rwlockattr_t attr;
|
|
1605 |
pthread_rwlockattr_init(&attr);
|
|
1606 |
pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
|
1607 |
pthread_rwlock_init(&vis_mmap->rwlock, &attr);
|
|
1608 |
vis_mmap->buf_size = VIS_BUF_SIZE;
|
|
1609 |
vis_mmap->running = false;
|
|
1610 |
vis_mmap->rate = output.current_sample_rate;
|
|
1611 |
pthread_rwlockattr_destroy(&attr);
|
|
1612 |
LOG_INFO("opened visulizer shared memory as %s", vis_shm_path);
|
|
1613 |
} else {
|
|
1614 |
LOG_WARN("unable to open visualizer shared memory");
|
|
1615 |
vis_mmap = NULL;
|
|
1616 |
}
|
|
1617 |
UNLOCK;
|
|
1618 |
}
|
|
1619 |
#endif
|
|
1620 |
|
1506 | 1621 |
void output_flush(void) {
|
1507 | 1622 |
LOG_INFO("flush output buffer");
|
1508 | 1623 |
buf_flush(outputbuf);
|
|
1546 | 1661 |
UNLOCK;
|
1547 | 1662 |
#endif
|
1548 | 1663 |
|
|
1664 |
#if VISEXPORT
|
|
1665 |
if (vis_mmap) {
|
|
1666 |
pthread_rwlock_destroy(&vis_mmap->rwlock);
|
|
1667 |
munmap(vis_mmap, sizeof(struct vis_t));
|
|
1668 |
}
|
|
1669 |
|
|
1670 |
if (vis_fd != -1) {
|
|
1671 |
shm_unlink(vis_shm_path);
|
|
1672 |
close(vis_fd);
|
|
1673 |
}
|
|
1674 |
#endif
|
|
1675 |
|
1549 | 1676 |
buf_destroy(outputbuf);
|
1550 | 1677 |
}
|