feat: waveform playhead during audition + docs (v1.5)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4672,6 +4672,12 @@ class MainWindow(QMainWindow):
|
||||
"Audition the current audio area (with the edits applied)")
|
||||
self._btn_audio_play.toggled.connect(self._on_audio_audition)
|
||||
self._audition_proc = None # QProcess while auditioning, else None
|
||||
from PyQt6.QtCore import QElapsedTimer
|
||||
self._audition_start_t = 0.0
|
||||
self._audition_elapsed = QElapsedTimer()
|
||||
self._audition_playhead_timer = QTimer(self)
|
||||
self._audition_playhead_timer.setInterval(50)
|
||||
self._audition_playhead_timer.timeout.connect(self._tick_audition_playhead)
|
||||
|
||||
# ── Merge (crossfade) pane widgets ───────────────────────────
|
||||
self._merge_list = QListWidget()
|
||||
@@ -5164,10 +5170,17 @@ class MainWindow(QMainWindow):
|
||||
|
||||
# ── Changelog ────────────────────────────────────────────
|
||||
|
||||
APP_VERSION = "1.4"
|
||||
APP_VERSION = "1.5"
|
||||
_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)
|
||||
CHANGELOG: list[tuple[str, list[str]]] = [
|
||||
("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 "
|
||||
"range) to set the exact clip, <b>wheel to zoom</b>, and a "
|
||||
"<b>playhead</b> tracks audition. The selection stays in sync with "
|
||||
"the length control both ways.",
|
||||
]),
|
||||
("1.4", [
|
||||
"<b>Merge (crossfade)</b> — a new <b>Merge</b> pane in the Audio tab "
|
||||
"assembles multiple clips into one, crossfading every join. Add the "
|
||||
@@ -6996,6 +7009,14 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
self._play_file(tmp)
|
||||
self._btn_audio_play.setText("■ Stop")
|
||||
# start the playhead timer AFTER _play_file (it calls _stop_audition, which stops this timer)
|
||||
self._audition_start_t = start # start = self._cursor used for the render
|
||||
self._audition_elapsed.restart()
|
||||
self._audition_playhead_timer.start()
|
||||
|
||||
def _tick_audition_playhead(self) -> None:
|
||||
secs = self._audition_elapsed.elapsed() / 1000.0
|
||||
self._wave.set_playhead(self._audition_start_t + secs)
|
||||
|
||||
def _on_audition_error(self, _err) -> None:
|
||||
self._show_status("Audio playback unavailable (ffplay not found)", 4000)
|
||||
@@ -7010,6 +7031,8 @@ class MainWindow(QMainWindow):
|
||||
if proc is not None:
|
||||
proc.deleteLater()
|
||||
self._btn_audio_play.setText("▶ Play")
|
||||
self._audition_playhead_timer.stop()
|
||||
self._wave.set_playhead(None)
|
||||
# Snap the toggle back without re-entering start logic.
|
||||
if self._btn_audio_play.isChecked():
|
||||
self._btn_audio_play.blockSignals(True)
|
||||
@@ -7029,6 +7052,8 @@ class MainWindow(QMainWindow):
|
||||
proc.waitForFinished(100)
|
||||
proc.deleteLater()
|
||||
self._btn_audio_play.setText("▶ Play")
|
||||
self._audition_playhead_timer.stop()
|
||||
self._wave.set_playhead(None)
|
||||
|
||||
def _on_extract_audio(self) -> None:
|
||||
"""Extract an exact-length audio slice starting at the playhead and
|
||||
|
||||
Reference in New Issue
Block a user