mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-14 17:52:10 -04:00
audio: add per-device max volume limit setting
This commit is contained in:
@@ -121,6 +121,8 @@ Singleton {
|
|||||||
|
|
||||||
property string vpnLastConnected: ""
|
property string vpnLastConnected: ""
|
||||||
|
|
||||||
|
property var deviceMaxVolumes: ({})
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (!isGreeterMode) {
|
if (!isGreeterMode) {
|
||||||
loadSettings();
|
loadSettings();
|
||||||
@@ -1052,6 +1054,35 @@ Singleton {
|
|||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setDeviceMaxVolume(nodeName, maxPercent) {
|
||||||
|
if (!nodeName)
|
||||||
|
return;
|
||||||
|
const updated = Object.assign({}, deviceMaxVolumes);
|
||||||
|
const clamped = Math.max(100, Math.min(200, Math.round(maxPercent)));
|
||||||
|
if (clamped === 100) {
|
||||||
|
delete updated[nodeName];
|
||||||
|
} else {
|
||||||
|
updated[nodeName] = clamped;
|
||||||
|
}
|
||||||
|
deviceMaxVolumes = updated;
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeviceMaxVolume(nodeName) {
|
||||||
|
if (!nodeName)
|
||||||
|
return 100;
|
||||||
|
return deviceMaxVolumes[nodeName] ?? 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeDeviceMaxVolume(nodeName) {
|
||||||
|
if (!nodeName)
|
||||||
|
return;
|
||||||
|
const updated = Object.assign({}, deviceMaxVolumes);
|
||||||
|
delete updated[nodeName];
|
||||||
|
deviceMaxVolumes = updated;
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
function syncWallpaperForCurrentMode() {
|
function syncWallpaperForCurrentMode() {
|
||||||
if (!perModeWallpaper)
|
if (!perModeWallpaper)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -72,7 +72,9 @@ var SPEC = {
|
|||||||
appOverrides: { def: {} },
|
appOverrides: { def: {} },
|
||||||
searchAppActions: { def: true },
|
searchAppActions: { def: true },
|
||||||
|
|
||||||
vpnLastConnected: { def: "" }
|
vpnLastConnected: { def: "" },
|
||||||
|
|
||||||
|
deviceMaxVolumes: { def: {} }
|
||||||
};
|
};
|
||||||
|
|
||||||
function getValidKeys() {
|
function getValidKeys() {
|
||||||
|
|||||||
@@ -451,10 +451,11 @@ Column {
|
|||||||
if (!AudioService.sink || !AudioService.sink.audio)
|
if (!AudioService.sink || !AudioService.sink.audio)
|
||||||
return;
|
return;
|
||||||
let delta = wheelEvent.angleDelta.y;
|
let delta = wheelEvent.angleDelta.y;
|
||||||
|
let maxVol = AudioService.sinkMaxVolume;
|
||||||
let currentVolume = AudioService.sink.audio.volume * 100;
|
let currentVolume = AudioService.sink.audio.volume * 100;
|
||||||
let newVolume;
|
let newVolume;
|
||||||
if (delta > 0)
|
if (delta > 0)
|
||||||
newVolume = Math.min(100, currentVolume + 5);
|
newVolume = Math.min(maxVol, currentVolume + 5);
|
||||||
else
|
else
|
||||||
newVolume = Math.max(0, currentVolume - 5);
|
newVolume = Math.max(0, currentVolume - 5);
|
||||||
AudioService.sink.audio.muted = false;
|
AudioService.sink.audio.muted = false;
|
||||||
|
|||||||
@@ -102,8 +102,8 @@ Rectangle {
|
|||||||
width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
|
width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
|
||||||
enabled: AudioService.sink && AudioService.sink.audio
|
enabled: AudioService.sink && AudioService.sink.audio
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 100
|
maximum: AudioService.sinkMaxVolume
|
||||||
value: AudioService.sink && AudioService.sink.audio ? Math.min(100, Math.round(AudioService.sink.audio.volume * 100)) : 0
|
value: AudioService.sink && AudioService.sink.audio ? Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100)) : 0
|
||||||
showValue: true
|
showValue: true
|
||||||
unit: "%"
|
unit: "%"
|
||||||
valueOverride: actualVolumePercent
|
valueOverride: actualVolumePercent
|
||||||
@@ -136,15 +136,15 @@ Rectangle {
|
|||||||
|
|
||||||
function normalizePinList(value) {
|
function normalizePinList(value) {
|
||||||
if (Array.isArray(value))
|
if (Array.isArray(value))
|
||||||
return value.filter(v => v)
|
return value.filter(v => v);
|
||||||
if (typeof value === "string" && value.length > 0)
|
if (typeof value === "string" && value.length > 0)
|
||||||
return [value]
|
return [value];
|
||||||
return []
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPinnedOutputs() {
|
function getPinnedOutputs() {
|
||||||
const pins = SettingsData.audioOutputDevicePins || {}
|
const pins = SettingsData.audioOutputDevicePins || {};
|
||||||
return normalizePinList(pins["preferredOutput"])
|
return normalizePinList(pins["preferredOutput"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -163,14 +163,14 @@ Rectangle {
|
|||||||
let sorted = [...nodes];
|
let sorted = [...nodes];
|
||||||
sorted.sort((a, b) => {
|
sorted.sort((a, b) => {
|
||||||
// Pinned device first
|
// Pinned device first
|
||||||
const aPinnedIndex = pinnedList.indexOf(a.name)
|
const aPinnedIndex = pinnedList.indexOf(a.name);
|
||||||
const bPinnedIndex = pinnedList.indexOf(b.name)
|
const bPinnedIndex = pinnedList.indexOf(b.name);
|
||||||
if (aPinnedIndex !== -1 || bPinnedIndex !== -1) {
|
if (aPinnedIndex !== -1 || bPinnedIndex !== -1) {
|
||||||
if (aPinnedIndex === -1)
|
if (aPinnedIndex === -1)
|
||||||
return 1
|
return 1;
|
||||||
if (bPinnedIndex === -1)
|
if (bPinnedIndex === -1)
|
||||||
return -1
|
return -1;
|
||||||
return aPinnedIndex - bPinnedIndex
|
return aPinnedIndex - bPinnedIndex;
|
||||||
}
|
}
|
||||||
// Then active device
|
// Then active device
|
||||||
if (a === AudioService.sink && b !== AudioService.sink)
|
if (a === AudioService.sink && b !== AudioService.sink)
|
||||||
@@ -292,24 +292,24 @@ Rectangle {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
const pins = JSON.parse(JSON.stringify(SettingsData.audioOutputDevicePins || {}))
|
const pins = JSON.parse(JSON.stringify(SettingsData.audioOutputDevicePins || {}));
|
||||||
let pinnedList = audioContent.normalizePinList(pins["preferredOutput"])
|
let pinnedList = audioContent.normalizePinList(pins["preferredOutput"]);
|
||||||
const pinIndex = pinnedList.indexOf(modelData.name)
|
const pinIndex = pinnedList.indexOf(modelData.name);
|
||||||
|
|
||||||
if (pinIndex !== -1) {
|
if (pinIndex !== -1) {
|
||||||
pinnedList.splice(pinIndex, 1)
|
pinnedList.splice(pinIndex, 1);
|
||||||
} else {
|
} else {
|
||||||
pinnedList.unshift(modelData.name)
|
pinnedList.unshift(modelData.name);
|
||||||
if (pinnedList.length > audioContent.maxPinnedOutputs)
|
if (pinnedList.length > audioContent.maxPinnedOutputs)
|
||||||
pinnedList = pinnedList.slice(0, audioContent.maxPinnedOutputs)
|
pinnedList = pinnedList.slice(0, audioContent.maxPinnedOutputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pinnedList.length > 0)
|
if (pinnedList.length > 0)
|
||||||
pins["preferredOutput"] = pinnedList
|
pins["preferredOutput"] = pinnedList;
|
||||||
else
|
else
|
||||||
delete pins["preferredOutput"]
|
delete pins["preferredOutput"];
|
||||||
|
|
||||||
SettingsData.set("audioOutputDevicePins", pins)
|
SettingsData.set("audioOutputDevicePins", pins);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ Row {
|
|||||||
width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
|
width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
|
||||||
enabled: defaultSink !== null
|
enabled: defaultSink !== null
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 100
|
maximum: AudioService.sinkMaxVolume
|
||||||
showValue: true
|
showValue: true
|
||||||
unit: "%"
|
unit: "%"
|
||||||
valueOverride: actualVolumePercent
|
valueOverride: actualVolumePercent
|
||||||
@@ -91,7 +91,7 @@ Row {
|
|||||||
Binding {
|
Binding {
|
||||||
target: volumeSlider
|
target: volumeSlider
|
||||||
property: "value"
|
property: "value"
|
||||||
value: defaultSink ? Math.min(100, Math.round(defaultSink.audio.volume * 100)) : 0
|
value: defaultSink ? Math.min(AudioService.sinkMaxVolume, Math.round(defaultSink.audio.volume * 100)) : 0
|
||||||
when: !volumeSlider.isDragging
|
when: !volumeSlider.isDragging
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,8 +140,9 @@ BasePill {
|
|||||||
volumeAccumulator = 0;
|
volumeAccumulator = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxVol = AudioService.sinkMaxVolume;
|
||||||
const currentVolume = AudioService.sink.audio.volume * 100;
|
const currentVolume = AudioService.sink.audio.volume * 100;
|
||||||
const newVolume = delta > 0 ? Math.min(100, currentVolume + step) : Math.max(0, currentVolume - step);
|
const newVolume = delta > 0 ? Math.min(maxVol, currentVolume + step) : Math.max(0, currentVolume - step);
|
||||||
AudioService.sink.audio.muted = false;
|
AudioService.sink.audio.muted = false;
|
||||||
AudioService.sink.audio.volume = newVolume / 100;
|
AudioService.sink.audio.volume = newVolume / 100;
|
||||||
AudioService.playVolumeChangeSoundIfEnabled();
|
AudioService.playVolumeChangeSoundIfEnabled();
|
||||||
|
|||||||
@@ -201,8 +201,9 @@ Item {
|
|||||||
function adjustVolume(step) {
|
function adjustVolume(step) {
|
||||||
if (!volumeAvailable)
|
if (!volumeAvailable)
|
||||||
return;
|
return;
|
||||||
|
const maxVol = usePlayerVolume ? 100 : AudioService.sinkMaxVolume;
|
||||||
const current = Math.round(currentVolume * 100);
|
const current = Math.round(currentVolume * 100);
|
||||||
const newVolume = Math.min(100, Math.max(0, current + step));
|
const newVolume = Math.min(maxVol, Math.max(0, current + step));
|
||||||
|
|
||||||
SessionData.suppressOSDTemporarily();
|
SessionData.suppressOSDTemporarily();
|
||||||
if (usePlayerVolume) {
|
if (usePlayerVolume) {
|
||||||
@@ -778,7 +779,8 @@ Item {
|
|||||||
SessionData.suppressOSDTemporarily();
|
SessionData.suppressOSDTemporarily();
|
||||||
const delta = wheelEvent.angleDelta.y;
|
const delta = wheelEvent.angleDelta.y;
|
||||||
const current = (currentVolume * 100) || 0;
|
const current = (currentVolume * 100) || 0;
|
||||||
const newVolume = delta > 0 ? Math.min(100, current + 5) : Math.max(0, current - 5);
|
const maxVol = usePlayerVolume ? 100 : AudioService.sinkMaxVolume;
|
||||||
|
const newVolume = delta > 0 ? Math.min(maxVol, current + 5) : Math.max(0, current - 5);
|
||||||
|
|
||||||
if (usePlayerVolume) {
|
if (usePlayerVolume) {
|
||||||
activePlayer.volume = newVolume / 100;
|
activePlayer.volume = newVolume / 100;
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ DankOSD {
|
|||||||
x: parent.gap * 2 + Theme.iconSize
|
x: parent.gap * 2 + Theme.iconSize
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 100
|
maximum: AudioService.sinkMaxVolume
|
||||||
enabled: AudioService.sink && AudioService.sink.audio
|
enabled: AudioService.sink && AudioService.sink.audio
|
||||||
showValue: true
|
showValue: true
|
||||||
unit: "%"
|
unit: "%"
|
||||||
@@ -105,7 +105,7 @@ DankOSD {
|
|||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (AudioService.sink && AudioService.sink.audio) {
|
if (AudioService.sink && AudioService.sink.audio) {
|
||||||
value = Math.min(100, Math.round(AudioService.sink.audio.volume * 100));
|
value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ DankOSD {
|
|||||||
|
|
||||||
function onVolumeChanged() {
|
function onVolumeChanged() {
|
||||||
if (volumeSlider && !volumeSlider.pressed) {
|
if (volumeSlider && !volumeSlider.pressed) {
|
||||||
volumeSlider.value = Math.min(100, Math.round(AudioService.sink.audio.volume * 100));
|
volumeSlider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ DankOSD {
|
|||||||
y: gap * 2 + Theme.iconSize
|
y: gap * 2 + Theme.iconSize
|
||||||
|
|
||||||
property bool dragging: false
|
property bool dragging: false
|
||||||
property int value: AudioService.sink && AudioService.sink.audio ? Math.min(100, Math.round(AudioService.sink.audio.volume * 100)) : 0
|
property int value: AudioService.sink && AudioService.sink.audio ? Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100)) : 0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: vertTrack
|
id: vertTrack
|
||||||
@@ -193,7 +193,7 @@ DankOSD {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: vertFill
|
id: vertFill
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: (vertSlider.value / 100) * parent.height
|
height: (vertSlider.value / AudioService.sinkMaxVolume) * parent.height
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
@@ -206,7 +206,7 @@ DankOSD {
|
|||||||
height: 8
|
height: 8
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
y: {
|
y: {
|
||||||
const ratio = vertSlider.value / 100;
|
const ratio = vertSlider.value / AudioService.sinkMaxVolume;
|
||||||
const travel = parent.height - height;
|
const travel = parent.height - height;
|
||||||
return Math.max(0, Math.min(travel, travel * (1 - ratio)));
|
return Math.max(0, Math.min(travel, travel * (1 - ratio)));
|
||||||
}
|
}
|
||||||
@@ -249,8 +249,9 @@ DankOSD {
|
|||||||
|
|
||||||
function updateVolume(mouse) {
|
function updateVolume(mouse) {
|
||||||
if (AudioService.sink && AudioService.sink.audio) {
|
if (AudioService.sink && AudioService.sink.audio) {
|
||||||
|
const maxVol = AudioService.sinkMaxVolume;
|
||||||
const ratio = 1.0 - (mouse.y / height);
|
const ratio = 1.0 - (mouse.y / height);
|
||||||
const volume = Math.max(0, Math.min(100, Math.round(ratio * 100)));
|
const volume = Math.max(0, Math.min(maxVol, Math.round(ratio * maxVol)));
|
||||||
SessionData.suppressOSDTemporarily();
|
SessionData.suppressOSDTemporarily();
|
||||||
AudioService.sink.audio.volume = volume / 100;
|
AudioService.sink.audio.volume = volume / 100;
|
||||||
resetHideTimer();
|
resetHideTimer();
|
||||||
@@ -262,7 +263,7 @@ DankOSD {
|
|||||||
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
|
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
|
||||||
|
|
||||||
function onVolumeChanged() {
|
function onVolumeChanged() {
|
||||||
vertSlider.value = Math.min(100, Math.round(AudioService.sink.audio.volume * 100));
|
vertSlider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -284,7 +285,7 @@ DankOSD {
|
|||||||
if (!useVertical) {
|
if (!useVertical) {
|
||||||
const slider = contentLoader.item.item.children[0].children[1];
|
const slider = contentLoader.item.item.children[0].children[1];
|
||||||
if (slider && slider.value !== undefined) {
|
if (slider && slider.value !== undefined) {
|
||||||
slider.value = Math.min(100, Math.round(AudioService.sink.audio.volume * 100));
|
slider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
|
||||||
import Quickshell.Services.Pipewire
|
import Quickshell.Services.Pipewire
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -131,21 +130,64 @@ Item {
|
|||||||
Repeater {
|
Repeater {
|
||||||
model: root.outputDevices
|
model: root.outputDevices
|
||||||
|
|
||||||
delegate: DeviceAliasRow {
|
delegate: Column {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
width: parent?.width ?? 0
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
deviceNode: modelData
|
DeviceAliasRow {
|
||||||
deviceType: "output"
|
deviceNode: modelData
|
||||||
|
deviceType: "output"
|
||||||
|
|
||||||
onEditRequested: device => {
|
onEditRequested: device => {
|
||||||
root.editingDevice = device;
|
root.editingDevice = device;
|
||||||
root.editingDeviceType = "output";
|
root.editingDeviceType = "output";
|
||||||
root.newDeviceName = AudioService.displayName(device);
|
root.newDeviceName = AudioService.displayName(device);
|
||||||
root.showEditDialog = true;
|
root.showEditDialog = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onResetRequested: device => {
|
||||||
|
AudioService.removeDeviceAlias(device.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onResetRequested: device => {
|
Item {
|
||||||
AudioService.removeDeviceAlias(device.name);
|
width: parent.width
|
||||||
|
height: 36
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: maxVolLabel
|
||||||
|
text: I18n.tr("Max Volume", "Audio settings: maximum volume limit per device")
|
||||||
|
x: Theme.spacingM + Theme.iconSize + Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankSlider {
|
||||||
|
id: maxVolSlider
|
||||||
|
anchors.left: maxVolLabel.right
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
height: 36
|
||||||
|
minimum: 100
|
||||||
|
maximum: 200
|
||||||
|
step: 5
|
||||||
|
showValue: true
|
||||||
|
unit: "%"
|
||||||
|
onSliderValueChanged: newValue => {
|
||||||
|
SessionData.setDeviceMaxVolume(modelData.name, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: maxVolSlider
|
||||||
|
property: "value"
|
||||||
|
value: SessionData.deviceMaxVolumes[modelData.name] ?? 100
|
||||||
|
when: !maxVolSlider.isDragging
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,12 +39,37 @@ Singleton {
|
|||||||
}
|
}
|
||||||
property bool wireplumberReloading: false
|
property bool wireplumberReloading: false
|
||||||
|
|
||||||
|
readonly property int sinkMaxVolume: {
|
||||||
|
const name = sink?.name ?? "";
|
||||||
|
if (!name)
|
||||||
|
return 100;
|
||||||
|
return SessionData.deviceMaxVolumes[name] ?? 100;
|
||||||
|
}
|
||||||
|
|
||||||
signal micMuteChanged
|
signal micMuteChanged
|
||||||
signal audioOutputCycled(string deviceName)
|
signal audioOutputCycled(string deviceName)
|
||||||
signal deviceAliasChanged(string nodeName, string newAlias)
|
signal deviceAliasChanged(string nodeName, string newAlias)
|
||||||
signal wireplumberReloadStarted()
|
signal wireplumberReloadStarted
|
||||||
signal wireplumberReloadCompleted(bool success)
|
signal wireplumberReloadCompleted(bool success)
|
||||||
|
|
||||||
|
function getMaxVolumePercent(node) {
|
||||||
|
if (!node?.name)
|
||||||
|
return 100;
|
||||||
|
return SessionData.deviceMaxVolumes[node.name] ?? 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SessionData
|
||||||
|
function onDeviceMaxVolumesChanged() {
|
||||||
|
if (!root.sink?.audio)
|
||||||
|
return;
|
||||||
|
const maxVol = root.sinkMaxVolume;
|
||||||
|
const currentPercent = Math.round(root.sink.audio.volume * 100);
|
||||||
|
if (currentPercent > maxVol)
|
||||||
|
root.sink.audio.volume = maxVol / 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getAvailableSinks() {
|
function getAvailableSinks() {
|
||||||
return Pipewire.nodes.values.filter(node => node.audio && node.isSink && !node.isStream);
|
return Pipewire.nodes.values.filter(node => node.audio && node.isSink && !node.isStream);
|
||||||
}
|
}
|
||||||
@@ -814,11 +839,11 @@ EOFCONFIG
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setVolume(percentage) {
|
function setVolume(percentage) {
|
||||||
if (!root.sink?.audio) {
|
if (!root.sink?.audio)
|
||||||
return "No audio sink available";
|
return "No audio sink available";
|
||||||
}
|
|
||||||
|
|
||||||
const clampedVolume = Math.max(0, Math.min(100, percentage));
|
const maxVol = root.sinkMaxVolume;
|
||||||
|
const clampedVolume = Math.max(0, Math.min(maxVol, percentage));
|
||||||
root.sink.audio.volume = clampedVolume / 100;
|
root.sink.audio.volume = clampedVolume / 100;
|
||||||
return `Volume set to ${clampedVolume}%`;
|
return `Volume set to ${clampedVolume}%`;
|
||||||
}
|
}
|
||||||
@@ -859,34 +884,32 @@ EOFCONFIG
|
|||||||
}
|
}
|
||||||
|
|
||||||
function increment(step: string): string {
|
function increment(step: string): string {
|
||||||
if (!root.sink?.audio) {
|
if (!root.sink?.audio)
|
||||||
return "No audio sink available";
|
return "No audio sink available";
|
||||||
}
|
|
||||||
|
|
||||||
if (root.sink.audio.muted) {
|
if (root.sink.audio.muted)
|
||||||
root.sink.audio.muted = false;
|
root.sink.audio.muted = false;
|
||||||
}
|
|
||||||
|
|
||||||
|
const maxVol = root.sinkMaxVolume;
|
||||||
const currentVolume = Math.round(root.sink.audio.volume * 100);
|
const currentVolume = Math.round(root.sink.audio.volume * 100);
|
||||||
const stepValue = parseInt(step || "5");
|
const stepValue = parseInt(step || "5");
|
||||||
const newVolume = Math.max(0, Math.min(100, currentVolume + stepValue));
|
const newVolume = Math.max(0, Math.min(maxVol, currentVolume + stepValue));
|
||||||
|
|
||||||
root.sink.audio.volume = newVolume / 100;
|
root.sink.audio.volume = newVolume / 100;
|
||||||
return `Volume increased to ${newVolume}%`;
|
return `Volume increased to ${newVolume}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function decrement(step: string): string {
|
function decrement(step: string): string {
|
||||||
if (!root.sink?.audio) {
|
if (!root.sink?.audio)
|
||||||
return "No audio sink available";
|
return "No audio sink available";
|
||||||
}
|
|
||||||
|
|
||||||
if (root.sink.audio.muted) {
|
if (root.sink.audio.muted)
|
||||||
root.sink.audio.muted = false;
|
root.sink.audio.muted = false;
|
||||||
}
|
|
||||||
|
|
||||||
|
const maxVol = root.sinkMaxVolume;
|
||||||
const currentVolume = Math.round(root.sink.audio.volume * 100);
|
const currentVolume = Math.round(root.sink.audio.volume * 100);
|
||||||
const stepValue = parseInt(step || "5");
|
const stepValue = parseInt(step || "5");
|
||||||
const newVolume = Math.max(0, Math.min(100, currentVolume - stepValue));
|
const newVolume = Math.max(0, Math.min(maxVol, currentVolume - stepValue));
|
||||||
|
|
||||||
root.sink.audio.volume = newVolume / 100;
|
root.sink.audio.volume = newVolume / 100;
|
||||||
return `Volume decreased to ${newVolume}%`;
|
return `Volume decreased to ${newVolume}%`;
|
||||||
@@ -912,7 +935,8 @@ EOFCONFIG
|
|||||||
if (root.sink?.audio) {
|
if (root.sink?.audio) {
|
||||||
const volume = Math.round(root.sink.audio.volume * 100);
|
const volume = Math.round(root.sink.audio.volume * 100);
|
||||||
const muteStatus = root.sink.audio.muted ? " (muted)" : "";
|
const muteStatus = root.sink.audio.muted ? " (muted)" : "";
|
||||||
result += `Output: ${volume}%${muteStatus}\n`;
|
const maxVol = root.sinkMaxVolume;
|
||||||
|
result += `Output: ${volume}%${muteStatus} (max: ${maxVol}%)\n`;
|
||||||
} else {
|
} else {
|
||||||
result += "Output: No sink available\n";
|
result += "Output: No sink available\n";
|
||||||
}
|
}
|
||||||
@@ -928,6 +952,36 @@ EOFCONFIG
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getmaxvolume(): string {
|
||||||
|
return `${root.sinkMaxVolume}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setmaxvolume(percent: string): string {
|
||||||
|
if (!root.sink?.name)
|
||||||
|
return "No audio sink available";
|
||||||
|
const val = parseInt(percent);
|
||||||
|
if (isNaN(val))
|
||||||
|
return "Invalid percentage";
|
||||||
|
SessionData.setDeviceMaxVolume(root.sink.name, val);
|
||||||
|
return `Max volume set to ${SessionData.getDeviceMaxVolume(root.sink.name)}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getmaxvolumefor(nodeName: string): string {
|
||||||
|
if (!nodeName)
|
||||||
|
return "No node name specified";
|
||||||
|
return `${SessionData.getDeviceMaxVolume(nodeName)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setmaxvolumefor(nodeName: string, percent: string): string {
|
||||||
|
if (!nodeName)
|
||||||
|
return "No node name specified";
|
||||||
|
const val = parseInt(percent);
|
||||||
|
if (isNaN(val))
|
||||||
|
return "Invalid percentage";
|
||||||
|
SessionData.setDeviceMaxVolume(nodeName, val);
|
||||||
|
return `Max volume for ${nodeName} set to ${SessionData.getDeviceMaxVolume(nodeName)}%`;
|
||||||
|
}
|
||||||
|
|
||||||
function cycleoutput(): string {
|
function cycleoutput(): string {
|
||||||
const result = root.cycleAudioOutput();
|
const result = root.cycleAudioOutput();
|
||||||
if (!result)
|
if (!result)
|
||||||
|
|||||||
Reference in New Issue
Block a user