Fixed issue 578: fade-in fade-out on (un)pause.
This commit is contained in:
parent
cc80d08121
commit
ec481d5874
@ -79,6 +79,8 @@ void Engine::Base::ReloadSettings() {
|
||||
crossfade_enabled_ = s.value("CrossfadeEnabled", true).toBool();
|
||||
autocrossfade_enabled_ = s.value("AutoCrossfadeEnabled", false).toBool();
|
||||
crossfade_same_album_ = !s.value("NoCrossfadeSameAlbum", true).toBool();
|
||||
fadeout_pause_enabled_ = s.value("FadeoutPauseEnabled", false).toBool();
|
||||
fadeout_pause_duration_nanosec_ = s.value("FadeoutPauseDuration", 250).toLongLong() * kNsecPerMsec;
|
||||
}
|
||||
|
||||
void Engine::Base::EmitAboutToEnd() {
|
||||
|
@ -147,6 +147,8 @@ class Base : public QObject, boost::noncopyable {
|
||||
bool autocrossfade_enabled_;
|
||||
bool crossfade_same_album_;
|
||||
int next_background_stream_id_;
|
||||
bool fadeout_pause_enabled_;
|
||||
qint64 fadeout_pause_duration_nanosec_;
|
||||
|
||||
private:
|
||||
bool about_to_end_emitted_;
|
||||
|
@ -85,7 +85,9 @@ GstEngine::GstEngine(TaskManager* task_manager)
|
||||
mono_playback_(false),
|
||||
seek_timer_(new QTimer(this)),
|
||||
timer_id_(-1),
|
||||
next_element_id_(0)
|
||||
next_element_id_(0),
|
||||
is_fading_out_to_pause_(false),
|
||||
has_faded_out_(false)
|
||||
{
|
||||
seek_timer_->setSingleShot(true);
|
||||
seek_timer_->setInterval(kSeekDelayNanosec / kNsecPerMsec);
|
||||
@ -358,6 +360,9 @@ bool GstEngine::Load(const QUrl& url, Engine::TrackChangeFlags change,
|
||||
}
|
||||
|
||||
void GstEngine::StartFadeout() {
|
||||
if (is_fading_out_to_pause_)
|
||||
return;
|
||||
|
||||
fadeout_pipeline_ = current_pipeline_;
|
||||
disconnect(fadeout_pipeline_.get(), 0, 0, 0);
|
||||
fadeout_pipeline_->RemoveAllBufferConsumers();
|
||||
@ -367,6 +372,24 @@ void GstEngine::StartFadeout() {
|
||||
connect(fadeout_pipeline_.get(), SIGNAL(FaderFinished()), SLOT(FadeoutFinished()));
|
||||
}
|
||||
|
||||
void GstEngine::StartFadeoutPause() {
|
||||
fadeout_pause_pipeline_ = current_pipeline_;
|
||||
disconnect(fadeout_pause_pipeline_.get(), SIGNAL(FaderFinished()), 0, 0);
|
||||
|
||||
fadeout_pause_pipeline_->StartFader(fadeout_pause_duration_nanosec_,
|
||||
QTimeLine::Backward,
|
||||
QTimeLine::EaseInOutCurve,
|
||||
false);
|
||||
if (fadeout_pipeline_ && fadeout_pipeline_->state() == GST_STATE_PLAYING) {
|
||||
qLog(Debug) << "start fadeout pipeline";
|
||||
fadeout_pipeline_->StartFader(fadeout_pause_duration_nanosec_,
|
||||
QTimeLine::Backward,
|
||||
QTimeLine::LinearCurve,
|
||||
false);
|
||||
}
|
||||
connect(fadeout_pause_pipeline_.get(), SIGNAL(FaderFinished()), SLOT(FadeoutPauseFinished()));
|
||||
is_fading_out_to_pause_ = true;
|
||||
}
|
||||
|
||||
bool GstEngine::Play(quint64 offset_nanosec) {
|
||||
EnsureInitialised();
|
||||
@ -380,6 +403,10 @@ bool GstEngine::Play(quint64 offset_nanosec) {
|
||||
watcher->setFuture(future);
|
||||
connect(watcher, SIGNAL(finished()), SLOT(PlayDone()));
|
||||
|
||||
if (is_fading_out_to_pause_) {
|
||||
current_pipeline_->SetState(GST_STATE_PAUSED);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -445,15 +472,46 @@ void GstEngine::FadeoutFinished() {
|
||||
emit FadeoutFinishedSignal();
|
||||
}
|
||||
|
||||
void GstEngine::FadeoutPauseFinished() {
|
||||
fadeout_pause_pipeline_->SetState(GST_STATE_PAUSED);
|
||||
current_pipeline_->SetState(GST_STATE_PAUSED);
|
||||
emit StateChanged(Engine::Paused);
|
||||
StopTimers();
|
||||
|
||||
is_fading_out_to_pause_ = false;
|
||||
has_faded_out_ = true;
|
||||
fadeout_pause_pipeline_.reset();
|
||||
fadeout_pipeline_.reset();
|
||||
|
||||
emit FadeoutFinishedSignal();
|
||||
}
|
||||
|
||||
void GstEngine::Pause() {
|
||||
if (!current_pipeline_ || current_pipeline_->is_buffering())
|
||||
return;
|
||||
|
||||
if ( current_pipeline_->state() == GST_STATE_PLAYING ) {
|
||||
current_pipeline_->SetState(GST_STATE_PAUSED);
|
||||
emit StateChanged(Engine::Paused);
|
||||
// Check if we started a fade out. If it isn't finished yet and the user
|
||||
// pressed play, we inverse the fader and resume the playback.
|
||||
if (is_fading_out_to_pause_) {
|
||||
disconnect(current_pipeline_.get(), SIGNAL(FaderFinished()), 0, 0);
|
||||
current_pipeline_->StartFader(fadeout_pause_duration_nanosec_,
|
||||
QTimeLine::Forward,
|
||||
QTimeLine::EaseInOutCurve,
|
||||
false);
|
||||
is_fading_out_to_pause_ = false;
|
||||
has_faded_out_ = false;
|
||||
emit StateChanged(Engine::Playing);
|
||||
return;
|
||||
}
|
||||
|
||||
StopTimers();
|
||||
if ( current_pipeline_->state() == GST_STATE_PLAYING ) {
|
||||
if (fadeout_pause_enabled_) {
|
||||
StartFadeoutPause();
|
||||
} else {
|
||||
current_pipeline_->SetState(GST_STATE_PAUSED);
|
||||
emit StateChanged(Engine::Paused);
|
||||
StopTimers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,6 +521,19 @@ void GstEngine::Unpause() {
|
||||
|
||||
if ( current_pipeline_->state() == GST_STATE_PAUSED ) {
|
||||
current_pipeline_->SetState(GST_STATE_PLAYING);
|
||||
|
||||
// Check if we faded out last time. If yes, fade in no matter what the
|
||||
// settings say. If we pause with fadeout, deactivate fadeout and resume
|
||||
// playback, the player would be muted if not faded in.
|
||||
if (has_faded_out_) {
|
||||
disconnect(current_pipeline_.get(), SIGNAL(FaderFinished()), 0, 0);
|
||||
current_pipeline_->StartFader(fadeout_pause_duration_nanosec_,
|
||||
QTimeLine::Forward,
|
||||
QTimeLine::EaseInOutCurve,
|
||||
false);
|
||||
has_faded_out_ = false;
|
||||
}
|
||||
|
||||
emit StateChanged(Engine::Playing);
|
||||
|
||||
StartTimers();
|
||||
|
@ -121,6 +121,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
void ClearScopeBuffers();
|
||||
void AddBufferToScope(GstBuffer* buf, int pipeline_id);
|
||||
void FadeoutFinished();
|
||||
void FadeoutPauseFinished();
|
||||
void SeekNow();
|
||||
void BackgroundStreamFinished();
|
||||
void BackgroundStreamPlayDone();
|
||||
@ -138,6 +139,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
PluginDetailsList GetPluginList(const QString& classname) const;
|
||||
|
||||
void StartFadeout();
|
||||
void StartFadeoutPause();
|
||||
|
||||
void StartTimers();
|
||||
void StopTimers();
|
||||
@ -170,6 +172,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
|
||||
boost::shared_ptr<GstEnginePipeline> current_pipeline_;
|
||||
boost::shared_ptr<GstEnginePipeline> fadeout_pipeline_;
|
||||
boost::shared_ptr<GstEnginePipeline> fadeout_pause_pipeline_;
|
||||
QUrl preloaded_url_;
|
||||
|
||||
QList<BufferConsumer*> buffer_consumers_;
|
||||
@ -203,6 +206,9 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
int next_element_id_;
|
||||
|
||||
QHash<int, boost::shared_ptr<GstEnginePipeline> > background_streams_;
|
||||
|
||||
bool is_fading_out_to_pause_;
|
||||
bool has_faded_out_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -700,7 +700,6 @@ bool GstEnginePipeline::HandoffCallback(GstPad*, GstBuffer* buf, gpointer self)
|
||||
// GstEngine will try to seek to the start of the new section, but
|
||||
// we're already there so ignore it.
|
||||
instance->ignore_next_seek_ = true;
|
||||
|
||||
emit instance->EndOfStreamReached(instance->id(), true);
|
||||
} else {
|
||||
// We have a next song but we can't cheat, so move to it normally.
|
||||
@ -918,14 +917,23 @@ void GstEnginePipeline::UpdateVolume() {
|
||||
|
||||
void GstEnginePipeline::StartFader(qint64 duration_nanosec,
|
||||
QTimeLine::Direction direction,
|
||||
QTimeLine::CurveShape shape) {
|
||||
QTimeLine::CurveShape shape,
|
||||
bool use_fudge_timer) {
|
||||
const int duration_msec = duration_nanosec / kNsecPerMsec;
|
||||
|
||||
// If there's already another fader running then start from the same time
|
||||
// that one was already at.
|
||||
int start_time = direction == QTimeLine::Forward ? 0 : duration_msec;
|
||||
if (fader_ && fader_->state() == QTimeLine::Running)
|
||||
start_time = fader_->currentTime();
|
||||
if (fader_ && fader_->state() == QTimeLine::Running) {
|
||||
if (duration_msec == fader_->duration()) {
|
||||
start_time = fader_->currentTime();
|
||||
} else {
|
||||
// Calculate the position in the new fader with the same value from
|
||||
// the old fader, so no volume jumps appear
|
||||
qreal time = qreal(duration_msec) * (qreal(fader_->currentTime()) / qreal(fader_->duration()));
|
||||
start_time = qRound(time);
|
||||
}
|
||||
}
|
||||
|
||||
fader_.reset(new QTimeLine(duration_msec, this));
|
||||
connect(fader_.get(), SIGNAL(valueChanged(qreal)), SLOT(SetVolumeModifier(qreal)));
|
||||
@ -936,6 +944,7 @@ void GstEnginePipeline::StartFader(qint64 duration_nanosec,
|
||||
fader_->resume();
|
||||
|
||||
fader_fudge_timer_.stop();
|
||||
use_fudge_timer_ = use_fudge_timer;
|
||||
|
||||
SetVolumeModifier(fader_->currentValue());
|
||||
}
|
||||
@ -946,7 +955,15 @@ void GstEnginePipeline::FaderTimelineFinished() {
|
||||
// Wait a little while longer before emitting the finished signal (and
|
||||
// probably distroying the pipeline) to account for delays in the audio
|
||||
// server/driver.
|
||||
fader_fudge_timer_.start(kFaderFudgeMsec, this);
|
||||
if (use_fudge_timer_) {
|
||||
fader_fudge_timer_.start(kFaderFudgeMsec, this);
|
||||
} else {
|
||||
// Even here we cannot emit the signal directly, as it result in a
|
||||
// stutter when resuming playback. So use a quest small time, so you
|
||||
// won't notice the difference when resuming playback
|
||||
// (You get here when the pause fading is active)
|
||||
fader_fudge_timer_.start(250, this);
|
||||
}
|
||||
}
|
||||
|
||||
void GstEnginePipeline::timerEvent(QTimerEvent* e) {
|
||||
|
@ -71,7 +71,8 @@ class GstEnginePipeline : public QObject {
|
||||
void SetVolume(int percent);
|
||||
void StartFader(qint64 duration_nanosec,
|
||||
QTimeLine::Direction direction = QTimeLine::Forward,
|
||||
QTimeLine::CurveShape shape = QTimeLine::LinearCurve);
|
||||
QTimeLine::CurveShape shape = QTimeLine::LinearCurve,
|
||||
bool use_fudge_timer = true);
|
||||
|
||||
// If this is set then it will be loaded automatically when playback finishes
|
||||
// for gapless playback
|
||||
@ -245,6 +246,7 @@ class GstEnginePipeline : public QObject {
|
||||
|
||||
boost::scoped_ptr<QTimeLine> fader_;
|
||||
QBasicTimer fader_fudge_timer_;
|
||||
bool use_fudge_timer_;
|
||||
|
||||
GstElement* pipeline_;
|
||||
|
||||
|
@ -74,6 +74,8 @@ void PlaybackSettingsPage::Load() {
|
||||
ui_->fading_auto->setChecked(s.value("AutoCrossfadeEnabled", false).toBool());
|
||||
ui_->fading_duration->setValue(s.value("FadeoutDuration", 2000).toInt());
|
||||
ui_->fading_samealbum->setChecked(s.value("NoCrossfadeSameAlbum", true).toBool());
|
||||
ui_->fadeout_pause->setChecked(s.value("FadeoutPauseEnabled", false).toBool());
|
||||
ui_->fading_pause_duration->setValue(s.value("FadeoutPauseDuration", 250).toInt());
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup(GstEngine::kSettingsGroup);
|
||||
@ -108,6 +110,8 @@ void PlaybackSettingsPage::Save() {
|
||||
s.setValue("CrossfadeEnabled", ui_->fading_cross->isChecked());
|
||||
s.setValue("AutoCrossfadeEnabled", ui_->fading_auto->isChecked());
|
||||
s.setValue("NoCrossfadeSameAlbum", ui_->fading_samealbum->isChecked());
|
||||
s.setValue("FadeoutPauseEnabled", ui_->fadeout_pause->isChecked());
|
||||
s.setValue("FadeoutPauseDuration", ui_->fading_pause_duration->value());
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup(GstEngine::kSettingsGroup);
|
||||
|
@ -115,6 +115,56 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="fadeout_pause">
|
||||
<property name="text">
|
||||
<string>Fade out on pause / fade in on resume</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Fading duration</string>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>22</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="fading_pause_duration">
|
||||
<property name="suffix">
|
||||
<string> ms</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>250</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@ -146,7 +196,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Replay Gain mode</string>
|
||||
<string>Fading duration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -167,14 +217,18 @@
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Pre-amp</string>
|
||||
<string>Fading duration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="replaygain_preamp_label"/>
|
||||
<widget class="QLabel" name="replaygain_preamp_label">
|
||||
<property name="text">
|
||||
<string>Fading duration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="StickySlider" name="replaygain_preamp">
|
||||
@ -222,7 +276,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Output plugin</string>
|
||||
<string>Fading duration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -241,7 +295,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Output device</string>
|
||||
<string>Fading duration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -258,7 +312,7 @@
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="buffer_duration_label">
|
||||
<property name="text">
|
||||
<string>Buffer duration</string>
|
||||
<string>Fading duration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
Loading…
x
Reference in New Issue
Block a user