player/CrossFade: use std::chrono::duration
This commit is contained in:
parent
863722545f
commit
224400074c
@ -152,13 +152,13 @@ handle_status(Client &client, gcc_unused Request args, Response &r)
|
||||
pc.GetMixRampDb(),
|
||||
state);
|
||||
|
||||
if (pc.GetCrossFade() > 0)
|
||||
if (pc.GetCrossFade() > FloatDuration::zero())
|
||||
r.Format(COMMAND_STATUS_CROSSFADE ": %lu\n",
|
||||
std::lround(pc.GetCrossFade()));
|
||||
std::lround(pc.GetCrossFade().count()));
|
||||
|
||||
if (pc.GetMixRampDelay() > 0)
|
||||
if (pc.GetMixRampDelay() > FloatDuration::zero())
|
||||
r.Format(COMMAND_STATUS_MIXRAMPDELAY ": %f\n",
|
||||
pc.GetMixRampDelay());
|
||||
pc.GetMixRampDelay().count());
|
||||
|
||||
song = playlist.GetCurrentPosition();
|
||||
if (song >= 0) {
|
||||
@ -316,8 +316,8 @@ handle_seekcur(Client &client, Request args, gcc_unused Response &r)
|
||||
CommandResult
|
||||
handle_crossfade(Client &client, Request args, gcc_unused Response &r)
|
||||
{
|
||||
unsigned xfade_time = args.ParseUnsigned(0);
|
||||
client.GetPlayerControl().SetCrossFade(xfade_time);
|
||||
FloatDuration duration{args.ParseUnsigned(0)};
|
||||
client.GetPlayerControl().SetCrossFade(duration);
|
||||
return CommandResult::OK;
|
||||
}
|
||||
|
||||
@ -332,7 +332,7 @@ handle_mixrampdb(Client &client, Request args, gcc_unused Response &r)
|
||||
CommandResult
|
||||
handle_mixrampdelay(Client &client, Request args, gcc_unused Response &r)
|
||||
{
|
||||
float delay_secs = args.ParseFloat(0);
|
||||
FloatDuration delay_secs{args.ParseFloat(0)};
|
||||
client.GetPlayerControl().SetMixRampDelay(delay_secs);
|
||||
return CommandResult::OK;
|
||||
}
|
||||
|
@ -283,11 +283,9 @@ PlayerControl::LockSeek(std::unique_ptr<DetachedSong> song, SongTime t)
|
||||
}
|
||||
|
||||
void
|
||||
PlayerControl::SetCrossFade(float _cross_fade_seconds) noexcept
|
||||
PlayerControl::SetCrossFade(FloatDuration duration) noexcept
|
||||
{
|
||||
if (_cross_fade_seconds < 0)
|
||||
_cross_fade_seconds = 0;
|
||||
cross_fade.duration = _cross_fade_seconds;
|
||||
cross_fade.duration = std::max(duration, FloatDuration::zero());
|
||||
|
||||
idle_add(IDLE_OPTIONS);
|
||||
}
|
||||
@ -301,9 +299,9 @@ PlayerControl::SetMixRampDb(float _mixramp_db) noexcept
|
||||
}
|
||||
|
||||
void
|
||||
PlayerControl::SetMixRampDelay(float _mixramp_delay_seconds) noexcept
|
||||
PlayerControl::SetMixRampDelay(FloatDuration _mixramp_delay) noexcept
|
||||
{
|
||||
cross_fade.mixramp_delay = _mixramp_delay_seconds;
|
||||
cross_fade.mixramp_delay = _mixramp_delay;
|
||||
|
||||
idle_add(IDLE_OPTIONS);
|
||||
}
|
||||
|
@ -557,9 +557,9 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
void SetCrossFade(float cross_fade_seconds) noexcept;
|
||||
void SetCrossFade(FloatDuration duration) noexcept;
|
||||
|
||||
float GetCrossFade() const noexcept {
|
||||
auto GetCrossFade() const noexcept {
|
||||
return cross_fade.duration;
|
||||
}
|
||||
|
||||
@ -569,9 +569,9 @@ public:
|
||||
return cross_fade.mixramp_db;
|
||||
}
|
||||
|
||||
void SetMixRampDelay(float mixramp_delay_seconds) noexcept;
|
||||
void SetMixRampDelay(FloatDuration mixramp_delay) noexcept;
|
||||
|
||||
float GetMixRampDelay() const noexcept {
|
||||
auto GetMixRampDelay() const noexcept {
|
||||
return cross_fade.mixramp_delay;
|
||||
}
|
||||
|
||||
|
@ -33,10 +33,11 @@
|
||||
static constexpr Domain cross_fade_domain("cross_fade");
|
||||
|
||||
gcc_pure
|
||||
static float
|
||||
static FloatDuration
|
||||
mixramp_interpolate(const char *ramp_list, float required_db) noexcept
|
||||
{
|
||||
float last_db = 0, last_secs = 0;
|
||||
float last_db = 0;
|
||||
FloatDuration last_duration = FloatDuration::zero();
|
||||
bool have_last = false;
|
||||
|
||||
/* ramp_list is a string of pairs of dBs and seconds that describe the
|
||||
@ -54,7 +55,7 @@ mixramp_interpolate(const char *ramp_list, float required_db) noexcept
|
||||
ramp_list = endptr + 1;
|
||||
|
||||
/* Parse the time. */
|
||||
float secs = ParseFloat(ramp_list, &endptr);
|
||||
FloatDuration duration{ParseFloat(ramp_list, &endptr)};
|
||||
if (endptr == ramp_list || (*endptr != ';' && *endptr != 0))
|
||||
break;
|
||||
|
||||
@ -64,27 +65,27 @@ mixramp_interpolate(const char *ramp_list, float required_db) noexcept
|
||||
|
||||
/* Check for exact match. */
|
||||
if (db == required_db) {
|
||||
return secs;
|
||||
return duration;
|
||||
}
|
||||
|
||||
/* Save if too quiet. */
|
||||
if (db < required_db) {
|
||||
last_db = db;
|
||||
last_secs = secs;
|
||||
last_duration = duration;
|
||||
have_last = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If required db < any stored value, use the least. */
|
||||
if (!have_last)
|
||||
return secs;
|
||||
return duration;
|
||||
|
||||
/* Finally, interpolate linearly. */
|
||||
secs = last_secs + (required_db - last_db) * (secs - last_secs) / (db - last_db);
|
||||
return secs;
|
||||
duration = last_duration + (required_db - last_db) * (duration - last_duration) / (db - last_db);
|
||||
return duration;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return FloatDuration(-1);
|
||||
}
|
||||
|
||||
unsigned
|
||||
@ -99,36 +100,38 @@ CrossFadeSettings::Calculate(SignedSongTime total_time,
|
||||
float chunks_f;
|
||||
|
||||
if (total_time.IsNegative() ||
|
||||
duration < 0 || duration >= total_time.ToDoubleS() ||
|
||||
duration <= FloatDuration::zero() ||
|
||||
duration >= std::chrono::duration_cast<FloatDuration>(total_time) ||
|
||||
/* we can't crossfade when the audio formats are different */
|
||||
af != old_format)
|
||||
return 0;
|
||||
|
||||
assert(duration >= 0);
|
||||
assert(duration > FloatDuration::zero());
|
||||
assert(af.IsValid());
|
||||
|
||||
chunks_f = (float)af.GetTimeToSize() / (float)sizeof(MusicChunk::data);
|
||||
|
||||
if (mixramp_delay <= 0 || !mixramp_start || !mixramp_prev_end) {
|
||||
chunks = std::lround(chunks_f * duration);
|
||||
if (mixramp_delay <= FloatDuration::zero() ||
|
||||
!mixramp_start || !mixramp_prev_end) {
|
||||
chunks = std::lround(chunks_f * duration.count());
|
||||
} else {
|
||||
/* Calculate mixramp overlap. */
|
||||
const float mixramp_overlap_current =
|
||||
const auto mixramp_overlap_current =
|
||||
mixramp_interpolate(mixramp_start,
|
||||
mixramp_db - replay_gain_db);
|
||||
const float mixramp_overlap_prev =
|
||||
const auto mixramp_overlap_prev =
|
||||
mixramp_interpolate(mixramp_prev_end,
|
||||
mixramp_db - replay_gain_prev_db);
|
||||
const float mixramp_overlap =
|
||||
const auto mixramp_overlap =
|
||||
mixramp_overlap_current + mixramp_overlap_prev;
|
||||
|
||||
if (mixramp_overlap_current >= 0 &&
|
||||
mixramp_overlap_prev >= 0 &&
|
||||
if (mixramp_overlap_current >= FloatDuration::zero() &&
|
||||
mixramp_overlap_prev >= FloatDuration::zero() &&
|
||||
mixramp_delay <= mixramp_overlap) {
|
||||
chunks = (chunks_f * (mixramp_overlap - mixramp_delay));
|
||||
chunks = (chunks_f * (mixramp_overlap - mixramp_delay).count());
|
||||
FormatDebug(cross_fade_domain,
|
||||
"will overlap %d chunks, %fs", chunks,
|
||||
mixramp_overlap - mixramp_delay);
|
||||
(mixramp_overlap - mixramp_delay).count());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#ifndef MPD_CROSSFADE_HXX
|
||||
#define MPD_CROSSFADE_HXX
|
||||
|
||||
#include "Chrono.hxx"
|
||||
#include "util/Compiler.h"
|
||||
|
||||
struct AudioFormat;
|
||||
@ -29,7 +30,7 @@ struct CrossFadeSettings {
|
||||
/**
|
||||
* The configured cross fade duration [s].
|
||||
*/
|
||||
float duration;
|
||||
FloatDuration duration;
|
||||
|
||||
float mixramp_db;
|
||||
|
||||
@ -37,7 +38,7 @@ struct CrossFadeSettings {
|
||||
* The configured MixRapm delay [s]. A non-positive value
|
||||
* disables MixRamp.
|
||||
*/
|
||||
float mixramp_delay;
|
||||
FloatDuration mixramp_delay;
|
||||
|
||||
CrossFadeSettings()
|
||||
:duration(0),
|
||||
|
@ -809,7 +809,7 @@ Player::PlayNextChunk() noexcept
|
||||
cross_fade_tag = Tag::Merge(std::move(cross_fade_tag),
|
||||
std::move(other_chunk->tag));
|
||||
|
||||
if (pc.cross_fade.mixramp_delay <= 0) {
|
||||
if (pc.cross_fade.mixramp_delay <= FloatDuration::zero()) {
|
||||
chunk->mix_ratio = ((float)cross_fade_position)
|
||||
/ cross_fade_chunks;
|
||||
} else {
|
||||
|
@ -92,10 +92,10 @@ playlist_state_save(BufferedOutputStream &os, const struct playlist &playlist,
|
||||
(int)playlist.queue.single);
|
||||
os.Format(PLAYLIST_STATE_FILE_CONSUME "%i\n", playlist.queue.consume);
|
||||
os.Format(PLAYLIST_STATE_FILE_CROSSFADE "%i\n",
|
||||
(int)pc.GetCrossFade());
|
||||
(int)pc.GetCrossFade().count());
|
||||
os.Format(PLAYLIST_STATE_FILE_MIXRAMPDB "%f\n", pc.GetMixRampDb());
|
||||
os.Format(PLAYLIST_STATE_FILE_MIXRAMPDELAY "%f\n",
|
||||
pc.GetMixRampDelay());
|
||||
pc.GetMixRampDelay().count());
|
||||
os.Write(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "\n");
|
||||
queue_save(os, playlist.queue);
|
||||
os.Write(PLAYLIST_STATE_FILE_PLAYLIST_END "\n");
|
||||
@ -159,14 +159,14 @@ playlist_state_restore(const StateFileConfig &config,
|
||||
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_CONSUME))) {
|
||||
playlist.SetConsume(StringIsEqual(p, "1"));
|
||||
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_CROSSFADE))) {
|
||||
pc.SetCrossFade(atoi(p));
|
||||
pc.SetCrossFade(FloatDuration(atoi(p)));
|
||||
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_MIXRAMPDB))) {
|
||||
pc.SetMixRampDb(ParseFloat(p));
|
||||
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_MIXRAMPDELAY))) {
|
||||
/* this check discards "nan" which was used
|
||||
prior to MPD 0.18 */
|
||||
if (IsDigitASCII(*p))
|
||||
pc.SetMixRampDelay(ParseFloat(p));
|
||||
pc.SetMixRampDelay(FloatDuration(ParseFloat(p)));
|
||||
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_RANDOM))) {
|
||||
random_mode = StringIsEqual(p, "1");
|
||||
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_CURRENT))) {
|
||||
@ -232,7 +232,7 @@ playlist_state_get_hash(const playlist &playlist,
|
||||
(playlist.current >= 0
|
||||
? (playlist.queue.OrderToPosition(playlist.current) << 16)
|
||||
: 0) ^
|
||||
((int)pc.GetCrossFade() << 20) ^
|
||||
((int)pc.GetCrossFade().count() << 20) ^
|
||||
(unsigned(player_status.state) << 24) ^
|
||||
/* note that this takes 2 bits */
|
||||
((int)playlist.queue.single << 25) ^
|
||||
|
@ -23,53 +23,53 @@ public:
|
||||
|
||||
char *foo = strdup(input);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(double(0),
|
||||
mixramp_interpolate(foo, 0),
|
||||
mixramp_interpolate(foo, 0).count(),
|
||||
0.05);
|
||||
free(foo);
|
||||
|
||||
foo = strdup(input);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(float(0),
|
||||
mixramp_interpolate(foo, 1),
|
||||
mixramp_interpolate(foo, 1).count(),
|
||||
0.005);
|
||||
free(foo);
|
||||
|
||||
foo = strdup(input);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(float(0.1),
|
||||
mixramp_interpolate(foo, 3),
|
||||
mixramp_interpolate(foo, 3).count(),
|
||||
0.005);
|
||||
free(foo);
|
||||
|
||||
foo = strdup(input);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(float(2.5),
|
||||
mixramp_interpolate(foo, 6),
|
||||
mixramp_interpolate(foo, 6).count(),
|
||||
0.01);
|
||||
free(foo);
|
||||
|
||||
foo = strdup(input);
|
||||
CPPUNIT_ASSERT(mixramp_interpolate(foo, 6.1) < 0);
|
||||
CPPUNIT_ASSERT(mixramp_interpolate(foo, 6.1) < FloatDuration::zero());
|
||||
free(foo);
|
||||
|
||||
foo = strdup(input);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(float(0.05),
|
||||
mixramp_interpolate(foo, 2),
|
||||
mixramp_interpolate(foo, 2).count(),
|
||||
0.05);
|
||||
free(foo);
|
||||
|
||||
foo = strdup(input);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(float(1.3),
|
||||
mixramp_interpolate(foo, 4.5),
|
||||
mixramp_interpolate(foo, 4.5).count(),
|
||||
0.05);
|
||||
free(foo);
|
||||
|
||||
foo = strdup(input);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(float(0.9),
|
||||
mixramp_interpolate(foo, 4),
|
||||
mixramp_interpolate(foo, 4).count(),
|
||||
0.05);
|
||||
free(foo);
|
||||
|
||||
foo = strdup(input);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(float(1.7),
|
||||
mixramp_interpolate(foo, 5),
|
||||
mixramp_interpolate(foo, 5).count(),
|
||||
0.05);
|
||||
free(foo);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user