Improve metadata output -- maybe it works reliably now. Add call to get audio parameters from back ends, to be able to give volume metadata.

This commit is contained in:
Mike Brady
2015-03-16 11:11:54 +00:00
parent 606c3fd080
commit a2790fc102
10 changed files with 83 additions and 25 deletions
+13
View File
@@ -3,6 +3,16 @@
#include <stdint.h>
typedef struct {
double airplay_volume;
double current_volume_dB;
int minimum_volume_dB;
int maximum_volume_dB;
int has_true_mute;
int is_muted;
int valid;
} audio_parameters;
typedef struct {
void (*help)(void);
char *name;
@@ -28,6 +38,9 @@ typedef struct {
// may be NULL, in which case soft volume is applied
void (*volume)(double vol);
// may be NULL, in which case soft volume parameters are used
void (*parameters)(audio_parameters* info);
} audio_output;
audio_output *audio_get_output(char *name);
+13 -1
View File
@@ -43,6 +43,7 @@ static void stop(void);
static void flush(void);
static uint32_t delay(void);
static void volume(double vol);
static void parameters(audio_parameters* info);
static int has_mute=0;
static int has_db_vol=0;
static double set_volume;
@@ -57,7 +58,8 @@ audio_output audio_alsa = {
.flush = &flush,
.delay = &delay,
.play = &play,
.volume = NULL
.volume = NULL, // to be set later on...
.parameters = NULL // to be set later on...
};
static pthread_mutex_t alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -158,6 +160,7 @@ static int init(int argc, char **argv) {
(snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem,alsa_mix_maxv,&alsa_mix_maxdb)==0)) {
has_db_vol=1;
audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff
audio_alsa.parameters = &parameters; // likewise the parameters stuff
if (alsa_mix_mindb==-9999999) {
// trying to say that the lowest vol is mute, maybe? Raspberry Pi does this
if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem,alsa_mix_minv+1,&alsa_mix_mindb)==0)
@@ -314,6 +317,15 @@ static void stop(void) {
// close_alsa_device();
}
static void parameters(audio_parameters* info) {
info->has_true_mute=has_mute;
info->is_muted = ((has_mute) && (set_volume == -144.0));
info->minimum_volume_dB=alsa_mix_mindb;
info->maximum_volume_dB=alsa_mix_maxdb;
info->airplay_volume=set_volume;
info->current_volume_dB=vol2attn(set_volume,alsa_mix_maxdb,alsa_mix_mindb);
}
static void volume(double vol) {
set_volume=vol;
double vol_setting = vol2attn(vol,alsa_mix_maxdb,alsa_mix_mindb);
+2 -1
View File
@@ -127,5 +127,6 @@ audio_output audio_ao = {
.flush = NULL,
.delay = NULL,
.play = &play,
.volume = NULL
.volume = NULL,
.parameters = NULL
};
+2 -1
View File
@@ -81,5 +81,6 @@ audio_output audio_dummy = {
.flush = NULL,
.delay = NULL,
.play = &play,
.volume = NULL
.volume = NULL,
.parameters = NULL
};
+2 -1
View File
@@ -86,5 +86,6 @@ audio_output audio_pipe = {
.flush = NULL,
.delay = NULL,
.play = &play,
.volume = NULL
.volume = NULL,
.parameters = NULL
};
+2 -1
View File
@@ -148,5 +148,6 @@ audio_output audio_pulse = {
.flush = NULL,
.delay = NULL,
.play = &play,
.volume = NULL
.volume = NULL,
.parameters = NULL
};
+2 -1
View File
@@ -82,5 +82,6 @@ audio_output audio_sndio = {
.flush = NULL,
.delay = NULL,
.play = &play,
.volume = &volume
.volume = &volume,
.parameters = NULL
};
+2
View File
@@ -399,6 +399,8 @@ void command_stop(void) {
// See http://tangentsoft.net/audio/atten.html for data on good attenuators.
// We want a smooth attenuation function, like, for example, the ALPS RK27 Potentiometer transfer functions referred to at the link above.
// Note that the max_db and min_db are given as dB*100
double vol2attn(double vol, long max_db, long min_db) {
// We use a little coordinate geometry to build a transfer function from the volume passed in to the device's dynamic range.
+29 -9
View File
@@ -95,7 +95,7 @@ static int32_t last_seqno_read;
// interthread variables
static double volume = 1.0;
static double software_mixer_volume = 1.0;
static int fix_volume = 0x10000;
static pthread_mutex_t vol_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -133,6 +133,8 @@ static pthread_cond_t flowcontrol;
static int64_t first_packet_time_to_play; // nanoseconds
static audio_parameters audio_information;
// stats
static uint64_t missing_packets,late_packets,too_late_packets,resend_requests;
@@ -740,7 +742,7 @@ static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) {
}
// finally, adjust the volume, if necessary
if (volume!=1.0) {
if (software_mixer_volume!=1.0) {
// pthread_mutex_lock(&vol_mutex);
op=outptr;
for (i=0; i<frame_size+stuff; i++) {
@@ -780,6 +782,7 @@ static void *player_thread_func(void *arg) {
int64_t minimum_dac_queue_size = 1000000;
int32_t minimum_buffer_occupancy = BUFFER_FRAMES;
int32_t maximum_buffer_occupancy = 0;
audio_information.valid=0;
int play_samples;
int64_t current_delay;
@@ -1031,25 +1034,42 @@ void player_volume(double f) {
// Here, we ask for an attenuation we will apply in software. The dB range of a value from 1 to 65536 is about 48.1 dB (log10 of 65536 is 4.8164).
// Thus, we ask our vol2attn function for an appropriate dB between -48.1 and 0 dB and translate it back to a number.
double linear_volume = pow(10,vol2attn(f,0,-4810)/1000);
double scaled_volume = vol2attn(f,0,-4810);
double linear_volume = pow(10,scaled_volume/1000);
if(f == -144.0)
linear_volume = 0.0;
if (config.output->volume) {
config.output->volume(f);
linear_volume=1.0; // no attenuation needed
}
config.output->volume(f); // volume will be sent as metadata by the config.output device
linear_volume=1.0; // no attenuation needed -- this value is used as a flag to avoid calculations
}
if (config.output->parameters)
config.output->parameters(&audio_information);
else {
audio_information.airplay_volume = f;
audio_information.minimum_volume_dB = -4810;
audio_information.maximum_volume_dB = 0;
audio_information.current_volume_dB = scaled_volume;
audio_information.has_true_mute = 0;
audio_information.is_muted = 0;
}
audio_information.valid=1;
pthread_mutex_lock(&vol_mutex);
volume = linear_volume;
fix_volume = 65536.0 * volume;
software_mixer_volume = linear_volume;
fix_volume = 65536.0 * software_mixer_volume;
pthread_mutex_unlock(&vol_mutex);
char *dv = malloc(64); // will be freed in the metadata thread
if (dv) {
memset(dv,0,64);
snprintf(dv,63,"%.2f",f);
snprintf(dv,63,"%.2f,%.2f,%.2f,%.2f",audio_information.airplay_volume,audio_information.current_volume_dB/100.0,audio_information.minimum_volume_dB/100.0,audio_information.maximum_volume_dB/100.0);
send_ssnc_metadata('pvol',dv,strlen(dv),1);
}
}
void player_flush(uint32_t timestamp) {
+16 -10
View File
@@ -797,7 +797,12 @@ static void handle_set_parameter_parameter(rtsp_conn_info *conn,
// 'pend' -- play stream end. No arguments
// 'pfls' -- play stream flush. No arguments
// 'prsm' -- play stream resume. No arguments
// 'pvol' -- play volume. The volume is sent as a string.
// 'pvol' -- play volume. The volume is sent as a string -- "airplay_volume,volume,lowest_volume,highest_volume,has_true_mute,is_muted"
// volume, lowest_volume and highest_volume are given in dB
// is_muted is 1 if [true] mute is enabled, 0 otherwise.
// The "airplay_volume" is what's sent to the player, and is from 0.00 down to -30.00, with -144.00 meaning mute.
// This is linear on the volume control slider of iTunes or iOS AirPLay
//
// 'mdst' -- a sequence of metadata is about to start
// 'mden' -- a sequence of metadata has ended
// 'snam' -- the name of the originator -- e.g. "Joe's iPhone" or "iTunes...".
@@ -894,7 +899,7 @@ void metadata_open(void) {
char* path = malloc(pl+1);
snprintf(path, pl+1, "%s/%s", config.meta_dir, fn);
fd = open(path, O_WRONLY);
fd = open(path, O_WRONLY | O_NONBLOCK);
//if (fd < 0)
// debug(1, "Could not open metadata FIFO %s. Will try again later.", path);
@@ -907,18 +912,19 @@ static void metadata_close(void) {
}
ssize_t non_blocking_write(int fd, const void *buf, size_t count) {
// debug(1,"writing %u to pipe...",count);
// debug(1,"writing %u to pipe...",count);
// we are assuming that the count is always smaller than the FIFO's buffer
struct pollfd ufds[1];
ssize_t reply;
do {
ufds[0].fd=fd;
ufds[0].events = POLLOUT;
int rv = poll(ufds,1,1000);
int rv = poll(ufds,1,5000);
if (rv==-1)
debug(1,"error waiting for pipe to unblock...");
if (rv==0)
debug(1,"timeout waiting for pipe to unblock");
reply=write(fd,buf,count);
if ((reply==-1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
debug(1,"writing to pipe will block...");
// else
@@ -940,12 +946,12 @@ void metadata_process(uint32_t type,uint32_t code,char *data,uint32_t length) {
return;
char thestring[1024];
snprintf(thestring,1024,"<type>%x</type><code>%x</code><length>%u</length>\n",type,code,length);
ret = write(fd, thestring, strlen(thestring));
ret = non_blocking_write(fd, thestring, strlen(thestring));
if (ret < 1)
return;
if ((data!=NULL) && (length>0)) {
snprintf(thestring,1024,"<data encoding=\"base64\">\n");
ret = write(fd, thestring, strlen(thestring));
ret = non_blocking_write(fd, thestring, strlen(thestring));
if (ret < 1) // no reader
return;
// here, we write the data in base64 form using our nice base64 encoder
@@ -963,7 +969,7 @@ void metadata_process(uint32_t type,uint32_t code,char *data,uint32_t length) {
if (base64_encode_so(remaining_data, towrite_count, outbuf, &outbuf_size)==NULL)
debug(1,"Error encoding base64 data.");
//debug(1,"Remaining count: %d ret: %d, outbuf_size: %d.",remaining_count,ret,outbuf_size);
ret = write(fd,outbuf,outbuf_size);
ret = non_blocking_write(fd,outbuf,outbuf_size);
if (ret<0)
return;
remaining_data+=towrite_count;
@@ -973,7 +979,7 @@ void metadata_process(uint32_t type,uint32_t code,char *data,uint32_t length) {
// return;
}
snprintf(thestring,1024,"</data>\n");
ret = write(fd, thestring, strlen(thestring));
ret = non_blocking_write(fd, thestring, strlen(thestring));
if (ret < 1) // no reader
return;
}
@@ -1070,8 +1076,8 @@ static void handle_set_parameter_metadata(rtsp_conn_info *conn,
static void handle_set_parameter(rtsp_conn_info *conn,
rtsp_message *req, rtsp_message *resp) {
if (!req->contentlength)
debug(1, "received empty SET_PARAMETER request.");
//if (!req->contentlength)
// debug(1, "received empty SET_PARAMETER request.");
char *ct = msg_get_header(req, "Content-Type");