Honour max_output_db setting even when ignore_volume_control is true. Spell out "Shairport Sync" in the Home app information.

This commit is contained in:
Mike Brady
2021-07-16 15:04:51 +01:00
parent c0ee8d1323
commit 3a6297bc9a
2 changed files with 198 additions and 185 deletions
+158 -154
View File
@@ -1760,27 +1760,25 @@ void *player_thread_func(void *arg) {
// successive rtptimes, at worst
conn->output_sample_ratio = config.output_rate / conn->input_rate;
// Sign extending rtptime calculations to 64 bit is needed from time to time.
// The standard rtptime is unsigned 32 bits,
// so you can do modulo 2^32 difference calculations
// and get a signed result simply by typing the result as a signed 32-bit number.
// Sign extending rtptime calculations to 64 bit is needed from time to time.
// So long as you can be sure the numbers are within 2^31 of each other,
// the sign of the result calculated in this way indicates the order of the operands.
// For example, if you subtract a from b and the result is positive, you can conclude
// b is the same as or comes after a in module 2^32 order.
// The standard rtptime is unsigned 32 bits,
// so you can do modulo 2^32 difference calculations
// and get a signed result simply by typing the result as a signed 32-bit number.
// We want to do the same with the rtptime calculations for multiples of
// the rtptimes (1, 2, 4 or 8 times), and we want to do this in signed 64-bit/
// Therefore we need to sign extend these modulo 2^32, 2^33, 2^34, or 2^35 bit unsigned
// numbers on the same basis.
// So long as you can be sure the numbers are within 2^31 of each other,
// the sign of the result calculated in this way indicates the order of the operands.
// For example, if you subtract a from b and the result is positive, you can conclude
// b is the same as or comes after a in module 2^32 order.
// That is what the output_rtptime_sign_bit, output_rtptime_mask, output_rtptime_mask_not and
// output_rtptime_sign_mask are for -- see later, calculating the sync error.
// We want to do the same with the rtptime calculations for multiples of
// the rtptimes (1, 2, 4 or 8 times), and we want to do this in signed 64-bit/
// Therefore we need to sign extend these modulo 2^32, 2^33, 2^34, or 2^35 bit unsigned
// numbers on the same basis.
// That is what the output_rtptime_sign_bit, output_rtptime_mask, output_rtptime_mask_not and
// output_rtptime_sign_mask are for -- see later, calculating the sync error.
int output_rtptime_sign_bit;
switch (conn->output_sample_ratio) {
@@ -2022,7 +2020,6 @@ void *player_thread_func(void *arg) {
}
#endif
#ifdef CONFIG_AIRPLAY_2
if (conn->timing_type == ts_ntp) {
#endif
@@ -2940,45 +2937,46 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
// if it's both, we haven't decided whether hw or sw should be on top
// we have to consider the settings ignore_volume_control and mute.
if (config.ignore_volume_control == 0) {
if (airplay_volume == -144.0) {
if (airplay_volume == -144.0) {
if ((config.output->mute) && (config.output->mute(1) == 0))
debug(2,
"player_volume_without_notification: volume mode is %d, airplay_volume is %f, "
"hardware mute is enabled.",
volume_mode, airplay_volume);
else {
conn->software_mute_enabled = 1;
debug(2,
"player_volume_without_notification: volume mode is %d, airplay_volume is %f, "
"software mute is enabled.",
volume_mode, airplay_volume);
}
if ((config.output->mute) && (config.output->mute(1) == 0))
debug(2,
"player_volume_without_notification: volume mode is %d, airplay_volume is %f, "
"hardware mute is enabled.",
volume_mode, airplay_volume);
else {
conn->software_mute_enabled = 1;
debug(2,
"player_volume_without_notification: volume mode is %d, airplay_volume is %f, "
"software mute is enabled.",
volume_mode, airplay_volume);
}
} else {
int32_t max_db = 0, min_db = 0;
switch (volume_mode) {
case vol_hw_only:
max_db = hw_max_db;
min_db = hw_min_db;
break;
case vol_sw_only:
max_db = sw_max_db;
min_db = sw_min_db;
break;
case vol_both:
// debug(1, "dB range passed is hw: %d, sw: %d, total: %d", hw_max_db - hw_min_db,
// sw_max_db - sw_min_db, (hw_max_db - hw_min_db) + (sw_max_db - sw_min_db));
max_db =
(hw_max_db - hw_min_db) + (sw_max_db - sw_min_db); // this should be the range requested
min_db = 0;
break;
default:
debug(1, "player_volume_without_notification: error: not in a volume mode");
break;
}
double scaled_attenuation = max_db;
if (config.ignore_volume_control == 0) {
} else {
int32_t max_db = 0, min_db = 0;
switch (volume_mode) {
case vol_hw_only:
max_db = hw_max_db;
min_db = hw_min_db;
break;
case vol_sw_only:
max_db = sw_max_db;
min_db = sw_min_db;
break;
case vol_both:
// debug(1, "dB range passed is hw: %d, sw: %d, total: %d", hw_max_db - hw_min_db,
// sw_max_db - sw_min_db, (hw_max_db - hw_min_db) + (sw_max_db - sw_min_db));
max_db =
(hw_max_db - hw_min_db) + (sw_max_db - sw_min_db); // this should be the range requested
min_db = 0;
break;
default:
debug(1, "player_volume_without_notification: error: not in a volume mode");
break;
}
double scaled_attenuation = 0.0;
if (config.volume_control_profile == VCP_standard)
scaled_attenuation = vol2attn(airplay_volume, max_db, min_db); // no cancellation points
else if (config.volume_control_profile == VCP_flat)
@@ -2986,116 +2984,122 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
flat_vol2attn(airplay_volume, max_db, min_db); // no cancellation points
else
debug(1, "player_volume_without_notification: unrecognised volume control profile");
// so here we have the scaled attenuation. If it's for hw or sw only, it's straightforward.
double hardware_attenuation = 0.0;
double software_attenuation = 0.0;
switch (volume_mode) {
case vol_hw_only:
hardware_attenuation = scaled_attenuation;
break;
case vol_sw_only:
software_attenuation = scaled_attenuation;
break;
case vol_both:
// here, we now the attenuation required, so we have to apportion it to the sw and hw mixers
// if we give the hw priority, that means when lowering the volume, set the hw volume to its
// lowest
// before using the sw attenuation.
// similarly, if we give the sw priority, that means when lowering the volume, set the sw
// volume to its lowest
// before using the hw attenuation.
// one imagines that hw priority is likely to be much better
// if (config.volume_range_hw_priority) {
if (config.volume_range_hw_priority != 0) {
// hw priority
if ((sw_max_db - sw_min_db) > scaled_attenuation) {
software_attenuation = sw_min_db + scaled_attenuation;
hardware_attenuation = hw_min_db;
} else {
software_attenuation = sw_max_db;
hardware_attenuation = hw_min_db + scaled_attenuation - (sw_max_db - sw_min_db);
}
} else {
// sw priority
if ((hw_max_db - hw_min_db) > scaled_attenuation) {
hardware_attenuation = hw_min_db + scaled_attenuation;
software_attenuation = sw_min_db;
} else {
hardware_attenuation = hw_max_db;
software_attenuation = sw_min_db + scaled_attenuation - (hw_max_db - hw_min_db);
}
}
break;
default:
debug(1, "player_volume_without_notification: error: not in a volume mode");
break;
}
if (((volume_mode == vol_hw_only) || (volume_mode == vol_both)) && (config.output->volume)) {
config.output->volume(hardware_attenuation); // otherwise set the output to the lowest value
// debug(1,"Hardware attenuation set to %f for airplay volume of
// %f.",hardware_attenuation,airplay_volume);
if (volume_mode == vol_hw_only)
conn->fix_volume = 0x10000;
}
if ((volume_mode == vol_sw_only) || (volume_mode == vol_both)) {
double temp_fix_volume = 65536.0 * pow(10, software_attenuation / 2000);
// debug(1,"Software attenuation set to %f, i.e %f out of 65,536, for airplay volume of
// %f",software_attenuation,temp_fix_volume,airplay_volume);
conn->fix_volume = temp_fix_volume;
// if (config.loudness)
loudness_set_volume(software_attenuation / 100);
}
if (config.logOutputLevel) {
inform("Output Level set to: %.2f dB.", scaled_attenuation / 100.0);
}
#ifdef CONFIG_METADATA
// here, send the 'pvol' metadata message when the airplay volume information
// is being used by shairport sync to control the output volume
char dv[128];
memset(dv, 0, 128);
if (volume_mode == vol_both) {
// normalise the maximum output to the hardware device's max output
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume,
(scaled_attenuation - max_db + hw_max_db) / 100.0,
(min_db - max_db + hw_max_db) / 100.0, (max_db - max_db + hw_max_db) / 100.0);
} else {
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, scaled_attenuation / 100.0,
min_db / 100.0, max_db / 100.0);
}
send_ssnc_metadata('pvol', dv, strlen(dv), 1);
#endif
if (config.output->mute)
config.output->mute(0);
conn->software_mute_enabled = 0;
debug(2,
"player_volume_without_notification: volume mode is %d, airplay volume is %f, "
"software_attenuation: %f, hardware_attenuation: %f, muting "
"is disabled.",
volume_mode, airplay_volume, software_attenuation, hardware_attenuation);
}
}
// so here we have the scaled attenuation. If it's for hw or sw only, it's straightforward.
double hardware_attenuation = 0.0;
double software_attenuation = 0.0;
switch (volume_mode) {
case vol_hw_only:
hardware_attenuation = scaled_attenuation;
break;
case vol_sw_only:
software_attenuation = scaled_attenuation;
break;
case vol_both:
// here, we now the attenuation required, so we have to apportion it to the sw and hw mixers
// if we give the hw priority, that means when lowering the volume, set the hw volume to its
// lowest
// before using the sw attenuation.
// similarly, if we give the sw priority, that means when lowering the volume, set the sw
// volume to its lowest
// before using the hw attenuation.
// one imagines that hw priority is likely to be much better
// if (config.volume_range_hw_priority) {
if (config.volume_range_hw_priority != 0) {
// hw priority
if ((sw_max_db - sw_min_db) > scaled_attenuation) {
software_attenuation = sw_min_db + scaled_attenuation;
hardware_attenuation = hw_min_db;
} else {
software_attenuation = sw_max_db;
hardware_attenuation = hw_min_db + scaled_attenuation - (sw_max_db - sw_min_db);
}
} else {
// sw priority
if ((hw_max_db - hw_min_db) > scaled_attenuation) {
hardware_attenuation = hw_min_db + scaled_attenuation;
software_attenuation = sw_min_db;
} else {
hardware_attenuation = hw_max_db;
software_attenuation = sw_min_db + scaled_attenuation - (hw_max_db - hw_min_db);
}
}
break;
default:
debug(1, "player_volume_without_notification: error: not in a volume mode");
break;
}
if (((volume_mode == vol_hw_only) || (volume_mode == vol_both)) && (config.output->volume)) {
config.output->volume(hardware_attenuation); // otherwise set the output to the lowest value
// debug(1,"Hardware attenuation set to %f for airplay volume of
// %f.",hardware_attenuation,airplay_volume);
if (volume_mode == vol_hw_only)
conn->fix_volume = 0x10000;
}
if ((volume_mode == vol_sw_only) || (volume_mode == vol_both)) {
double temp_fix_volume = 65536.0 * pow(10, software_attenuation / 2000);
if (config.ignore_volume_control == 0)
debug(2, "Software attenuation set to %f, i.e %f out of 65,536, for airplay volume of %f",
software_attenuation, temp_fix_volume, airplay_volume);
else
debug(2, "Software attenuation set to %f, i.e %f out of 65,536. Volume control is ignored.",
software_attenuation, temp_fix_volume);
conn->fix_volume = temp_fix_volume;
// if (config.loudness)
loudness_set_volume(software_attenuation / 100);
}
if (config.logOutputLevel) {
inform("Output Level set to: %.2f dB.", scaled_attenuation / 100.0);
}
#ifdef CONFIG_METADATA
else {
// here, send the 'pvol' metadata message when the airplay volume information
// is being used by shairport sync to control the output volume
char dv[128];
memset(dv, 0, 128);
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0);
if (volume_mode == vol_both) {
// normalise the maximum output to the hardware device's max output
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume,
(scaled_attenuation - max_db + hw_max_db) / 100.0,
(min_db - max_db + hw_max_db) / 100.0, (max_db - max_db + hw_max_db) / 100.0);
} else {
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, scaled_attenuation / 100.0,
min_db / 100.0, max_db / 100.0);
}
send_ssnc_metadata('pvol', dv, strlen(dv), 1);
}
#endif
if (config.output->mute)
config.output->mute(0);
conn->software_mute_enabled = 0;
debug(2,
"player_volume_without_notification: volume mode is %d, airplay volume is %f, "
"software_attenuation: %f, hardware_attenuation: %f, muting "
"is disabled.",
volume_mode, airplay_volume, software_attenuation, hardware_attenuation);
}
/*
}
#ifdef CONFIG_METADATA
else {
// here, send the 'pvol' metadata message when the airplay volume information
// is being used by shairport sync to control the output volume
char dv[128];
memset(dv, 0, 128);
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0);
send_ssnc_metadata('pvol', dv, strlen(dv), 1);
}
#endif
*/
// here, store the volume for possible use in the future
config.airplay_volume = airplay_volume;
debug_mutex_unlock(&conn->volume_control_mutex, 3);
+40 -31
View File
@@ -50,8 +50,8 @@
#include <sys/ioctl.h>
#include "config.h"
#include "activity_monitor.h"
#include "config.h"
#ifdef CONFIG_OPENSSL
#include <openssl/md5.h>
@@ -1560,10 +1560,9 @@ struct pairings {
uint8_t public_key[32];
struct pairings *next;
} *pairings;
} * pairings;
static struct pairings * pairing_find(const char *device_id)
{
static struct pairings *pairing_find(const char *device_id) {
for (struct pairings *pairing = pairings; pairing; pairing = pairing->next) {
if (strcmp(device_id, pairing->device_id) == 0)
return pairing;
@@ -1595,7 +1594,8 @@ static void pairing_remove(struct pairings *pairing) {
free(pairing);
}
static int pairing_add_cb(uint8_t public_key[32], const char *device_id, void *cb_arg __attribute__((unused))) {
static int pairing_add_cb(uint8_t public_key[32], const char *device_id,
void *cb_arg __attribute__((unused))) {
debug(1, "pair-add cb for %s", device_id);
struct pairings *pairing = pairing_find(device_id);
@@ -1608,7 +1608,8 @@ static int pairing_add_cb(uint8_t public_key[32], const char *device_id, void *c
return 0;
}
static int pairing_remove_cb(uint8_t public_key[32] __attribute__((unused)), const char *device_id, void *cb_arg __attribute__((unused))) {
static int pairing_remove_cb(uint8_t public_key[32] __attribute__((unused)), const char *device_id,
void *cb_arg __attribute__((unused))) {
debug(1, "pair-remove cb for %s", device_id);
struct pairings *pairing = pairing_find(device_id);
@@ -1621,7 +1622,8 @@ static int pairing_remove_cb(uint8_t public_key[32] __attribute__((unused)), con
return 0;
}
static void pairing_list_cb(pair_cb enum_cb, void *enum_cb_arg, void *cb_arg __attribute__((unused))) {
static void pairing_list_cb(pair_cb enum_cb, void *enum_cb_arg,
void *cb_arg __attribute__((unused))) {
debug(1, "pair-list cb");
for (struct pairings *pairing = pairings; pairing; pairing = pairing->next) {
@@ -1629,10 +1631,12 @@ static void pairing_list_cb(pair_cb enum_cb, void *enum_cb_arg, void *cb_arg __a
}
}
void handle_pair_add(rtsp_conn_info *conn __attribute__((unused)), rtsp_message *req, rtsp_message *resp) {
void handle_pair_add(rtsp_conn_info *conn __attribute__((unused)), rtsp_message *req,
rtsp_message *resp) {
uint8_t *body = NULL;
size_t body_len = 0;
int ret = pair_add(PAIR_SERVER_HOMEKIT, &body, &body_len, pairing_add_cb, NULL, (const uint8_t *)req->content, req->contentlength);
int ret = pair_add(PAIR_SERVER_HOMEKIT, &body, &body_len, pairing_add_cb, NULL,
(const uint8_t *)req->content, req->contentlength);
if (ret < 0) {
debug(1, "pair-add returned an error");
resp->respcode = 451;
@@ -1644,10 +1648,12 @@ void handle_pair_add(rtsp_conn_info *conn __attribute__((unused)), rtsp_message
debug_log_rtsp_message(2, "pair-add response", resp);
}
void handle_pair_list(rtsp_conn_info *conn __attribute__((unused)), rtsp_message *req, rtsp_message *resp) {
void handle_pair_list(rtsp_conn_info *conn __attribute__((unused)), rtsp_message *req,
rtsp_message *resp) {
uint8_t *body = NULL;
size_t body_len = 0;
int ret = pair_list(PAIR_SERVER_HOMEKIT, &body, &body_len, pairing_list_cb, NULL, (const uint8_t *)req->content, req->contentlength);
int ret = pair_list(PAIR_SERVER_HOMEKIT, &body, &body_len, pairing_list_cb, NULL,
(const uint8_t *)req->content, req->contentlength);
if (ret < 0) {
debug(1, "pair-list returned an error");
resp->respcode = 451;
@@ -1659,10 +1665,12 @@ void handle_pair_list(rtsp_conn_info *conn __attribute__((unused)), rtsp_message
debug_log_rtsp_message(2, "pair-list response", resp);
}
void handle_pair_remove(rtsp_conn_info *conn __attribute__((unused)), rtsp_message *req, rtsp_message *resp) {
void handle_pair_remove(rtsp_conn_info *conn __attribute__((unused)), rtsp_message *req,
rtsp_message *resp) {
uint8_t *body = NULL;
size_t body_len = 0;
int ret = pair_remove(PAIR_SERVER_HOMEKIT, &body, &body_len, pairing_remove_cb, NULL, (const uint8_t *)req->content, req->contentlength);
int ret = pair_remove(PAIR_SERVER_HOMEKIT, &body, &body_len, pairing_remove_cb, NULL,
(const uint8_t *)req->content, req->contentlength);
if (ret < 0) {
debug(1, "pair-remove returned an error");
resp->respcode = 451;
@@ -1867,20 +1875,21 @@ void handle_fp_setup(__attribute__((unused)) rtsp_conn_info *conn, rtsp_message
}
/*
<key>Identifier</key>
<string>21cc689d-d5de-4814-872c-71d1426b57e0</string>
<key>Enable_HK_Access_Control</key>
<true/>
<key>PublicKey</key>
<data>
qXJDhhL5F3OACL+HO7LVLQVdy0OJtavepjpF720PaOQ=
</data>
<key>Device_Name</key>
<string>MyDevice</string>
<key>Access_Control_Level</key>
<integer>0</integer>
<key>Identifier</key>
<string>21cc689d-d5de-4814-872c-71d1426b57e0</string>
<key>Enable_HK_Access_Control</key>
<true/>
<key>PublicKey</key>
<data>
qXJDhhL5F3OACL+HO7LVLQVdy0OJtavepjpF720PaOQ=
</data>
<key>Device_Name</key>
<string>MyDevice</string>
<key>Access_Control_Level</key>
<integer>0</integer>
*/
void handle_configure(rtsp_conn_info *conn __attribute__((unused)), rtsp_message *req __attribute__((unused)), rtsp_message *resp) {
void handle_configure(rtsp_conn_info *conn __attribute__((unused)),
rtsp_message *req __attribute__((unused)), rtsp_message *resp) {
uint8_t public_key[32];
pair_public_key_get(PAIR_SERVER_HOMEKIT, public_key, config.airplay_device_id);
@@ -1889,7 +1898,8 @@ void handle_configure(rtsp_conn_info *conn __attribute__((unused)), rtsp_message
plist_dict_set_item(response_plist, "Identifier", plist_new_string(config.airplay_pi));
plist_dict_set_item(response_plist, "Enable_HK_Access_Control", plist_new_bool(1));
plist_dict_set_item(response_plist, "PublicKey", plist_new_data((const char *)public_key, sizeof(public_key)));
plist_dict_set_item(response_plist, "PublicKey",
plist_new_data((const char *)public_key, sizeof(public_key)));
plist_dict_set_item(response_plist, "Device_Name", plist_new_string(config.service_name));
plist_dict_set_item(response_plist, "Access_Control_Level", plist_new_uint(0));
@@ -2238,7 +2248,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
// hack.
conn->max_frames_per_packet = 352; // number of audio frames per packet.
conn->input_rate = 44100; // we are stuck with this for the moment.
conn->input_rate = 44100; // we are stuck with this for the moment.
conn->input_num_channels = 2;
conn->input_bit_depth = 16;
conn->input_bytes_per_frame = conn->input_num_channels * ((conn->input_bit_depth + 7) / 8);
@@ -2254,7 +2264,6 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
plist_dict_set_item(stream0dict, "audioBufferSize",
plist_new_uint(conn->ap2_audio_buffer_size));
// this should be cancelled by an activity_monitor_signify_activity(1)
// call in the SETRATEANCHORI handler, which should come up right away
activity_monitor_signify_activity(0);
@@ -4414,7 +4423,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
*p++ = ap1_featuresString;
*p++ = firmware_version;
*p++ = "md=2";
*p++ = "am=SPS";
*p++ = "am=Shairport Sync";
*p++ = "sf=0x4";
*p++ = "tp=UDP";
*p++ = "vn=65537";
@@ -4475,7 +4484,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
*p++ = "acl=0";
*p++ = "rsf=0x0";
*p++ = firmware_version;
*p++ = "model=SPS";
*p++ = "model=Shairport Sync";
char piString[64];
snprintf(piString, sizeof(piString), "pi=%s", config.airplay_pi);
*p++ = piString;