player/CrossFade: use std::chrono::duration

This commit is contained in:
Max Kellermann 2018-09-22 19:24:34 +02:00
parent 863722545f
commit 224400074c
8 changed files with 56 additions and 54 deletions

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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),

View File

@ -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 {

View File

@ -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) ^

View File

@ -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);
}