v0.5 — cleanup orphan code, add MIDI master volume learn, fix turn-deck crashes

- Remove vestigial lufs_meter_widget.py and debug_audio.py
- Delete unused WaveformWidget/WaveformMonitor classes, dead stubs, duplicate code
- Fix turn-mode deck crashes (deck3/deck4 missing from engine.decks, uninitialized manual_elapsed/duration)
- Replace raw print() with logging in main.py
- Add master_volume MIDI learn signal + settings popup entry
- Bump version to 0.5
This commit is contained in:
2026-05-14 22:25:10 +10:00
parent 6067f442b0
commit 89fb7010fb
13 changed files with 356 additions and 752 deletions
+43 -27
View File
@@ -89,6 +89,8 @@ class DeckState:
self.position = 0
self.playing = False
self.lock = threading.Lock()
self.manual_elapsed = 0.0
self.manual_duration = 0.0
def load(self, track: Track):
with self.lock:
@@ -131,15 +133,17 @@ class DeckState:
self.position = self.track.num_samples - 1
def get_elapsed(self) -> str:
if not self.track:
return "00:00"
s = self.position / self.track.sr
with self.lock:
if not self.track:
return "00:00"
s = self.position / self.track.sr
return f"{int(s//60):02d}:{int(s%60):02d}"
def get_remaining(self) -> str:
if not self.track:
return "-00:00"
remaining = max(0, self.track.duration - (self.position / self.track.sr))
with self.lock:
if not self.track:
return "-00:00"
remaining = max(0, self.track.duration - (self.position / self.track.sr))
return f"-{int(remaining//60):02d}:{int(remaining%60):02d}"
@@ -228,6 +232,8 @@ class MasterBus:
self.limiter = Limiter()
self.vu_peak = 0.0
self.lufs_current = -70.0
self.volume = 1.0
self._lufs_accum = 0
class AudioEngine:
@@ -289,6 +295,8 @@ class AudioEngine:
self.decks = {
'deck1': DeckState(),
'deck2': DeckState(),
'deck3': DeckState(),
'deck4': DeckState(),
'cart1': DeckState(),
'cart2': DeckState(),
'cart3': DeckState(),
@@ -301,10 +309,6 @@ class AudioEngine:
def get_channel(self, index: int) -> MixerChannel:
return self.mixer['channels'][index]
def set_channel_name(self, index: int, name: str):
if 0 <= index < NUM_MIXER_CHANNELS:
self.mixer['channels'][index].name = name
def set_channel_volume(self, index: int, volume: float):
if 0 <= index < NUM_MIXER_CHANNELS:
self.mixer['channels'][index].volume = max(0.0, min(1.0, volume))
@@ -327,25 +331,30 @@ class AudioEngine:
buf_L.fill(0.0)
buf_R.fill(0.0)
if not deck.playing or deck.track is None:
with deck.lock:
playing = deck.playing
track = deck.track
pos = deck.position
if not playing or track is None:
return
pos = deck.position
track = deck.track
end = min(pos + frames, track.num_samples)
n = end - pos
if n <= 0:
deck.playing = False
with deck.lock:
deck.playing = False
return
buf_L[:n] = track.audio[0, pos:end]
buf_R[:n] = track.audio[1, pos:end]
deck.position += n
if deck.position >= track.num_samples:
deck.playing = False
deck.position = track.num_samples - 1
with deck.lock:
deck.position += n
if deck.position >= track.num_samples:
deck.playing = False
deck.position = track.num_samples - 1
def _process(self, frames: int):
# ── Digital decks ───────────────────────────────────────────────
@@ -360,21 +369,25 @@ class AudioEngine:
for cart_key in ('cart1', 'cart2', 'cart3', 'cart4'):
deck = self.decks[cart_key]
if not deck.playing or deck.track is None:
with deck.lock:
playing = deck.playing
track = deck.track
pos = deck.position
if not playing or track is None:
continue
pos = deck.position
track = deck.track
end = min(pos + frames, track.num_samples)
n = end - pos
if n <= 0:
deck.playing = False
with deck.lock:
deck.playing = False
continue
buf_L[:n] += track.audio[0, pos:end]
buf_R[:n] += track.audio[1, pos:end]
deck.position += n
if deck.position >= track.num_samples:
deck.playing = False
deck.position = track.num_samples - 1
with deck.lock:
deck.position += n
if deck.position >= track.num_samples:
deck.playing = False
deck.position = track.num_samples - 1
# ── External input monitors (deck3, deck4) ─────────────────────
gate_linear = 10 ** (-46.0 / 20.0)
@@ -456,7 +469,10 @@ class AudioEngine:
master_peak = max(np.max(np.abs(master_audio[0])), np.max(np.abs(master_audio[1])))
self.mixer['master'].vu_peak = master_peak
if frames % 512 < frames:
master = self.mixer['master']
master._lufs_accum += frames
if master._lufs_accum >= 512:
master._lufs_accum -= 512
self._calculate_lufs(master_audio)
def _calculate_lufs(self, audio: np.ndarray):