From 3c8c93d77413ac4f79ff4d36a35f6cc3f9811cfb Mon Sep 17 00:00:00 2001 From: scorpion-26 <58082714+scorpion-26@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:45:29 +0100 Subject: [PATCH] Add Microphone support This commit adds a new widget for the bar: A Microphone widget. It is disabled by default and can can be enabled with AudioInput: true. Implements a flyin as well. https://github.com/scorpion-26/gBar/issues/5 for the ping --- README.md | 6 +++ css/style.css | 81 ++++++++++++++++++++++++-------------- css/style.css.map | 2 +- css/style.scss | 94 ++++++++++++++++++++++++++++++--------------- data/config | 3 ++ src/AudioFlyin.cpp | 84 ++++++++++++++++++++++++++++++++-------- src/AudioFlyin.h | 7 +++- src/Bar.cpp | 96 +++++++++++++++++++++++++++++++++++++--------- src/Config.cpp | 1 + src/Config.h | 3 +- src/PulseAudio.h | 73 ++++++++++++++++++++++++++++------- src/System.cpp | 8 +++- src/System.h | 10 +++-- src/gBar.cpp | 37 +++++++++++------- 14 files changed, 376 insertions(+), 129 deletions(-) diff --git a/README.md b/README.md index efe9d0e..d4e5897 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,10 @@ gBar bar 0 ``` gBar audio [monitor] ``` +*Open microphone flyin, this is equivalent to the audio flyin* +``` +gBar mic [monitor] +``` *Open bluetooth widget* ``` gBar bluetooth [monitor] @@ -68,6 +72,7 @@ Bar: - Time - Bluetooth (BlueZ only) - Audio control +- Microphone control - Power control - Shutdown - Restart @@ -87,6 +92,7 @@ Bluetooth: Audio Flyin: - Audio control +- Microphone control ## Configuration for your system Copy the example config (found under data/config) into ~/.config/gBar/config and modify it to your needs. diff --git a/css/style.css b/css/style.css index 61e55a8..98b89e3 100644 --- a/css/style.css +++ b/css/style.css @@ -42,12 +42,65 @@ margin-right: 6px; } +trough { + border-radius: 3px; + border-width: 1px; + border-style: none; + background-color: #44475a; + margin-top: 2px; + min-width: 4px; + min-height: 4px; +} + +slider { + border-radius: 0%; + border-width: 1px; + border-style: none; + margin: -9px -9px -9px -9px; + min-width: 16px; + min-height: 16px; + background-color: transparent; +} + +highlight { + border-radius: 3px; + border-width: 1px; + border-style: none; + min-height: 6px; +} + .audio-icon { font-size: 24px; color: #ffb86c; margin-right: 12px; } +.audio-volume trough { + background-color: #44475a; +} +.audio-volume slider { + background-color: transparent; +} +.audio-volume highlight { + background-color: #ffb86c; +} + +.mic-icon { + font-size: 24px; + color: #bd93f9; + margin-right: 0px; +} + +.mic-volume trough { + background-color: #44475a; +} +.mic-volume slider { + background-color: transparent; +} +.mic-volume highlight { + background-color: #bd93f9; +} + .bt-num { font-size: 16px; color: #1793D1; @@ -325,32 +378,4 @@ animation-fill-mode: forwards; } -trough { - border-radius: 3px; - border-width: 1px; - border-style: none; - background-color: #44475a; - margin-top: 2px; - min-width: 4px; - min-height: 4px; -} - -slider { - border-radius: 0%; - border-width: 1px; - border-style: none; - margin: -9px -9px -9px -9px; - min-width: 16px; - min-height: 16px; - background-color: transparent; -} - -highlight { - border-radius: 3px; - border-width: 1px; - border-style: none; - min-height: 6px; - background-color: #ffb86c; -} - /*# sourceMappingURL=style.css.map */ diff --git a/css/style.css.map b/css/style.css.map index 95bec90..a42251b 100644 --- a/css/style.css.map +++ b/css/style.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["style.scss"],"names":[],"mappings":"AAmBA;EACI;EACA;;;AASJ;EACI,kBA7BC;EA8BD;;;AAGJ;EACI;EACA;;;AAGJ;EACI,WAxBO;;;AA2BX;EACI;EAEA;EAEA,OA5CO;;;AA8CX;EACI;EAGA,OAlDO;;;AAoDX;EACI;EAGA,OAxDO;;;AA2DX;EACI;EAGA,OAzDE;;;AA+DN;EACI;;;AAGJ;EACI;EACA,OAxEK;EAyEL;;;AAGJ;EACI,WApEO;EAqEP,OAvEK;EAwEL;;;AAEJ;EACI;EACA,OA5EK;EA6EL;;;AAEJ;EACI;EACA,OAjFK;EAkFL;;;AAEJ;EACI;EACA,OAtFK;EAuFL;;;AAGJ;EACI,OAhGK;EAiGL,kBAvGO;EAwGP,WA3FO;;;AA6FX;EACI,OArGK;EAsGL;EACA,WAhGO;;;AAmGX;EACI,OA7GK;EA8GL,kBAlHO;;;AAoHX;EACI,OAjHK;EAkHL;EACA,WA1GO;;;AA6GX;EACI,OAnHK;EAoHL,kBA5HO;;;AA8HX;EACI,OAvHK;EAwHL;EACA,WApHO;;;AAuHX;EACI,OAnIG;EAoIH,kBAtIO;;;AAwIX;EACI,OAvIG;EAwIH;EACA,WA9HO;;;AAiIX;EACI,OA5II;EA6IJ,kBAhJO;EAiJP,WApIO;;;AAsIX;EACI,OAjJI;EAkJJ;EACA,WAzIO;;;AA4IX;EACI,OArJG;EAsJH,kBA3JO;EA4JP,WA/IO;;;AAiJX;EACI,OA1JG;EA2JH;EACA,WApJO;;;AAuJX;EACI,OAlKI;EAmKJ;EACA,WA1JO;;;AA8JX;EACI,OA5KO;;;AA+KX;EACI,OA7KI;;;AAgLR;EACI,OA5KK;;;AA+KT;EACI,OApLK;;;AAuLT;EACI,OAtLK;;;AAyLT;EACI,OAzLE;;;AA6LN;EACI,OArMO;;;AAwMX;EACI,OAtMI;;;AAyMR;EACI,OArMK;;;AAwMT;EACI,OA7MK;;;AAgNT;EACI,OA/MK;;;AAkNT;EACI,OAlNE;;;AAqNN;EACI,OA7NO;EA8NP,WAjNO;;;AAmNX;EACI,OAhOO;EAiOP,WArNO;;;AAuNX;EACI,OAnOG;EAoOH,WAzNO;;;AA2NX;EACI,OAjOK;EAkOL,WA7NO;;;AA+NX;EACI,OA1OI;EA2OJ,WAjOO;;;AAqOX;EACI;IACI;;EAEJ;IACI;;;AAGR;EACI;IACI;;EAEJ;IACI;;;AAIR;EACI;IACI,OA7PC;;EA+PL;IACI,OArQA;;;AAwQR;EACI;IACI,OA1QA;;EA4QJ;IACI,OAxQC;;;AA4QT;EACI,kBAvRC;EAwRD;;;AAEJ;EACI;EACA;EACA;EACA;EACA,OAlRK;;;AAoRT;EACI;EACA;;;AAEJ;EAgBI;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;AAxBA;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI,OA1SF;;;AAwTN;EACI,OAzTE;EA0TF,kBAjUO;EAkUP;EACH;EACA;;;AAED;EAaI,OA3UK;EA4UL,kBApVO;EAqVP;EACA;EACH;EACG;;AAjBA;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;;AAWR;EACI;EACA;EACH;EACG,kBAhWO;EAiWP;EACH;EACA;;;AAGD;EAEC;EACG;EACH;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACG;EACH;EACG;EACH,kBAlXQ","file":"style.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["style.scss"],"names":[],"mappings":"AAmBA;EACI;EACA;;;AASJ;EACI,kBA7BC;EA8BD;;;AAGJ;EACI;EACA;;;AAGJ;EACI,WAxBO;;;AA2BX;EACI;EAEA;EAEA,OA5CO;;;AA8CX;EACI;EAGA,OAlDO;;;AAoDX;EACI;EAGA,OAxDO;;;AA2DX;EACI;EAGA,OAzDE;;;AA+DN;EACI;;;AAIJ;EACI;EACA;EACA;EACA,kBA/EO;EAgFP;EACA;EACA;;;AAGJ;EAEI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA,OArGK;EAsGL;;;AAIA;EACI,kBA/GG;;AAkHP;EACI;;AAGJ;EACI,kBAnHC;;;AAuHT;EACI;EACA,OAvHK;EAwHL;;;AAIA;EACI,kBAnIG;;AAsIP;EACI;;AAGJ;EACI,kBArIC;;;AAyIT;EACI,WAnIO;EAoIP,OAtIK;EAuIL;;;AAEJ;EACI;EACA,OA3IK;EA4IL;;;AAEJ;EACI;EACA,OAhJK;EAiJL;;;AAEJ;EACI;EACA,OArJK;EAsJL;;;AAGJ;EACI,OA/JK;EAgKL,kBAtKO;EAuKP,WA1JO;;;AA4JX;EACI,OApKK;EAqKL;EACA,WA/JO;;;AAkKX;EACI,OA5KK;EA6KL,kBAjLO;;;AAmLX;EACI,OAhLK;EAiLL;EACA,WAzKO;;;AA4KX;EACI,OAlLK;EAmLL,kBA3LO;;;AA6LX;EACI,OAtLK;EAuLL;EACA,WAnLO;;;AAsLX;EACI,OAlMG;EAmMH,kBArMO;;;AAuMX;EACI,OAtMG;EAuMH;EACA,WA7LO;;;AAgMX;EACI,OA3MI;EA4MJ,kBA/MO;EAgNP,WAnMO;;;AAqMX;EACI,OAhNI;EAiNJ;EACA,WAxMO;;;AA2MX;EACI,OApNG;EAqNH,kBA1NO;EA2NP,WA9MO;;;AAgNX;EACI,OAzNG;EA0NH;EACA,WAnNO;;;AAsNX;EACI,OAjOI;EAkOJ;EACA,WAzNO;;;AA6NX;EACI,OA3OO;;;AA8OX;EACI,OA5OI;;;AA+OR;EACI,OA3OK;;;AA8OT;EACI,OAnPK;;;AAsPT;EACI,OArPK;;;AAwPT;EACI,OAxPE;;;AA4PN;EACI,OApQO;;;AAuQX;EACI,OArQI;;;AAwQR;EACI,OApQK;;;AAuQT;EACI,OA5QK;;;AA+QT;EACI,OA9QK;;;AAiRT;EACI,OAjRE;;;AAoRN;EACI,OA5RO;EA6RP,WAhRO;;;AAkRX;EACI,OA/RO;EAgSP,WApRO;;;AAsRX;EACI,OAlSG;EAmSH,WAxRO;;;AA0RX;EACI,OAhSK;EAiSL,WA5RO;;;AA8RX;EACI,OAzSI;EA0SJ,WAhSO;;;AAoSX;EACI;IACI;;EAEJ;IACI;;;AAGR;EACI;IACI;;EAEJ;IACI;;;AAIR;EACI;IACI,OA5TC;;EA8TL;IACI,OApUA;;;AAuUR;EACI;IACI,OAzUA;;EA2UJ;IACI,OAvUC;;;AA2UT;EACI,kBAtVC;EAuVD;;;AAEJ;EACI;EACA;EACA;EACA;EACA,OAjVK;;;AAmVT;EACI;EACA;;;AAEJ;EAgBI;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;AAxBA;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI,OAzWF;;;AAuXN;EACI,OAxXE;EAyXF,kBAhYO;EAiYP;EACH;EACA;;;AAED;EAaI,OA1YK;EA2YL,kBAnZO;EAoZP;EACA;EACH;EACG;;AAjBA;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA","file":"style.css"} \ No newline at end of file diff --git a/css/style.scss b/css/style.scss index ea01a32..dcf02f1 100644 --- a/css/style.scss +++ b/css/style.scss @@ -76,12 +76,75 @@ $textsize: 16px; margin-right: 6px; } +// Common slider settings +trough { + border-radius: 3px; + border-width: 1px; + border-style: none; + background-color: $inactive; + margin-top: 2px; + min-width: 4px; + min-height: 4px; +} + +slider { + // Controls the size of the control area (set border-style to solid to see) + border-radius: 0%; + border-width: 1px; + border-style: none; + margin: -9px -9px -9px -9px; + min-width: 16px; + min-height: 16px; + background-color: transparent; +} + +highlight { + border-radius: 3px; + border-width: 1px; + border-style: none; + min-height: 6px; +} + .audio-icon { font-size: 24px; color: $orange; margin-right: 12px; } +.audio-volume { + trough { + background-color: $inactive; + } + + slider { + background-color: transparent; + } + + highlight { + background-color: $orange; + } +} + +.mic-icon { + font-size: 24px; + color: $purple; + margin-right: 0px; +} + +.mic-volume { + trough { + background-color: $inactive; + } + + slider { + background-color: transparent; + } + + highlight { + background-color: $purple; + } +} + .bt-num { font-size: $textsize; color: $btblue; @@ -348,34 +411,3 @@ $textsize: 16px; margin: 0px 0px 0px 10px; font-size: 18px; } - -// Sliders -trough { - border-radius: 3px; - border-width: 1px; - border-style: none; - background-color: $inactive; - margin-top: 2px; - min-width: 4px; - min-height: 4px; -} - -slider { - // Controls the size of the control area (set border-style to solid to see) - border-radius: 0%; - border-width: 1px; - border-style: none; - margin: -9px -9px -9px -9px; - min-width: 16px; - min-height: 16px; - background-color: transparent; -} - -highlight { - border-radius: 3px; - border-width: 1px; - border-style: none; - min-height: 6px; - background-color: $orange; -} - diff --git a/data/config b/data/config index 273f2f3..f03f1eb 100644 --- a/data/config +++ b/data/config @@ -38,6 +38,9 @@ WorkspaceScrollInvert: false # This can cause issues, if there is not enough space on screen (e.g. when opening the text) CenterTime: true +# Adds a audio input(aka. microphone) widget +AudioInput: false + # Sets the audio slider to be on reveal (Just like the sensors) when true. Only affects the bar. AudioRevealer: false diff --git a/src/AudioFlyin.cpp b/src/AudioFlyin.cpp index 8743110..527bd14 100644 --- a/src/AudioFlyin.cpp +++ b/src/AudioFlyin.cpp @@ -5,6 +5,8 @@ namespace AudioFlyin { namespace DynCtx { + Type type; + Window* win; Slider* slider; Text* icon; @@ -19,28 +21,59 @@ namespace AudioFlyin void OnChangeVolume(Slider&, double value) { - System::SetVolume(value); + if (type == Type::Speaker) + { + System::SetVolumeSink(value); + } + else if (type == Type::Microphone) + { + System::SetVolumeSource(value); + } } TimerResult Main(Box&) { System::AudioInfo info = System::GetAudioInfo(); - if (sliderVal != info.volume || muted != info.muted) + if (type == Type::Speaker) { - // Extend timer - curCloseTime = msOpen + closeTime; - - sliderVal = info.volume; - slider->SetValue(info.volume); - - muted = info.muted; - if (info.muted) + if (sliderVal != info.sinkVolume || muted != info.sinkMuted) { - icon->SetText("ﱝ"); + // Extend timer + curCloseTime = msOpen + closeTime; + + sliderVal = info.sinkVolume; + slider->SetValue(info.sinkVolume); + + muted = info.sinkMuted; + if (info.sinkMuted) + { + icon->SetText("ﱝ"); + } + else + { + icon->SetText("墳"); + } } - else + } + else if (type == Type::Microphone) + { + if (sliderVal != info.sourceVolume || muted != info.sourceMuted) { - icon->SetText("墳"); + // Extend timer + curCloseTime = msOpen + closeTime; + + sliderVal = info.sourceVolume; + slider->SetValue(info.sourceVolume); + + muted = info.sourceMuted; + if (info.sourceMuted) + { + icon->SetText("󰍭"); + } + else + { + icon->SetText("󰍬"); + } } } @@ -66,23 +99,40 @@ namespace AudioFlyin slider->SetOrientation(Orientation::Horizontal); slider->SetHorizontalTransform({100, true, Alignment::Fill}); slider->SetInverted(true); - slider->SetClass("audio-volume"); + if (DynCtx::type == Type::Speaker) + { + slider->SetClass("audio-volume"); + } + else if (DynCtx::type == Type::Microphone) + { + slider->SetClass("mic-volume"); + } slider->OnValueChange(DynCtx::OnChangeVolume); slider->SetRange({0, 1, 0.01}); DynCtx::slider = slider.get(); auto icon = Widget::Create(); - icon->SetClass("audio-icon"); - icon->SetText("墳"); + if (DynCtx::type == Type::Speaker) + { + icon->SetClass("audio-icon"); + icon->SetText("󰕾 "); + } + else if (DynCtx::type == Type::Microphone) + { + icon->SetClass("mic-icon"); + icon->SetText("󰍬"); + } + DynCtx::icon = icon.get(); parent.AddChild(std::move(slider)); parent.AddChild(std::move(icon)); } - void Create(Window& window, int32_t monitor) + void Create(Window& window, int32_t monitor, Type type) { DynCtx::win = &window; + DynCtx::type = type; auto mainWidget = Widget::Create(); mainWidget->SetSpacing({8, false}); mainWidget->SetVerticalTransform({16, true, Alignment::Fill}); diff --git a/src/AudioFlyin.h b/src/AudioFlyin.h index 9764105..55389c1 100644 --- a/src/AudioFlyin.h +++ b/src/AudioFlyin.h @@ -4,5 +4,10 @@ namespace AudioFlyin { - void Create(Window& window, int32_t monitor); + enum class Type + { + Speaker, + Microphone + }; + void Create(Window& window, int32_t monitor, Type type); } diff --git a/src/Bar.cpp b/src/Bar.cpp index 1c0914d..d6accd5 100644 --- a/src/Bar.cpp +++ b/src/Bar.cpp @@ -134,23 +134,43 @@ namespace Bar } #endif - void OnChangeVolume(Slider&, double value) + void OnChangeVolumeSink(Slider&, double value) { - System::SetVolume(value); + System::SetVolumeSink(value); } + void OnChangeVolumeSource(Slider&, double value) + { + System::SetVolumeSource(value); + } + + Slider* audioSlider; + Slider* micSlider; Text* audioIcon; - TimerResult UpdateAudio(Slider& slider) + Text* micIcon; + TimerResult UpdateAudio(Widget&) { System::AudioInfo info = System::GetAudioInfo(); - slider.SetValue(info.volume); - if (info.muted) + audioSlider->SetValue(info.sinkVolume); + if (info.sinkMuted) { - audioIcon->SetText("ﱝ"); + audioIcon->SetText("󰝟"); } else { - audioIcon->SetText("墳"); + audioIcon->SetText("󰕾"); + } + if (Config::Get().audioInput) + { + micSlider->SetValue(info.sourceVolume); + if (info.sourceMuted) + { + micIcon->SetText("󰍭"); + } + else + { + micIcon->SetText("󰍬"); + } } return TimerResult::Ok; } @@ -256,33 +276,59 @@ namespace Bar parent.AddChild(std::move(eventBox)); } + // Handles in and out void WidgetAudio(Widget& parent) { - auto widgetAudioSlider = [](Widget& parent) + enum class AudioType + { + Input, + Output + }; + auto widgetAudioSlider = [](Widget& parent, AudioType type) { auto slider = Widget::Create(); slider->SetOrientation(Orientation::Horizontal); slider->SetHorizontalTransform({100, true, Alignment::Fill}); slider->SetInverted(true); - slider->SetClass("audio-volume"); - slider->AddTimer(DynCtx::UpdateAudio, DynCtx::updateTimeFast); - slider->OnValueChange(DynCtx::OnChangeVolume); + switch (type) + { + case AudioType::Input: + slider->SetClass("mic-volume"); + slider->OnValueChange(DynCtx::OnChangeVolumeSource); + DynCtx::micSlider = slider.get(); + break; + case AudioType::Output: + slider->SetClass("audio-volume"); + slider->OnValueChange(DynCtx::OnChangeVolumeSink); + DynCtx::audioSlider = slider.get(); + break; + } slider->SetRange({0, 1, 0.01}); slider->SetScrollSpeed((double)Config::Get().audioScrollSpeed / 100.); parent.AddChild(std::move(slider)); }; - auto widgetAudioBody = [&widgetAudioSlider](Widget& parent) + auto widgetAudioBody = [&widgetAudioSlider](Widget& parent, AudioType type) { auto box = Widget::Create(); box->SetSpacing({8, false}); box->SetHorizontalTransform({-1, true, Alignment::Right}); { auto icon = Widget::Create(); - icon->SetClass("audio-icon"); - icon->SetText("墳"); - DynCtx::audioIcon = icon.get(); + switch (type) + { + case AudioType::Input: + icon->SetClass("mic-icon"); + icon->SetText("󰍬"); + DynCtx::micIcon = icon.get(); + break; + case AudioType::Output: + icon->SetClass("audio-icon"); + icon->SetText("󰕾 "); + DynCtx::audioIcon = icon.get(); + break; + } if (Config::Get().audioRevealer) { @@ -296,7 +342,7 @@ namespace Bar slideRevealer->SetRevealed(hovered); }); { - widgetAudioSlider(*revealer); + widgetAudioSlider(*revealer, type); } box->AddChild(std::move(revealer)); @@ -304,7 +350,7 @@ namespace Bar else { // Straight forward - widgetAudioSlider(*box); + widgetAudioSlider(*box, type); } box->AddChild(std::move(icon)); @@ -314,16 +360,28 @@ namespace Bar if (Config::Get().audioRevealer) { + // Need an EventBox + if (Config::Get().audioInput) + { + auto eventBox = Widget::Create(); + widgetAudioBody(*eventBox, AudioType::Input); + parent.AddChild(std::move(eventBox)); + } // Need an EventBox auto eventBox = Widget::Create(); - widgetAudioBody(*eventBox); + widgetAudioBody(*eventBox, AudioType::Output); parent.AddChild(std::move(eventBox)); } else { // Just invoke it. - widgetAudioBody(parent); + if (Config::Get().audioInput) + { + widgetAudioBody(parent, AudioType::Input); + } + widgetAudioBody(parent, AudioType::Output); } + parent.AddTimer(DynCtx::UpdateAudio, DynCtx::updateTimeFast); } #ifdef WITH_BLUEZ diff --git a/src/Config.cpp b/src/Config.cpp index 6bdec86..2ed8dff 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -132,6 +132,7 @@ void Config::Load() } AddConfigVar("CenterTime", config.centerTime, lineView, foundProperty); + AddConfigVar("AudioInput", config.audioInput, lineView, foundProperty); AddConfigVar("AudioRevealer", config.audioRevealer, lineView, foundProperty); AddConfigVar("NetworkWidget", config.networkWidget, lineView, foundProperty); AddConfigVar("WorkspaceScrollOnMonitor", config.workspaceScrollOnMonitor, lineView, foundProperty); diff --git a/src/Config.h b/src/Config.h index dfa711a..c54161a 100644 --- a/src/Config.h +++ b/src/Config.h @@ -16,9 +16,10 @@ public: bool centerTime = true; bool audioRevealer = false; + bool audioInput = false; bool networkWidget = true; bool workspaceScrollOnMonitor = true; // Scroll through workspaces on monitor instead of all - bool workspaceScrollInvert = false; // Up = +1, instead of Up = -1 + bool workspaceScrollInvert = false; // Up = +1, instead of Up = -1 // Controls for color progression of the network widget uint32_t minUploadBytes = 0; // Bottom limit of the network widgets upload. Everything below it is considered "under" diff --git a/src/PulseAudio.h b/src/PulseAudio.h index 16f28d1..55c7cf9 100644 --- a/src/PulseAudio.h +++ b/src/PulseAudio.h @@ -2,6 +2,7 @@ #include "System.h" #include "Common.h" +#include #include #include #include @@ -77,20 +78,36 @@ namespace PulseAudio ASSERT(res >= 0, "pa_context_connect failed!"); } + inline double PAVolumeToDouble(const pa_cvolume* volume) + { + double vol = (double)pa_cvolume_avg(volume) / (double)PA_VOLUME_NORM; + // Just round to 1% precision, should be enough + constexpr double precision = 0.01; + double volRounded = std::round(vol * 1 / precision) * precision; + return volRounded; + } + inline System::AudioInfo GetInfo() { - const char* defaultSink = nullptr; + struct ServerInfo + { + const char* defaultSink = nullptr; + const char* defaultSource = nullptr; + } serverInfo; + System::AudioInfo info{}; // 1. Get default sink - auto serverInfo = [](pa_context*, const pa_server_info* info, void* sink) + auto getServerInfo = [](pa_context*, const pa_server_info* info, void* out) { if (!info) return; - *(const char**)sink = info->default_sink_name; + ServerInfo* serverInfo = (ServerInfo*)out; + serverInfo->defaultSink = info->default_sink_name; + serverInfo->defaultSource = info->default_source_name; }; - pa_operation* op = pa_context_get_server_info(context, +serverInfo, &defaultSink); + pa_operation* op = pa_context_get_server_info(context, +getServerInfo, &serverInfo); pa_operation_ref(op); pendingOperations.push_back(op); @@ -103,22 +120,43 @@ namespace PulseAudio System::AudioInfo* out = (System::AudioInfo*)audioInfo; - double vol = (double)pa_cvolume_avg(&info->volume) / (double)PA_VOLUME_NORM; - out->volume = vol; - out->muted = info->mute; - + double vol = PAVolumeToDouble(&info->volume); + out->sinkVolume = vol; + out->sinkMuted = info->mute; }; - op = pa_context_get_sink_info_by_name(context, defaultSink, +sinkInfo, &info); + if (serverInfo.defaultSink) + { + op = pa_context_get_sink_info_by_name(context, serverInfo.defaultSink, +sinkInfo, &info); + pa_operation_ref(op); + pendingOperations.push_back(op); - pa_operation_ref(op); - pendingOperations.push_back(op); + FlushLoop(); + } - FlushLoop(); + auto sourceInfo = [](pa_context*, const pa_source_info* info, int, void* audioInfo) + { + if (!info) + return; + + System::AudioInfo* out = (System::AudioInfo*)audioInfo; + + double vol = PAVolumeToDouble(&info->volume); + out->sourceVolume = vol; + out->sourceMuted = info->mute; + }; + if (serverInfo.defaultSource) + { + op = pa_context_get_source_info_by_name(context, serverInfo.defaultSource, +sourceInfo, &info); + pa_operation_ref(op); + pendingOperations.push_back(op); + + FlushLoop(); + } return info; } - inline void SetVolume(double value) + inline void SetVolumeSink(double value) { double valClamped = std::clamp(value, 0., 1.); // I'm too lazy to implement the c api for this. Since it will only be called when needed and doesn't pipe, it shouldn't be a problem to @@ -127,6 +165,15 @@ namespace PulseAudio system(cmd.c_str()); } + inline void SetVolumeSource(double value) + { + double valClamped = std::clamp(value, 0., 1.); + // I'm too lazy to implement the c api for this. Since it will only be called when needed and doesn't pipe, it shouldn't be a problem to + // fallback for a command + std::string cmd = "pamixer --default-source --set-volume " + std::to_string((uint32_t)(valClamped * 100)); + system(cmd.c_str()); + } + inline void Shutdown() { pa_mainloop_free(mainLoop); diff --git a/src/System.cpp b/src/System.cpp index 5e0b7bb..b2d5969 100644 --- a/src/System.cpp +++ b/src/System.cpp @@ -437,9 +437,13 @@ namespace System { return PulseAudio::GetInfo(); } - void SetVolume(double volume) + void SetVolumeSink(double volume) { - PulseAudio::SetVolume(volume); + PulseAudio::SetVolumeSink(volume); + } + void SetVolumeSource(double volume) + { + PulseAudio::SetVolumeSource(volume); } #ifdef WITH_HYPRLAND diff --git a/src/System.h b/src/System.h index 117c634..e19675e 100644 --- a/src/System.h +++ b/src/System.h @@ -73,11 +73,15 @@ namespace System struct AudioInfo { - double volume; - bool muted; + double sinkVolume; + bool sinkMuted; + + double sourceVolume; + bool sourceMuted; }; AudioInfo GetAudioInfo(); - void SetVolume(double volume); + void SetVolumeSink(double volume); + void SetVolumeSource(double volume); #ifdef WITH_HYPRLAND enum class WorkspaceStatus diff --git a/src/gBar.cpp b/src/gBar.cpp index ea3c781..7747976 100644 --- a/src/gBar.cpp +++ b/src/gBar.cpp @@ -13,6 +13,24 @@ const char* audioTmpFileOpen = "/tmp/gBar__audio"; +static bool flyin = false; +void OpenAudioFlyin(Window& window, int32_t monitor, AudioFlyin::Type type) +{ + flyin = true; + if (access(audioTmpFileOpen, F_OK) != 0) + { + FILE* audioTempFile = fopen(audioTmpFileOpen, "w"); + AudioFlyin::Create(window, monitor, type); + fclose(audioTempFile); + } + else + { + // Already open, close + LOG("Audio flyin already open"); + exit(0); + } +} + int main(int argc, char** argv) { System::Init(); @@ -31,18 +49,11 @@ int main(int argc, char** argv) } else if (strcmp(argv[1], "audio") == 0) { - if (access(audioTmpFileOpen, F_OK) != 0) - { - FILE* audioTempFile = fopen(audioTmpFileOpen, "w"); - AudioFlyin::Create(window, monitor); - fclose(audioTempFile); - } - else - { - // Already open, close - LOG("Audio already open"); - exit(0); - } + OpenAudioFlyin(window, monitor, AudioFlyin::Type::Speaker); + } + else if (strcmp(argv[1], "mic") == 0) + { + OpenAudioFlyin(window, monitor, AudioFlyin::Type::Microphone); } #ifdef WITH_BLUEZ else if (strcmp(argv[1], "bluetooth") == 0) @@ -66,7 +77,7 @@ int main(int argc, char** argv) window.Run(argc, argv); System::FreeResources(); - if (strcmp(argv[1], "audio") == 0) + if (flyin) { remove(audioTmpFileOpen); }