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 and pactl.
  • Handles direct interaction with PipeWire and PulseAudio.

pmctl.py (Python Wrapper)

  • A Python interface over pmctl and the pulsectl 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

  1. 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.
  2. 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.
  3. 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