From 7b977ea839f19b07e0d2f2dfc11f93eaef22172d Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sun, 3 Nov 2019 19:53:08 +0100 Subject: [PATCH] Rename EngineDevice --> DeviceFinders, Add MMDeviceFinder --- src/CMakeLists.txt | 5 +- src/context/contextview.cpp | 4 +- src/core/application.cpp | 10 +- src/core/application.h | 4 +- .../{enginedevice.cpp => devicefinders.cpp} | 11 +- .../{enginedevice.h => devicefinders.h} | 18 ++- src/engine/directsounddevicefinder.cpp | 4 +- src/engine/enginebase.h | 2 +- src/engine/gstenginepipeline.cpp | 16 ++- src/engine/mmdevicefinder.cpp | 115 ++++++++++++++++++ src/engine/mmdevicefinder.h | 34 ++++++ src/settings/backendsettingspage.cpp | 4 +- 12 files changed, 192 insertions(+), 35 deletions(-) rename src/engine/{enginedevice.cpp => devicefinders.cpp} (88%) rename src/engine/{enginedevice.h => devicefinders.h} (78%) create mode 100644 src/engine/mmdevicefinder.cpp create mode 100644 src/engine/mmdevicefinder.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fbe3d9eab..12d713ef5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -121,7 +121,7 @@ set(SOURCES engine/enginetype.cpp engine/enginebase.cpp - engine/enginedevice.cpp + engine/devicefinders.cpp engine/devicefinder.cpp analyzer/fht.cpp @@ -315,7 +315,7 @@ set(HEADERS core/mimedata.h engine/enginebase.h - engine/enginedevice.h + engine/devicefinders.h analyzer/analyzerbase.h analyzer/analyzercontainer.h @@ -877,6 +877,7 @@ endif() optional_source(WIN32 SOURCES engine/directsounddevicefinder.cpp + engine/mmdevicefinder.cpp widgets/osd_win.cpp core/windows7thumbbar.cpp HEADERS diff --git a/src/context/contextview.cpp b/src/context/contextview.cpp index d749b5510..443731025 100644 --- a/src/context/contextview.cpp +++ b/src/context/contextview.cpp @@ -51,7 +51,7 @@ #include "engine/engine_fwd.h" #include "engine/enginebase.h" #include "engine/enginetype.h" -#include "engine/enginedevice.h" +#include "engine/devicefinders.h" #include "engine/devicefinder.h" #include "collection/collection.h" #include "collection/collectionbackend.h" @@ -341,7 +341,7 @@ void ContextView::SetSong(const Song &song) { ui_->spacer_play_output->changeSize(20, 20, QSizePolicy::Fixed); DeviceFinder::Device device; - for (DeviceFinder *f : app_->enginedevice()->device_finders_) { + for (DeviceFinder *f : app_->device_finders()->ListFinders()) { for (const DeviceFinder::Device &d : f->ListDevices()) { if (d.value != app_->player()->engine()->device()) continue; device = d; diff --git a/src/core/application.cpp b/src/core/application.cpp index 13fde7e3c..e83787b26 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -41,7 +41,7 @@ #include "player.h" #include "appearance.h" -#include "engine/enginedevice.h" +#include "engine/devicefinders.h" #ifndef Q_OS_WIN # include "device/devicemanager.h" #endif @@ -105,7 +105,7 @@ class ApplicationImpl { appearance_([=]() { return new Appearance(app); }), task_manager_([=]() { return new TaskManager(app); }), player_([=]() { return new Player(app, app); }), - enginedevice_([=]() { return new EngineDevice(app); }), + device_finders_([=]() { return new DeviceFinders(app); }), #ifndef Q_OS_WIN device_manager_([=]() { return new DeviceManager(app, app); }), #endif @@ -175,7 +175,7 @@ class ApplicationImpl { Lazy appearance_; Lazy task_manager_; Lazy player_; - Lazy enginedevice_; + Lazy device_finders_; #ifndef Q_OS_WIN Lazy device_manager_; #endif @@ -205,7 +205,7 @@ class ApplicationImpl { Application::Application(QObject *parent) : QObject(parent), p_(new ApplicationImpl(this)) { - enginedevice()->Init(); + device_finders()->Init(); collection()->Init(); tag_reader_client(); @@ -307,7 +307,7 @@ Appearance *Application::appearance() const { return p_->appearance_.get(); } Database *Application::database() const { return p_->database_.get(); } TaskManager *Application::task_manager() const { return p_->task_manager_.get(); } Player *Application::player() const { return p_->player_.get(); } -EngineDevice *Application::enginedevice() const { return p_->enginedevice_.get(); } +DeviceFinders *Application::device_finders() const { return p_->device_finders_.get(); } #ifndef Q_OS_WIN DeviceManager *Application::device_manager() const { return p_->device_manager_.get(); } #endif diff --git a/src/core/application.h b/src/core/application.h index dc9710928..418d3e4cb 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -41,7 +41,7 @@ class TaskManager; class ApplicationImpl; class TagReaderClient; class Database; -class EngineDevice; +class DeviceFinders; class Player; class Appearance; class SCollection; @@ -78,7 +78,7 @@ class Application : public QObject { Appearance *appearance() const; TaskManager *task_manager() const; Player *player() const; - EngineDevice *enginedevice() const; + DeviceFinders *device_finders() const; #ifndef Q_OS_WIN DeviceManager *device_manager() const; #endif diff --git a/src/engine/enginedevice.cpp b/src/engine/devicefinders.cpp similarity index 88% rename from src/engine/enginedevice.cpp rename to src/engine/devicefinders.cpp index 184f07dd9..af87d3359 100644 --- a/src/engine/enginedevice.cpp +++ b/src/engine/devicefinders.cpp @@ -26,8 +26,8 @@ #include #include "core/logging.h" +#include "devicefinders.h" #include "devicefinder.h" -#include "enginedevice.h" #ifdef HAVE_ALSA # include "alsadevicefinder.h" @@ -43,16 +43,16 @@ #ifdef Q_OS_WIN32 # include "directsounddevicefinder.h" +# include "mmdevicefinder.h" #endif -EngineDevice::EngineDevice(QObject *parent) : QObject(parent) { -} +DeviceFinders::DeviceFinders(QObject *parent) : QObject(parent) {} -EngineDevice::~EngineDevice() { +DeviceFinders::~DeviceFinders() { qDeleteAll(device_finders_); } -void EngineDevice::Init() { +void DeviceFinders::Init() { QList device_finders; @@ -67,6 +67,7 @@ void EngineDevice::Init() { #endif #ifdef Q_OS_WIN32 device_finders.append(new DirectSoundDeviceFinder); + device_finders.append(new MMDeviceFinder); #endif for (DeviceFinder *finder : device_finders) { diff --git a/src/engine/enginedevice.h b/src/engine/devicefinders.h similarity index 78% rename from src/engine/enginedevice.h rename to src/engine/devicefinders.h index 795b265db..a938cdfd8 100644 --- a/src/engine/enginedevice.h +++ b/src/engine/devicefinders.h @@ -17,8 +17,8 @@ * */ -#ifndef ENGINEDEVICE_H -#define ENGINEDEVICE_H +#ifndef DEVICEFINDERS_H +#define DEVICEFINDERS_H #include "config.h" @@ -28,21 +28,19 @@ class DeviceFinder; -class EngineDevice : public QObject { +class DeviceFinders : public QObject { Q_OBJECT public: - explicit EngineDevice(QObject *parent = nullptr); - ~EngineDevice(); + explicit DeviceFinders(QObject *parent = nullptr); + ~DeviceFinders(); void Init(); - - QList device_finders_; + QList ListFinders() { return device_finders_; } private: - QString output_; + QList device_finders_; }; -#endif // ENGINEDEVICE_H - +#endif // DEVICEFINDERS_H diff --git a/src/engine/directsounddevicefinder.cpp b/src/engine/directsounddevicefinder.cpp index 294a1c3dc..17afb836b 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", "directx", "directx2", "wasapisink" }) { + : DeviceFinder("directsound", { "directsound", "dsound", "directsoundsink", "directx", "directx2" }) { } QList DirectSoundDeviceFinder::ListDevices() { @@ -52,9 +52,7 @@ BOOL DirectSoundDeviceFinder::EnumerateCallback(LPGUID guid, LPCSTR description, Device dev; dev.description = QString::fromLatin1(description); - //if (guid) dev.value = QUuid(*guid).toByteArray(); if (guid) dev.value = QUuid(*guid).toString(); - else dev.value = QVariant(); dev.iconname = GuessIconName(dev.description); state->devices.append(dev); diff --git a/src/engine/enginebase.h b/src/engine/enginebase.h index 0bb323798..370f52199 100644 --- a/src/engine/enginebase.h +++ b/src/engine/enginebase.h @@ -41,7 +41,7 @@ #include "engine_fwd.h" #include "enginetype.h" -#include "enginedevice.h" +#include "devicefinders.h" #include "core/song.h" namespace Engine { diff --git a/src/engine/gstenginepipeline.cpp b/src/engine/gstenginepipeline.cpp index 3c57021f8..ee52ba362 100644 --- a/src/engine/gstenginepipeline.cpp +++ b/src/engine/gstenginepipeline.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include "core/concurrentrun.h" #include "core/logging.h" @@ -197,11 +198,11 @@ bool GstEnginePipeline::InitAudioBin() { if (device_.isValid() && g_object_class_find_property(G_OBJECT_GET_CLASS(audiosink), "device")) { switch (device_.type()) { case QVariant::String: - if (device_.toString().isEmpty()) break; - g_object_set(G_OBJECT(audiosink), "device", device_.toString().toUtf8().constData(), nullptr); + if (device_.toString().isEmpty()) break; + g_object_set(G_OBJECT(audiosink), "device", device_.toString().toUtf8().constData(), nullptr); break; case QVariant::ByteArray: - g_object_set(G_OBJECT(audiosink), "device", device_.toByteArray().constData(), nullptr); + g_object_set(G_OBJECT(audiosink), "device", device_.toByteArray().constData(), nullptr); break; case QVariant::LongLong: g_object_set(G_OBJECT(audiosink), "device", device_.toLongLong(), nullptr); @@ -209,12 +210,20 @@ bool GstEnginePipeline::InitAudioBin() { case QVariant::Int: g_object_set(G_OBJECT(audiosink), "device", device_.toInt(), nullptr); break; + case QVariant::Uuid: + g_object_set(G_OBJECT(audiosink), "device", device_.toUuid(), nullptr); + break; default: qLog(Warning) << "Unknown device type" << device_; break; } } + if (output_ == "wasapisink") { + g_object_set(G_OBJECT(audiosink), "exclusive", true, nullptr); + g_object_set(G_OBJECT(audiosink), "low-latency", true, nullptr); + } + // Create all the other elements audioqueue_ = engine_->CreateElement("queue2", audiobin_); @@ -901,6 +910,7 @@ void GstEnginePipeline::SourceSetupCallback(GstPlayBin *bin, GParamSpec *, gpoin if (g_object_class_find_property(G_OBJECT_GET_CLASS(element), "device") && !instance->source_device().isEmpty()) { // Gstreamer is not able to handle device in URL (referring to Gstreamer documentation, this might be added in the future). // Despite that, for now we include device inside URL: we decompose it during Init and set device here, when this callback is called. + qLog(Info) << instance->source_device().toLocal8Bit().constData(); g_object_set(element, "device", instance->source_device().toLocal8Bit().constData(), nullptr); } diff --git a/src/engine/mmdevicefinder.cpp b/src/engine/mmdevicefinder.cpp new file mode 100644 index 000000000..6f4f6652e --- /dev/null +++ b/src/engine/mmdevicefinder.cpp @@ -0,0 +1,115 @@ +/* + * Strawberry Music Player + * Copyright 2019, Jonas Kvinge + * + * Strawberry is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Strawberry is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Strawberry. If not, see . + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "mmdevicefinder.h" +#include "core/logging.h" + +MMDeviceFinder::MMDeviceFinder() : DeviceFinder("mmdevice", { "wasapisink" }) {} + +QList MMDeviceFinder::ListDevices() { + + HRESULT hr = S_OK; + + IMMDeviceEnumerator *enumerator = nullptr; + hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&enumerator); + if (FAILED(hr)) { + return QList(); + } + + IMMDeviceCollection *collection = nullptr; + hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection); + if (FAILED(hr)) { + enumerator->Release(); + return QList(); + } + + UINT count; + hr = collection->GetCount(&count); + if (FAILED(hr)) { + collection->Release(); + enumerator->Release(); + return QList(); + } + + QList devices; + Device default_device; + default_device.description = "Default device"; + default_device.iconname = GuessIconName(default_device.description); + devices.append(default_device); + + for (ULONG i = 0 ; i < count ; i++) { + + IMMDevice *endpoint = nullptr; + hr = collection->Item(i, &endpoint); + if (FAILED(hr)) { return devices; } + + LPWSTR pwszid = nullptr; + hr = endpoint->GetId(&pwszid); + if (FAILED(hr)) { + endpoint->Release(); + continue; + } + + IPropertyStore *props = nullptr; + hr = endpoint->OpenPropertyStore(STGM_READ, &props); + if (FAILED(hr)) { + CoTaskMemFree(pwszid); + endpoint->Release(); + continue; + } + + PROPVARIANT var_name; + PropVariantInit(&var_name); + hr = props->GetValue(PKEY_Device_FriendlyName, &var_name); + if (FAILED(hr)) { + props->Release(); + CoTaskMemFree(pwszid); + endpoint->Release(); + continue; + } + + Device device; + device.description = QString::fromWCharArray(var_name.pwszVal); + device.iconname = GuessIconName(device.description); + device.value = QString::fromStdWString(pwszid); + devices.append(device); + + PropVariantClear(&var_name); + props->Release(); + CoTaskMemFree(pwszid); + endpoint->Release(); + + } + collection->Release(); + enumerator->Release(); + + return devices; + +} diff --git a/src/engine/mmdevicefinder.h b/src/engine/mmdevicefinder.h new file mode 100644 index 000000000..39ea682b8 --- /dev/null +++ b/src/engine/mmdevicefinder.h @@ -0,0 +1,34 @@ +/* + * Strawberry Music Player + * Copyright 2019, Jonas Kvinge + * + * Strawberry is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Strawberry is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Strawberry. If not, see . + * + */ + +#ifndef MMDEVICEFINDER_H +#define MMDEVICEFINDER_H + +#include "config.h" + +#include "devicefinder.h" + +class MMDeviceFinder : public DeviceFinder { + public: + MMDeviceFinder(); + virtual bool Initialise() { return true; } + virtual QList ListDevices(); +}; + +#endif // MMDEVICEFINDER_H diff --git a/src/settings/backendsettingspage.cpp b/src/settings/backendsettingspage.cpp index 164ff090b..16e24e9a2 100644 --- a/src/settings/backendsettingspage.cpp +++ b/src/settings/backendsettingspage.cpp @@ -41,7 +41,7 @@ #include "core/logging.h" #include "engine/engine_fwd.h" #include "engine/enginebase.h" -#include "engine/enginedevice.h" +#include "engine/devicefinders.h" #include "engine/enginetype.h" #include "engine/devicefinder.h" #include "widgets/lineedit.h" @@ -291,7 +291,7 @@ void BackendSettingsPage::Load_Device(QString output, QVariant device) { #endif ui_->combobox_device->addItem(IconLoader::Load("soundcard"), "Automatically select", QVariant()); - for (DeviceFinder *f : dialog()->app()->enginedevice()->device_finders_) { + for (DeviceFinder *f : dialog()->app()->device_finders()->ListFinders()) { if (!f->outputs().contains(output)) continue; for (const DeviceFinder::Device &d : f->ListDevices()) { devices++;