From 04f1d296ea8fc0b35948edcc460ff1ad3f0f73d1 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sun, 1 Jul 2018 01:29:52 +0200 Subject: [PATCH] More engine fixes --- CMakeLists.txt | 3 + dist/maketarball.sh.in | 17 ++- src/core/player.cpp | 104 +++++++---------- src/core/player.h | 8 +- src/engine/directsounddevicefinder.cpp | 2 +- src/engine/enginebase.cpp | 7 +- src/engine/enginebase.h | 3 +- src/engine/gstengine.cpp | 17 ++- src/engine/gstengine.h | 4 +- src/engine/phononengine.cpp | 19 +++- src/engine/phononengine.h | 3 +- src/engine/vlcengine.cpp | 50 ++++++--- src/engine/vlcengine.h | 4 +- src/engine/xineengine.cpp | 147 +++++++++++++++---------- src/engine/xineengine.h | 56 +++++----- src/engine/xinefader.cpp | 2 +- src/musicbrainz/chromaprinter.cpp | 4 + src/settings/backendsettingspage.cpp | 64 +++++++---- src/settings/backendsettingspage.h | 16 ++- src/settings/playbacksettingspage.cpp | 3 +- src/settings/settingsdialog.cpp | 9 +- src/settings/settingsdialog.h | 5 + 22 files changed, 330 insertions(+), 217 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 02c9dde6..92f7a61c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,9 @@ project(strawberry) cmake_minimum_required(VERSION 2.8.11) cmake_policy(SET CMP0054 NEW) +if(${CMAKE_VERSION} VERSION_GREATER "3.10.3") + cmake_policy(SET CMP0072 NEW) +endif() include(CheckCXXCompilerFlag) include(CheckIncludeFiles) diff --git a/dist/maketarball.sh.in b/dist/maketarball.sh.in index 975acd03..7d7fd404 100755 --- a/dist/maketarball.sh.in +++ b/dist/maketarball.sh.in @@ -2,19 +2,26 @@ name=strawberry version="@STRAWBERRY_VERSION_PACKAGE@" +gitrev="@INCLUDE_GIT_REVISION@" root=$(cd "${0%/*}/.." && echo $PWD/${0##*/}) root=`dirname "$root"` rootnoslash=`echo $root | sed "s/^\///"` +if ! [ "$gitrev" = "ON" ]; then + exclude_vcs="--exclude-vcs" +fi + echo "Creating $name-$version.tar.xz..." rm -f "$name-$version.tar.xz" tar -cJf $name-$version.tar.xz \ - --transform "s,^$rootnoslash,$name-$version," \ - --exclude-vcs \ - --exclude "$root/dist/*.tar" \ - --exclude "$root/dist/*.tar.*" \ + --transform "s,^$rootnoslash,$name-$version," $exclude_vcs \ + --exclude "*.tar" \ + --exclude "*.tar.*" \ + --exclude "*.bz" \ + --exclude "*.bz2" \ + --exclude "*.xz" \ + --exclude ".directory" \ --exclude "$root/CMakeLists.txt.user" \ --exclude "$root/build" \ - --exclude ".directory" \ "$root" diff --git a/src/core/player.cpp b/src/core/player.cpp index 0949f4ae..ff5d819c 100644 --- a/src/core/player.cpp +++ b/src/core/player.cpp @@ -81,8 +81,7 @@ Player::Player(Application *app, QObject *parent) volume_before_mute_(50), last_pressed_previous_(QDateTime::currentDateTime()), menu_previousmode_(PreviousBehaviour_DontRestart), - seek_step_sec_(10) - { + seek_step_sec_(10) { QSettings s; s.beginGroup(BackendSettingsPage::kSettingsGroup); @@ -90,103 +89,86 @@ Player::Player(Application *app, QObject *parent) s.endGroup(); CreateEngine(enginetype); - settings_.beginGroup("Player"); - SetVolume(settings_.value("volume", 100).toInt()); - -#if 0 - connect(engine_.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString))); - connect(engine_.get(), SIGNAL(ValidSongRequested(QUrl)), SLOT(ValidSongRequested(QUrl))); - connect(engine_.get(), SIGNAL(InvalidSongRequested(QUrl)), SLOT(InvalidSongRequested(QUrl))); -#endif + int volume = settings_.value("volume", 50).toInt(); + SetVolume(volume); } -Player::~Player() {} +Player::~Player() { + settings_.endGroup(); +} -EngineBase *Player::CreateEngine(Engine::EngineType enginetype) { - - bool engine = false; - EngineBase *enginebase = nullptr; +void Player::CreateEngine(Engine::EngineType enginetype) { - for (int i = 1 ; !engine ; i++) { + Engine::EngineType use_enginetype = Engine::None; + + for (int i = 0 ; use_enginetype == Engine::None ; i++) { switch(enginetype) { case Engine::None: #ifdef HAVE_GSTREAMER case Engine::GStreamer: - engine=true; - enginetype=Engine::GStreamer; - enginebase = new GstEngine(app_->task_manager()); + use_enginetype=Engine::GStreamer; + engine_.reset(new GstEngine(app_->task_manager())); break; #endif #ifdef HAVE_XINE case Engine::Xine: - engine=true; - enginetype=Engine::Xine; - enginebase = new XineEngine(app_->task_manager()); + use_enginetype=Engine::Xine; + engine_.reset(new XineEngine(app_->task_manager())); break; #endif #ifdef HAVE_VLC case Engine::VLC: - engine=true; - enginetype=Engine::VLC; - enginebase = new VLCEngine(app_->task_manager()); + use_enginetype=Engine::VLC; + engine_.reset(new VLCEngine(app_->task_manager())); break; #endif #ifdef HAVE_PHONON case Engine::Phonon: - engine=true; - enginetype=Engine::Phonon; - enginebase = new PhononEngine(app_->task_manager()); + use_enginetype=Engine::Phonon; + engine_.reset(new PhononEngine(app_->task_manager())); break; #endif default: - if (i > 1) { qFatal("No engine available!"); return nullptr; } - QSettings s; - s.beginGroup(BackendSettingsPage::kSettingsGroup); - s.setValue("engine", ""); - s.setValue("output", ""); - s.setValue("device", QVariant("")); - s.endGroup(); - enginetype = Engine::None; - break; + if (i > 0) { qFatal("No engine available!"); } + enginetype = Engine::None; + break; } } - QSettings s; - s.beginGroup(BackendSettingsPage::kSettingsGroup); - s.setValue("engine", Engine::EngineName(enginetype)); - s.endGroup(); - - if (enginebase == nullptr) { - qFatal("Failed to create engine!"); - return nullptr; + if (use_enginetype != enginetype) { // Engine was set to something else. Reset output and device. + QSettings s; + s.beginGroup(BackendSettingsPage::kSettingsGroup); + s.setValue("engine", EngineName(use_enginetype)); + s.setValue("output", engine_->DefaultOutput()); + s.setValue("device", QVariant("")); + s.endGroup(); } - engine_.reset(enginebase); - - return enginebase; + if (!engine_) { + qFatal("Failed to create engine!"); + } } - void Player::Init() { + + if (!engine_->Init()) { qFatal("Error initialising audio engine"); } + + analyzer_->SetEngine(engine_.get()); connect(engine_.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString))); connect(engine_.get(), SIGNAL(ValidSongRequested(QUrl)), SLOT(ValidSongRequested(QUrl))); connect(engine_.get(), SIGNAL(InvalidSongRequested(QUrl)), SLOT(InvalidSongRequested(QUrl))); - - if (!engine_->Init()) qFatal("Error initialising audio engine"); - connect(engine_.get(), SIGNAL(StateChanged(Engine::State)), SLOT(EngineStateChanged(Engine::State))); connect(engine_.get(), SIGNAL(TrackAboutToEnd()), SLOT(TrackAboutToEnd())); connect(engine_.get(), SIGNAL(TrackEnded()), SLOT(TrackEnded())); connect(engine_.get(), SIGNAL(MetaData(Engine::SimpleMetaBundle)), SLOT(EngineMetadataReceived(Engine::SimpleMetaBundle))); - engine_->SetVolume(settings_.value("volume", 50).toInt()); - - analyzer_->SetEngine(engine_.get()); + int volume = settings_.value("volume", 50).toInt(); + engine_->SetVolume(volume); // Equalizer qLog(Debug) << "Creating equalizer"; @@ -195,7 +177,6 @@ void Player::Init() { connect(equalizer_, SIGNAL(StereoBalanceChanged(float)), app_->player()->engine(), SLOT(SetStereoBalance(float))); engine_->SetEqualizerEnabled(equalizer_->is_enabled()); - engine_->SetEqualizerParameters(equalizer_->preamp_value(), equalizer_->gain_values()); engine_->SetStereoBalance(equalizer_->stereo_balance()); @@ -203,15 +184,6 @@ void Player::Init() { } -void Player::SetAnalyzer(AnalyzerContainer *analyzer) { - - analyzer_ = analyzer; - -} -void Player::SetEqualizer(Equalizer *equalizer) { - equalizer_ = equalizer; -} - void Player::ReloadSettings() { QSettings s; @@ -224,7 +196,7 @@ void Player::ReloadSettings() { seek_step_sec_ = s.value("seek_step_sec", 10).toInt(); s.endGroup(); - if (engine_.get()) engine_->ReloadSettings(); + engine_->ReloadSettings(); } diff --git a/src/core/player.h b/src/core/player.h index 2b937a2d..e247f258 100644 --- a/src/core/player.h +++ b/src/core/player.h @@ -129,7 +129,7 @@ class Player : public PlayerInterface { PreviousBehaviour_Restart = 2 }; - EngineBase *CreateEngine(Engine::EngineType enginetype); + void CreateEngine(Engine::EngineType enginetype); void Init(); EngineBase *engine() const { return engine_.get(); } @@ -145,9 +145,9 @@ class Player : public PlayerInterface { const UrlHandler *HandlerForUrl(const QUrl &url) const; bool PreviousWouldRestartTrack() const; - - void SetAnalyzer(AnalyzerContainer *analyzer); - void SetEqualizer(Equalizer *equalizer); + + void SetAnalyzer(AnalyzerContainer *analyzer) { analyzer_ = analyzer; } + void SetEqualizer(Equalizer *equalizer) { equalizer_ = equalizer; } public slots: void ReloadSettings(); diff --git a/src/engine/directsounddevicefinder.cpp b/src/engine/directsounddevicefinder.cpp index 9b2a2018..d6462643 100644 --- a/src/engine/directsounddevicefinder.cpp +++ b/src/engine/directsounddevicefinder.cpp @@ -35,7 +35,7 @@ #include "core/logging.h" DirectSoundDeviceFinder::DirectSoundDeviceFinder() - : DeviceFinder("directsound", { "directsound", "dsound", "directsoundsink" }) { + : DeviceFinder("directsound", { "directsound", "dsound", "directsoundsink", "directx", "directx2" }) { } QList DirectSoundDeviceFinder::ListDevices() { diff --git a/src/engine/enginebase.cpp b/src/engine/enginebase.cpp index ed1ee0d9..890f5622 100644 --- a/src/engine/enginebase.cpp +++ b/src/engine/enginebase.cpp @@ -84,7 +84,6 @@ bool Engine::Base::Play(const QUrl &url, TrackChangeFlags flags, bool force_stop void Engine::Base::SetVolume(uint value) { volume_ = value; - SetVolumeSW(MakeVolumeLogarithmic(value)); } @@ -132,3 +131,9 @@ void Engine::Base::EmitAboutToEnd() { about_to_end_emitted_ = true; emit TrackAboutToEnd(); } + +bool Engine::Base::ValidOutput(const QString &output) { + + return (true); + +} diff --git a/src/engine/enginebase.h b/src/engine/enginebase.h index d3133bea..847c7e8a 100644 --- a/src/engine/enginebase.h +++ b/src/engine/enginebase.h @@ -89,8 +89,9 @@ public: } virtual OutputDetailsList GetOutputsList() const = 0; + virtual bool ValidOutput(const QString &output) = 0; virtual QString DefaultOutput() = 0; - virtual bool CustomDeviceSupport(const QString &name) = 0; + virtual bool CustomDeviceSupport(const QString &output) = 0; // Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length). // Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown. diff --git a/src/engine/gstengine.cpp b/src/engine/gstengine.cpp index ddfead50..ae3a7b56 100644 --- a/src/engine/gstengine.cpp +++ b/src/engine/gstengine.cpp @@ -99,6 +99,7 @@ GstEngine::GstEngine(TaskManager *task_manager) scope_chunk_(0), have_new_buffer_(false) { + type_ = Engine::GStreamer; seek_timer_->setSingleShot(true); seek_timer_->setInterval(kSeekDelayNanosec / kNsecPerMsec); connect(seek_timer_, SIGNAL(timeout()), SLOT(SeekNow())); @@ -129,7 +130,6 @@ bool GstEngine::Init() { SetEnvironment(); - type_ = Engine::GStreamer; initialising_ = QtConcurrent::run(this, &GstEngine::InitialiseGStreamer); return true; @@ -388,8 +388,19 @@ EngineBase::OutputDetailsList GstEngine::GetOutputsList() const { } -bool GstEngine::CustomDeviceSupport(const QString &name) { - return (name == kALSASink || name == kOpenALSASink || name == kOSSSink || name == kOSS4Sink || name == kPulseSink || name == kA2DPSink || name == kAVDTPSink); +bool GstEngine::ValidOutput(const QString &output) { + + PluginDetailsList plugins = GetPluginList("Sink/Audio"); + for (const PluginDetails &plugin : plugins) { + if (plugin.name == output) return(true); + } + return(false); + +} + + +bool GstEngine::CustomDeviceSupport(const QString &output) { + return (output == kALSASink || output == kOpenALSASink || output == kOSSSink || output == kOSS4Sink || output == kPulseSink || output == kA2DPSink || output == kAVDTPSink); } void GstEngine::ReloadSettings() { diff --git a/src/engine/gstengine.h b/src/engine/gstengine.h index 6709a1a6..ad577dd4 100644 --- a/src/engine/gstengine.h +++ b/src/engine/gstengine.h @@ -84,8 +84,9 @@ class GstEngine : public Engine::Base, public GstBufferConsumer { const Engine::Scope &scope(int chunk_length); OutputDetailsList GetOutputsList() const; + bool ValidOutput(const QString &output); QString DefaultOutput() { return kAutoSink; } - bool CustomDeviceSupport(const QString &name); + bool CustomDeviceSupport(const QString &output); void EnsureInitialised() { initialising_.waitForFinished(); } void InitialiseGStreamer(); @@ -132,7 +133,6 @@ class GstEngine : public Engine::Base, public GstBufferConsumer { void BufferingFinished(); private: - static const char *kAutoSink; static const char *kALSASink; static const char *kOpenALSASink; diff --git a/src/engine/phononengine.cpp b/src/engine/phononengine.cpp index 566af258..2bb9c0c2 100644 --- a/src/engine/phononengine.cpp +++ b/src/engine/phononengine.cpp @@ -32,11 +32,13 @@ #include "core/logging.h" PhononEngine::PhononEngine(TaskManager *task_manager) - : media_object_(new Phonon::MediaObject(this)), + : EngineBase(), + media_object_(new Phonon::MediaObject(this)), audio_output_(new Phonon::AudioOutput(Phonon::MusicCategory, this)), state_timer_(new QTimer(this)), - seek_offset_(-1) -{ + seek_offset_(-1) { + + type_ = Engine::Phonon; Phonon::createPath(media_object_, audio_output_); @@ -54,8 +56,6 @@ PhononEngine::~PhononEngine() { } bool PhononEngine::Init() { - //qLog(Debug) << __PRETTY_FUNCTION__; - type_ = Engine::Phonon; return true; } @@ -179,6 +179,13 @@ EngineBase::OutputDetailsList PhononEngine::GetOutputsList() const { return ret; } -bool PhononEngine::CustomDeviceSupport(const QString &name) { +bool PhononEngine::ValidOutput(const QString &output) { + + return (output == "auto" || output == "" || output == DefaultOutput); + return(false); + +} + +bool PhononEngine::CustomDeviceSupport(const QString &output) { return false; } diff --git a/src/engine/phononengine.h b/src/engine/phononengine.h index af035f7c..2a392e75 100644 --- a/src/engine/phononengine.h +++ b/src/engine/phononengine.h @@ -64,7 +64,8 @@ class PhononEngine : public Engine::Base { qint64 length_nanosec() const; QString DefaultOutput() { return ""; } - bool CustomDeviceSupport(const QString &name); + bool ValidOutput(const QString &output); + bool CustomDeviceSupport(const QString &output); protected: void SetVolumeSW( uint percent ); diff --git a/src/engine/vlcengine.cpp b/src/engine/vlcengine.cpp index c2e1586b..0802881d 100644 --- a/src/engine/vlcengine.cpp +++ b/src/engine/vlcengine.cpp @@ -40,13 +40,14 @@ VLCEngine *VLCEngine::sInstance = nullptr; VLCEngine::VLCEngine(TaskManager *task_manager) - : instance_(nullptr), + : EngineBase(), + instance_(nullptr), player_(nullptr), state_(Engine::Empty), - scope_data_(4096) -{ + scope_data_(4096) { - Init(); + type_ = Engine::VLC; + ReloadSettings(); } @@ -61,8 +62,6 @@ VLCEngine::~VLCEngine() { bool VLCEngine::Init() { - type_ = Engine::VLC; - /* FIXME: Do we need this? static const char *const args[] = { "-I", "dummy", // Don't use any interface @@ -113,8 +112,15 @@ bool VLCEngine::Init() { } -bool VLCEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { +bool VLCEngine::Initialised() const { + if (instance_ && player_) return true; + return false; + +} + +bool VLCEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { + if (!Initialised()) return false; // Create the media object VlcScopedRef media(libvlc_media_new_location(instance_, url.toEncoded().constData())); @@ -125,7 +131,7 @@ bool VLCEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool forc } bool VLCEngine::Play(quint64 offset_nanosec) { - + if (!Initialised()) return false; // Set audio output if (!output_.isEmpty() || output_ != "auto") { int result = libvlc_audio_output_set(player_, output_.toUtf8().constData()); @@ -148,27 +154,29 @@ bool VLCEngine::Play(quint64 offset_nanosec) { } void VLCEngine::Stop(bool stop_after) { - + if (!Initialised()) return; libvlc_media_player_stop(player_); HandleErrors(); } void VLCEngine::Pause() { - + if (!Initialised()) return; libvlc_media_player_pause(player_); HandleErrors(); } void VLCEngine::Unpause() { - + if (!Initialised()) return; libvlc_media_player_play(player_); HandleErrors(); } void VLCEngine::Seek(quint64 offset_nanosec) { + + if (!Initialised()) return; int offset = (offset_nanosec / kNsecPerMsec); @@ -183,7 +191,7 @@ void VLCEngine::Seek(quint64 offset_nanosec) { } void VLCEngine::SetVolumeSW(uint percent) { - + if (!Initialised()) return; libvlc_audio_set_volume(player_, percent); HandleErrors(); } @@ -248,12 +256,24 @@ EngineBase::OutputDetailsList VLCEngine::GetOutputsList() const { } -bool VLCEngine::CustomDeviceSupport(const QString &name) { - return (name == "auto" ? false : true); +bool VLCEngine::ValidOutput(const QString &output) { + + PluginDetailsList plugins = GetPluginList(); + for (const PluginDetails &plugin : plugins) { + if (plugin.name == output) return(true); + } + return(false); + +} + +bool VLCEngine::CustomDeviceSupport(const QString &output) { + return (output == "auto" ? false : true); } uint VLCEngine::position() const { + if (!Initialised()) return (0); + bool is_playing = libvlc_media_player_is_playing(player_); HandleErrors(); @@ -268,6 +288,8 @@ uint VLCEngine::position() const { uint VLCEngine::length() const { + if (!Initialised()) return(0); + bool is_playing = libvlc_media_player_is_playing(player_); HandleErrors(); diff --git a/src/engine/vlcengine.h b/src/engine/vlcengine.h index 0ef0c86e..ae49cc51 100644 --- a/src/engine/vlcengine.h +++ b/src/engine/vlcengine.h @@ -63,8 +63,9 @@ class VLCEngine : public Engine::Base { const Engine::Scope& Scope(); OutputDetailsList GetOutputsList() const; + bool ValidOutput(const QString &output); QString DefaultOutput() { return ""; } - bool CustomDeviceSupport(const QString &name); + bool CustomDeviceSupport(const QString &value); private: libvlc_instance_t *instance_; @@ -75,6 +76,7 @@ class VLCEngine : public Engine::Base { static VLCEngine *sInstance; QMutex scope_mutex_; + bool Initialised() const; uint position() const; uint length() const; bool CanDecode(const QUrl &url); diff --git a/src/engine/xineengine.cpp b/src/engine/xineengine.cpp index c9f0d5ca..d23b4649 100644 --- a/src/engine/xineengine.cpp +++ b/src/engine/xineengine.cpp @@ -65,12 +65,8 @@ using std::shared_ptr; #define LLONG_MAX 9223372036854775807LL #endif - -//define this to use xine in a more standard way -//#ifdef Q_OS_WIN32 +// Define this to use xine in a more standard way //#define XINE_SAFE_MODE -//#endif - const char *XineEngine::kAutoOutput = "auto"; int XineEngine::last_error_ = XINE_MSG_NO_ERROR; @@ -88,56 +84,28 @@ XineEngine::XineEngine(TaskManager *task_manager) fadeout_running_ (false), prune_(nullptr) { + type_ = Engine::Xine; ReloadSettings(); } XineEngine::~XineEngine() { - // Wait until the fader thread is done - if (s_fader_) { - stop_fader_ = true; - s_fader_->resume(); // safety call if the engine is in the pause state - s_fader_->wait(); - } - - // Wait until the prune scope thread is done - if (prune_) { - prune_->exit(); - prune_->wait(); - } - - s_fader_.reset(); - s_outfader_.reset(); - prune_.reset(); - if (fadeout_enabled_) { bool terminateFader = false; FadeOut(fadeout_duration_, &terminateFader, true); // true == exiting } - if (stream_) xine_close(stream_); - if (eventqueue_) xine_event_dispose_queue(eventqueue_); - if (stream_) xine_dispose(stream_); - if (audioport_) xine_close_audio_driver(xine_, audioport_); - if (post_) xine_post_dispose(xine_, post_); - if (xine_) xine_exit(xine_); - - //qLog(Debug) << "xine closed"; - //qLog(Debug) << "Scope statistics:"; - //qLog(Debug) << "Average list size: " << log_buffer_count_ / log_scope_call_count_; - //qLog(Debug) << "Buffer failure: " << double(log_no_suitable_buffer_*100) / log_scope_call_count_ << "%"; + Cleanup(); } bool XineEngine::Init() { - type_ = Engine::Xine; - + Cleanup(); SetEnvironment(); QMutexLocker l(&init_mutex_); - xine_ = xine_new(); if (!xine_) { emit Error("Could not initialize xine."); @@ -147,17 +115,76 @@ bool XineEngine::Init() { #ifdef XINE_SAFE_MODE xine_engine_set_param(xine_, XINE_ENGINE_PARAM_VERBOSITY, 99); #endif - - xine_init(xine_); - MakeNewStream(); + xine_init(xine_); #ifndef XINE_SAFE_MODE prune_.reset(new PruneScopeThread(this)); prune_->start(); #endif + + SetDevice(); + + if (!ValidOutput(output_)) { + qLog(Error) << "Invalid output detected:" << output_ << " - Resetting to default."; + output_ = DefaultOutput(); + } + audioport_ = xine_open_audio_driver(xine_, (output_.isEmpty() || output_ == kAutoOutput ? nullptr : output_.toUtf8().constData()), nullptr); + if (!audioport_) { + emit Error("Xine was unable to initialize any audio drivers."); + return false; + } return true; + +} + +void XineEngine::Cleanup() { + + // Wait until the prune scope thread is done + if (prune_) { + prune_->exit(); + prune_->wait(); + } + prune_.reset(); + + // Wait until the fader thread is done + if (s_fader_) { + stop_fader_ = true; + s_fader_->resume(); // safety call if the engine is in the pause state + s_fader_->wait(); + } + + s_fader_.reset(); + s_outfader_.reset(); + + if (stream_) + xine_close(stream_); + if (eventqueue_) { + xine_event_dispose_queue(eventqueue_); + eventqueue_ = nullptr; + } + if (stream_) { + xine_dispose(stream_); + stream_ = nullptr; + } + if (audioport_) { + xine_close_audio_driver(xine_, audioport_); + audioport_ = nullptr; + } + if (post_) { + xine_post_dispose(xine_, post_); + post_ = nullptr; + } + + if (xine_) xine_exit(xine_); + xine_ = nullptr; + + //qLog(Debug) << "xine closed"; + //qLog(Debug) << "Scope statistics:"; + //qLog(Debug) << "Average list size: " << log_buffer_count_ / log_scope_call_count_; + //qLog(Debug) << "Buffer failure: " << double(log_no_suitable_buffer_*100) / log_scope_call_count_ << "%"; + } Engine::State XineEngine::state() const { @@ -183,7 +210,7 @@ bool XineEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool for if (s_outfader_) { s_outfader_->finish(); - if (s_outfader_) s_outfader_.reset(); + s_outfader_.reset(); } if (fade_length_ > 0 && xine_get_status(stream_) == XINE_STATUS_PLAY && url.scheme().toLower() == "file" && xine_get_param(stream_, XINE_PARAM_SPEED) != XINE_SPEED_PAUSE && (fade_next_track_ || crossfade_enabled_)) { @@ -370,14 +397,22 @@ EngineBase::OutputDetailsList XineEngine::GetOutputsList() const { return ret; } -bool XineEngine::CustomDeviceSupport(const QString &name) { - return (name == "alsa" || name == "oss" || name == "jack" || name == "pulseaudio"); +bool XineEngine::ValidOutput(const QString &output) { + + PluginDetailsList plugins = GetPluginList(); + for (const PluginDetails &plugin : plugins) { + if (plugin.name == output) return(true); + } + return(false); + +} + +bool XineEngine::CustomDeviceSupport(const QString &output) { + return (output == "alsa" || output == "oss" || output == "jack" || output == "pulseaudio"); } void XineEngine::ReloadSettings() { - QSettings s; - Engine::Base::ReloadSettings(); if (output_ == "") output_ = DefaultOutput(); @@ -386,13 +421,13 @@ void XineEngine::ReloadSettings() { void XineEngine::SetEnvironment() { -#ifdef Q_OS_WIN32 - putenv(QString("XINE_PLUGIN_PATH=" + QCoreApplication::applicationDirPath() + "/xine/plugins").toLatin1().constData()); -#endif // Q_OS_WIN32 +#ifdef Q_OS_WIN + putenv(QString("XINE_PLUGIN_PATH=" + QCoreApplication::applicationDirPath() + "/xine-plugins").toLatin1().constData()); +#endif #ifdef Q_OS_DARWIN setenv("XINE_PLUGIN_PATH", QString(QCoreApplication::applicationDirPath() + "/../PlugIns/xine").toLatin1().constData(), 1); -#endif // Q_OS_DARWIN +#endif } @@ -868,7 +903,7 @@ Engine::SimpleMetaBundle XineEngine::fetchMetaData() const { } -bool XineEngine::MakeNewStream() { +void XineEngine::SetDevice() { if (device_.isValid()) { bool valid(false); @@ -892,12 +927,11 @@ bool XineEngine::MakeNewStream() { xine_config_update_entry(xine_, &entry); } } + current_device_ = device_; - audioport_ = xine_open_audio_driver(xine_, (output_.isEmpty() || output_ == kAutoOutput ? nullptr : output_.toUtf8().constData()), nullptr); - if (!audioport_) { - emit Error("Xine was unable to initialize any audio drivers."); - return false; - } +} + +bool XineEngine::CreateStream() { stream_ = xine_stream_new(xine_, audioport_, nullptr); if (!stream_) { @@ -908,7 +942,6 @@ bool XineEngine::MakeNewStream() { } if (eventqueue_) xine_event_dispose_queue(eventqueue_); - eventqueue_ = xine_event_new_queue(stream_); xine_event_create_listener_thread(eventqueue_, &XineEngine::XineEventListener, (void*)this); @@ -923,7 +956,7 @@ bool XineEngine::MakeNewStream() { if (xine_check_version(1, 1, 1) && !(fade_length_ > 0)) { // Enable gapless playback qLog(Debug) << "gapless playback enabled."; - // xine_set_param(stream_, XINE_PARAM_EARLY_FINISHED_EVENT, 1); + xine_set_param(stream_, XINE_PARAM_EARLY_FINISHED_EVENT, 1); } #endif return true; @@ -931,7 +964,7 @@ bool XineEngine::MakeNewStream() { bool XineEngine::EnsureStream() { - if (!stream_) return MakeNewStream(); + if (!stream_) return CreateStream(); return true; } diff --git a/src/engine/xineengine.h b/src/engine/xineengine.h index e9f353dc..032854a3 100644 --- a/src/engine/xineengine.h +++ b/src/engine/xineengine.h @@ -65,9 +65,8 @@ private: class XineEngine : public Engine::Base { Q_OBJECT - -public: + public: XineEngine(TaskManager *task_manager); ~XineEngine(); @@ -86,38 +85,21 @@ public: const Engine::Scope& scope(int chunk_length); - QString DefaultOutput() { return "auto"; } OutputDetailsList GetOutputsList() const; - bool CustomDeviceSupport(const QString &name); + bool ValidOutput(const QString &output); + QString DefaultOutput() { return "auto"; } + bool CustomDeviceSupport(const QString &output); void ReloadSettings(); - void SetEnvironment(); - - uint length() const; - uint position() const; - bool CanDecode(const QUrl &); - - bool MetaDataForUrl(const QUrl &url, Engine::SimpleMetaBundle &b); - bool GetAudioCDContents(const QString &device, QList &urls); - bool FlushBuffer(); + bool CreateStream(); void SetEqualizerEnabled(bool enabled); void SetEqualizerParameters(int preamp, const QList&); - + void FadeOut(uint fadeLength, bool* terminate, bool exiting = false); - static void XineEventListener(void*, const xine_event_t*); - bool event(QEvent*); - - Engine::SimpleMetaBundle fetchMetaData() const; - - bool MakeNewStream(); - bool EnsureStream(); - - void DetermineAndShowErrorMessage(); //call after failure to load/play - // Simple accessors xine_stream_t *stream() { return stream_; } @@ -125,10 +107,12 @@ public: bool stop_fader() { return stop_fader_; } void set_stop_fader(bool stop_fader) { stop_fader_ = stop_fader; } -private: - + private: static const char *kAutoOutput; + QString current_output_; + QVariant current_device_; + xine_t *xine_; xine_stream_t *stream_; xine_audio_port_t *audioport_; @@ -160,6 +144,26 @@ private: mutable Engine::SimpleMetaBundle current_bundle_; + void SetEnvironment(); + + void Cleanup(); + bool EnsureStream(); + void SetDevice(); + + uint length() const; + uint position() const; + + bool MetaDataForUrl(const QUrl &url, Engine::SimpleMetaBundle &b); + bool GetAudioCDContents(const QString &device, QList &urls); + bool FlushBuffer(); + + static void XineEventListener(void*, const xine_event_t*); + bool event(QEvent*); + + Engine::SimpleMetaBundle fetchMetaData() const; + + void DetermineAndShowErrorMessage(); //call after failure to load/play + PluginDetailsList GetPluginList() const; private slots: diff --git a/src/engine/xinefader.cpp b/src/engine/xinefader.cpp index f8f40d64..8a7691a8 100644 --- a/src/engine/xinefader.cpp +++ b/src/engine/xinefader.cpp @@ -38,7 +38,7 @@ XineFader::XineFader(XineEngine *engine, xine_t *xine, xine_stream_t *stream, xi paused_(false), terminated_(false) { - if (engine->MakeNewStream()) { + if (engine->CreateStream()) { increase_ = stream_; xine_set_param(increase_, XINE_PARAM_AUDIO_AMP_LEVEL, 0); } diff --git a/src/musicbrainz/chromaprinter.cpp b/src/musicbrainz/chromaprinter.cpp index 2daaf8bf..49ea2649 100644 --- a/src/musicbrainz/chromaprinter.cpp +++ b/src/musicbrainz/chromaprinter.cpp @@ -43,6 +43,10 @@ #include "core/logging.h" #include "core/signalchecker.h" +#ifndef u_int32_t +typedef unsigned int u_int32_t; +#endif + static const int kDecodeRate = 11025; static const int kDecodeChannels = 1; static const int kPlayLengthSecs = 30; diff --git a/src/settings/backendsettingspage.cpp b/src/settings/backendsettingspage.cpp index ac4404a2..61b78798 100644 --- a/src/settings/backendsettingspage.cpp +++ b/src/settings/backendsettingspage.cpp @@ -79,7 +79,8 @@ void BackendSettingsPage::Load() { engineloaded_ = false; xinewarning_ = false; - Engine::EngineType enginetype = Engine::EngineTypeFromName(s_.value("engine", EngineDescription(Engine::GStreamer)).toString()); + Engine::EngineType enginetype = Engine::EngineTypeFromName(s_.value("engine", EngineName(Engine::None)).toString()); + if (enginetype == Engine::None && engine()) enginetype = engine()->type(); ui_->combobox_engine->clear(); #ifdef HAVE_GSTREAMER @@ -95,7 +96,9 @@ void BackendSettingsPage::Load() { ui_->combobox_engine->addItem(IconLoader::Load("speaker"), EngineDescription(Engine::Phonon), Engine::Phonon); #endif - enginereset_ = false; + enginetype_current_ = enginetype; + output_current_ = s_.value("output", "").toString(); + device_current_ = s_.value("device", QVariant()); ui_->combobox_engine->setCurrentIndex(ui_->combobox_engine->findData(enginetype)); if (EngineInitialised()) Load_Engine(enginetype); @@ -152,8 +155,8 @@ void BackendSettingsPage::Load_Engine(Engine::EngineType enginetype) { if (!EngineInitialised()) return; - QString output = s_.value("output", "").toString(); - QVariant device = s_.value("device", QVariant()); + QString output = output_current_; + QVariant device = device_current_; ui_->combobox_output->clear(); ui_->combobox_device->clear(); @@ -163,13 +166,15 @@ void BackendSettingsPage::Load_Engine(Engine::EngineType enginetype) { ui_->lineedit_device->setEnabled(false); ui_->lineedit_device->setText(""); - + ui_->groupbox_replaygain->setEnabled(false); if (engine()->type() != enginetype) { + qLog(Debug) << "Switching engine."; dialog()->app()->player()->CreateEngine(enginetype); dialog()->app()->player()->ReloadSettings(); dialog()->app()->player()->Init(); + dialog()->set_output_changed(false); } engineloaded_ = true; @@ -203,6 +208,7 @@ void BackendSettingsPage::Load_Output(QString output, QVariant device) { } if (!found) { // Output is invalid for this engine, reset to default output. output = engine()->DefaultOutput(); + device = (engine()->CustomDeviceSupport(output) == true ? QVariant("") : QVariant()); for (int i = 0; i < ui_->combobox_output->count(); ++i) { EngineBase::OutputDetails o = ui_->combobox_output->itemData(i).value(); if (o.name == output) { @@ -212,8 +218,16 @@ void BackendSettingsPage::Load_Output(QString output, QVariant device) { } } - if (engine()->type() == Engine::GStreamer) ui_->groupbox_replaygain->setEnabled(true); - else ui_->groupbox_replaygain->setEnabled(false); + if (engine()->type() == Engine::GStreamer) { + ui_->groupbox_buffer->setEnabled(true); + ui_->groupbox_replaygain->setEnabled(true); + ui_->checkbox_monoplayback->setEnabled(true); + } + else { + ui_->groupbox_buffer->setEnabled(false); + ui_->groupbox_replaygain->setEnabled(false); + ui_->checkbox_monoplayback->setEnabled(false); + } if (ui_->combobox_output->count() < 1) { ShowWarning("Engine may take some time to initialize. Close settings and reopen to set output and devices."); @@ -315,23 +329,35 @@ void BackendSettingsPage::Save() { s_.setValue("rgpreamp", float(ui_->stickslider_replaygainpreamp->value()) / 10 - 15); s_.setValue("rgcompression", ui_->checkbox_replaygaincompression->isChecked()); + // If engine has not been changed, but output or device has been changed, + // then set_output_changed(true) to reinitialize engine when dialog closes. + if (enginetype == enginetype_current_ && (output_name != output_current_ || device_value != device_current_)) dialog()->set_output_changed(true); + +} + +void BackendSettingsPage::Cancel() { + if (engine() && engine()->type() != enginetype_current_) { // Reset engine back to the original because user cancelled. + dialog()->app()->player()->CreateEngine(enginetype_current_); + dialog()->app()->player()->ReloadSettings(); + dialog()->app()->player()->Init(); + } } void BackendSettingsPage::EngineChanged(int index) { if (!configloaded_ || !EngineInitialised()) return; - - if (engine()->state() != Engine::Empty) { - if (enginereset_ == true) { enginereset_ = false; return; } - errordialog_.ShowMessage("Can't switch engine while playing!"); - enginereset_ = true; - ui_->combobox_engine->setCurrentIndex(ui_->combobox_engine->findData(engineloaded_)); - return; - } - + QVariant v = ui_->combobox_engine->itemData(index); Engine::EngineType enginetype = v.value(); + if (engine()->type() == enginetype) return; + + if (engine()->state() != Engine::Empty) { + errordialog_.ShowMessage("Can't switch engine while playing!"); + ui_->combobox_engine->setCurrentIndex(ui_->combobox_engine->findData(engine()->type())); + return; + } + engineloaded_ = false; xinewarning_ = false; ResetWarning(); @@ -346,7 +372,7 @@ void BackendSettingsPage::OutputChanged(int index) { EngineBase::OutputDetails output = ui_->combobox_output->itemData(index).value(); Load_Device(output.name, QVariant()); - if (engine()->type() == Engine::Xine) XineWarning(); + if (engine()->type() == Engine::Xine && engine()->state() != Engine::Empty) XineWarning(); } @@ -371,7 +397,7 @@ void BackendSettingsPage::DeviceSelectionChanged(int index) { if (!ui_->lineedit_device->text().isEmpty()) ui_->lineedit_device->setText(""); } - if (engine()->type() == Engine::Xine) XineWarning(); + if (engine()->type() == Engine::Xine && engine()->state() != Engine::Empty) XineWarning(); } @@ -467,8 +493,8 @@ void BackendSettingsPage::XineWarning() { if (!engineloaded_) return; if (!configloaded_) return; - if (engine()->type() != Engine::Xine) return; + if (engine()->state() == Engine::Empty) return; if (xinewarning_) return; ShowWarning("You need to restart Strawberry for output/device changes to take affect for Xine."); diff --git a/src/settings/backendsettingspage.h b/src/settings/backendsettingspage.h index 643a9b01..957b3143 100644 --- a/src/settings/backendsettingspage.h +++ b/src/settings/backendsettingspage.h @@ -52,6 +52,7 @@ public: void Load(); void Save(); + void Cancel(); EngineBase *engine() const { return dialog()->app()->player()->engine(); } @@ -65,10 +66,10 @@ public: private: Ui_BackendSettingsPage *ui_; - + void ConnectSignals(); bool EngineInitialised(); - + void EngineChanged(Engine::EngineType enginetype); void Load_Engine(Engine::EngineType enginetype); @@ -77,14 +78,17 @@ private: void ShowWarning(QString text); void ResetWarning(); void XineWarning(); - + QSettings s_; bool configloaded_; bool engineloaded_; - ErrorDialog errordialog_; - bool enginereset_; bool xinewarning_; - + ErrorDialog errordialog_; + + Engine::EngineType enginetype_current_; + QString output_current_; + QVariant device_current_; + }; #endif // BACKENDSETTINGSPAGE_H diff --git a/src/settings/playbacksettingspage.cpp b/src/settings/playbacksettingspage.cpp index 1bddf06f..c9f6c30c 100644 --- a/src/settings/playbacksettingspage.cpp +++ b/src/settings/playbacksettingspage.cpp @@ -30,6 +30,7 @@ #include #include "core/iconloader.h" +#include "core/logging.h" #include "settingspage.h" #include "playbacksettingspage.h" #include "ui_playbacksettingspage.h" @@ -50,9 +51,7 @@ PlaybackSettingsPage::PlaybackSettingsPage(SettingsDialog *dialog) : SettingsPag } PlaybackSettingsPage::~PlaybackSettingsPage() { - delete ui_; - } void PlaybackSettingsPage::Load() { diff --git a/src/settings/settingsdialog.cpp b/src/settings/settingsdialog.cpp index 4f522e40..d562fff6 100644 --- a/src/settings/settingsdialog.cpp +++ b/src/settings/settingsdialog.cpp @@ -45,6 +45,7 @@ #include "core/application.h" #include "core/player.h" +#include "core/logging.h" #include "engine/enginebase.h" #include "widgets/groupediconview.h" #include "collection/collectionmodel.h" @@ -104,7 +105,8 @@ SettingsDialog::SettingsDialog(Application *app, QWidget *parent) model_(app_->collection_model()->directory_model()), appearance_(app_->appearance()), ui_(new Ui_SettingsDialog), - loading_settings_(false) { + loading_settings_(false), + output_changed_(false) { ui_->setupUi(this); ui_->list->setItemDelegate(new SettingsItemDelegate(this)); @@ -203,6 +205,11 @@ void SettingsDialog::Save() { void SettingsDialog::accept() { Save(); + // Only Xine needs to reinitialize to switch output and device. + if (output_changed_ && engine() && engine()->type() == Engine::Xine && engine()->state() == Engine::Empty) { + engine()->ReloadSettings(); + engine()->Init(); + } QDialog::accept(); } diff --git a/src/settings/settingsdialog.h b/src/settings/settingsdialog.h index e863b130..4577b81e 100644 --- a/src/settings/settingsdialog.h +++ b/src/settings/settingsdialog.h @@ -37,6 +37,7 @@ #include #include +#include "core/logging.h" #include "widgets/osd.h" class QModelIndex; @@ -104,6 +105,8 @@ public: // QWidget void showEvent(QShowEvent *e); + void set_output_changed(bool output_changed) { output_changed_ = output_changed; } + signals: void NotificationPreview(OSD::Behaviour, QString, QString); @@ -135,6 +138,8 @@ private: bool loading_settings_; QMap pages_; + + bool output_changed_; }; #endif // SETTINGSDIALOG_H