Skip to content

Commit

Permalink
Improve metadata output -- maybe it works reliably now. Add call to g…
Browse files Browse the repository at this point in the history
…et audio parameters from back ends, to be able to give volume metadata.
  • Loading branch information
mikebrady committed Mar 16, 2015
1 parent 606c3fd commit a2790fc
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 25 deletions.
13 changes: 13 additions & 0 deletions audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
14 changes: 13 additions & 1 deletion audio_alsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion audio_ao.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,5 +127,6 @@ audio_output audio_ao = {
.flush = NULL,
.delay = NULL,
.play = &play,
.volume = NULL
.volume = NULL,
.parameters = NULL
};
3 changes: 2 additions & 1 deletion audio_dummy.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,6 @@ audio_output audio_dummy = {
.flush = NULL,
.delay = NULL,
.play = &play,
.volume = NULL
.volume = NULL,
.parameters = NULL
};
3 changes: 2 additions & 1 deletion audio_pipe.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,6 @@ audio_output audio_pipe = {
.flush = NULL,
.delay = NULL,
.play = &play,
.volume = NULL
.volume = NULL,
.parameters = NULL
};
3 changes: 2 additions & 1 deletion audio_pulse.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,6 @@ audio_output audio_pulse = {
.flush = NULL,
.delay = NULL,
.play = &play,
.volume = NULL
.volume = NULL,
.parameters = NULL
};
3 changes: 2 additions & 1 deletion audio_sndio.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,6 @@ audio_output audio_sndio = {
.flush = NULL,
.delay = NULL,
.play = &play,
.volume = &volume
.volume = &volume,
.parameters = NULL
};
2 changes: 2 additions & 0 deletions common.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@ void command_stop(void) {
// See http:https://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.
Expand Down
38 changes: 29 additions & 9 deletions player.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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++) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
26 changes: 16 additions & 10 deletions rtsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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...".
Expand Down Expand Up @@ -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);

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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");

Expand Down

0 comments on commit a2790fc

Please sign in to comment.