Skip to content

Commit 6b4d6bb

Browse files
authored
Prepare Mini EQ 0.7.3 release (#23)
1 parent 5061636 commit 6b4d6bb

17 files changed

Lines changed: 1044 additions & 233 deletions

.github/workflows/ci.yml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ concurrency:
4444
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
4545
cancel-in-progress: true
4646

47+
env:
48+
PWG_VERSION: "0.3.6"
49+
PWG_REQUIREMENT: "pipewire-gobject>=0.3.6,<0.4"
50+
4751
jobs:
4852
changes:
4953
runs-on: ubuntu-24.04
@@ -291,11 +295,11 @@ jobs:
291295
run: |
292296
python3 -m venv /tmp/mini-eq-pwg-build
293297
/tmp/mini-eq-pwg-build/bin/python -m pip install --upgrade pip
294-
/tmp/mini-eq-pwg-build/bin/python -m pip wheel 'pipewire-gobject>=0.3.5,<0.4' -w /tmp/mini-eq-wheelhouse
298+
/tmp/mini-eq-pwg-build/bin/python -m pip wheel "$PWG_REQUIREMENT" -w /tmp/mini-eq-wheelhouse
295299
296300
python3 -m venv --system-site-packages .venv
297301
.venv/bin/python -m pip install --upgrade pip
298-
.venv/bin/python -m pip install --no-index --find-links /tmp/mini-eq-wheelhouse 'pipewire-gobject>=0.3.5,<0.4'
302+
.venv/bin/python -m pip install --no-index --find-links /tmp/mini-eq-wheelhouse "$PWG_REQUIREMENT"
299303
.venv/bin/python -m pip install -e '.[dev]'
300304
301305
- name: Lint
@@ -323,7 +327,7 @@ jobs:
323327
run: |
324328
python3 -m venv --system-site-packages /tmp/mini-eq-wheel-test
325329
/tmp/mini-eq-wheel-test/bin/python -m pip install --upgrade pip
326-
/tmp/mini-eq-wheel-test/bin/python -m pip install --no-index --find-links /tmp/mini-eq-wheelhouse 'pipewire-gobject>=0.3.5,<0.4'
330+
/tmp/mini-eq-wheel-test/bin/python -m pip install --no-index --find-links /tmp/mini-eq-wheelhouse "$PWG_REQUIREMENT"
327331
/tmp/mini-eq-wheel-test/bin/python -m pip install dist/mini_eq-*.whl
328332
/tmp/mini-eq-wheel-test/bin/mini-eq --help
329333
@@ -369,7 +373,7 @@ jobs:
369373
- name: Check pipewire-gobject GI compatibility
370374
run: |
371375
flatpak run --filesystem="$PWD":ro --command=python3 io.github.bhack.mini-eq \
372-
"$PWD/tools/check_pipewire_gobject.py" --expect-version 0.3.5
376+
"$PWD/tools/check_pipewire_gobject.py" --expect-version "$PWG_VERSION"
373377
374378
- name: Smoke-test Flatpak runtime modules
375379
run: |

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## 0.7.3 - 2026-05-11
4+
5+
- Require pipewire-gobject 0.3.6 and use its synchronous PipeWire registry,
6+
metadata, node, and device APIs for startup and routing state.
7+
- Replace Mini EQ's PipeWire startup and routing sleep loops with bounded sync
8+
or registry-event waits for route params, virtual sink creation, and stream
9+
routing.
10+
- Avoid a headless startup hang when system-wide EQ fails synchronously before
11+
the GLib main loop starts.
12+
313
## 0.7.2 - 2026-05-10
414

515
- Finish GNOME Shell startup notification after Mini EQ's delayed first-window

data/io.github.bhack.mini-eq.metainfo.xml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,27 @@
3333
</description>
3434
<screenshots>
3535
<screenshot type="default">
36-
<image>https://raw.githubusercontent.com/bhack/mini-eq/v0.7.2/docs/screenshots/mini-eq.png</image>
36+
<image>https://raw.githubusercontent.com/bhack/mini-eq/v0.7.3/docs/screenshots/mini-eq.png</image>
3737
<caption>Adjust sound output with equalizer controls</caption>
3838
</screenshot>
3939
<screenshot>
40-
<image>https://raw.githubusercontent.com/bhack/mini-eq/v0.7.2/docs/screenshots/mini-eq-dark.png</image>
40+
<image>https://raw.githubusercontent.com/bhack/mini-eq/v0.7.3/docs/screenshots/mini-eq-dark.png</image>
4141
<caption>Use the equalizer with dark style</caption>
4242
</screenshot>
4343
</screenshots>
4444
<url type="homepage">https://github.com/bhack/mini-eq</url>
4545
<url type="bugtracker">https://github.com/bhack/mini-eq/issues</url>
4646
<url type="vcs-browser">https://github.com/bhack/mini-eq</url>
4747
<releases>
48+
<release version="0.7.3" date="2026-05-11">
49+
<description>
50+
<ul>
51+
<li>Require pipewire-gobject 0.3.6 and use its synchronous PipeWire registry, metadata, node, and device APIs for startup and routing state.</li>
52+
<li>Replace Mini EQ's PipeWire startup and routing sleep loops with bounded sync or registry-event waits for route params, virtual sink creation, and stream routing.</li>
53+
<li>Avoid a headless startup hang when system-wide EQ fails synchronously before the GLib main loop starts.</li>
54+
</ul>
55+
</description>
56+
</release>
4857
<release version="0.7.2" date="2026-05-10">
4958
<description>
5059
<ul>

docs/development.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ Install the Python package after the system packages are present:
8484
```bash
8585
python3 -m venv /tmp/mini-eq-pwg-build
8686
/tmp/mini-eq-pwg-build/bin/python -m pip install --upgrade pip
87-
/tmp/mini-eq-pwg-build/bin/python -m pip wheel 'pipewire-gobject>=0.3.5,<0.4' -w /tmp/mini-eq-wheelhouse
87+
/tmp/mini-eq-pwg-build/bin/python -m pip wheel 'pipewire-gobject>=0.3.6,<0.4' -w /tmp/mini-eq-wheelhouse
8888

8989
python3 -m venv --system-site-packages ~/.local/share/mini-eq/venv
9090
~/.local/share/mini-eq/venv/bin/python -m pip install --upgrade pip
91-
~/.local/share/mini-eq/venv/bin/python -m pip install --no-index --find-links /tmp/mini-eq-wheelhouse 'pipewire-gobject>=0.3.5,<0.4'
91+
~/.local/share/mini-eq/venv/bin/python -m pip install --no-index --find-links /tmp/mini-eq-wheelhouse 'pipewire-gobject>=0.3.6,<0.4'
9292
~/.local/share/mini-eq/venv/bin/python -m pip install mini-eq
9393
~/.local/share/mini-eq/venv/bin/mini-eq --check-deps
9494
~/.local/share/mini-eq/venv/bin/mini-eq

io.github.bhack.mini-eq.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ modules:
124124
sources:
125125
- type: git
126126
url: https://github.com/bhack/pipewire-gobject.git
127-
tag: 0.3.5
128-
commit: b570bc4ff0a53223416fff9a4fc05d367273789d
127+
tag: 0.3.6
128+
commit: 7e20b29865c0a869695bc730bacb68e2f0853077
129129

130130
- python3-dependencies.yaml
131131

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "mini-eq"
7-
version = "0.7.2"
7+
version = "0.7.3"
88
description = "Compact PipeWire system-wide parametric equalizer for Linux desktops."
99
readme = "README.md"
1010
requires-python = ">=3.11"
@@ -40,7 +40,7 @@ classifiers = [
4040
"Topic :: Multimedia :: Sound/Audio :: Analysis",
4141
"Topic :: Multimedia :: Sound/Audio :: Mixers",
4242
]
43-
dependencies = ["numpy>=1.26", "pipewire-gobject>=0.3.5,<0.4"]
43+
dependencies = ["numpy>=1.26", "pipewire-gobject>=0.3.6,<0.4"]
4444

4545
[project.urls]
4646
Homepage = "https://github.com/bhack/mini-eq"

src/mini_eq/app.py

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ def __init__(self, args: Namespace) -> None:
4141
self.dbus_control: MiniEqDbusControl | None = None
4242
self.signal_source_ids: list[int] = []
4343
self.window_present_source_id = 0
44+
self.window_starting = False
45+
self.window_start_hold = False
46+
self.pending_present_when_ready = False
4447
self.background_mode = load_background_mode() or bool(getattr(args, "background", False))
4548
self.start_at_login = load_start_at_login()
4649
self.start_active_at_login = load_start_active_at_login() and self.start_at_login
@@ -94,13 +97,15 @@ def ensure_window(self, *, present: bool) -> None:
9497
self.window.schedule_startup_ready()
9598
return
9699

100+
if self.window_starting:
101+
self.pending_present_when_ready = self.pending_present_when_ready or present
102+
return
103+
97104
controller: SystemWideEqController | None = None
98105
initial_curve_label: str | None = None
99106

100107
try:
101108
controller = SystemWideEqController(self.args.output_sink)
102-
controller.start()
103-
104109
if self.args.import_apo:
105110
controller.import_apo_preset(self.args.import_apo)
106111
initial_curve_label = imported_apo_curve_label(self.args.import_apo)
@@ -110,14 +115,52 @@ def ensure_window(self, *, present: bool) -> None:
110115
raise SystemExit(str(exc)) from exc
111116

112117
self.controller = controller
113-
self.window = MiniEqWindow(self, self.controller, self.args.auto_route, initial_curve_label=initial_curve_label)
114-
self.window.set_icon_name(APP_ICON_NAME)
115-
self.window.present_when_ready = present
116-
self.window.set_visible(False)
117-
self.window.schedule_startup_ready()
118-
if not present:
119-
self.update_background_status()
120-
self.emit_control_state_changed()
118+
self.window_starting = True
119+
self.window_start_hold = True
120+
self.pending_present_when_ready = present
121+
self.hold()
122+
123+
def release_start_hold() -> None:
124+
if not self.window_start_hold:
125+
return
126+
127+
self.window_start_hold = False
128+
self.release()
129+
130+
def on_ready() -> None:
131+
if self.controller is not controller:
132+
return
133+
134+
try:
135+
self.window_starting = False
136+
present_when_ready = self.pending_present_when_ready
137+
self.pending_present_when_ready = False
138+
self.window = MiniEqWindow(
139+
self, self.controller, self.args.auto_route, initial_curve_label=initial_curve_label
140+
)
141+
self.window.set_icon_name(APP_ICON_NAME)
142+
self.window.present_when_ready = present_when_ready
143+
self.window.set_visible(False)
144+
self.window.schedule_startup_ready()
145+
if not present_when_ready:
146+
self.update_background_status()
147+
self.emit_control_state_changed()
148+
finally:
149+
release_start_hold()
150+
151+
def on_error(exc: Exception) -> None:
152+
self.window_starting = False
153+
self.pending_present_when_ready = False
154+
try:
155+
controller.shutdown()
156+
finally:
157+
if self.controller is controller:
158+
self.controller = None
159+
print(str(exc), file=sys.stderr)
160+
release_start_hold()
161+
self.quit()
162+
163+
controller.start(on_ready=on_ready, on_error=on_error)
121164

122165
def present_main_window(self) -> None:
123166
self.ensure_window(present=True)
@@ -220,34 +263,50 @@ def run_headless(args: Namespace) -> int:
220263
duration_ms = 0
221264

222265
controller: SystemWideEqController | None = None
266+
exit_code = 0
267+
loop = GLib.MainLoop()
268+
signal_source_ids = install_unix_signal_handlers(loop.quit)
223269

224270
try:
225271
controller = SystemWideEqController(args.output_sink)
226-
controller.start()
227272

228273
if args.import_apo:
229274
controller.import_apo_preset(args.import_apo)
230275

231-
if args.auto_route:
232-
controller.route_system_audio(True)
233-
234-
loop = GLib.MainLoop()
235-
signal_source_ids = install_unix_signal_handlers(loop.quit)
236-
237276
if duration_ms > 0:
238277
GLib.timeout_add(duration_ms, lambda: (loop.quit(), False)[1])
239278

279+
def on_ready() -> None:
280+
nonlocal exit_code
281+
282+
try:
283+
if args.auto_route:
284+
controller.route_system_audio(True)
285+
except Exception as exc:
286+
exit_code = 1
287+
print(str(exc), file=sys.stderr)
288+
loop.quit()
289+
290+
def on_error(exc: Exception) -> None:
291+
nonlocal exit_code
292+
293+
exit_code = 1
294+
print(str(exc), file=sys.stderr)
295+
loop.quit()
296+
297+
controller.start(on_ready=on_ready, on_error=on_error)
298+
240299
try:
241-
loop.run()
300+
if exit_code == 0:
301+
loop.run()
242302
except KeyboardInterrupt:
243303
pass
244-
finally:
245-
signal_source_ids.clear()
246304
finally:
305+
signal_source_ids.clear()
247306
if controller is not None:
248307
controller.shutdown()
249308

250-
return 0
309+
return exit_code
251310

252311

253312
def install_unix_signal_handlers(callback) -> list[int]:

src/mini_eq/deps.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,21 @@
1212

1313
Status = Literal["ok", "missing", "warning"]
1414

15-
PWG_REQUIRED_VERSION = "0.3.5"
16-
PWG_REQUIRED_VERSION_PARTS = (0, 3, 5)
15+
PWG_REQUIRED_VERSION = "0.3.6"
16+
PWG_REQUIRED_VERSION_PARTS = (0, 3, 6)
1717
PWG_REQUIRED_SYMBOLS = (
1818
"Core.set_pipewire_property",
19+
"Core.sync",
1920
"Device.enum_all_params",
2021
"Device.enum_params",
22+
"Device.enum_params_sync",
2123
"Device.new",
2224
"Device.subscribe_params",
25+
"Device.sync",
26+
"Metadata.sync",
27+
"Node.sync",
2328
"Param.new_props_controls",
29+
"Registry.sync",
2430
"RouteInfo.new_from_param",
2531
"Stream.set_pipewire_property",
2632
)

0 commit comments

Comments
 (0)