Stack Overview - theRealCarneiro/pulsemeeter GitHub Wiki
Pulsemeeter Stack Overview
Pulsemeeter is organized in a layered architecture, from low-level system calls to a modular GTK interface. Each layer has a clear responsibility and communicates upward in a controlled way.
Low-Level Control
pmctl
(Shell Script)
- A shell wrapper around
pw-cli
andpactl
. - Handles direct interaction with PipeWire and PulseAudio.
pmctl.py
(Python Wrapper)
- A Python interface over
pmctl
and thepulsectl
library. - Makes it easier for the rest of the application to work with audio devices programmatically.
Model Layer
The model layer is the core of Pulsemeeter’s logic and device management.
- Device Model: Represents an individual audio device (input/output), exposing its properties and actions (e.g. connect, set volume).
- Device Manager Model: Manages all available devices.
- Pulsemeeter's design depends on being aware of multiple devices.
- This layer is responsible for creating, updating, and coordinating them.
GUI Layer
The GUI is written in GTK and directly communicates with the model layer. There is no separate controller class; instead, logic is distributed through a structured architecture.
GUI Layers
-
Adapters
- Contain logic but no UI.
- Handle internal state and GTK signals.
- Emit custom signals (e.g.
"connect"
,"volume"
) for the upper layer to handle.
-
Widgets
- Inherit from adapters and implement the actual GTK UI.
- Use GTK's built-in signals (like
toggled
,value-changed
) to react to user input. - Instead of directly calling the model, they emit higher-level custom signals via the adapter layer.
-
Application Adapter
- Acts as the controller for the GUI.
- Owns the
DeviceManagerModel
. - Listens to signals emitted by adapters and updates the model accordingly.
- Only this layer is allowed to call the model layer.
- Other components only receive device objects and use them for display.
Signal Flow Example
- A "Connect" button is toggled in a widget.
- The adapter layer catches GTK’s
"toggled"
signal and emits a"connect"
signal with relevant info. - The application adapter listens to this
"connect"
signal and applies the change to the appropriate device in the model. - This decouples the interface logic from the core logic, making it scalable