More engine fixes

This commit is contained in:
Jonas Kvinge 2018-07-01 01:29:52 +02:00
parent 67df8f2243
commit 04f1d296ea
22 changed files with 330 additions and 217 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@
#include "core/logging.h"
DirectSoundDeviceFinder::DirectSoundDeviceFinder()
: DeviceFinder("directsound", { "directsound", "dsound", "directsoundsink" }) {
: DeviceFinder("directsound", { "directsound", "dsound", "directsoundsink", "directx", "directx2" }) {
}
QList<DeviceFinder::Device> DirectSoundDeviceFinder::ListDevices() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<QUrl> &urls);
bool FlushBuffer();
bool CreateStream();
void SetEqualizerEnabled(bool enabled);
void SetEqualizerParameters(int preamp, const QList<int>&);
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<QUrl> &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:

View File

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

View File

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

View File

@ -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<EngineBase::OutputDetails>();
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<Engine::EngineType>();
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<EngineBase::OutputDetails>();
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.");

View File

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

View File

@ -30,6 +30,7 @@
#include <QSpinBox>
#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() {

View File

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

View File

@ -37,6 +37,7 @@
#include <QScrollArea>
#include <QTreeWidgetItem>
#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<Page, PageData> pages_;
bool output_changed_;
};
#endif // SETTINGSDIALOG_H