refactor: audiobackend code, ui.hpp, iconfetcher, structure
- Changed Structure (Moved Settings, Data and Enums into own header files) - Changed how AudioBackend is created to fix issues (fixes #208) - Made AudioBackend switch to pipewire when pipewire-pulse was detected as server in pulseaudio - Refactored Code for IconFetcher and moved getPpid from misc into IconFetcher - Removed unused code in proccessingqueue, renamed processingqueue to queue.
This commit is contained in:
parent
bcfccf14ad
commit
48132d86b9
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,4 +7,5 @@ cmake-build-debug
|
||||
analyze
|
||||
node_modules
|
||||
yarn.lock
|
||||
package.json
|
||||
package.json
|
||||
compile_commands.json
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <core/global/objects.hpp>
|
||||
#include <core/objects/data.hpp>
|
||||
#include <core/objects/settings.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace Soundux
|
||||
|
61
src/core/enums/enums.hpp
Normal file
61
src/core/enums/enums.hpp
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace Enums
|
||||
{
|
||||
enum class ErrorCode : std::uint8_t
|
||||
{
|
||||
FailedToPlay,
|
||||
FailedToSeek,
|
||||
FailedToPause,
|
||||
FailedToRepeat,
|
||||
FailedToResume,
|
||||
FailedToMoveToSink,
|
||||
SoundNotFound,
|
||||
FolderDoesNotExist,
|
||||
TabDoesNotExist,
|
||||
FailedToSetHotkey,
|
||||
FailedToStartPassthrough,
|
||||
FailedToMoveBack,
|
||||
FailedToMoveBackPassthrough,
|
||||
FailedToRevertDefaultSource,
|
||||
FailedToSetDefaultSource,
|
||||
YtdlNotFound,
|
||||
YtdlInvalidUrl,
|
||||
YtdlInvalidJson,
|
||||
YtdlInformationUnknown,
|
||||
FailedToDelete
|
||||
};
|
||||
|
||||
enum class SortMode : std::uint8_t
|
||||
{
|
||||
ModifiedDate_Ascending,
|
||||
ModifiedDate_Descending,
|
||||
Alphabetical_Ascending,
|
||||
Alphabetical_Descending,
|
||||
};
|
||||
|
||||
enum class Theme : std::uint8_t
|
||||
{
|
||||
System,
|
||||
Dark,
|
||||
Light
|
||||
};
|
||||
|
||||
enum class ViewMode : std::uint8_t
|
||||
{
|
||||
List,
|
||||
Grid,
|
||||
EmulatedLaunchpad,
|
||||
};
|
||||
|
||||
enum class BackendType : std::uint8_t
|
||||
{
|
||||
None,
|
||||
PipeWire,
|
||||
PulseAudio,
|
||||
};
|
||||
} // namespace Enums
|
||||
} // namespace Soundux
|
@ -3,11 +3,13 @@
|
||||
#if defined(__linux__)
|
||||
#include <helper/audio/linux/backend.hpp>
|
||||
#endif
|
||||
#include "objects.hpp"
|
||||
#include <core/config/config.hpp>
|
||||
#include <core/hotkeys/hotkeys.hpp>
|
||||
#include <core/objects/data.hpp>
|
||||
#include <core/objects/objects.hpp>
|
||||
#include <core/objects/settings.hpp>
|
||||
#include <helper/icons/icons.hpp>
|
||||
#include <helper/threads/processing.hpp>
|
||||
#include <helper/queue/queue.hpp>
|
||||
#include <helper/ytdl/youtube-dl.hpp>
|
||||
#include <memory>
|
||||
#include <ui/ui.hpp>
|
||||
@ -20,15 +22,15 @@ namespace Soundux
|
||||
inline Objects::Data gData;
|
||||
inline Objects::Audio gAudio;
|
||||
#if defined(__linux__)
|
||||
inline Objects::IconFetcher gIcons;
|
||||
inline std::optional<Objects::IconFetcher> gIcons;
|
||||
inline std::shared_ptr<Objects::AudioBackend> gAudioBackend;
|
||||
#endif
|
||||
inline Objects::Queue gQueue;
|
||||
inline Objects::Config gConfig;
|
||||
inline Objects::YoutubeDl gYtdl;
|
||||
inline Objects::Hotkeys gHotKeys;
|
||||
inline Objects::Settings gSettings;
|
||||
inline std::unique_ptr<Objects::Window> gGui;
|
||||
inline Objects::ProcessingQueue<std::uintptr_t> gQueue;
|
||||
|
||||
/* Allows for fast & easy sound access, is populated on start up */
|
||||
inline sxl::var_guard<std::map<std::uint32_t, std::reference_wrapper<Objects::Sound>>> gSounds;
|
||||
|
@ -1,144 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
template <typename, typename> struct adl_serializer;
|
||||
} // namespace nlohmann
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace Objects
|
||||
{
|
||||
struct AudioDevice;
|
||||
|
||||
enum class ErrorCode : std::uint8_t
|
||||
{
|
||||
FailedToPlay,
|
||||
FailedToSeek,
|
||||
FailedToPause,
|
||||
FailedToRepeat,
|
||||
FailedToResume,
|
||||
FailedToMoveToSink,
|
||||
SoundNotFound,
|
||||
FolderDoesNotExist,
|
||||
TabDoesNotExist,
|
||||
FailedToSetHotkey,
|
||||
FailedToStartPassthrough,
|
||||
FailedToMoveBack,
|
||||
FailedToMoveBackPassthrough,
|
||||
FailedToRevertDefaultSource,
|
||||
FailedToSetDefaultSource,
|
||||
YtdlNotFound,
|
||||
YtdlInvalidUrl,
|
||||
YtdlInvalidJson,
|
||||
YtdlInformationUnknown,
|
||||
FailedToDelete
|
||||
};
|
||||
|
||||
enum class SortMode : std::uint8_t
|
||||
{
|
||||
ModifiedDate_Ascending,
|
||||
ModifiedDate_Descending,
|
||||
Alphabetical_Ascending,
|
||||
Alphabetical_Descending,
|
||||
};
|
||||
|
||||
enum class Theme : std::uint8_t
|
||||
{
|
||||
System,
|
||||
Dark,
|
||||
Light
|
||||
};
|
||||
|
||||
enum class ViewMode : std::uint8_t
|
||||
{
|
||||
List,
|
||||
Grid,
|
||||
EmulatedLaunchpad,
|
||||
};
|
||||
|
||||
enum class BackendType : std::uint8_t
|
||||
{
|
||||
PulseAudio,
|
||||
PipeWire,
|
||||
};
|
||||
|
||||
struct Sound
|
||||
{
|
||||
std::uint32_t id;
|
||||
std::string name;
|
||||
std::string path;
|
||||
bool isFavorite = false;
|
||||
|
||||
std::vector<int> hotkeys;
|
||||
std::uint64_t modifiedDate;
|
||||
};
|
||||
struct Tab
|
||||
{
|
||||
std::uint32_t id; //* Equal to index
|
||||
std::string name;
|
||||
std::string path;
|
||||
|
||||
std::vector<Sound> sounds;
|
||||
};
|
||||
|
||||
struct Settings
|
||||
{
|
||||
SortMode sortMode = SortMode::ModifiedDate_Descending;
|
||||
BackendType audioBackend = BackendType::PulseAudio;
|
||||
ViewMode viewMode = ViewMode::List;
|
||||
Theme theme = Theme::System;
|
||||
|
||||
std::vector<int> pushToTalkKeys;
|
||||
std::vector<int> stopHotkey;
|
||||
|
||||
std::uint32_t selectedTab = 0;
|
||||
std::string output;
|
||||
|
||||
float remoteVolume = 1.f;
|
||||
float localVolume = 0.5f;
|
||||
bool syncVolumes = false;
|
||||
|
||||
bool useAsDefaultDevice = false;
|
||||
bool muteDuringPlayback = false;
|
||||
bool allowOverlapping = true;
|
||||
bool minimizeToTray = false;
|
||||
bool tabHotkeysOnly = false;
|
||||
bool deleteToTrash = true;
|
||||
};
|
||||
class Data
|
||||
{
|
||||
template <typename, typename> friend struct nlohmann::adl_serializer;
|
||||
|
||||
private:
|
||||
std::vector<Tab> tabs;
|
||||
|
||||
public:
|
||||
bool isOnFavorites = false;
|
||||
int width = 1280, height = 720;
|
||||
std::uint32_t soundIdCounter = 0;
|
||||
|
||||
std::vector<Tab> getTabs() const;
|
||||
void setTabs(const std::vector<Tab> &);
|
||||
std::optional<Tab> setTab(const std::uint32_t &, const Tab &);
|
||||
|
||||
Tab addTab(Tab);
|
||||
void removeTabById(const std::uint32_t &);
|
||||
|
||||
std::optional<Tab> getTab(const std::uint32_t &) const;
|
||||
std::optional<std::reference_wrapper<Sound>> getSound(const std::uint32_t &);
|
||||
|
||||
std::vector<Sound> getFavorites();
|
||||
std::vector<std::uint32_t> getFavoriteIds();
|
||||
void markFavorite(const std::uint32_t &, bool);
|
||||
|
||||
void set(const Data &other);
|
||||
Data &operator=(const Data &other) = delete;
|
||||
};
|
||||
} // namespace Objects
|
||||
} // namespace Soundux
|
@ -1,8 +1,6 @@
|
||||
#include "objects.hpp"
|
||||
#include "globals.hpp"
|
||||
#include "data.hpp"
|
||||
#include <core/global/globals.hpp>
|
||||
#include <fancy.hpp>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
namespace Soundux::Objects
|
||||
{
|
46
src/core/objects/data.hpp
Normal file
46
src/core/objects/data.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include "objects.hpp"
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
template <typename, typename> struct adl_serializer;
|
||||
} // namespace nlohmann
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace Objects
|
||||
{
|
||||
class Data
|
||||
{
|
||||
template <typename, typename> friend struct nlohmann::adl_serializer;
|
||||
|
||||
private:
|
||||
std::vector<Tab> tabs;
|
||||
|
||||
public:
|
||||
bool isOnFavorites = false;
|
||||
int width = 1280, height = 720;
|
||||
std::uint32_t soundIdCounter = 0;
|
||||
|
||||
std::vector<Tab> getTabs() const;
|
||||
void setTabs(const std::vector<Tab> &);
|
||||
std::optional<Tab> setTab(const std::uint32_t &, const Tab &);
|
||||
|
||||
Tab addTab(Tab);
|
||||
void removeTabById(const std::uint32_t &);
|
||||
|
||||
std::optional<Tab> getTab(const std::uint32_t &) const;
|
||||
std::optional<std::reference_wrapper<Sound>> getSound(const std::uint32_t &);
|
||||
|
||||
std::vector<Sound> getFavorites();
|
||||
std::vector<std::uint32_t> getFavoriteIds();
|
||||
void markFavorite(const std::uint32_t &, bool);
|
||||
|
||||
void set(const Data &other);
|
||||
Data &operator=(const Data &other) = delete;
|
||||
};
|
||||
} // namespace Objects
|
||||
} // namespace Soundux
|
32
src/core/objects/objects.hpp
Normal file
32
src/core/objects/objects.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include <core/enums/enums.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace Objects
|
||||
{
|
||||
struct AudioDevice;
|
||||
|
||||
struct Sound
|
||||
{
|
||||
std::uint32_t id;
|
||||
std::string name;
|
||||
std::string path;
|
||||
bool isFavorite = false;
|
||||
|
||||
std::vector<int> hotkeys;
|
||||
std::uint64_t modifiedDate;
|
||||
};
|
||||
|
||||
struct Tab
|
||||
{
|
||||
std::uint32_t id; //* Equal to index
|
||||
std::string name;
|
||||
std::string path;
|
||||
|
||||
std::vector<Sound> sounds;
|
||||
};
|
||||
} // namespace Objects
|
||||
} // namespace Soundux
|
35
src/core/objects/settings.hpp
Normal file
35
src/core/objects/settings.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include <core/enums/enums.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace Objects
|
||||
{
|
||||
struct Settings
|
||||
{
|
||||
Enums::SortMode sortMode = Enums::SortMode::ModifiedDate_Descending;
|
||||
Enums::BackendType audioBackend = Enums::BackendType::PulseAudio;
|
||||
Enums::ViewMode viewMode = Enums::ViewMode::List;
|
||||
Enums::Theme theme = Enums::Theme::System;
|
||||
|
||||
std::vector<int> pushToTalkKeys;
|
||||
std::vector<int> stopHotkey;
|
||||
|
||||
std::uint32_t selectedTab = 0;
|
||||
std::string output;
|
||||
|
||||
float remoteVolume = 1.f;
|
||||
float localVolume = 0.5f;
|
||||
bool syncVolumes = false;
|
||||
|
||||
bool useAsDefaultDevice = false;
|
||||
bool muteDuringPlayback = false;
|
||||
bool allowOverlapping = true;
|
||||
bool minimizeToTray = false;
|
||||
bool tabHotkeysOnly = false;
|
||||
bool deleteToTrash = true;
|
||||
};
|
||||
} // namespace Objects
|
||||
} // namespace Soundux
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <core/global/objects.hpp>
|
||||
#include <core/objects/objects.hpp>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
@ -1,96 +1,54 @@
|
||||
#if defined(__linux__)
|
||||
#include "backend.hpp"
|
||||
#include "pipewire/pipewire.hpp"
|
||||
#include "pulseaudio/pulseaudio.hpp"
|
||||
#include <core/enums/enums.hpp>
|
||||
#include <core/global/globals.hpp>
|
||||
#include <fancy.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace Soundux::Objects
|
||||
{
|
||||
void AudioBackend::setup()
|
||||
std::shared_ptr<AudioBackend> AudioBackend::createInstance(Enums::BackendType backend)
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "setup(): not implemented (Possibly using null-audiobackend)" << std::endl;
|
||||
}
|
||||
void AudioBackend::destroy()
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "destroy(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
}
|
||||
bool AudioBackend::useAsDefault()
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "useAsDefault(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
bool AudioBackend::revertDefault()
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "revertDefault(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
bool AudioBackend::muteInput([[maybe_unused]] bool state)
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "muteInput(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
std::shared_ptr<AudioBackend> instance;
|
||||
if (backend == Enums::BackendType::PulseAudio)
|
||||
{
|
||||
instance = std::shared_ptr<PulseAudio>(new PulseAudio()); // NOLINT
|
||||
auto pulseInstance = std::dynamic_pointer_cast<PulseAudio>(instance);
|
||||
|
||||
bool AudioBackend::stopPassthrough()
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "stopPassthrough(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
bool AudioBackend::isCurrentlyPassingThrough()
|
||||
{
|
||||
Fancy::fancy.logTime().warning()
|
||||
<< "isCurrentlyPassingThrough(): not implemented (Possibly using null-audiobackend)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
bool AudioBackend::passthroughFrom([[maybe_unused]] std::shared_ptr<PlaybackApp> app) // NOLINT
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "passthroughFrom(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
if (pulseInstance && pulseInstance->setup())
|
||||
{
|
||||
if (!pulseInstance->switchOnConnectPresent())
|
||||
{
|
||||
if (pulseInstance->loadModules())
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioBackend::stopSoundInput()
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "stopSoundInput(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
if (pulseInstance && pulseInstance->isRunningPipeWire())
|
||||
{
|
||||
backend = Enums::BackendType::PipeWire;
|
||||
Globals::gSettings.audioBackend = backend;
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioBackend::inputSoundTo([[maybe_unused]] std::shared_ptr<RecordingApp> app) // NOLINT
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "inputSoundTo(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
if (backend == Enums::BackendType::PipeWire)
|
||||
{
|
||||
instance = std::shared_ptr<PipeWire>(new PipeWire()); // NOLINT
|
||||
if (instance->setup())
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<PlaybackApp> AudioBackend::getPlaybackApp([[maybe_unused]] const std::string &name)
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "getPlaybackApp(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
Fancy::fancy.logTime().failure() << "Failed to create AudioBackend instance" << std::endl;
|
||||
Globals::gSettings.audioBackend = Enums::BackendType::None;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<RecordingApp> AudioBackend::getRecordingApp([[maybe_unused]] const std::string &name)
|
||||
{
|
||||
|
||||
Fancy::fancy.logTime().warning() << "getRecordingApp(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<PlaybackApp>> AudioBackend::getPlaybackApps()
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "getPlaybackApps(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
return {};
|
||||
}
|
||||
std::vector<std::shared_ptr<RecordingApp>> AudioBackend::getRecordingApps()
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "getRecordingApps(): not implemented (Possibly using null-audiobackend)"
|
||||
<< std::endl;
|
||||
return {};
|
||||
}
|
||||
} // namespace Soundux::Objects
|
||||
#endif
|
||||
} // namespace Soundux::Objects
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#if defined(__linux__)
|
||||
#include <core/enums/enums.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -25,28 +26,31 @@ namespace Soundux
|
||||
|
||||
class AudioBackend
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
virtual bool setup() = 0;
|
||||
AudioBackend() = default;
|
||||
|
||||
virtual void setup();
|
||||
virtual void destroy();
|
||||
public:
|
||||
static std::shared_ptr<AudioBackend> createInstance(Enums::BackendType);
|
||||
|
||||
virtual bool useAsDefault();
|
||||
virtual bool revertDefault();
|
||||
virtual bool muteInput(bool);
|
||||
public:
|
||||
virtual void destroy() = 0;
|
||||
virtual bool useAsDefault() = 0;
|
||||
virtual bool revertDefault() = 0;
|
||||
virtual bool muteInput(bool) = 0;
|
||||
|
||||
virtual bool stopPassthrough();
|
||||
virtual bool isCurrentlyPassingThrough();
|
||||
virtual bool passthroughFrom(std::shared_ptr<PlaybackApp>);
|
||||
virtual bool stopPassthrough() = 0;
|
||||
virtual bool isCurrentlyPassingThrough() = 0;
|
||||
virtual bool passthroughFrom(std::shared_ptr<PlaybackApp>) = 0;
|
||||
|
||||
virtual bool stopSoundInput();
|
||||
virtual bool inputSoundTo(std::shared_ptr<RecordingApp>);
|
||||
virtual bool stopSoundInput() = 0;
|
||||
virtual bool inputSoundTo(std::shared_ptr<RecordingApp>) = 0;
|
||||
|
||||
virtual std::shared_ptr<PlaybackApp> getPlaybackApp(const std::string &);
|
||||
virtual std::shared_ptr<RecordingApp> getRecordingApp(const std::string &);
|
||||
virtual std::shared_ptr<PlaybackApp> getPlaybackApp(const std::string &) = 0;
|
||||
virtual std::shared_ptr<RecordingApp> getRecordingApp(const std::string &) = 0;
|
||||
|
||||
virtual std::vector<std::shared_ptr<PlaybackApp>> getPlaybackApps();
|
||||
virtual std::vector<std::shared_ptr<RecordingApp>> getRecordingApps();
|
||||
virtual std::vector<std::shared_ptr<PlaybackApp>> getPlaybackApps() = 0;
|
||||
virtual std::vector<std::shared_ptr<RecordingApp>> getRecordingApps() = 0;
|
||||
};
|
||||
} // namespace Objects
|
||||
} // namespace Soundux
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "forward.hpp"
|
||||
#include <core/global/globals.hpp>
|
||||
#include <dlfcn.h>
|
||||
#include <exception>
|
||||
#include <fancy.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
@ -20,34 +21,41 @@ bool Soundux::PipeWireApi::setup()
|
||||
auto *libpulse = dlopen("libpipewire-0.3.so", RTLD_LAZY);
|
||||
if (!libpulse)
|
||||
{
|
||||
//* For flatpak
|
||||
//* For Ubuntu
|
||||
libpulse = dlopen("/usr/lib/x86_64-linux-gnu/libpipewire-0.3.so.0", RTLD_LAZY);
|
||||
}
|
||||
|
||||
if (libpulse)
|
||||
{
|
||||
#define load(name) loadFunc(libpulse, name, #name)
|
||||
load(pw_context_connect);
|
||||
load(pw_context_new);
|
||||
load(pw_main_loop_new);
|
||||
load(pw_main_loop_get_loop);
|
||||
load(pw_proxy_add_listener);
|
||||
load(pw_properties_setf);
|
||||
load(pw_properties_set);
|
||||
load(pw_properties_new);
|
||||
load(pw_properties_free);
|
||||
load(pw_main_loop_destroy);
|
||||
load(pw_main_loop_quit);
|
||||
load(pw_context_destroy);
|
||||
load(pw_core_disconnect);
|
||||
load(pw_main_loop_run);
|
||||
load(pw_proxy_destroy);
|
||||
load(pw_init);
|
||||
return true;
|
||||
try
|
||||
{
|
||||
#define stringify(what) #what
|
||||
#define load(name) loadFunc(libpulse, name, stringify(pw_##name))
|
||||
load(init);
|
||||
load(context_new);
|
||||
load(main_loop_run);
|
||||
load(main_loop_new);
|
||||
load(proxy_destroy);
|
||||
load(main_loop_quit);
|
||||
load(properties_new);
|
||||
load(properties_set);
|
||||
load(context_connect);
|
||||
load(properties_setf);
|
||||
load(context_destroy);
|
||||
load(properties_free);
|
||||
load(core_disconnect);
|
||||
load(main_loop_destroy);
|
||||
load(main_loop_get_loop);
|
||||
load(proxy_add_listener);
|
||||
return true;
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Loading Functions failed: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().failure() << "Failed to load pipewire" << std::endl;
|
||||
Globals::gAudioBackend = std::make_shared<Objects::AudioBackend>();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -9,24 +9,24 @@ namespace Soundux
|
||||
{
|
||||
bool setup();
|
||||
|
||||
inline pw_core *(*pw_context_connect)(pw_context *, pw_properties *, std::size_t);
|
||||
inline pw_context *(*pw_context_new)(pw_loop *, pw_properties *, std::size_t);
|
||||
inline pw_main_loop *(*pw_main_loop_new)(pw_properties *);
|
||||
inline pw_loop *(*pw_main_loop_get_loop)(pw_main_loop *);
|
||||
|
||||
inline void (*pw_proxy_add_listener)(pw_proxy *, spa_hook *, pw_proxy_events *, void *);
|
||||
inline int (*pw_properties_setf)(pw_properties *, const char *, const char *, ...);
|
||||
inline int (*pw_properties_set)(pw_properties *, const char *, const char *);
|
||||
inline pw_properties *(*pw_properties_new)(const char *, ...);
|
||||
inline void (*pw_main_loop_destroy)(pw_main_loop *);
|
||||
inline void (*pw_properties_free)(pw_properties *);
|
||||
inline int (*pw_main_loop_quit)(pw_main_loop *);
|
||||
inline void (*pw_context_destroy)(pw_context *);
|
||||
inline int (*pw_main_loop_run)(pw_main_loop *);
|
||||
inline int (*pw_core_disconnect)(pw_core *);
|
||||
inline void (*pw_proxy_destroy)(pw_proxy *);
|
||||
inline void (*pw_init)(int *, char **);
|
||||
//* We declare function pointers here so that we can use dlsym to assign them later.
|
||||
inline pw_core *(*context_connect)(pw_context *, pw_properties *, std::size_t);
|
||||
inline pw_context *(*context_new)(pw_loop *, pw_properties *, std::size_t);
|
||||
inline pw_main_loop *(*main_loop_new)(pw_properties *);
|
||||
inline pw_loop *(*main_loop_get_loop)(pw_main_loop *);
|
||||
|
||||
inline void (*proxy_add_listener)(pw_proxy *, spa_hook *, pw_proxy_events *, void *);
|
||||
inline int (*properties_setf)(pw_properties *, const char *, const char *, ...);
|
||||
inline int (*properties_set)(pw_properties *, const char *, const char *);
|
||||
inline pw_properties *(*properties_new)(const char *, ...);
|
||||
inline void (*main_loop_destroy)(pw_main_loop *);
|
||||
inline void (*properties_free)(pw_properties *);
|
||||
inline int (*main_loop_quit)(pw_main_loop *);
|
||||
inline void (*context_destroy)(pw_context *);
|
||||
inline int (*main_loop_run)(pw_main_loop *);
|
||||
inline int (*core_disconnect)(pw_core *);
|
||||
inline void (*proxy_destroy)(pw_proxy *);
|
||||
inline void (*init)(int *, char **);
|
||||
} // namespace PipeWireApi
|
||||
} // namespace Soundux
|
||||
#endif
|
@ -22,18 +22,22 @@ namespace Soundux::Objects
|
||||
if (id == PW_ID_CORE && seq == *info->second)
|
||||
{
|
||||
*info->second = -1;
|
||||
PipeWireApi::pw_main_loop_quit(info->first->loop);
|
||||
PipeWireApi::main_loop_quit(info->first->loop);
|
||||
}
|
||||
}
|
||||
};
|
||||
coreEvents.error = [](void *data, std::uint32_t id, int seq, int res, const char *message) {
|
||||
Fancy::fancy.logTime() << "Core Failure - Seq " << seq << " - Res " << res << ": " << message << std::endl;
|
||||
auto *info = reinterpret_cast<std::pair<PipeWire *, int *> *>(data);
|
||||
|
||||
if (info && id == PW_ID_CORE)
|
||||
if (info)
|
||||
{
|
||||
*info->second = -1;
|
||||
PipeWireApi::pw_main_loop_quit(info->first->loop);
|
||||
if (id == PW_ID_CORE && seq == *info->second)
|
||||
{
|
||||
Fancy::fancy.logTime()
|
||||
<< "Core Failure - Seq " << seq << " - Res " << res << ": " << message << std::endl;
|
||||
|
||||
*info->second = -1;
|
||||
PipeWireApi::main_loop_quit(info->first->loop);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -43,7 +47,7 @@ namespace Soundux::Objects
|
||||
pending = pw_core_sync(core, PW_ID_CORE, 0); // NOLINT
|
||||
while (pending != -1)
|
||||
{
|
||||
PipeWireApi::pw_main_loop_run(loop);
|
||||
PipeWireApi::main_loop_run(loop);
|
||||
}
|
||||
|
||||
spa_hook_remove(&coreListener);
|
||||
@ -135,7 +139,7 @@ namespace Soundux::Objects
|
||||
pw_node_add_listener(boundNode, &listener, &events, thiz); // NOLINT
|
||||
thiz->sync();
|
||||
spa_hook_remove(&listener);
|
||||
PipeWireApi::pw_proxy_destroy(reinterpret_cast<pw_proxy *>(boundNode));
|
||||
PipeWireApi::proxy_destroy(reinterpret_cast<pw_proxy *>(boundNode));
|
||||
}
|
||||
}
|
||||
if (strcmp(type, PW_TYPE_INTERFACE_Port) == 0)
|
||||
@ -164,7 +168,7 @@ namespace Soundux::Objects
|
||||
pw_port_add_listener(boundPort, &listener, &events, thiz); // NOLINT
|
||||
thiz->sync();
|
||||
spa_hook_remove(&listener);
|
||||
PipeWireApi::pw_proxy_destroy(reinterpret_cast<pw_proxy *>(boundPort));
|
||||
PipeWireApi::proxy_destroy(reinterpret_cast<pw_proxy *>(boundPort));
|
||||
|
||||
auto scopedNodes = thiz->nodes.scoped();
|
||||
auto scopedPorts = thiz->ports.scoped();
|
||||
@ -202,34 +206,37 @@ namespace Soundux::Objects
|
||||
}
|
||||
}
|
||||
|
||||
void PipeWire::setup()
|
||||
bool PipeWire::setup()
|
||||
{
|
||||
if (!PipeWireApi::setup())
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
PipeWireApi::pw_init(nullptr, nullptr);
|
||||
loop = PipeWireApi::pw_main_loop_new(nullptr);
|
||||
PipeWireApi::init(nullptr, nullptr);
|
||||
loop = PipeWireApi::main_loop_new(nullptr);
|
||||
if (!loop)
|
||||
{
|
||||
throw std::runtime_error("Failed to create main loop");
|
||||
Fancy::fancy.logTime().failure() << "Failed to create main loop" << std::endl;
|
||||
return false;
|
||||
}
|
||||
context = PipeWireApi::pw_context_new(PipeWireApi::pw_main_loop_get_loop(loop), nullptr, 0);
|
||||
context = PipeWireApi::context_new(PipeWireApi::main_loop_get_loop(loop), nullptr, 0);
|
||||
if (!context)
|
||||
{
|
||||
throw std::runtime_error("Failed to create context");
|
||||
Fancy::fancy.logTime().failure() << "Failed to create context" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
core = PipeWireApi::pw_context_connect(context, nullptr, 0);
|
||||
core = PipeWireApi::context_connect(context, nullptr, 0);
|
||||
if (!core)
|
||||
{
|
||||
throw std::runtime_error("Failed to connect context");
|
||||
Fancy::fancy.logTime().failure() << "Failed to connect context" << std::endl;
|
||||
return false;
|
||||
}
|
||||
registry = pw_core_get_registry(core, PW_VERSION_REGISTRY, 0);
|
||||
if (!registry)
|
||||
{
|
||||
throw std::runtime_error("Failed to get registry");
|
||||
Fancy::fancy.logTime().failure() << "Failed to get registry" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
registryEvents.global = onGlobalAdded;
|
||||
@ -239,31 +246,38 @@ namespace Soundux::Objects
|
||||
pw_registry_add_listener(registry, ®istryListener, ®istryEvents, this); // NOLINT
|
||||
|
||||
sync();
|
||||
createNullSink();
|
||||
|
||||
if (!createNullSink())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to create null sink" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PipeWire::destroy()
|
||||
{
|
||||
PipeWireApi::pw_proxy_destroy(reinterpret_cast<pw_proxy *>(registry));
|
||||
PipeWireApi::pw_core_disconnect(core);
|
||||
PipeWireApi::pw_context_destroy(context);
|
||||
PipeWireApi::pw_main_loop_destroy(loop);
|
||||
PipeWireApi::proxy_destroy(reinterpret_cast<pw_proxy *>(registry));
|
||||
PipeWireApi::core_disconnect(core);
|
||||
PipeWireApi::context_destroy(context);
|
||||
PipeWireApi::main_loop_destroy(loop);
|
||||
}
|
||||
|
||||
bool PipeWire::createNullSink()
|
||||
{
|
||||
pw_properties *props = PipeWireApi::pw_properties_new(nullptr, nullptr);
|
||||
pw_properties *props = PipeWireApi::properties_new(nullptr, nullptr);
|
||||
|
||||
PipeWireApi::pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
|
||||
PipeWireApi::pw_properties_set(props, PW_KEY_NODE_NAME, "soundux_sink");
|
||||
PipeWireApi::pw_properties_set(props, PW_KEY_FACTORY_NAME, "support.null-audio-sink");
|
||||
PipeWireApi::properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
|
||||
PipeWireApi::properties_set(props, PW_KEY_NODE_NAME, "soundux_sink");
|
||||
PipeWireApi::properties_set(props, PW_KEY_FACTORY_NAME, "support.null-audio-sink");
|
||||
|
||||
auto *proxy = reinterpret_cast<pw_proxy *>(
|
||||
pw_core_create_object(core, "adapter", PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, &props->dict, 0));
|
||||
|
||||
if (!proxy)
|
||||
{
|
||||
PipeWireApi::pw_properties_free(props);
|
||||
PipeWireApi::properties_free(props);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -277,11 +291,11 @@ namespace Soundux::Objects
|
||||
*reinterpret_cast<bool *>(data) = false;
|
||||
};
|
||||
|
||||
PipeWireApi::pw_proxy_add_listener(proxy, &listener, &linkEvent, &success);
|
||||
PipeWireApi::proxy_add_listener(proxy, &listener, &linkEvent, &success);
|
||||
sync();
|
||||
|
||||
spa_hook_remove(&listener);
|
||||
PipeWireApi::pw_properties_free(props);
|
||||
PipeWireApi::properties_free(props);
|
||||
|
||||
return success;
|
||||
}
|
||||
@ -296,18 +310,18 @@ namespace Soundux::Objects
|
||||
|
||||
std::optional<int> PipeWire::linkPorts(std::uint32_t in, std::uint32_t out)
|
||||
{
|
||||
pw_properties *props = PipeWireApi::pw_properties_new(nullptr, nullptr);
|
||||
pw_properties *props = PipeWireApi::properties_new(nullptr, nullptr);
|
||||
|
||||
PipeWireApi::pw_properties_set(props, PW_KEY_APP_NAME, "soundux");
|
||||
PipeWireApi::pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%u", in);
|
||||
PipeWireApi::pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%u", out);
|
||||
PipeWireApi::properties_set(props, PW_KEY_APP_NAME, "soundux");
|
||||
PipeWireApi::properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%u", in);
|
||||
PipeWireApi::properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%u", out);
|
||||
|
||||
auto *proxy = reinterpret_cast<pw_proxy *>(
|
||||
pw_core_create_object(core, "link-factory", PW_TYPE_INTERFACE_Link, PW_VERSION_LINK, &props->dict, 0));
|
||||
|
||||
if (!proxy)
|
||||
{
|
||||
PipeWireApi::pw_properties_free(props);
|
||||
PipeWireApi::properties_free(props);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@ -324,11 +338,11 @@ namespace Soundux::Objects
|
||||
*reinterpret_cast<std::optional<std::uint32_t> *>(data) = std::nullopt;
|
||||
};
|
||||
|
||||
PipeWireApi::pw_proxy_add_listener(proxy, &listener, &linkEvent, &result);
|
||||
PipeWireApi::proxy_add_listener(proxy, &listener, &linkEvent, &result);
|
||||
sync();
|
||||
|
||||
spa_hook_remove(&listener);
|
||||
PipeWireApi::pw_properties_free(props);
|
||||
PipeWireApi::properties_free(props);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -5,6 +5,13 @@
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <var_guard.hpp>
|
||||
|
||||
// TODO(pipewire):
|
||||
//* From the pipewire news of 0.3.26
|
||||
//* - The link factory can now also make links between nodes and
|
||||
//* ports by name so that it can be used in scripts.
|
||||
//*
|
||||
//* Maybe we could try to make use of that, it could reduce loc.
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace Objects
|
||||
@ -43,6 +50,9 @@ namespace Soundux
|
||||
|
||||
class PipeWire : public AudioBackend
|
||||
{
|
||||
friend class AudioBackend;
|
||||
|
||||
private:
|
||||
pw_core *core;
|
||||
pw_main_loop *loop;
|
||||
pw_context *context;
|
||||
@ -72,9 +82,11 @@ namespace Soundux
|
||||
static void onGlobalAdded(void *, std::uint32_t, std::uint32_t, const char *, std::uint32_t,
|
||||
const spa_dict *);
|
||||
|
||||
protected:
|
||||
bool setup() override;
|
||||
|
||||
public:
|
||||
PipeWire() = default;
|
||||
void setup() override;
|
||||
void destroy() override;
|
||||
|
||||
bool useAsDefault() override;
|
||||
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
#if defined(__linux__)
|
||||
#include <cstdint>
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace PulseApi
|
||||
{
|
||||
bool setup();
|
||||
|
||||
inline pa_mainloop *(*mainloop_new)();
|
||||
inline int (*mainloop_iterate)(pa_mainloop *, int, int *);
|
||||
inline pa_mainloop_api *(*mainloop_get_api)(pa_mainloop *);
|
||||
inline pa_context *(*context_new)(pa_mainloop_api *, const char *);
|
||||
inline int (*context_connect)(pa_context *, const char *, unsigned int, const void *);
|
||||
inline void (*context_set_state_callback)(pa_context *, pa_context_notify_cb_t, void *);
|
||||
inline pa_operation *(*context_load_module)(pa_context *, const char *, const char *, pa_context_index_cb_t,
|
||||
void *);
|
||||
inline pa_operation *(*context_get_module_info_list)(pa_context *, pa_module_info_cb_t, void *);
|
||||
inline pa_operation *(*context_get_source_output_info_list)(pa_context *, pa_source_output_info_cb_t, void *);
|
||||
inline pa_operation *(*context_get_sink_input_info_list)(pa_context *, pa_sink_input_info_cb_t, void *);
|
||||
inline pa_operation *(*context_get_server_info)(pa_context *, pa_server_info_cb_t, void *);
|
||||
|
||||
inline const char *(*proplist_gets)(const pa_proplist *, const char *);
|
||||
|
||||
inline pa_operation *(*context_set_default_source)(pa_context *, const char *, pa_context_success_cb_t, void *);
|
||||
inline pa_operation *(*context_move_sink_input_by_name)(pa_context *, std::uint32_t, const char *,
|
||||
pa_context_success_cb_t, void *);
|
||||
inline pa_operation *(*context_move_sink_input_by_index)(pa_context *, std::uint32_t, std::uint32_t,
|
||||
pa_context_success_cb_t, void *);
|
||||
inline pa_operation *(*context_move_source_output_by_name)(pa_context *, std::uint32_t, const char *,
|
||||
pa_context_success_cb_t, void *);
|
||||
inline pa_operation *(*context_move_source_output_by_index)(pa_context *, std::uint32_t, std::uint32_t,
|
||||
pa_context_success_cb_t, void *);
|
||||
inline pa_operation *(*context_set_sink_input_mute)(pa_context *, uint32_t, int, pa_context_success_cb_t,
|
||||
void *);
|
||||
inline pa_operation *(*context_unload_module)(pa_context *, std::uint32_t, pa_context_success_cb_t, void *);
|
||||
inline pa_context_state (*context_get_state)(const pa_context *);
|
||||
inline pa_operation_state (*operation_get_state)(const pa_operation *);
|
||||
} // namespace PulseApi
|
||||
} // namespace Soundux
|
||||
#endif
|
@ -44,37 +44,50 @@ bool Soundux::PulseApi::setup()
|
||||
return true;
|
||||
#else
|
||||
auto *libpulse = dlopen("libpulse.so", RTLD_LAZY);
|
||||
if (!libpulse)
|
||||
{
|
||||
//* For Ubuntu
|
||||
libpulse = dlopen("/usr/lib/x86_64-linux-gnu/libpulse.so.0", RTLD_LAZY);
|
||||
}
|
||||
|
||||
if (libpulse)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
#define stringify(what) #what
|
||||
#define load(name) loadFunc(libpulse, name, stringify(pa_##name))
|
||||
load(mainloop_new);
|
||||
load(mainloop_iterate);
|
||||
load(mainloop_get_api);
|
||||
load(context_new);
|
||||
load(context_connect);
|
||||
load(context_set_state_callback);
|
||||
load(context_load_module);
|
||||
load(context_get_module_info_list);
|
||||
load(context_get_source_output_info_list);
|
||||
load(context_get_sink_input_info_list);
|
||||
load(context_get_server_info);
|
||||
load(proplist_gets);
|
||||
load(context_set_default_source);
|
||||
load(context_move_sink_input_by_name);
|
||||
load(context_get_server_info);
|
||||
load(context_move_sink_input_by_index);
|
||||
load(context_move_source_output_by_name);
|
||||
load(context_move_source_output_by_index);
|
||||
load(context_set_sink_input_mute);
|
||||
load(context_unload_module);
|
||||
load(context_get_state);
|
||||
load(operation_get_state);
|
||||
return true;
|
||||
load(mainloop_new);
|
||||
load(mainloop_iterate);
|
||||
load(mainloop_get_api);
|
||||
load(context_new);
|
||||
load(context_connect);
|
||||
load(context_set_state_callback);
|
||||
load(context_load_module);
|
||||
load(context_get_module_info_list);
|
||||
load(context_get_source_output_info_list);
|
||||
load(context_get_sink_input_info_list);
|
||||
load(context_get_server_info);
|
||||
load(proplist_gets);
|
||||
load(context_set_default_source);
|
||||
load(context_move_sink_input_by_name);
|
||||
load(context_get_server_info);
|
||||
load(context_move_sink_input_by_index);
|
||||
load(context_move_source_output_by_name);
|
||||
load(context_move_source_output_by_index);
|
||||
load(context_set_sink_input_mute);
|
||||
load(context_unload_module);
|
||||
load(context_get_state);
|
||||
load(operation_get_state);
|
||||
return true;
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Loading Functions failed: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().failure() << "Failed to load pulseaudio" << std::endl;
|
||||
Globals::gAudioBackend = std::make_shared<Objects::AudioBackend>();
|
||||
return false;
|
||||
#endif
|
||||
}
|
39
src/helper/audio/linux/pulseaudio/forward.hpp
Normal file
39
src/helper/audio/linux/pulseaudio/forward.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#if defined(__linux__)
|
||||
#include <cstdint>
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace PulseApi
|
||||
{
|
||||
bool setup();
|
||||
|
||||
//* We declare function pointers here so that we can use dlsym to assign them later.
|
||||
#define pulse_forward_decl(function) inline std::add_pointer_t<decltype(pa_##function)> function;
|
||||
|
||||
pulse_forward_decl(context_new);
|
||||
pulse_forward_decl(mainloop_new);
|
||||
pulse_forward_decl(proplist_gets);
|
||||
pulse_forward_decl(context_connect);
|
||||
pulse_forward_decl(mainloop_iterate);
|
||||
pulse_forward_decl(mainloop_get_api);
|
||||
pulse_forward_decl(context_get_state);
|
||||
pulse_forward_decl(operation_get_state);
|
||||
pulse_forward_decl(context_load_module);
|
||||
pulse_forward_decl(context_unload_module);
|
||||
pulse_forward_decl(context_get_server_info);
|
||||
pulse_forward_decl(context_set_state_callback);
|
||||
pulse_forward_decl(context_set_default_source);
|
||||
pulse_forward_decl(context_set_sink_input_mute);
|
||||
pulse_forward_decl(context_get_module_info_list);
|
||||
pulse_forward_decl(context_move_sink_input_by_name);
|
||||
pulse_forward_decl(context_move_sink_input_by_index);
|
||||
pulse_forward_decl(context_get_sink_input_info_list);
|
||||
pulse_forward_decl(context_move_source_output_by_name);
|
||||
pulse_forward_decl(context_get_source_output_info_list);
|
||||
pulse_forward_decl(context_move_source_output_by_index);
|
||||
} // namespace PulseApi
|
||||
} // namespace Soundux
|
||||
#endif
|
@ -1,23 +1,23 @@
|
||||
#if defined(__linux__)
|
||||
#include "pulse.hpp"
|
||||
#include "pulseaudio.hpp"
|
||||
#include "forward.hpp"
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <fancy.hpp>
|
||||
#include <helper/audio/linux/pulse/forward.hpp>
|
||||
|
||||
namespace Soundux::Objects
|
||||
{
|
||||
void PulseAudio::setup()
|
||||
bool PulseAudio::setup()
|
||||
{
|
||||
if (!PulseApi::setup())
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
mainloop = PulseApi::mainloop_new();
|
||||
mainloopApi = PulseApi::mainloop_get_api(mainloop);
|
||||
context = PulseApi::context_new(mainloopApi, "soundux");
|
||||
PulseApi::context_connect(context, nullptr, 0, nullptr);
|
||||
PulseApi::context_connect(context, nullptr, pa_context_flags::PA_CONTEXT_NOFLAGS, nullptr);
|
||||
|
||||
auto data = std::make_pair(false, false);
|
||||
PulseApi::context_set_state_callback(
|
||||
@ -53,13 +53,15 @@ namespace Soundux::Objects
|
||||
|
||||
if (!data.second)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
unloadLeftOvers();
|
||||
fetchDefaultSource();
|
||||
|
||||
return !(defaultSource.empty() || serverName.empty() || isRunningPipeWire());
|
||||
}
|
||||
void PulseAudio::loadModules()
|
||||
bool PulseAudio::loadModules()
|
||||
{
|
||||
auto playbackApps = getPlaybackApps();
|
||||
auto recordingApps = getRecordingApps();
|
||||
@ -74,7 +76,7 @@ namespace Soundux::Objects
|
||||
}
|
||||
else
|
||||
{
|
||||
*reinterpret_cast<std::uint32_t *>(userData) = id;
|
||||
*reinterpret_cast<std::optional<std::uint32_t> *>(userData) = id;
|
||||
}
|
||||
},
|
||||
&nullSink));
|
||||
@ -90,7 +92,7 @@ namespace Soundux::Objects
|
||||
}
|
||||
else
|
||||
{
|
||||
*reinterpret_cast<std::uint32_t *>(userData) = id;
|
||||
*reinterpret_cast<std::optional<std::uint32_t> *>(userData) = id;
|
||||
}
|
||||
},
|
||||
&loopBack));
|
||||
@ -105,7 +107,7 @@ namespace Soundux::Objects
|
||||
}
|
||||
else
|
||||
{
|
||||
*reinterpret_cast<std::uint32_t *>(userData) = id;
|
||||
*reinterpret_cast<std::optional<std::uint32_t> *>(userData) = id;
|
||||
}
|
||||
},
|
||||
&passthrough));
|
||||
@ -120,7 +122,7 @@ namespace Soundux::Objects
|
||||
}
|
||||
else
|
||||
{
|
||||
*reinterpret_cast<std::uint32_t *>(userData) = id;
|
||||
*reinterpret_cast<std::optional<std::uint32_t> *>(userData) = id;
|
||||
}
|
||||
},
|
||||
&passthroughSink));
|
||||
@ -134,14 +136,23 @@ namespace Soundux::Objects
|
||||
}
|
||||
else
|
||||
{
|
||||
*reinterpret_cast<std::uint32_t *>(userData) = id;
|
||||
*reinterpret_cast<std::optional<std::uint32_t> *>(userData) = id;
|
||||
}
|
||||
},
|
||||
&passthroughLoopBack));
|
||||
|
||||
fetchLoopBackSinkId();
|
||||
|
||||
if (!nullSink || !loopBack || !loopBackSink || !passthrough || !passthroughSink || !passthroughLoopBack)
|
||||
{
|
||||
unloadLeftOvers();
|
||||
return false;
|
||||
}
|
||||
|
||||
fixPlaybackApps(playbackApps);
|
||||
fixRecordingApps(recordingApps);
|
||||
|
||||
return true;
|
||||
}
|
||||
void PulseAudio::destroy()
|
||||
{
|
||||
@ -149,14 +160,19 @@ namespace Soundux::Objects
|
||||
stopSoundInput();
|
||||
stopPassthrough();
|
||||
|
||||
//* We only have to unload these 3 because the other modules depend on these and will automatically be deleted
|
||||
await(PulseApi::context_unload_module(context, nullSink, nullptr, nullptr));
|
||||
await(PulseApi::context_unload_module(context, loopBack, nullptr, nullptr));
|
||||
await(PulseApi::context_unload_module(context, loopBackSink, nullptr, nullptr));
|
||||
if (nullSink)
|
||||
await(PulseApi::context_unload_module(context, *nullSink, nullptr, nullptr));
|
||||
if (loopBack)
|
||||
await(PulseApi::context_unload_module(context, *loopBack, nullptr, nullptr));
|
||||
if (loopBackSink)
|
||||
await(PulseApi::context_unload_module(context, *loopBackSink, nullptr, nullptr));
|
||||
|
||||
await(PulseApi::context_unload_module(context, passthrough, nullptr, nullptr));
|
||||
await(PulseApi::context_unload_module(context, passthroughSink, nullptr, nullptr));
|
||||
await(PulseApi::context_unload_module(context, passthroughLoopBack, nullptr, nullptr));
|
||||
if (passthrough)
|
||||
await(PulseApi::context_unload_module(context, *passthrough, nullptr, nullptr));
|
||||
if (passthroughSink)
|
||||
await(PulseApi::context_unload_module(context, *passthroughSink, nullptr, nullptr));
|
||||
if (passthroughLoopBack)
|
||||
await(PulseApi::context_unload_module(context, *passthroughLoopBack, nullptr, nullptr));
|
||||
}
|
||||
void PulseAudio::await(pa_operation *operation)
|
||||
{
|
||||
@ -174,6 +190,7 @@ namespace Soundux::Objects
|
||||
if (info)
|
||||
{
|
||||
reinterpret_cast<PulseAudio *>(userData)->defaultSource = info->default_source_name;
|
||||
reinterpret_cast<PulseAudio *>(userData)->serverName = info->server_name;
|
||||
}
|
||||
},
|
||||
this));
|
||||
@ -267,7 +284,7 @@ namespace Soundux::Objects
|
||||
if (!defaultSource.empty())
|
||||
{
|
||||
|
||||
await(PulseApi::context_unload_module(context, loopBack, nullptr, nullptr));
|
||||
await(PulseApi::context_unload_module(context, *loopBack, nullptr, nullptr));
|
||||
|
||||
await(PulseApi::context_load_module(
|
||||
context, "module-loopback", ("rate=44100 source=" + defaultSource + " sink=soundux_sink").c_str(),
|
||||
@ -275,16 +292,16 @@ namespace Soundux::Objects
|
||||
if (static_cast<int>(id) < 0)
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to load loopback" << std::endl;
|
||||
*reinterpret_cast<std::uint32_t *>(userData) = 0;
|
||||
*reinterpret_cast<std::optional<std::uint32_t> *>(userData) = std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
*reinterpret_cast<std::uint32_t *>(userData) = id;
|
||||
*reinterpret_cast<std::optional<std::uint32_t> *>(userData) = id;
|
||||
}
|
||||
},
|
||||
&loopBack));
|
||||
|
||||
if (loopBack == 0)
|
||||
if (!loopBack)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -313,7 +330,7 @@ namespace Soundux::Objects
|
||||
{
|
||||
if (!defaultSource.empty())
|
||||
{
|
||||
await(PulseApi::context_unload_module(context, loopBack, nullptr, nullptr));
|
||||
await(PulseApi::context_unload_module(context, *loopBack, nullptr, nullptr));
|
||||
|
||||
auto result = std::make_pair(&loopBack, false);
|
||||
|
||||
@ -353,12 +370,14 @@ namespace Soundux::Objects
|
||||
}
|
||||
bool PulseAudio::passthroughFrom(std::shared_ptr<PlaybackApp> app)
|
||||
{
|
||||
if (movedPassthroughApplication && movedPassthroughApplication->name == app->name)
|
||||
auto movedPassthroughScoped = movedPassthroughApplication.scoped();
|
||||
if (*movedPassthroughScoped && movedPassthroughScoped->get()->name == app->name)
|
||||
{
|
||||
Fancy::fancy.logTime().message()
|
||||
<< "Ignoring sound passthrough request because requested app is already moved" << std::endl;
|
||||
return true;
|
||||
}
|
||||
movedPassthroughScoped.unlock();
|
||||
if (!stopPassthrough())
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "Failed to stop current passthrough" << std::endl;
|
||||
@ -396,23 +415,26 @@ namespace Soundux::Objects
|
||||
}
|
||||
}
|
||||
|
||||
movedPassthroughApplication = std::dynamic_pointer_cast<PulsePlaybackApp>(app);
|
||||
movedPassthroughScoped.lock();
|
||||
movedPassthroughScoped = std::dynamic_pointer_cast<PulsePlaybackApp>(app);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PulseAudio::stopPassthrough()
|
||||
{
|
||||
if (movedPassthroughApplication)
|
||||
auto movedPassthroughScoped = movedPassthroughApplication.scoped();
|
||||
if (*movedPassthroughScoped)
|
||||
{
|
||||
bool success = false;
|
||||
for (const auto &app : getPlaybackApps())
|
||||
{
|
||||
auto pulseApp = std::dynamic_pointer_cast<PulsePlaybackApp>(app);
|
||||
|
||||
if (app->name == movedPassthroughApplication->name)
|
||||
if (app->name == movedPassthroughApplication->get()->name)
|
||||
{
|
||||
await(PulseApi::context_move_sink_input_by_index(
|
||||
context, pulseApp->id, movedPassthroughApplication->sink,
|
||||
context, pulseApp->id, movedPassthroughApplication->get()->sink,
|
||||
[]([[maybe_unused]] pa_context *ctx, int success, void *userData) {
|
||||
if (success)
|
||||
{
|
||||
@ -421,7 +443,7 @@ namespace Soundux::Objects
|
||||
},
|
||||
&success));
|
||||
}
|
||||
movedPassthroughApplication.reset();
|
||||
movedPassthroughApplication->reset();
|
||||
return success;
|
||||
}
|
||||
}
|
||||
@ -436,15 +458,14 @@ namespace Soundux::Objects
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock lock(movedAppMutex);
|
||||
if (movedApplication && movedApplication->name == app->name)
|
||||
auto movedAppScoped = movedApplication.scoped();
|
||||
if (*movedAppScoped && movedAppScoped->get()->name == app->name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
movedAppScoped.unlock();
|
||||
stopSoundInput();
|
||||
lock.lock();
|
||||
|
||||
for (const auto &recordingApp : getRecordingApps())
|
||||
{
|
||||
@ -471,25 +492,25 @@ namespace Soundux::Objects
|
||||
}
|
||||
}
|
||||
|
||||
movedApplication = std::dynamic_pointer_cast<PulseRecordingApp>(app);
|
||||
movedAppScoped.lock();
|
||||
movedAppScoped = std::dynamic_pointer_cast<PulseRecordingApp>(app);
|
||||
|
||||
return true;
|
||||
}
|
||||
bool PulseAudio::stopSoundInput()
|
||||
{
|
||||
std::unique_lock lock(movedAppMutex);
|
||||
|
||||
bool success = true;
|
||||
if (movedApplication)
|
||||
|
||||
auto movedAppScoped = movedApplication.scoped();
|
||||
if (*movedAppScoped)
|
||||
{
|
||||
for (const auto &recordingApp : getRecordingApps())
|
||||
{
|
||||
auto pulseApp = std::dynamic_pointer_cast<PulseRecordingApp>(recordingApp);
|
||||
|
||||
if (pulseApp->name == movedApplication->name)
|
||||
if (pulseApp->name == movedAppScoped->get()->name)
|
||||
{
|
||||
await(PulseApi::context_move_source_output_by_index(
|
||||
context, pulseApp->id, movedApplication->source,
|
||||
context, pulseApp->id, movedAppScoped->get()->source,
|
||||
[]([[maybe_unused]] pa_context *ctx, int success, void *userData) {
|
||||
if (!success)
|
||||
{
|
||||
@ -506,7 +527,7 @@ namespace Soundux::Objects
|
||||
}
|
||||
}
|
||||
}
|
||||
movedApplication.reset();
|
||||
movedAppScoped->reset();
|
||||
}
|
||||
|
||||
return success;
|
||||
@ -587,7 +608,7 @@ namespace Soundux::Objects
|
||||
bool success = false;
|
||||
|
||||
await(PulseApi::context_set_sink_input_mute(
|
||||
context, loopBackSink, state,
|
||||
context, *loopBackSink, state,
|
||||
+[]([[maybe_unused]] pa_context *ctx, int success, void *userData) {
|
||||
*reinterpret_cast<bool *>(userData) = success;
|
||||
},
|
||||
@ -603,7 +624,7 @@ namespace Soundux::Objects
|
||||
|
||||
bool PulseAudio::isCurrentlyPassingThrough()
|
||||
{
|
||||
return movedPassthroughApplication != nullptr;
|
||||
return **movedPassthroughApplication != nullptr;
|
||||
}
|
||||
|
||||
bool PulseAudio::switchOnConnectPresent()
|
||||
@ -643,5 +664,17 @@ namespace Soundux::Objects
|
||||
},
|
||||
nullptr));
|
||||
}
|
||||
bool PulseAudio::isRunningPipeWire()
|
||||
{
|
||||
//* The stuff we do here does is broken on pipewire-pulse, use the native backend instead.
|
||||
if (serverName.find("PipeWire") != std::string::npos)
|
||||
{
|
||||
Fancy::fancy.logTime().message()
|
||||
<< "Detected PipeWire-Pulse, please use the native pipewire backend" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace Soundux::Objects
|
||||
#endif
|
@ -2,6 +2,8 @@
|
||||
#include "../backend.hpp"
|
||||
#include "forward.hpp"
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <var_guard.hpp>
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
@ -27,25 +29,29 @@ namespace Soundux
|
||||
|
||||
class PulseAudio : public AudioBackend
|
||||
{
|
||||
friend class AudioBackend;
|
||||
|
||||
private:
|
||||
pa_context *context;
|
||||
pa_mainloop *mainloop;
|
||||
pa_mainloop_api *mainloopApi;
|
||||
|
||||
//* ~= The modules we create =~
|
||||
std::uint32_t nullSink;
|
||||
std::uint32_t loopBack;
|
||||
std::uint32_t loopBackSink;
|
||||
std::optional<std::uint32_t> nullSink;
|
||||
std::optional<std::uint32_t> loopBack;
|
||||
std::optional<std::uint32_t> loopBackSink;
|
||||
|
||||
std::uint32_t passthrough;
|
||||
std::uint32_t passthroughSink;
|
||||
std::uint32_t passthroughLoopBack;
|
||||
std::optional<std::uint32_t> passthrough;
|
||||
std::optional<std::uint32_t> passthroughSink;
|
||||
std::optional<std::uint32_t> passthroughLoopBack;
|
||||
//* ~= ~~~~~~~~~~~~~~~~~~~~~ =~
|
||||
|
||||
std::string serverName;
|
||||
std::string defaultSource;
|
||||
std::shared_ptr<PulseRecordingApp> movedApplication;
|
||||
std::shared_ptr<PulsePlaybackApp> movedPassthroughApplication;
|
||||
|
||||
std::mutex movedAppMutex;
|
||||
sxl::var_guard<std::shared_ptr<PulseRecordingApp>> movedApplication;
|
||||
sxl::var_guard<std::shared_ptr<PulsePlaybackApp>> movedPassthroughApplication;
|
||||
|
||||
std::mutex operationMutex;
|
||||
|
||||
void unloadLeftOvers();
|
||||
@ -56,12 +62,15 @@ namespace Soundux
|
||||
void fixPlaybackApps(const std::vector<std::shared_ptr<PlaybackApp>> &);
|
||||
void fixRecordingApps(const std::vector<std::shared_ptr<RecordingApp>> &);
|
||||
|
||||
public:
|
||||
PulseAudio() = default;
|
||||
protected:
|
||||
bool setup() override;
|
||||
|
||||
public:
|
||||
//! Is not ran by default to avoid problems with switch-on-connect
|
||||
bool loadModules();
|
||||
|
||||
void setup() override;
|
||||
void loadModules(); //! Is not ran by default to avoid problems with switch-on-connect
|
||||
void destroy() override;
|
||||
bool isRunningPipeWire();
|
||||
|
||||
bool useAsDefault() override;
|
||||
bool revertDefault() override;
|
21
src/helper/icons/forward.cpp
Normal file
21
src/helper/icons/forward.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "forward.hpp"
|
||||
#include <dlfcn.h>
|
||||
#include <fancy.hpp>
|
||||
|
||||
bool Soundux::LibWnck::setup()
|
||||
{
|
||||
auto *libWnck = dlopen("libwnck-3.so", RTLD_LAZY);
|
||||
if (libWnck)
|
||||
{
|
||||
getWindowPID = reinterpret_cast<decltype(getWindowPID)>(dlsym(libWnck, "wnck_window_get_pid"));
|
||||
getWindowIcon = reinterpret_cast<decltype(getWindowIcon)>(dlsym(libWnck, "wnck_window_get_icon"));
|
||||
forceUpdate = reinterpret_cast<decltype(forceUpdate)>(dlsym(libWnck, "wnck_screen_force_update"));
|
||||
getDefaultScreen = reinterpret_cast<decltype(getDefaultScreen)>(dlsym(libWnck, "wnck_screen_get_default"));
|
||||
getScreenWindows = reinterpret_cast<decltype(getScreenWindows)>(dlsym(libWnck, "wnck_screen_get_windows"));
|
||||
|
||||
Fancy::fancy.logTime().success() << "LibWnck found - Icon support is enabled" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
19
src/helper/icons/forward.hpp
Normal file
19
src/helper/icons/forward.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace LibWnck
|
||||
{
|
||||
struct Screen;
|
||||
struct Window;
|
||||
|
||||
inline Screen *(*getDefaultScreen)();
|
||||
inline void (*forceUpdate)(Screen *);
|
||||
inline int (*getWindowPID)(Window *);
|
||||
inline GList *(*getScreenWindows)(Screen *);
|
||||
inline GdkPixbuf *(*getWindowIcon)(Window *);
|
||||
|
||||
bool setup();
|
||||
} // namespace LibWnck
|
||||
} // namespace Soundux
|
@ -2,67 +2,94 @@
|
||||
#include "icons.hpp"
|
||||
#include <dlfcn.h>
|
||||
#include <fancy.hpp>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <helper/base64/base64.hpp>
|
||||
#include <helper/misc/misc.hpp>
|
||||
#include <optional>
|
||||
#include <regex>
|
||||
|
||||
namespace Soundux::Objects
|
||||
{
|
||||
void IconFetcher::setup()
|
||||
bool IconFetcher::setup()
|
||||
{
|
||||
auto *libWnck = dlopen("libwnck-3.so.0", RTLD_LAZY);
|
||||
if (libWnck)
|
||||
{
|
||||
isAvailable = true;
|
||||
Fancy::fancy.logTime().success() << "LibWnck found - Icon support is enabled" << std::endl;
|
||||
|
||||
Lib::wnckGetDefaultScreen =
|
||||
reinterpret_cast<decltype(Lib::wnckGetDefaultScreen)>(dlsym(libWnck, "wnck_screen_get_default"));
|
||||
Lib::wnckForceUpdate =
|
||||
reinterpret_cast<decltype(Lib::wnckForceUpdate)>(dlsym(libWnck, "wnck_screen_force_update"));
|
||||
Lib::wnckGetScreenWindows =
|
||||
reinterpret_cast<decltype(Lib::wnckGetScreenWindows)>(dlsym(libWnck, "wnck_screen_get_windows"));
|
||||
Lib::wnckGetWindowPID =
|
||||
reinterpret_cast<decltype(Lib::wnckGetWindowPID)>(dlsym(libWnck, "wnck_window_get_pid"));
|
||||
Lib::wnckGetWindowIcon =
|
||||
reinterpret_cast<decltype(Lib::wnckGetWindowIcon)>(dlsym(libWnck, "wnck_window_get_icon"));
|
||||
}
|
||||
else
|
||||
if (!LibWnck::setup())
|
||||
{
|
||||
Fancy::fancy.logTime().message() << "LibWnck was not found - Icon support is not available" << std::endl;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
gdk_init(nullptr, nullptr);
|
||||
screen = Lib::wnckGetDefaultScreen();
|
||||
screen = LibWnck::getDefaultScreen();
|
||||
|
||||
if (!screen)
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "Failed to get default screen!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
std::optional<IconFetcher> IconFetcher::createInstance()
|
||||
{
|
||||
IconFetcher instance;
|
||||
if (instance.setup())
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().failure() << "Could not create IconFetcher instance" << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<int> IconFetcher::getPpid(int pid)
|
||||
{
|
||||
std::filesystem::path path("/proc/" + std::to_string(pid));
|
||||
if (std::filesystem::exists(path))
|
||||
{
|
||||
auto statusFile = path / "status";
|
||||
if (std::filesystem::exists(statusFile) && std::filesystem::is_regular_file(statusFile))
|
||||
{
|
||||
static const std::regex pidRegex(R"(PPid:(\ +|\t)(\d+))");
|
||||
std::ifstream statusStream(statusFile);
|
||||
|
||||
std::string line;
|
||||
std::smatch match;
|
||||
while (std::getline(statusStream, line))
|
||||
{
|
||||
if (std::regex_search(line, match, pidRegex))
|
||||
{
|
||||
if (match[2].matched)
|
||||
{
|
||||
return std::stoi(match[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().warning() << "Failed to find ppid of " >> pid << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().warning() << "Failed to find ppid of " >> pid << ", process does not exist" << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<std::string> IconFetcher::getIcon(int pid, bool recursive)
|
||||
{
|
||||
if (!isAvailable)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (cache.find(pid) != cache.end())
|
||||
{
|
||||
return cache.at(pid);
|
||||
}
|
||||
|
||||
Lib::wnckForceUpdate(screen);
|
||||
auto *windows = Lib::wnckGetScreenWindows(screen);
|
||||
LibWnck::forceUpdate(screen);
|
||||
auto *windows = LibWnck::getScreenWindows(screen);
|
||||
|
||||
for (auto *item = windows; item != nullptr; item = item->next)
|
||||
{
|
||||
auto *window = reinterpret_cast<Lib::WnckWindow *>(item->data);
|
||||
auto _pid = Lib::wnckGetWindowPID(window);
|
||||
auto *window = reinterpret_cast<LibWnck::Window *>(item->data);
|
||||
auto _pid = LibWnck::getWindowPID(window);
|
||||
|
||||
if (pid == _pid)
|
||||
{
|
||||
auto *icon = Lib::wnckGetWindowIcon(window);
|
||||
auto *icon = LibWnck::getWindowIcon(window);
|
||||
|
||||
gsize size = 4096;
|
||||
auto *iconBuff = new gchar[size];
|
||||
@ -90,7 +117,7 @@ namespace Soundux::Objects
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
auto parentProcess = Helpers::getPpid(pid);
|
||||
auto parentProcess = getPpid(pid);
|
||||
if (parentProcess)
|
||||
{
|
||||
auto recursiveResult = getIcon(*parentProcess, false);
|
||||
|
@ -1,32 +1,28 @@
|
||||
#if defined(__linux__)
|
||||
#pragma once
|
||||
#include <gdk/gdk.h>
|
||||
#include "forward.hpp"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace Lib
|
||||
{
|
||||
struct WnckScreen;
|
||||
struct WnckWindow;
|
||||
inline WnckScreen *(*wnckGetDefaultScreen)();
|
||||
inline void (*wnckForceUpdate)(WnckScreen *);
|
||||
inline int (*wnckGetWindowPID)(WnckWindow *);
|
||||
inline GList *(*wnckGetScreenWindows)(WnckScreen *);
|
||||
inline GdkPixbuf *(*wnckGetWindowIcon)(WnckWindow *);
|
||||
} // namespace Lib
|
||||
namespace Objects
|
||||
{
|
||||
class IconFetcher
|
||||
{
|
||||
Lib::WnckScreen *screen;
|
||||
bool isAvailable = false;
|
||||
LibWnck::Screen *screen;
|
||||
std::map<int, std::string> cache;
|
||||
|
||||
private:
|
||||
IconFetcher() = default;
|
||||
|
||||
bool setup();
|
||||
std::optional<int> getPpid(int pid);
|
||||
|
||||
public:
|
||||
void setup();
|
||||
static std::optional<IconFetcher> createInstance();
|
||||
std::optional<std::string> getIcon(int pid, bool recursive = true);
|
||||
};
|
||||
} // namespace Objects
|
||||
|
@ -71,40 +71,6 @@ namespace Soundux
|
||||
WideCharToMultiByte(65001, 0, s.c_str(), -1, &out[0], wsz, nullptr, nullptr);
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
#if defined(__linux__)
|
||||
std::optional<int> Helpers::getPpid(int pid)
|
||||
{
|
||||
std::filesystem::path path("/proc/" + std::to_string(pid));
|
||||
if (std::filesystem::exists(path))
|
||||
{
|
||||
auto statusFile = path / "status";
|
||||
if (std::filesystem::exists(statusFile) && std::filesystem::is_regular_file(statusFile))
|
||||
{
|
||||
static const std::regex pidRegex(R"(PPid:(\ +|\t)(\d+))");
|
||||
std::ifstream statusStream(statusFile);
|
||||
|
||||
std::string line;
|
||||
std::smatch match;
|
||||
while (std::getline(statusStream, line))
|
||||
{
|
||||
if (std::regex_search(line, match, pidRegex))
|
||||
{
|
||||
if (match[2].matched)
|
||||
{
|
||||
return std::stoi(match[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().warning() << "Failed to find ppid of " >> pid << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().warning() << "Failed to find ppid of " >> pid << ", process does not exist" << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
#endif
|
||||
bool Helpers::deleteFile(const std::string &path, bool trash)
|
||||
{
|
||||
|
@ -8,18 +8,14 @@ namespace Soundux
|
||||
namespace Helpers
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
std::wstring widen(const std::string &s);
|
||||
std::string narrow(const std::wstring &s);
|
||||
#endif
|
||||
#if defined(__linux__)
|
||||
std::optional<int> getPpid(int pid);
|
||||
std::wstring widen(const std::string &);
|
||||
std::string narrow(const std::wstring &);
|
||||
#endif
|
||||
bool deleteFile(const std::string &, bool = true);
|
||||
|
||||
bool deleteFile(const std::string &path, bool trash = true);
|
||||
|
||||
bool run(const std::string &command);
|
||||
std::pair<std::string, bool> getResultCompact(const std::string &command);
|
||||
std::pair<std::vector<std::string>, bool> getResult(const std::string &command);
|
||||
bool run(const std::string &);
|
||||
std::pair<std::string, bool> getResultCompact(const std::string &);
|
||||
std::pair<std::vector<std::string>, bool> getResult(const std::string &);
|
||||
|
||||
} // namespace Helpers
|
||||
} // namespace Soundux
|
51
src/helper/queue/queue.cpp
Normal file
51
src/helper/queue/queue.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "queue.hpp"
|
||||
|
||||
namespace Soundux::Objects
|
||||
{
|
||||
void Queue::handle()
|
||||
{
|
||||
std::unique_lock lock(queueMutex);
|
||||
while (!stop)
|
||||
{
|
||||
cv.wait(lock, [&]() { return !queue.empty() || stop; });
|
||||
while (!queue.empty())
|
||||
{
|
||||
auto front = std::move(*queue.begin());
|
||||
|
||||
lock.unlock();
|
||||
front.second();
|
||||
lock.lock();
|
||||
|
||||
queue.erase(front.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Queue::push_unique(std::uint64_t id, std::function<void()> function)
|
||||
{
|
||||
{
|
||||
std::lock_guard lock(queueMutex);
|
||||
if (queue.find(id) != queue.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock lock(queueMutex);
|
||||
queue.emplace(id, std::move(function));
|
||||
lock.unlock();
|
||||
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
Queue::Queue()
|
||||
{
|
||||
handler = std::thread([this] { handle(); });
|
||||
}
|
||||
Queue::~Queue()
|
||||
{
|
||||
stop = true;
|
||||
cv.notify_all();
|
||||
handler.join();
|
||||
}
|
||||
} // namespace Soundux::Objects
|
32
src/helper/queue/queue.hpp
Normal file
32
src/helper/queue/queue.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace Objects
|
||||
{
|
||||
class Queue
|
||||
{
|
||||
std::map<std::uint64_t, std::function<void()>> queue;
|
||||
std::mutex queueMutex;
|
||||
|
||||
std::condition_variable cv;
|
||||
std::atomic<bool> stop;
|
||||
std::thread handler;
|
||||
|
||||
private:
|
||||
void handle();
|
||||
|
||||
public:
|
||||
Queue();
|
||||
~Queue();
|
||||
|
||||
void push_unique(std::uint64_t, std::function<void()>);
|
||||
};
|
||||
} // namespace Objects
|
||||
} // namespace Soundux
|
@ -1,95 +0,0 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <shared_mutex>
|
||||
#include <thread>
|
||||
|
||||
namespace Soundux
|
||||
{
|
||||
namespace Objects
|
||||
{
|
||||
template <typename UID = int> class ProcessingQueue
|
||||
{
|
||||
struct Item
|
||||
{
|
||||
std::shared_ptr<std::atomic<bool>> handled;
|
||||
std::function<void()> callback;
|
||||
UID identifier;
|
||||
};
|
||||
|
||||
private:
|
||||
std::queue<Item> queue;
|
||||
std::mutex queueMutex;
|
||||
|
||||
std::vector<UID> unhandled;
|
||||
std::shared_mutex unhandledMutex;
|
||||
|
||||
std::condition_variable cv;
|
||||
std::atomic<bool> stop;
|
||||
std::thread handler;
|
||||
|
||||
void handle()
|
||||
{
|
||||
std::unique_lock lock(queueMutex);
|
||||
while (!stop)
|
||||
{
|
||||
cv.wait(lock, [&]() { return !queue.empty() || stop; });
|
||||
while (!queue.empty())
|
||||
{
|
||||
auto front = std::move(queue.front());
|
||||
queue.pop();
|
||||
|
||||
lock.unlock();
|
||||
|
||||
front.callback();
|
||||
if (front.handled)
|
||||
{
|
||||
front.handled->store(true);
|
||||
}
|
||||
auto found = std::find(unhandled.begin(), unhandled.end(), front.identifier);
|
||||
if (found != unhandled.end())
|
||||
{
|
||||
unhandled.erase(found);
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void push_unique(const UID &identifier, const std::function<void()> &item)
|
||||
{
|
||||
{
|
||||
std::shared_lock lock(unhandledMutex);
|
||||
if (std::find(unhandled.begin(), unhandled.end(), identifier) != unhandled.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock lock(queueMutex);
|
||||
std::unique_lock lock_(unhandledMutex);
|
||||
|
||||
unhandled.emplace_back(identifier);
|
||||
|
||||
queue.emplace(Item{nullptr, item, identifier});
|
||||
lock.unlock();
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
ProcessingQueue()
|
||||
{
|
||||
handler = std::thread([this] { handle(); });
|
||||
}
|
||||
~ProcessingQueue()
|
||||
{
|
||||
stop = true;
|
||||
cv.notify_all();
|
||||
handler.join();
|
||||
}
|
||||
};
|
||||
} // namespace Objects
|
||||
} // namespace Soundux
|
@ -44,7 +44,7 @@ namespace Soundux::Objects
|
||||
if (json.is_discarded())
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "Failed to parse youtube-dl information" << std::endl;
|
||||
Globals::gGui->onError(ErrorCode::YtdlInvalidJson);
|
||||
Globals::gGui->onError(Enums::ErrorCode::YtdlInvalidJson);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@ -66,14 +66,14 @@ namespace Soundux::Objects
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().warning() << "Failed to get info from youtube-dl" << std::endl;
|
||||
Globals::gGui->onError(ErrorCode::YtdlInformationUnknown);
|
||||
Globals::gGui->onError(Enums::ErrorCode::YtdlInformationUnknown);
|
||||
return std::nullopt;
|
||||
}
|
||||
bool YoutubeDl::download(const std::string &url)
|
||||
{
|
||||
if (!isAvailable)
|
||||
{
|
||||
Globals::gGui->onError(ErrorCode::YtdlNotFound);
|
||||
Globals::gGui->onError(Enums::ErrorCode::YtdlNotFound);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ namespace Soundux::Objects
|
||||
if (!std::regex_match(url, urlRegex))
|
||||
{
|
||||
Fancy::fancy.logTime().warning() << "Bad url " >> url << std::endl;
|
||||
Globals::gGui->onError(ErrorCode::YtdlInvalidUrl);
|
||||
Globals::gGui->onError(Enums::ErrorCode::YtdlInvalidUrl);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -121,7 +121,7 @@ namespace Soundux::Objects
|
||||
return rtn;
|
||||
}
|
||||
|
||||
Globals::gGui->onError(ErrorCode::TabDoesNotExist);
|
||||
Globals::gGui->onError(Enums::ErrorCode::TabDoesNotExist);
|
||||
return false;
|
||||
}
|
||||
void YoutubeDl::killDownload()
|
||||
|
78
src/main.cpp
78
src/main.cpp
@ -1,14 +1,10 @@
|
||||
#include "core/global/globals.hpp"
|
||||
#include "helper/exceptions/crashhandler.hpp"
|
||||
#include "ui/impl/webview/webview.hpp"
|
||||
#include <InstanceGuard.hpp>
|
||||
#include <core/enums/enums.hpp>
|
||||
#include <core/global/globals.hpp>
|
||||
#include <fancy.hpp>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <dlfcn.h>
|
||||
#include <helper/audio/linux/pipewire/pipewire.hpp>
|
||||
#include <helper/audio/linux/pulse/pulse.hpp>
|
||||
#endif
|
||||
#include <helper/audio/linux/backend.hpp>
|
||||
#include <helper/exceptions/crashhandler.hpp>
|
||||
#include <ui/impl/webview/webview.hpp>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "../assets/icon.h"
|
||||
@ -18,6 +14,10 @@ int __stdcall WinMain([[maybe_unused]] HINSTANCE hInstrance, [[maybe_unused]] HI
|
||||
int main()
|
||||
#endif
|
||||
{
|
||||
using namespace Soundux::Globals; // NOLINT
|
||||
using namespace Soundux::Objects; // NOLINT
|
||||
using namespace Soundux::Enums; // NOLINT
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (std::getenv("SOUNDUX_DEBUG"))
|
||||
{
|
||||
@ -46,60 +46,46 @@ int main()
|
||||
return 1;
|
||||
}
|
||||
|
||||
Soundux::Globals::gConfig.load();
|
||||
Soundux::Globals::gData.set(Soundux::Globals::gConfig.data);
|
||||
Soundux::Globals::gSettings = Soundux::Globals::gConfig.settings;
|
||||
gConfig.load();
|
||||
gData.set(gConfig.data);
|
||||
gSettings = gConfig.settings;
|
||||
|
||||
#if defined(__linux__)
|
||||
Soundux::Globals::gIcons.setup();
|
||||
|
||||
if (Soundux::Globals::gSettings.audioBackend == Soundux::Objects::BackendType::PulseAudio)
|
||||
{
|
||||
Soundux::Globals::gAudioBackend = std::make_shared<Soundux::Objects::PulseAudio>();
|
||||
Soundux::Globals::gAudioBackend->setup();
|
||||
|
||||
auto pulseBackend = std::dynamic_pointer_cast<Soundux::Objects::PulseAudio>(Soundux::Globals::gAudioBackend);
|
||||
|
||||
if (pulseBackend && !pulseBackend->switchOnConnectPresent())
|
||||
{
|
||||
pulseBackend->loadModules();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Soundux::Globals::gSettings.audioBackend = Soundux::Objects::BackendType::PipeWire;
|
||||
Soundux::Globals::gAudioBackend = std::make_shared<Soundux::Objects::PipeWire>();
|
||||
Soundux::Globals::gAudioBackend->setup();
|
||||
}
|
||||
|
||||
gIcons = gIcons->createInstance();
|
||||
gAudioBackend = AudioBackend::createInstance(gSettings.audioBackend);
|
||||
#endif
|
||||
Soundux::Globals::gAudio.setup();
|
||||
Soundux::Globals::gYtdl.setup();
|
||||
|
||||
gAudio.setup();
|
||||
gYtdl.setup();
|
||||
|
||||
#if defined(__linux__)
|
||||
if (Soundux::Globals::gSettings.audioBackend == Soundux::Objects::BackendType::PulseAudio &&
|
||||
Soundux::Globals::gConfig.settings.useAsDefaultDevice)
|
||||
if (gAudioBackend && gSettings.audioBackend == BackendType::PulseAudio && gConfig.settings.useAsDefaultDevice)
|
||||
{
|
||||
Soundux::Globals::gAudioBackend->useAsDefault();
|
||||
gAudioBackend->useAsDefault();
|
||||
}
|
||||
#endif
|
||||
|
||||
Soundux::Globals::gGui = std::make_unique<Soundux::Objects::WebView>();
|
||||
Soundux::Globals::gGui->setup();
|
||||
gGui = std::make_unique<Soundux::Objects::WebView>();
|
||||
gGui->setup();
|
||||
|
||||
#if defined(_WIN32)
|
||||
HICON hIcon = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_ICON1));
|
||||
SendMessage(GetActiveWindow(), WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIcon));
|
||||
SendMessage(GetActiveWindow(), WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon));
|
||||
#endif
|
||||
Soundux::Globals::gGui->mainLoop();
|
||||
|
||||
Soundux::Globals::gAudio.destroy();
|
||||
gGui->mainLoop();
|
||||
|
||||
gAudio.destroy();
|
||||
#if defined(__linux__)
|
||||
Soundux::Globals::gAudioBackend->destroy();
|
||||
if (gAudioBackend)
|
||||
{
|
||||
gAudioBackend->destroy();
|
||||
}
|
||||
#endif
|
||||
Soundux::Globals::gConfig.data.set(Soundux::Globals::gData);
|
||||
Soundux::Globals::gConfig.settings = Soundux::Globals::gSettings;
|
||||
Soundux::Globals::gConfig.save();
|
||||
gConfig.data.set(gData);
|
||||
gConfig.settings = gSettings;
|
||||
gConfig.save();
|
||||
|
||||
return 0;
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
#include <cstdint>
|
||||
#include <fancy.hpp>
|
||||
#include <filesystem>
|
||||
#include <helper/audio/linux/pulse/pulse.hpp>
|
||||
#include <helper/audio/linux/pulseaudio/pulseaudio.hpp>
|
||||
#include <helper/json/bindings.hpp>
|
||||
#include <helper/systeminfo/systeminfo.hpp>
|
||||
#include <helper/version/check.hpp>
|
||||
@ -101,17 +101,18 @@ namespace Soundux::Objects
|
||||
webview->expose(Webview::Function("requestHotkey", [](bool state) { Globals::gHotKeys.shouldNotify(state); }));
|
||||
webview->expose(Webview::Function(
|
||||
"setHotkey", [this](std::uint32_t id, const std::vector<int> &keys) { return setHotkey(id, keys); }));
|
||||
webview->expose(Webview::Function("getHotkeySequence",
|
||||
[this](const std::vector<int> &keys) { return getHotkeySequence(keys); }));
|
||||
webview->expose(Webview::Function("getHotkeySequence", [this](const std::vector<int> &keys) {
|
||||
return Globals::gHotKeys.getKeySequence(keys);
|
||||
}));
|
||||
webview->expose(Webview::Function("removeTab", [this](std::uint32_t id) { return removeTab(id); }));
|
||||
webview->expose(Webview::Function("refreshTab", [this](std::uint32_t id) { return refreshTab(id); }));
|
||||
webview->expose(Webview::Function(
|
||||
"moveTabs", [this](const std::vector<int> &newOrder) { return changeTabOrder(newOrder); }));
|
||||
webview->expose(Webview::Function("markFavorite", [this](const std::uint32_t &id, bool favourite) {
|
||||
markFavourite(id, favourite);
|
||||
return getFavouriteIds();
|
||||
webview->expose(Webview::Function("markFavorite", [this](const std::uint32_t &id, bool favorite) {
|
||||
Globals::gData.markFavorite(id, favorite);
|
||||
return Globals::gData.getFavoriteIds();
|
||||
}));
|
||||
webview->expose(Webview::Function("getFavorites", [this] { return getFavouriteIds(); }));
|
||||
webview->expose(Webview::Function("getFavorites", [this] { return Globals::gData.getFavoriteIds(); }));
|
||||
webview->expose(Webview::Function("isYoutubeDLAvailable", []() { return Globals::gYtdl.available(); }));
|
||||
webview->expose(
|
||||
Webview::AsyncFunction("getYoutubeDLInfo", [this](Webview::Promise promise, const std::string &url) {
|
||||
@ -131,7 +132,7 @@ namespace Soundux::Objects
|
||||
webview->expose(Webview::Function("getSystemInfo", []() -> std::string { return SystemInfo::getSummary(); }));
|
||||
webview->expose(Webview::AsyncFunction(
|
||||
"updateCheck", [this](Webview::Promise promise) { promise.resolve(VersionCheck::getStatus()); }));
|
||||
webview->expose(Webview::Function("isOnFavorites", [this](bool state) { isOnFavorites(state); }));
|
||||
webview->expose(Webview::Function("isOnFavorites", [this](bool state) { setIsOnFavorites(state); }));
|
||||
webview->expose(Webview::Function("deleteSound", [this](std::uint32_t id) { return deleteSound(id); }));
|
||||
|
||||
#if !defined(__linux__)
|
||||
@ -337,7 +338,7 @@ namespace Soundux::Objects
|
||||
{
|
||||
webview->callFunction<void>(Webview::JavaScriptFunction("window.downloadProgressed", progress, eta));
|
||||
}
|
||||
void WebView::onError(const ErrorCode &error)
|
||||
void WebView::onError(const Enums::ErrorCode &error)
|
||||
{
|
||||
webview->callFunction<void>(Webview::JavaScriptFunction("window.onError", static_cast<std::uint8_t>(error)));
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ namespace Soundux
|
||||
void onSoundFinished(const PlayingSound &sound) override;
|
||||
void onHotKeyReceived(const std::vector<int> &keys) override;
|
||||
|
||||
void onError(const ErrorCode &error) override;
|
||||
void onError(const Enums::ErrorCode &error) override;
|
||||
void onSoundPlayed(const PlayingSound &sound) override;
|
||||
void onSoundProgressed(const PlayingSound &sound) override;
|
||||
void onDownloadProgressed(float progress, const std::string &eta) override;
|
||||
|
341
src/ui/ui.cpp
341
src/ui/ui.cpp
@ -5,7 +5,7 @@
|
||||
#include <filesystem>
|
||||
#include <helper/audio/linux/backend.hpp>
|
||||
#include <helper/audio/linux/pipewire/pipewire.hpp>
|
||||
#include <helper/audio/linux/pulse/pulse.hpp>
|
||||
#include <helper/audio/linux/pulseaudio/pulseaudio.hpp>
|
||||
#include <helper/misc/misc.hpp>
|
||||
#include <nfd.hpp>
|
||||
#include <optional>
|
||||
@ -101,7 +101,7 @@ namespace Soundux::Objects
|
||||
Fancy::fancy.logTime().warning() << "Path " >> tab.path << " does not exist" << std::endl;
|
||||
return {};
|
||||
}
|
||||
std::optional<Tab> Window::addTab() // NOLINT
|
||||
std::optional<Tab> Window::addTab()
|
||||
{
|
||||
nfdnchar_t *outpath = {};
|
||||
auto result = NFD::PickFolder(outpath, nullptr);
|
||||
@ -132,7 +132,7 @@ namespace Soundux::Objects
|
||||
return tab;
|
||||
}
|
||||
Fancy::fancy.logTime().warning() << "Selected Folder does not exist!" << std::endl;
|
||||
onError(ErrorCode::FolderDoesNotExist);
|
||||
onError(Enums::ErrorCode::FolderDoesNotExist);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
@ -148,7 +148,10 @@ namespace Soundux::Objects
|
||||
}
|
||||
if (Globals::gSettings.muteDuringPlayback)
|
||||
{
|
||||
Globals::gAudioBackend->muteInput(true);
|
||||
if (Globals::gAudioBackend)
|
||||
{
|
||||
Globals::gAudioBackend->muteInput(true);
|
||||
}
|
||||
}
|
||||
if (!Globals::gSettings.pushToTalkKeys.empty())
|
||||
{
|
||||
@ -165,7 +168,7 @@ namespace Soundux::Objects
|
||||
{
|
||||
return *playingSound;
|
||||
}
|
||||
if (!Globals::gSettings.output.empty())
|
||||
if (!Globals::gSettings.output.empty() && Globals::gAudioBackend)
|
||||
{
|
||||
auto moveSuccess = Globals::gAudioBackend->inputSoundTo(
|
||||
Globals::gAudioBackend->getRecordingApp(Globals::gSettings.output));
|
||||
@ -179,7 +182,7 @@ namespace Soundux::Objects
|
||||
|
||||
Fancy::fancy.logTime().failure() << "Failed to move Application " << Globals::gSettings.output
|
||||
<< " to soundux sink for sound " << id << std::endl;
|
||||
onError(ErrorCode::FailedToMoveToSink);
|
||||
onError(Enums::ErrorCode::FailedToMoveToSink);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@ -190,12 +193,12 @@ namespace Soundux::Objects
|
||||
else
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Sound " << id << " not found" << std::endl;
|
||||
onError(ErrorCode::SoundNotFound);
|
||||
onError(Enums::ErrorCode::SoundNotFound);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().failure() << "Failed to play sound " << id << std::endl;
|
||||
onError(ErrorCode::FailedToPlay);
|
||||
onError(Enums::ErrorCode::FailedToPlay);
|
||||
return std::nullopt;
|
||||
}
|
||||
#else
|
||||
@ -247,12 +250,12 @@ namespace Soundux::Objects
|
||||
else
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Sound " << id << " not found" << std::endl;
|
||||
onError(ErrorCode::SoundNotFound);
|
||||
onError(Enums::ErrorCode::SoundNotFound);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().failure() << "Failed to play sound " << id << std::endl;
|
||||
onError(ErrorCode::FailedToPlay);
|
||||
onError(Enums::ErrorCode::FailedToPlay);
|
||||
return std::nullopt;
|
||||
}
|
||||
#endif
|
||||
@ -286,7 +289,7 @@ namespace Soundux::Objects
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().warning() << "Failed to pause sound " << id << std::endl;
|
||||
onError(ErrorCode::FailedToPause);
|
||||
onError(Enums::ErrorCode::FailedToPause);
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<PlayingSound> Window::resumeSound(const std::uint32_t &id)
|
||||
@ -319,7 +322,7 @@ namespace Soundux::Objects
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().warning() << "Failed to resume sound " << id << std::endl;
|
||||
onError(ErrorCode::FailedToResume);
|
||||
onError(Enums::ErrorCode::FailedToResume);
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<PlayingSound> Window::seekSound(const std::uint32_t &id, std::uint64_t seekTo)
|
||||
@ -352,7 +355,7 @@ namespace Soundux::Objects
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().warning() << "Failed to seek sound " << id << " to " << seekTo << std::endl;
|
||||
onError(ErrorCode::FailedToSeek);
|
||||
onError(Enums::ErrorCode::FailedToSeek);
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<PlayingSound> Window::repeatSound(const std::uint32_t &id, bool shouldRepeat)
|
||||
@ -386,7 +389,7 @@ namespace Soundux::Objects
|
||||
|
||||
Fancy::fancy.logTime().failure() << "Failed to set repeat-state of sound " << id << " to " << shouldRepeat
|
||||
<< std::endl;
|
||||
onError(ErrorCode::FailedToRepeat);
|
||||
onError(Enums::ErrorCode::FailedToRepeat);
|
||||
return std::nullopt;
|
||||
}
|
||||
std::vector<Tab> Window::removeTab(const std::uint32_t &id)
|
||||
@ -435,15 +438,18 @@ namespace Soundux::Objects
|
||||
onAllSoundsFinished();
|
||||
|
||||
#if defined(__linux__)
|
||||
if (!Globals::gAudioBackend->stopSoundInput())
|
||||
if (Globals::gAudioBackend)
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current application" << std::endl;
|
||||
onError(ErrorCode::FailedToMoveBack);
|
||||
}
|
||||
if (!Globals::gAudioBackend->stopPassthrough())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current passthrough application" << std::endl;
|
||||
onError(ErrorCode::FailedToMoveBackPassthrough);
|
||||
if (!Globals::gAudioBackend->stopSoundInput())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current application" << std::endl;
|
||||
onError(Enums::ErrorCode::FailedToMoveBack);
|
||||
}
|
||||
if (!Globals::gAudioBackend->stopPassthrough())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current passthrough application" << std::endl;
|
||||
onError(Enums::ErrorCode::FailedToMoveBackPassthrough);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -453,75 +459,66 @@ namespace Soundux::Objects
|
||||
if (settings.audioBackend != Globals::gSettings.audioBackend)
|
||||
{
|
||||
stopSounds(true);
|
||||
Globals::gAudioBackend->destroy();
|
||||
if (settings.audioBackend == BackendType::PulseAudio)
|
||||
{
|
||||
Soundux::Globals::gAudioBackend = std::make_shared<Soundux::Objects::PulseAudio>();
|
||||
Soundux::Globals::gAudioBackend->setup();
|
||||
|
||||
auto pulseBackend =
|
||||
std::dynamic_pointer_cast<Soundux::Objects::PulseAudio>(Soundux::Globals::gAudioBackend);
|
||||
if (pulseBackend)
|
||||
{
|
||||
pulseBackend->loadModules();
|
||||
}
|
||||
}
|
||||
else
|
||||
if (Globals::gAudioBackend)
|
||||
{
|
||||
Soundux::Globals::gAudioBackend = std::make_shared<Soundux::Objects::PipeWire>();
|
||||
Soundux::Globals::gAudioBackend->setup();
|
||||
Globals::gAudioBackend->destroy();
|
||||
}
|
||||
Globals::gAudioBackend = AudioBackend::createInstance(settings.audioBackend);
|
||||
|
||||
Globals::gAudio.setup();
|
||||
}
|
||||
if (!Globals::gAudio.getPlayingSounds().empty())
|
||||
if (Globals::gAudioBackend)
|
||||
{
|
||||
if (settings.muteDuringPlayback && !Globals::gSettings.muteDuringPlayback)
|
||||
if (!Globals::gAudio.getPlayingSounds().empty())
|
||||
{
|
||||
if (!Globals::gAudioBackend->muteInput(true))
|
||||
if (settings.muteDuringPlayback && !Globals::gSettings.muteDuringPlayback)
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to mute input" << std::endl;
|
||||
if (!Globals::gAudioBackend->muteInput(true))
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to mute input" << std::endl;
|
||||
}
|
||||
}
|
||||
else if (!settings.muteDuringPlayback && Globals::gSettings.muteDuringPlayback)
|
||||
{
|
||||
if (!Globals::gAudioBackend->muteInput(false))
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to un-mute input" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!settings.muteDuringPlayback && Globals::gSettings.muteDuringPlayback)
|
||||
if (!settings.useAsDefaultDevice && Globals::gSettings.useAsDefaultDevice)
|
||||
{
|
||||
if (!Globals::gAudioBackend->muteInput(false))
|
||||
if (!Globals::gAudioBackend->revertDefault())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to un-mute input" << std::endl;
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back default source" << std::endl;
|
||||
onError(Enums::ErrorCode::FailedToRevertDefaultSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!settings.useAsDefaultDevice && Globals::gSettings.useAsDefaultDevice)
|
||||
{
|
||||
if (!Globals::gAudioBackend->revertDefault())
|
||||
else if (settings.useAsDefaultDevice && !Globals::gSettings.useAsDefaultDevice)
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back default source" << std::endl;
|
||||
onError(ErrorCode::FailedToRevertDefaultSource);
|
||||
Globals::gSettings.output = "";
|
||||
if (!Globals::gAudioBackend->stopSoundInput())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current application" << std::endl;
|
||||
onError(Enums::ErrorCode::FailedToMoveBack);
|
||||
}
|
||||
if (!Globals::gAudioBackend->useAsDefault())
|
||||
{
|
||||
onError(Enums::ErrorCode::FailedToSetDefaultSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (settings.useAsDefaultDevice && !Globals::gSettings.useAsDefaultDevice)
|
||||
{
|
||||
Globals::gSettings.output = "";
|
||||
if (!Globals::gAudioBackend->stopSoundInput())
|
||||
if (settings.output != Globals::gSettings.output)
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current application" << std::endl;
|
||||
onError(ErrorCode::FailedToMoveBack);
|
||||
}
|
||||
if (!Globals::gAudioBackend->useAsDefault())
|
||||
{
|
||||
onError(ErrorCode::FailedToSetDefaultSource);
|
||||
}
|
||||
}
|
||||
if (settings.output != Globals::gSettings.output)
|
||||
{
|
||||
if (!Globals::gAudioBackend->stopSoundInput())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current application" << std::endl;
|
||||
onError(ErrorCode::FailedToMoveBack);
|
||||
}
|
||||
if (!settings.output.empty() && !Globals::gAudio.getPlayingSounds().empty())
|
||||
{
|
||||
Globals::gAudioBackend->inputSoundTo(Globals::gAudioBackend->getRecordingApp(settings.output));
|
||||
if (!Globals::gAudioBackend->stopSoundInput())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current application" << std::endl;
|
||||
onError(Enums::ErrorCode::FailedToMoveBack);
|
||||
}
|
||||
if (!settings.output.empty() && !Globals::gAudio.getPlayingSounds().empty())
|
||||
{
|
||||
Globals::gAudioBackend->inputSoundTo(Globals::gAudioBackend->getRecordingApp(settings.output));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -544,7 +541,7 @@ namespace Soundux::Objects
|
||||
}
|
||||
}
|
||||
Fancy::fancy.logTime().failure() << "Failed to refresh tab " << id << " tab does not exist" << std::endl;
|
||||
onError(ErrorCode::TabDoesNotExist);
|
||||
onError(Enums::ErrorCode::TabDoesNotExist);
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<Sound> Window::setHotkey(const std::uint32_t &id, const std::vector<int> &hotkeys)
|
||||
@ -557,13 +554,9 @@ namespace Soundux::Objects
|
||||
}
|
||||
Fancy::fancy.logTime().failure() << "Failed to set hotkey for sound " << id << ", sound does not exist"
|
||||
<< std::endl;
|
||||
onError(ErrorCode::FailedToSetHotkey);
|
||||
onError(Enums::ErrorCode::FailedToSetHotkey);
|
||||
return std::nullopt;
|
||||
}
|
||||
std::string Window::getHotkeySequence(const std::vector<int> &hotkeys)
|
||||
{
|
||||
return Globals::gHotKeys.getKeySequence(hotkeys);
|
||||
}
|
||||
std::vector<Tab> Window::changeTabOrder(const std::vector<int> &newOrder)
|
||||
{
|
||||
std::vector<Tab> newTabs;
|
||||
@ -583,33 +576,41 @@ namespace Soundux::Objects
|
||||
//* once. The backend (gPulse.getRecordingStreams()) will work with multiple instances, so we need to filter out
|
||||
//* duplicates here.
|
||||
|
||||
auto streams = Globals::gAudioBackend->getRecordingApps();
|
||||
std::vector<std::shared_ptr<IconRecordingApp>> uniqueStreams;
|
||||
for (auto &stream : streams)
|
||||
{
|
||||
auto item = std::find_if(std::begin(uniqueStreams), std::end(uniqueStreams),
|
||||
[&](const auto &_stream) { return stream->name == _stream->name; });
|
||||
if (stream && item == std::end(uniqueStreams))
|
||||
{
|
||||
auto iconStream = std::make_shared<IconRecordingApp>(*stream);
|
||||
if (auto pulseApp = std::dynamic_pointer_cast<PulseRecordingApp>(stream); pulseApp)
|
||||
{
|
||||
auto icon = Soundux::Globals::gIcons.getIcon(static_cast<int>(pulseApp->pid));
|
||||
if (icon)
|
||||
{
|
||||
iconStream->appIcon = *icon;
|
||||
}
|
||||
}
|
||||
else if (auto pipeWireApp = std::dynamic_pointer_cast<PipeWireRecordingApp>(stream); pipeWireApp)
|
||||
{
|
||||
auto icon = Soundux::Globals::gIcons.getIcon(static_cast<int>(pipeWireApp->pid));
|
||||
if (icon)
|
||||
{
|
||||
iconStream->appIcon = *icon;
|
||||
}
|
||||
}
|
||||
|
||||
uniqueStreams.emplace_back(iconStream);
|
||||
if (Globals::gAudioBackend)
|
||||
{
|
||||
auto streams = Globals::gAudioBackend->getRecordingApps();
|
||||
for (auto &stream : streams)
|
||||
{
|
||||
auto item = std::find_if(std::begin(uniqueStreams), std::end(uniqueStreams),
|
||||
[&](const auto &_stream) { return stream->name == _stream->name; });
|
||||
if (stream && item == std::end(uniqueStreams))
|
||||
{
|
||||
auto iconStream = std::make_shared<IconRecordingApp>(*stream);
|
||||
if (Globals::gIcons)
|
||||
{
|
||||
if (auto pulseApp = std::dynamic_pointer_cast<PulseRecordingApp>(stream); pulseApp)
|
||||
{
|
||||
auto icon = Soundux::Globals::gIcons->getIcon(static_cast<int>(pulseApp->pid));
|
||||
if (icon)
|
||||
{
|
||||
iconStream->appIcon = *icon;
|
||||
}
|
||||
}
|
||||
else if (auto pipeWireApp = std::dynamic_pointer_cast<PipeWireRecordingApp>(stream);
|
||||
pipeWireApp)
|
||||
{
|
||||
auto icon = Soundux::Globals::gIcons->getIcon(static_cast<int>(pipeWireApp->pid));
|
||||
if (icon)
|
||||
{
|
||||
iconStream->appIcon = *icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uniqueStreams.emplace_back(iconStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -617,34 +618,42 @@ namespace Soundux::Objects
|
||||
}
|
||||
std::vector<std::shared_ptr<IconPlaybackApp>> Window::getPlayback()
|
||||
{
|
||||
auto streams = Globals::gAudioBackend->getPlaybackApps();
|
||||
std::vector<std::shared_ptr<IconPlaybackApp>> uniqueStreams;
|
||||
|
||||
for (auto &stream : streams)
|
||||
if (Globals::gAudioBackend)
|
||||
{
|
||||
auto item = std::find_if(std::begin(uniqueStreams), std::end(uniqueStreams),
|
||||
[&](const auto &_stream) { return stream->name == _stream->name; });
|
||||
if (stream && item == std::end(uniqueStreams))
|
||||
{
|
||||
auto iconStream = std::make_shared<IconPlaybackApp>(*stream);
|
||||
if (auto pulseApp = std::dynamic_pointer_cast<PulsePlaybackApp>(stream); pulseApp)
|
||||
{
|
||||
auto icon = Soundux::Globals::gIcons.getIcon(static_cast<int>(pulseApp->pid));
|
||||
if (icon)
|
||||
{
|
||||
iconStream->appIcon = *icon;
|
||||
}
|
||||
}
|
||||
if (auto pipeWireApp = std::dynamic_pointer_cast<PipeWirePlaybackApp>(stream); pipeWireApp)
|
||||
{
|
||||
auto icon = Soundux::Globals::gIcons.getIcon(static_cast<int>(pipeWireApp->pid));
|
||||
if (icon)
|
||||
{
|
||||
iconStream->appIcon = *icon;
|
||||
}
|
||||
}
|
||||
auto streams = Globals::gAudioBackend->getPlaybackApps();
|
||||
|
||||
uniqueStreams.emplace_back(iconStream);
|
||||
for (auto &stream : streams)
|
||||
{
|
||||
auto item = std::find_if(std::begin(uniqueStreams), std::end(uniqueStreams),
|
||||
[&](const auto &_stream) { return stream->name == _stream->name; });
|
||||
if (stream && item == std::end(uniqueStreams))
|
||||
{
|
||||
auto iconStream = std::make_shared<IconPlaybackApp>(*stream);
|
||||
|
||||
if (Globals::gIcons)
|
||||
{
|
||||
if (auto pulseApp = std::dynamic_pointer_cast<PulsePlaybackApp>(stream); pulseApp)
|
||||
{
|
||||
auto icon = Soundux::Globals::gIcons->getIcon(static_cast<int>(pulseApp->pid));
|
||||
if (icon)
|
||||
{
|
||||
iconStream->appIcon = *icon;
|
||||
}
|
||||
}
|
||||
if (auto pipeWireApp = std::dynamic_pointer_cast<PipeWirePlaybackApp>(stream); pipeWireApp)
|
||||
{
|
||||
auto icon = Soundux::Globals::gIcons->getIcon(static_cast<int>(pipeWireApp->pid));
|
||||
if (icon)
|
||||
{
|
||||
iconStream->appIcon = *icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uniqueStreams.emplace_back(iconStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -652,37 +661,44 @@ namespace Soundux::Objects
|
||||
}
|
||||
bool Window::startPassthrough(const std::string &name)
|
||||
{
|
||||
if (Globals::gSettings.output.empty() ||
|
||||
Globals::gAudioBackend->inputSoundTo(Globals::gAudioBackend->getRecordingApp(Globals::gSettings.output)))
|
||||
if (Globals::gAudioBackend)
|
||||
{
|
||||
if (!Globals::gAudioBackend->passthroughFrom(Globals::gAudioBackend->getPlaybackApp(name)))
|
||||
if (Globals::gSettings.output.empty() ||
|
||||
Globals::gAudioBackend->inputSoundTo(
|
||||
Globals::gAudioBackend->getRecordingApp(Globals::gSettings.output)))
|
||||
{
|
||||
Fancy::fancy.logTime().failure()
|
||||
<< "Failed to move application: " << name << " to passthrough" << std::endl;
|
||||
onError(ErrorCode::FailedToStartPassthrough);
|
||||
return false;
|
||||
if (!Globals::gAudioBackend->passthroughFrom(Globals::gAudioBackend->getPlaybackApp(name)))
|
||||
{
|
||||
Fancy::fancy.logTime().failure()
|
||||
<< "Failed to move application: " << name << " to passthrough" << std::endl;
|
||||
onError(Enums::ErrorCode::FailedToStartPassthrough);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().failure() << "Failed to start passthrough for application: " << name << std::endl;
|
||||
onError(ErrorCode::FailedToStartPassthrough);
|
||||
onError(Enums::ErrorCode::FailedToStartPassthrough);
|
||||
return false;
|
||||
}
|
||||
void Window::stopPassthrough()
|
||||
{
|
||||
if (Globals::gAudio.getPlayingSounds().empty())
|
||||
if (Globals::gAudioBackend)
|
||||
{
|
||||
if (!Globals::gAudioBackend->stopSoundInput())
|
||||
if (Globals::gAudio.getPlayingSounds().empty())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current application" << std::endl;
|
||||
onError(ErrorCode::FailedToMoveBack);
|
||||
if (!Globals::gAudioBackend->stopSoundInput())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current application" << std::endl;
|
||||
onError(Enums::ErrorCode::FailedToMoveBack);
|
||||
}
|
||||
}
|
||||
if (!Globals::gAudioBackend->stopPassthrough())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current passthrough application" << std::endl;
|
||||
onError(Enums::ErrorCode::FailedToMoveBackPassthrough);
|
||||
}
|
||||
}
|
||||
if (!Globals::gAudioBackend->stopPassthrough())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current passthrough application" << std::endl;
|
||||
onError(ErrorCode::FailedToMoveBackPassthrough);
|
||||
}
|
||||
}
|
||||
#else
|
||||
@ -705,14 +721,6 @@ namespace Soundux::Objects
|
||||
onAllSoundsFinished();
|
||||
}
|
||||
}
|
||||
std::vector<std::uint32_t> Window::getFavouriteIds()
|
||||
{
|
||||
return Globals::gData.getFavoriteIds();
|
||||
}
|
||||
void Window::markFavourite(const std::uint32_t &id, bool favorite)
|
||||
{
|
||||
Globals::gData.markFavorite(id, favorite);
|
||||
}
|
||||
void Window::onAllSoundsFinished()
|
||||
{
|
||||
if (!Globals::gSettings.pushToTalkKeys.empty())
|
||||
@ -721,16 +729,19 @@ namespace Soundux::Objects
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
if (Globals::gSettings.muteDuringPlayback)
|
||||
if (Globals::gAudioBackend)
|
||||
{
|
||||
Globals::gAudioBackend->muteInput(false);
|
||||
}
|
||||
if (!Globals::gAudioBackend->isCurrentlyPassingThrough())
|
||||
{
|
||||
if (!Globals::gAudioBackend->stopSoundInput())
|
||||
if (Globals::gSettings.muteDuringPlayback)
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current application" << std::endl;
|
||||
onError(ErrorCode::FailedToMoveBack);
|
||||
Globals::gAudioBackend->muteInput(false);
|
||||
}
|
||||
if (!Globals::gAudioBackend->isCurrentlyPassingThrough())
|
||||
{
|
||||
if (!Globals::gAudioBackend->stopSoundInput())
|
||||
{
|
||||
Fancy::fancy.logTime().failure() << "Failed to move back current application" << std::endl;
|
||||
onError(Enums::ErrorCode::FailedToMoveBack);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -742,7 +753,7 @@ namespace Soundux::Objects
|
||||
Globals::gHotKeys.pressKeys(Globals::gSettings.pushToTalkKeys);
|
||||
}
|
||||
}
|
||||
void Window::isOnFavorites(bool state)
|
||||
void Window::setIsOnFavorites(bool state)
|
||||
{
|
||||
Globals::gData.isOnFavorites = state;
|
||||
}
|
||||
@ -753,14 +764,14 @@ namespace Soundux::Objects
|
||||
{
|
||||
if (!Helpers::deleteFile(sound->get().path, Globals::gSettings.deleteToTrash))
|
||||
{
|
||||
onError(ErrorCode::FailedToDelete);
|
||||
onError(Enums::ErrorCode::FailedToDelete);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Fancy::fancy.logTime().failure() << "Sound " << id << " not found" << std::endl;
|
||||
onError(ErrorCode::SoundNotFound);
|
||||
onError(Enums::ErrorCode::SoundNotFound);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <core/global/objects.hpp>
|
||||
#include <core/objects/settings.hpp>
|
||||
#include <helper/audio/audio.hpp>
|
||||
#if defined(__linux__)
|
||||
#include <helper/audio/linux/backend.hpp>
|
||||
@ -46,53 +46,53 @@ namespace Soundux
|
||||
std::string muteDuringPlayback;
|
||||
} translations;
|
||||
|
||||
protected:
|
||||
virtual void onAllSoundsFinished();
|
||||
|
||||
virtual void isOnFavorites(bool);
|
||||
virtual void stopSounds(bool = false);
|
||||
virtual bool stopSound(const std::uint32_t &);
|
||||
protected:
|
||||
virtual std::vector<Sound> getTabContent(const Tab &) const;
|
||||
|
||||
#if defined(__linux__)
|
||||
virtual std::vector<std::shared_ptr<IconRecordingApp>> getOutputs();
|
||||
virtual std::vector<std::shared_ptr<IconPlaybackApp>> getPlayback();
|
||||
#else
|
||||
virtual std::vector<AudioDevice> getOutputs();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void setIsOnFavorites(bool);
|
||||
virtual void changeSettings(const Settings &);
|
||||
virtual bool deleteSound(const std::uint32_t &);
|
||||
|
||||
void stopPassthrough();
|
||||
virtual bool startPassthrough(const std::string &);
|
||||
|
||||
protected:
|
||||
virtual std::optional<Tab> addTab();
|
||||
virtual std::vector<Tab> removeTab(const std::uint32_t &);
|
||||
virtual std::optional<Tab> refreshTab(const std::uint32_t &);
|
||||
virtual std::vector<Tab> changeTabOrder(const std::vector<int> &);
|
||||
|
||||
protected:
|
||||
virtual void stopSounds(bool = false);
|
||||
virtual bool stopSound(const std::uint32_t &);
|
||||
|
||||
protected:
|
||||
virtual std::optional<PlayingSound> playSound(const std::uint32_t &);
|
||||
virtual std::optional<PlayingSound> pauseSound(const std::uint32_t &);
|
||||
virtual std::optional<PlayingSound> resumeSound(const std::uint32_t &);
|
||||
virtual std::optional<PlayingSound> repeatSound(const std::uint32_t &, bool);
|
||||
virtual std::optional<PlayingSound> seekSound(const std::uint32_t &, std::uint64_t);
|
||||
|
||||
virtual std::optional<Sound> setHotkey(const std::uint32_t &, const std::vector<int> &);
|
||||
|
||||
virtual std::string getHotkeySequence(const std::vector<int> &);
|
||||
|
||||
virtual void changeSettings(const Settings &);
|
||||
|
||||
virtual std::vector<std::uint32_t> getFavouriteIds();
|
||||
virtual void markFavourite(const std::uint32_t &, bool);
|
||||
|
||||
virtual std::optional<Tab> addTab();
|
||||
virtual std::vector<Sound> getTabContent(const Tab &) const;
|
||||
virtual std::vector<Tab> changeTabOrder(const std::vector<int> &);
|
||||
|
||||
virtual bool deleteSound(const std::uint32_t &);
|
||||
|
||||
#if defined(__linux__)
|
||||
virtual std::vector<std::shared_ptr<IconRecordingApp>> getOutputs();
|
||||
virtual std::vector<std::shared_ptr<IconPlaybackApp>> getPlayback();
|
||||
|
||||
void stopPassthrough();
|
||||
virtual bool startPassthrough(const std::string &);
|
||||
|
||||
#else
|
||||
virtual std::vector<AudioDevice> getOutputs();
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual ~Window();
|
||||
virtual void setup();
|
||||
virtual void mainLoop() = 0;
|
||||
|
||||
virtual void onError(const ErrorCode &) = 0;
|
||||
virtual void onSoundPlayed(
|
||||
const PlayingSound &); //* This will be called when a sound is played through a hotkey. PlaySound
|
||||
//* will be called before this gets called
|
||||
virtual void onError(const Enums::ErrorCode &) = 0;
|
||||
virtual void onSoundPlayed(const PlayingSound &);
|
||||
virtual void onSoundFinished(const PlayingSound &);
|
||||
virtual void onHotKeyReceived(const std::vector<int> &);
|
||||
virtual void onSoundProgressed(const PlayingSound &) = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user