feat: editor playback + temp cleanup on close + docs (v1.6)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4112,6 +4112,7 @@ class AudioEditorDialog(QDialog):
|
||||
self._versions: list[str] = [path]
|
||||
self._ver_idx = 0
|
||||
self._temps: set[str] = set() # rendered version temps to clean up
|
||||
self._play_proc = None # ffplay audition process
|
||||
self._clip_dur = 0.0 # true duration of the current version
|
||||
|
||||
self._wave = AudioWaveform()
|
||||
@@ -4250,7 +4251,61 @@ class AudioEditorDialog(QDialog):
|
||||
self._ver_idx += 1
|
||||
self._reload()
|
||||
|
||||
def _on_play(self): pass
|
||||
def _on_play(self, checked: bool) -> None:
|
||||
if not checked:
|
||||
self._stop_play()
|
||||
return
|
||||
from PyQt6.QtCore import QProcess
|
||||
self._stop_play()
|
||||
self._play_proc = QProcess(self)
|
||||
self._play_proc.finished.connect(self._on_play_finished)
|
||||
self._play_proc.errorOccurred.connect(self._on_play_error)
|
||||
self._play_proc.start(
|
||||
_bin("ffplay"),
|
||||
["-autoexit", "-nodisp", "-loglevel", "error", self._current()])
|
||||
self._btn_play.setText("■ Stop")
|
||||
|
||||
def _on_play_error(self, _err) -> None:
|
||||
self._set_status("Playback unavailable (ffplay not found)")
|
||||
self._teardown_play()
|
||||
|
||||
def _on_play_finished(self, *_a) -> None:
|
||||
self._teardown_play()
|
||||
|
||||
def _teardown_play(self) -> None:
|
||||
proc = self._play_proc
|
||||
self._play_proc = None
|
||||
if proc is not None:
|
||||
proc.deleteLater()
|
||||
self._btn_play.setText("▶ Play")
|
||||
if self._btn_play.isChecked():
|
||||
self._btn_play.blockSignals(True)
|
||||
self._btn_play.setChecked(False)
|
||||
self._btn_play.blockSignals(False)
|
||||
|
||||
def _stop_play(self) -> None:
|
||||
proc = self._play_proc
|
||||
self._play_proc = None
|
||||
if proc is not None:
|
||||
try:
|
||||
proc.finished.disconnect()
|
||||
proc.errorOccurred.disconnect()
|
||||
except Exception:
|
||||
pass
|
||||
proc.kill()
|
||||
proc.waitForFinished(100)
|
||||
proc.deleteLater()
|
||||
self._btn_play.setText("▶ Play")
|
||||
|
||||
def closeEvent(self, ev) -> None:
|
||||
self._stop_play()
|
||||
for _t in list(self._temps):
|
||||
try:
|
||||
os.remove(_t)
|
||||
except OSError:
|
||||
pass
|
||||
self._temps.clear()
|
||||
super().closeEvent(ev)
|
||||
|
||||
def _on_save_as(self):
|
||||
path, _sel = QFileDialog.getSaveFileName(
|
||||
@@ -5364,13 +5419,20 @@ class MainWindow(QMainWindow):
|
||||
|
||||
# ── Changelog ────────────────────────────────────────────
|
||||
|
||||
APP_VERSION = "1.5"
|
||||
APP_VERSION = "1.6"
|
||||
_SPLIT_HEADER_H = 22 # deck split-column header height (keep both deck spots in sync)
|
||||
_WAVE_MIN_VIEW = 3.0 # minimum waveform view window (seconds)
|
||||
_MERGE_CURVES = (("Triangular", "tri"), ("Exponential", "exp"),
|
||||
("Logarithmic", "log"), ("Quarter sine", "qsin"),
|
||||
("Half sine", "hsin"))
|
||||
CHANGELOG: list[tuple[str, list[str]]] = [
|
||||
("1.6", [
|
||||
"<b>Destructive clip editor</b> — <b>✎ Edit clip…</b> in the Audio "
|
||||
"tab opens the current area in an editor: drag to select a region "
|
||||
"and <b>Delete</b>, <b>Silence</b>, <b>Reverse</b>, or <b>Trim to "
|
||||
"selection</b>, with <b>undo/redo</b> and audition, then <b>Save "
|
||||
"as…</b>. Non-destructive — the original is never touched.",
|
||||
]),
|
||||
("1.5", [
|
||||
"<b>Interactive waveform</b> — the Audio tab's waveform is now a "
|
||||
"selection surface: <b>drag the in/out handles</b> (or drag a new "
|
||||
|
||||
Reference in New Issue
Block a user