mirror of
https://github.com/morgan9e/UxPlay
synced 2026-04-14 00:04:13 +09:00
Merge pull request #258 from FDH2/volume
rework -db option implementation; add -taper vol control option
This commit is contained in:
15
README.html
15
README.html
@@ -521,7 +521,8 @@ the audio played by the server.</li>
|
||||
range -30dB:0dB can be rescaled from <em>Low</em>:0 (<em>Low</em> <
|
||||
0), or <em>Low</em>:<em>High</em>, using the option
|
||||
“<code>-db _Low_</code>” or “<code>-db _Low_:_High_</code>” (Rescaling
|
||||
is linear in decibels).</p>
|
||||
is linear in decibels). The option <code>-taper</code> provides a
|
||||
“tapered” AirPlay volume-control profile some users may prefer.</p>
|
||||
<p>The -vsync and -async options also allow an optional positive (or
|
||||
negative) audio-delay adjustment in <em>milliseconds</em> for
|
||||
fine-tuning : <code>-vsync 20.5</code> delays audio relative to video by
|
||||
@@ -957,6 +958,13 @@ attenuation by -7dB is translated to a -7 x (60/30) = -14dB attenuation,
|
||||
and the maximum volume (AirPlay 0dB) is a 10dB augmentation, and Airplay
|
||||
-30dB would become -50dB. Note that the minimum AirPlay value (-30dB
|
||||
exactly) is translated to “mute”.</p>
|
||||
<p><strong>-taper</strong> Provides a “tapered” Airplay volume-control
|
||||
profile (matching the one called “dasl-tapering” in <a
|
||||
href="https://github.com/mikebrady/shairport-sync">shairport-sync</a>):
|
||||
each time the length of the volume slider (or the number of steps above
|
||||
mute, where 16 steps = full volume) is reduced by 50%, the perceived
|
||||
volume is halved (a 10dB attenuation). (This is modified at low volumes,
|
||||
to use the “untapered” volume if it is louder.)</p>
|
||||
<p><strong>-s wxh</strong> (e.g. -s 1920x1080 , which is the default )
|
||||
sets the display resolution (width and height, in pixels). (This may be
|
||||
a request made to the AirPlay client, and perhaps will not be the final
|
||||
@@ -1510,13 +1518,14 @@ an AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen,
|
||||
introduced 2017, running tvOS 12.2.1), so it does not seem to matter
|
||||
what version UxPlay claims to be.</p>
|
||||
<h1 id="changelog">Changelog</h1>
|
||||
<p>1.68 2023-12-26 Introduced a simpler (default) method for generating
|
||||
<p>1.68 2023-12-29 Introduced a simpler (default) method for generating
|
||||
a persistent public key from the server MAC address (which can now be
|
||||
set with the -m option). (The previous pem-file method is still
|
||||
available with -key option). New option -reg to maintain a register of
|
||||
pin-authenticated clients. Corrected volume-control: now inteprets
|
||||
AirPlay volume range -30dB:0dB as (gain/amplitude) decibel attenuation,
|
||||
with new option -db low[:high] for “flat” rescaling of the dB range.</p>
|
||||
with new option -db low[:high] for “flat” rescaling of the dB range. Add
|
||||
-taper option for a “tapered” AirPlay volume-control profile.</p>
|
||||
<p>1.67 2023-11-30 Add support for Apple-style one-time pin
|
||||
authentication of clients with option “-pin”: (uses SRP6a authentication
|
||||
protocol and public key persistence). Detection with error message of
|
||||
|
||||
13
README.md
13
README.md
@@ -416,7 +416,8 @@ delays the video on the client to match audio on the server, so leads to
|
||||
a slight delay before a pause or track-change initiated on the client takes effect on the audio played by the server.
|
||||
|
||||
AirPlay volume-control attenuates volume (gain) by up to -30dB: the range -30dB:0dB can be rescaled from _Low_:0 (_Low_ < 0), or _Low_:_High_, using the
|
||||
option "`-db _Low_`" or "``-db _Low_:_High_``" (Rescaling is linear in decibels).
|
||||
option "`-db _Low_`" or "``-db _Low_:_High_``" (Rescaling is linear in decibels). The option ```-taper``` provides a "tapered" AirPlay volume-control
|
||||
profile some users may prefer.
|
||||
|
||||
The -vsync and -async options
|
||||
also allow an optional positive (or negative) audio-delay adjustment in _milliseconds_ for fine-tuning : `-vsync 20.5`
|
||||
@@ -751,6 +752,11 @@ using UxPlay as a second monitor for a mac computer, or monitoring a webcam; wit
|
||||
and the maximum volume (AirPlay 0dB) is a 10dB augmentation, and Airplay -30dB would become -50dB. Note that the minimum AirPlay value (-30dB exactly)
|
||||
is translated to "mute".
|
||||
|
||||
**-taper** Provides a "tapered" Airplay volume-control profile (matching the one called "dasl-tapering"
|
||||
in [shairport-sync](https://github.com/mikebrady/shairport-sync)): each time the length of the
|
||||
volume slider (or the number of steps above mute, where 16 steps = full volume) is reduced by 50%, the perceived volume is halved (a 10dB attenuation).
|
||||
(This is modified at low volumes, to use the "untapered" volume if it is louder.)
|
||||
|
||||
**-s wxh** (e.g. -s 1920x1080 , which is the default ) sets the display resolution (width and height,
|
||||
in pixels). (This may be a
|
||||
request made to the AirPlay client, and perhaps will not
|
||||
@@ -1188,11 +1194,12 @@ tvOS 12.2.1), so it does not seem to matter what version UxPlay claims to be.
|
||||
|
||||
|
||||
# Changelog
|
||||
1.68 2023-12-26 Introduced a simpler (default) method for generating a persistent public key from the server MAC
|
||||
1.68 2023-12-29 Introduced a simpler (default) method for generating a persistent public key from the server MAC
|
||||
address (which can now be set with the -m option). (The previous pem-file method is still available
|
||||
with -key option). New option -reg to maintain a register of pin-authenticated clients. Corrected
|
||||
volume-control: now inteprets AirPlay volume range -30dB:0dB as (gain/amplitude) decibel attenuation,
|
||||
with new option -db low[:high] for "flat" rescaling of the dB range.
|
||||
with new option -db low[:high] for "flat" rescaling of the dB range. Add -taper option for a "tapered"
|
||||
AirPlay volume-control profile.
|
||||
|
||||
1.67 2023-11-30 Add support for Apple-style one-time pin authentication of clients with option "-pin":
|
||||
(uses SRP6a authentication protocol and public key persistence). Detection with error message
|
||||
|
||||
14
README.txt
14
README.txt
@@ -513,7 +513,8 @@ helped to prevent this previously when timestamps were not being used.)
|
||||
AirPlay volume-control attenuates volume (gain) by up to -30dB: the
|
||||
range -30dB:0dB can be rescaled from *Low*:0 (*Low* \< 0), or
|
||||
*Low*:*High*, using the option "`-db _Low_`" or "`-db _Low_:_High_`"
|
||||
(Rescaling is linear in decibels).
|
||||
(Rescaling is linear in decibels). The option `-taper` provides a
|
||||
"tapered" AirPlay volume-control profile some users may prefer.
|
||||
|
||||
The -vsync and -async options also allow an optional positive (or
|
||||
negative) audio-delay adjustment in *milliseconds* for fine-tuning :
|
||||
@@ -960,6 +961,14 @@ it cannot exceed +20dB). The rescaling is "flat", so that for -db
|
||||
10dB augmentation, and Airplay -30dB would become -50dB. Note that the
|
||||
minimum AirPlay value (-30dB exactly) is translated to "mute".
|
||||
|
||||
**-taper** Provides a "tapered" Airplay volume-control profile (matching
|
||||
the one called "dasl-tapering" in
|
||||
[shairport-sync](https://github.com/mikebrady/shairport-sync)): each
|
||||
time the length of the volume slider (or the number of steps above mute,
|
||||
where 16 steps = full volume) is reduced by 50%, the perceived volume is
|
||||
halved (a 10dB attenuation). (This is modified at low volumes, to use
|
||||
the "untapered" volume if it is louder.)
|
||||
|
||||
**-s wxh** (e.g. -s 1920x1080 , which is the default ) sets the display
|
||||
resolution (width and height, in pixels). (This may be a request made to
|
||||
the AirPlay client, and perhaps will not be the final resolution you
|
||||
@@ -1547,13 +1556,14 @@ what version UxPlay claims to be.
|
||||
|
||||
# Changelog
|
||||
|
||||
1.68 2023-12-26 Introduced a simpler (default) method for generating a
|
||||
1.68 2023-12-29 Introduced a simpler (default) method for generating a
|
||||
persistent public key from the server MAC address (which can now be set
|
||||
with the -m option). (The previous pem-file method is still available
|
||||
with -key option). New option -reg to maintain a register of
|
||||
pin-authenticated clients. Corrected volume-control: now inteprets
|
||||
AirPlay volume range -30dB:0dB as (gain/amplitude) decibel attenuation,
|
||||
with new option -db low\[:high\] for "flat" rescaling of the dB range.
|
||||
Add -taper option for a "tapered" AirPlay volume-control profile.
|
||||
|
||||
1.67 2023-11-30 Add support for Apple-style one-time pin authentication
|
||||
of clients with option "-pin": (uses SRP6a authentication protocol and
|
||||
|
||||
@@ -33,12 +33,11 @@ extern "C" {
|
||||
#include "../lib/logger.h"
|
||||
|
||||
bool gstreamer_init();
|
||||
void audio_renderer_init(logger_t *logger, const char* audiosink, const bool *audio_sync, const bool *video_sync,
|
||||
float db_low, float db_high);
|
||||
void audio_renderer_init(logger_t *logger, const char* audiosink, const bool *audio_sync, const bool *video_sync);
|
||||
void audio_renderer_start(unsigned char* compression_type);
|
||||
void audio_renderer_stop();
|
||||
void audio_renderer_render_buffer(unsigned char* data, int *data_len, unsigned short *seqnum, uint64_t *ntp_time);
|
||||
void audio_renderer_set_volume(float volume);
|
||||
void audio_renderer_set_volume(double volume);
|
||||
void audio_renderer_flush();
|
||||
void audio_renderer_destroy();
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ static gboolean render_audio = FALSE;
|
||||
static gboolean async = FALSE;
|
||||
static gboolean vsync = FALSE;
|
||||
static gboolean sync = FALSE;
|
||||
static float vol_low, vol_high;
|
||||
|
||||
typedef struct audio_renderer_s {
|
||||
GstElement *appsrc;
|
||||
@@ -126,16 +125,13 @@ bool gstreamer_init(){
|
||||
return (bool) check_plugins ();
|
||||
}
|
||||
|
||||
void audio_renderer_init(logger_t *render_logger, const char* audiosink, const bool* audio_sync,
|
||||
const bool* video_sync, const float db_low, const float db_high) {
|
||||
void audio_renderer_init(logger_t *render_logger, const char* audiosink, const bool* audio_sync, const bool* video_sync) {
|
||||
GError *error = NULL;
|
||||
GstCaps *caps = NULL;
|
||||
GstClock *clock = gst_system_clock_obtain();
|
||||
g_object_set(clock, "clock-type", GST_CLOCK_TYPE_REALTIME, NULL);
|
||||
|
||||
logger = render_logger;
|
||||
vol_low = db_low;
|
||||
vol_high = db_high;
|
||||
|
||||
aac = check_plugin_feature (avdec_aac);
|
||||
alac = check_plugin_feature (avdec_alac);
|
||||
@@ -359,16 +355,10 @@ void audio_renderer_render_buffer(unsigned char* data, int *data_len, unsigned s
|
||||
}
|
||||
}
|
||||
|
||||
void audio_renderer_set_volume(float volume) {
|
||||
/* scale volume from range -30dB:0dB to vol_low: vol_high */
|
||||
double vol = (double) vol_low;
|
||||
if ((volume <= 0) && (volume > -30)) {
|
||||
vol = (double) (vol_low + ((vol_high - vol_low) * (30.0 + volume) / 30));
|
||||
}
|
||||
gdouble avol = (gdouble) pow(10, vol/20);
|
||||
if (avol > 10) avol = 10;
|
||||
if (volume <= -30) avol = 0;
|
||||
g_object_set(renderer->volume, "volume", avol, NULL);
|
||||
void audio_renderer_set_volume(double volume) {
|
||||
volume = (volume > 10.0) ? 10.0 : volume;
|
||||
volume = (volume < 0.0) ? 0.0 : volume;
|
||||
g_object_set(renderer->volume, "volume", volume, NULL);
|
||||
}
|
||||
|
||||
void audio_renderer_flush() {
|
||||
|
||||
2
uxplay.1
2
uxplay.1
@@ -36,6 +36,8 @@ UxPlay 1.68: An open\-source AirPlay mirroring (+ audio streaming) server:
|
||||
optional: set maximum to h dB (+ or -); default -30.0:0.0
|
||||
.PP
|
||||
.TP
|
||||
\fB\-taper\fR Use a "tapered" AirPlay volume-control profile.
|
||||
.TP
|
||||
\fB\-s\fR wxh[@r]Set display resolution [refresh_rate] default 1920x1080[@60]
|
||||
.TP
|
||||
\fB\-o\fR Set display "overscanned" mode on (not usually needed)
|
||||
|
||||
64
uxplay.cpp
64
uxplay.cpp
@@ -34,6 +34,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <cstdio>
|
||||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef _WIN32 /*modifications for Windows compilation */
|
||||
#include <glib.h>
|
||||
@@ -136,8 +137,9 @@ static std::string dacpfile = "";
|
||||
static bool registration_list = false;
|
||||
static std::string pairing_register = "";
|
||||
static std::vector <std::string> registered_keys;
|
||||
static float db_low = -30;
|
||||
static float db_high = 0;
|
||||
static double db_low = -30.0;
|
||||
static double db_high = 0.0;
|
||||
static bool taper_volume = true;
|
||||
|
||||
/* logging */
|
||||
|
||||
@@ -577,7 +579,8 @@ static void print_info (char *name) {
|
||||
printf("-async [x]Audio-Only mode: sync audio to client video (default: no)\n");
|
||||
printf("-async no Switch off audio/(client)video timestamp synchronization\n");
|
||||
printf("-db l[:h] Set minimum volume attenuation to l dB (decibels, negative);\n");
|
||||
printf(" optional: set maximum to h dB (+ or -); default -30.0:0.0 dB\n");
|
||||
printf(" optional: set maximum to h dB (+ or -) default: -30.0:0.0 dB\n");
|
||||
printf("-taper Use a \"tapered\" AirPlay volume-qcontrol profile\n");
|
||||
printf("-s wxh[@r]Set display resolution [refresh_rate] default 1920x1080[@60]\n");
|
||||
printf("-o Set display \"overscanned\" mode on (not usually needed)\n");
|
||||
printf("-fs Full-screen (only works with X11, Wayland and VAAPI)\n");
|
||||
@@ -1094,22 +1097,24 @@ static void parse_arguments (int argc, char *argv[]) {
|
||||
dacpfile.append(get_homedir());
|
||||
dacpfile.append("/.uxplay.dacp");
|
||||
}
|
||||
} else if (arg == "-taper") {
|
||||
taper_volume = true;
|
||||
} else if (arg == "-db") {
|
||||
bool db_bad = true;
|
||||
float db1, db2;
|
||||
double db1, db2;
|
||||
char *start = NULL;
|
||||
if ( i < argc -1) {
|
||||
char *end1, *end2;
|
||||
start = argv[i+1];
|
||||
db1 = strtof(start, &end1);
|
||||
db1 = strtod(start, &end1);
|
||||
if (end1 > start && *end1 == ':') {
|
||||
db2 = strtof(++end1, &end2);
|
||||
db2 = strtod(++end1, &end2);
|
||||
if ( *end2 == '\0' && end2 > end1 && db1 < 0 && db1 < db2) {
|
||||
db_bad = false;
|
||||
}
|
||||
} else if (*end1 =='\0' && end1 > start && db1 < 0 ) {
|
||||
db_bad = false;
|
||||
db2 = 0;
|
||||
db2 = 0.0;
|
||||
}
|
||||
}
|
||||
if (db_bad) {
|
||||
@@ -1504,9 +1509,48 @@ extern "C" void video_flush (void *cls) {
|
||||
}
|
||||
|
||||
extern "C" void audio_set_volume (void *cls, float volume) {
|
||||
if (use_audio) {
|
||||
audio_renderer_set_volume(volume);
|
||||
double db, db_flat, frac, gst_volume;
|
||||
if (!use_audio) {
|
||||
return;
|
||||
}
|
||||
/* convert from AirPlay dB volume in range {-30dB : 0dB}, to GStreamer volume */
|
||||
if (volume == -144.0f) { /* AirPlay "mute" signal */
|
||||
frac = 0.0;
|
||||
} else if (volume < -30.0f) {
|
||||
LOGE(" invalid AirPlay volume %f", volume);
|
||||
frac = 0.0;
|
||||
} else if (volume > 0.0f) {
|
||||
LOGE(" invalid AirPlay volume %f", volume);
|
||||
frac = 1.0;
|
||||
} else if (volume == -30.0f) {
|
||||
frac = 0.0;
|
||||
} else if (volume == 0.0f) {
|
||||
frac = 1.0;
|
||||
} else {
|
||||
frac = (double) ( (30.0f + volume) / 30.0f);
|
||||
frac = (frac > 1.0) ? 1.0 : frac;
|
||||
}
|
||||
|
||||
/* frac is length of volume slider as fraction of max length */
|
||||
/* also (steps/16) where steps is number of discrete steps above mute (16 = full volume) */
|
||||
if (frac == 0.0) {
|
||||
gst_volume = 0.0;
|
||||
} else {
|
||||
/* flat rescaling of decibel range from {-30dB : 0dB} to {db_low : db_high} */
|
||||
db_flat = db_low + (db_high-db_low) * frac;
|
||||
if (taper_volume) {
|
||||
/* taper the volume reduction by the (rescaled) Airplay {-30:0} range so each reduction of
|
||||
* the remaining slider length by 50% reduces the perceived volume by 50% (-10dB gain)
|
||||
* (This is the "dasl-tapering" scheme offered by shairport-sync) */
|
||||
db = db_high + 10.0 * (log10(frac) / log10(2.0));
|
||||
db = (db > db_flat) ? db : db_flat;
|
||||
} else {
|
||||
db = db_flat;
|
||||
}
|
||||
/* conversion from (gain) decibels to GStreamer's linear volume scale */
|
||||
gst_volume = pow(10.0, 0.05*db);
|
||||
}
|
||||
audio_renderer_set_volume(gst_volume);
|
||||
}
|
||||
|
||||
extern "C" void audio_get_format (void *cls, unsigned char *ct, unsigned short *spf, bool *usingScreen, bool *isMedia, uint64_t *audioFormat) {
|
||||
@@ -1959,7 +2003,7 @@ int main (int argc, char *argv[]) {
|
||||
logger_set_level(render_logger, log_level);
|
||||
|
||||
if (use_audio) {
|
||||
audio_renderer_init(render_logger, audiosink.c_str(), &audio_sync, &video_sync, db_low, db_high);
|
||||
audio_renderer_init(render_logger, audiosink.c_str(), &audio_sync, &video_sync);
|
||||
} else {
|
||||
LOGI("audio_disabled");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user