feat: built-in volume management
- Volume slider on every node (green bar, draggable)
- Mute toggle button (M/m) on every node
- Backend: read volume/mute from PipeWire node props
- Backend: POST /api/volume {node_id, volume} to set volume
- Backend: POST /api/mute {node_id, mute} to toggle mute
- Graph JSON includes volume and mute fields per node
- Slider supports drag-to-adjust with mouse
This commit is contained in:
@@ -3,6 +3,10 @@
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <spa/utils/result.h>
|
||||
#include <spa/utils/list.h>
|
||||
#include <spa/param/props.h>
|
||||
#include <spa/pod/pod.h>
|
||||
#include <spa/pod/builder.h>
|
||||
#include <spa/pod/iter.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
@@ -65,16 +69,30 @@ static void on_node_info(void *data, const struct pw_node_info *info) {
|
||||
info = pw_node_info_update((struct pw_node_info*)obj->info, info);
|
||||
obj->info = (void*)info;
|
||||
|
||||
auto *nobj = static_cast<GraphEngine::NodeObj*>(obj);
|
||||
|
||||
if (info && (info->change_mask & PW_NODE_CHANGE_MASK_PROPS)) {
|
||||
auto *nobj = static_cast<GraphEngine::NodeObj*>(obj);
|
||||
if (info->props) {
|
||||
const char *media_name = spa_dict_lookup(info->props, PW_KEY_MEDIA_NAME);
|
||||
if (media_name && strlen(media_name) > 0)
|
||||
nobj->node.media_name = media_name;
|
||||
|
||||
// Read volume from props
|
||||
const char *vol_str = spa_dict_lookup(info->props, "volume");
|
||||
if (vol_str) {
|
||||
nobj->node.volume = pw_properties_parse_float(vol_str);
|
||||
}
|
||||
|
||||
// Read mute from props
|
||||
const char *mute_str = spa_dict_lookup(info->props, "mute");
|
||||
if (mute_str) {
|
||||
nobj->node.mute = pw_properties_parse_bool(mute_str);
|
||||
}
|
||||
}
|
||||
nobj->node.changed = true;
|
||||
nobj->node.ready = true;
|
||||
}
|
||||
|
||||
nobj->node.changed = true;
|
||||
nobj->node.ready = true;
|
||||
}
|
||||
|
||||
static const struct pw_node_events node_events = {
|
||||
@@ -770,4 +788,65 @@ bool GraphEngine::disconnectPorts(uint32_t output_port_id, uint32_t input_port_i
|
||||
return found;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Volume control
|
||||
// ============================================================================
|
||||
|
||||
bool GraphEngine::setNodeVolume(uint32_t node_id, float volume) {
|
||||
if (!m_pw.loop) return false;
|
||||
|
||||
pw_thread_loop_lock(m_pw.loop);
|
||||
|
||||
NodeObj *nobj = findNode(node_id);
|
||||
if (!nobj || !nobj->proxy) {
|
||||
pw_thread_loop_unlock(m_pw.loop);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build Props param with volume
|
||||
uint8_t buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
||||
|
||||
struct spa_pod *param = (struct spa_pod*)spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
|
||||
SPA_PROP_volume, SPA_POD_Float(volume));
|
||||
|
||||
pw_node_set_param((pw_node*)nobj->proxy,
|
||||
SPA_PARAM_Props, 0, param);
|
||||
|
||||
nobj->node.volume = volume;
|
||||
nobj->node.changed = true;
|
||||
|
||||
pw_thread_loop_unlock(m_pw.loop);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GraphEngine::setNodeMute(uint32_t node_id, bool mute) {
|
||||
if (!m_pw.loop) return false;
|
||||
|
||||
pw_thread_loop_lock(m_pw.loop);
|
||||
|
||||
NodeObj *nobj = findNode(node_id);
|
||||
if (!nobj || !nobj->proxy) {
|
||||
pw_thread_loop_unlock(m_pw.loop);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
||||
|
||||
struct spa_pod *param = (struct spa_pod*)spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
|
||||
SPA_PROP_mute, SPA_POD_Bool(mute));
|
||||
|
||||
pw_node_set_param((pw_node*)nobj->proxy,
|
||||
SPA_PARAM_Props, 0, param);
|
||||
|
||||
nobj->node.mute = mute;
|
||||
nobj->node.changed = true;
|
||||
|
||||
pw_thread_loop_unlock(m_pw.loop);
|
||||
return true;
|
||||
}
|
||||
|
||||
// end of graph_engine.cpp
|
||||
|
||||
Reference in New Issue
Block a user