Package list squeezelite / upstream/1.6.1 output_vis.c
upstream/1.6.1

Tree @upstream/1.6.1 (Download .tar.gz)

output_vis.c @upstream/1.6.1raw · history · blame

/* 
 *  Squeezelite - lightweight headless squeezebox emulator
 *
 *  (c) Adrian Smith 2012-2014, triode1@btinternet.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

// Export audio samples for visualiser process (16 bit only best endevours)

#include "squeezelite.h"

#if VISEXPORT

#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

#define VIS_BUF_SIZE 16384
#define VIS_LOCK_NS  1000000 // ns to wait for vis wrlock

static struct vis_t {
	pthread_rwlock_t rwlock;
	u32_t buf_size;
	u32_t buf_index;
	bool running;
	u32_t rate;
	time_t updated;
	s16_t buffer[VIS_BUF_SIZE];
} *vis_mmap = NULL;

static char vis_shm_path[40];
static int vis_fd = -1;

static log_level loglevel;

// attempt to write audio to vis_mmap but do not wait more than VIS_LOCK_NS to get wrlock
// this can result in missing audio export to the mmap region, but this is preferable dropping audio
void _vis_export(struct buffer *outputbuf, struct outputstate *output, frames_t out_frames, bool silence) {
	if (vis_mmap) {
		int err;
		
		err = pthread_rwlock_trywrlock(&vis_mmap->rwlock);
		if (err) {
			struct timespec ts;
			clock_gettime(CLOCK_REALTIME, &ts);
			ts.tv_nsec += VIS_LOCK_NS;
			if (ts.tv_nsec > 1000000000) {
				ts.tv_sec  += 1;
				ts.tv_nsec -= 1000000000;
			}
			err = pthread_rwlock_timedwrlock(&vis_mmap->rwlock, &ts);
		}
		
		if (err) {
			LOG_DEBUG("failed to get wrlock - skipping visulizer export");
			
		} else {
			
			if (silence) {
				vis_mmap->running = false;
			} else {
				frames_t vis_cnt = out_frames;
				s32_t *ptr = (s32_t *) outputbuf->readp;
				unsigned i = vis_mmap->buf_index;
				
				if (!output->current_replay_gain) {
					while (vis_cnt--) {
						vis_mmap->buffer[i++] = *(ptr++) >> 16;
						vis_mmap->buffer[i++] = *(ptr++) >> 16;
						if (i == VIS_BUF_SIZE) i = 0;
					}
				} else {
					while (vis_cnt--) {
						vis_mmap->buffer[i++] = gain(*(ptr++), output->current_replay_gain) >> 16;
						vis_mmap->buffer[i++] = gain(*(ptr++), output->current_replay_gain) >> 16;
						if (i == VIS_BUF_SIZE) i = 0;
					}
				}
				
				vis_mmap->updated = time(NULL);
				vis_mmap->running = true;
				vis_mmap->buf_index = i;
				vis_mmap->rate = output->current_sample_rate;
			}
			
			pthread_rwlock_unlock(&vis_mmap->rwlock);
		}
	}
}

void vis_stop(void) {
	if (vis_mmap) {
		pthread_rwlock_wrlock(&vis_mmap->rwlock);
		vis_mmap->running = false;
		pthread_rwlock_unlock(&vis_mmap->rwlock);
	}
}

void output_vis_init(log_level level, u8_t *mac) {
	loglevel = level;

	sprintf(vis_shm_path, "/squeezelite-%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

	mode_t old_mask = umask(000); // allow any user to read our shm when created

	vis_fd = shm_open(vis_shm_path, O_CREAT | O_RDWR, 0666);
	if (vis_fd != -1) {
		if (ftruncate(vis_fd, sizeof(struct vis_t)) == 0) {
			vis_mmap = (struct vis_t *)mmap(NULL, sizeof(struct vis_t), PROT_READ | PROT_WRITE, MAP_SHARED, vis_fd, 0);
		}
	}
	
	if (vis_mmap > 0) {
		pthread_rwlockattr_t attr;
		pthread_rwlockattr_init(&attr);
		pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
		pthread_rwlock_init(&vis_mmap->rwlock, &attr);
		vis_mmap->buf_size = VIS_BUF_SIZE;
		vis_mmap->running = false;
		vis_mmap->rate = 44100;
		pthread_rwlockattr_destroy(&attr);
		LOG_INFO("opened visulizer shared memory as %s", vis_shm_path);
	} else {
		LOG_WARN("unable to open visualizer shared memory");
		vis_mmap = NULL;
	}

	umask(old_mask);
}

#endif // VISEXPORT