This commit is contained in:
Keith Edmunds 2024-08-02 12:44:55 +01:00
parent 50d1e8bd4a
commit 5f5bb27a5f
2 changed files with 87 additions and 36 deletions

View File

@ -5,6 +5,7 @@ import ctypes
from dataclasses import dataclass, field
import datetime as dt
from enum import auto, Enum
import os
import platform
import threading
from time import sleep
@ -12,6 +13,7 @@ from typing import Any, Optional, NamedTuple
# Third party imports
import numpy as np
from pprint import pprint as pp
import pyqtgraph as pg # type: ignore
import vlc # type: ignore
@ -39,6 +41,7 @@ from helpers import (
)
lock = threading.Lock()
players: dict[int, str] = {}
# Define the VLC callback function type
VLC_LOG_CB = ctypes.CFUNCTYPE(
@ -199,6 +202,7 @@ class _FadeTrack(QRunnable):
if not self.player:
return
log.info("fade starting")
# Reduce volume logarithmically
total_steps = self.fade_seconds * Config.FADEOUT_STEPS_PER_SECOND
db_reduction_per_step = Config.FADEOUT_DB / total_steps
@ -212,7 +216,13 @@ class _FadeTrack(QRunnable):
)
sleep(1 / Config.FADEOUT_STEPS_PER_SECOND)
self.player.stop()
log.info("fade ended")
if self.player:
log.info(f"Releasing {self.player=}")
self.player.release() # Release resources
del players[id(self.player)]
pp(players)
self.player = None # Clear the reference
class _Music:
@ -275,27 +285,6 @@ class _Music:
else:
self.start_dt = dt.datetime.now() - dt.timedelta(milliseconds=ms)
def stop(self) -> None:
"""Immediately stop playing"""
log.debug(f"Music[{self.name}].stop()")
self.start_dt = None
if not self.player:
return
p = self.player
self.player = None
self.start_dt = None
with lock:
p.stop()
p.release()
self.player_count -= 1
log.debug(f"_Music.stop: Releasing player {p=}, {self.player_count=}")
p = None
def fade(self, fade_seconds: int) -> None:
"""
Fade the currently playing track.
@ -318,6 +307,13 @@ class _Music:
# started without interfering here
with lock:
p = self.player
# Connect to the end-of-playback event
p.event_manager().event_attach(
vlc.EventType.MediaPlayerEndReached, self.on_playback_end
)
del players[id(self.player)]
players[id(p)] = f"From fade {self.player=}"
pp(players)
self.player = None
pool = QThreadPool.globalInstance()
@ -367,6 +363,18 @@ class _Music:
< dt.timedelta(microseconds=Config.PLAY_SETTLE)
)
def on_playback_end(self, *args, **kwargs):
"""
Release player when playing has ended
"""
if self.player:
log.info(f"Releasing {self.player=}")
del players[id(self.player)]
pp(players)
self.player.release() # Release resources
self.player = None # Clear the reference
def play(
self,
path: str,
@ -394,6 +402,15 @@ class _Music:
show_warning(None, "Error loading file", f"Cannot play file ({path})")
return
self.player = media.player_new_from_media()
log.info(f"Created {self.player=}")
players[id(self.player)] = os.path.basename(path)
pp(players)
# Connect to the end-of-playback event
self.player.event_manager().event_attach(
vlc.EventType.MediaPlayerEndReached, self.on_playback_end
)
if self.player:
_ = self.player.play()
self.set_volume(self.max_volume)
@ -456,6 +473,31 @@ class _Music:
log.debug(f"Reset from {volume=}")
sleep(0.1)
def stop(self) -> None:
"""Immediately stop playing"""
log.debug(f"Music[{self.name}].stop()")
self.start_dt = None
if not self.player:
return
p = self.player
del players[id(self.player)]
players[id(p)] = f"From stop {self.player=}"
pp(players)
self.player = None
self.start_dt = None
with lock:
p.stop()
p.release()
del players[id(p)]
pp(players)
log.info(f"_Music.stop: Releasing player {p=}, {self.player_count=}")
p = None
@singleton
@dataclass
@ -514,7 +556,9 @@ class RowAndTrack:
self.fade_at = playlist_row.track.fade_at
self.intro = playlist_row.track.intro
if playlist_row.track.playdates:
self.lastplayed = max([a.lastplayed for a in playlist_row.track.playdates])
self.lastplayed = max(
[a.lastplayed for a in playlist_row.track.playdates]
)
else:
self.lastplayed = Config.EPOCH
self.path = playlist_row.track.path
@ -544,6 +588,7 @@ class RowAndTrack:
self.start_time: Optional[dt.datetime] = None
# Other object initialisation
self.music = _Music(name=Config.VLC_MAIN_PLAYER_NAME)
self.signals = MusicMusterSignals()
def __repr__(self) -> str:
@ -564,7 +609,7 @@ class RowAndTrack:
if self.end_of_track_signalled:
return
if not self.player.is_playing():
if not self.music.is_playing():
self.start_time = None
if self.fade_graph:
self.fade_graph.clear()
@ -596,15 +641,15 @@ class RowAndTrack:
"""
if enable:
self.player.set_volume(volume=Config.VLC_VOLUME_DROP3db, set_default=False)
self.music.set_volume(volume=Config.VLC_VOLUME_DROP3db, set_default=False)
else:
self.player.set_volume(volume=Config.VLC_VOLUME_DEFAULT, set_default=False)
self.music.set_volume(volume=Config.VLC_VOLUME_DEFAULT, set_default=False)
def fade(self, fade_seconds: int = Config.FADEOUT_SECONDS) -> None:
"""Fade music"""
self.resume_marker = self.player.get_position()
self.player.fade(fade_seconds)
self.resume_marker = self.music.get_position()
self.music.fade(fade_seconds)
self.signal_end_of_track()
def is_playing(self) -> bool:
@ -615,21 +660,21 @@ class RowAndTrack:
if self.start_time is None:
return False
return self.player.is_playing()
return self.music.is_playing()
def move_back(self, ms: int = Config.PREVIEW_BACK_MS) -> None:
"""
Rewind player by ms milliseconds
"""
self.player.adjust_by_ms(ms * -1)
self.music.adjust_by_ms(ms * -1)
def move_forward(self, ms: int = Config.PREVIEW_ADVANCE_MS) -> None:
"""
Rewind player by ms milliseconds
"""
self.player.adjust_by_ms(ms)
self.music.adjust_by_ms(ms)
def play(self, position: Optional[float] = None) -> None:
"""Play track"""
@ -639,8 +684,7 @@ class RowAndTrack:
self.start_time = now
# Initialise player
self.player = _Music(name=Config.VLC_MAIN_PLAYER_NAME)
self.player.play(self.path, start_time=now, position=position)
self.music.play(self.path, start_time=now, position=position)
self.end_time = now + dt.timedelta(milliseconds=self.duration)
@ -658,7 +702,7 @@ class RowAndTrack:
Restart player
"""
self.player.adjust_by_ms(self.time_playing() * -1)
self.music.adjust_by_ms(self.time_playing() * -1)
def set_forecast_start_time(
self, modified_rows: list[int], start: Optional[dt.datetime]
@ -705,7 +749,7 @@ class RowAndTrack:
Stop this track playing
"""
self.resume_marker = self.player.get_position()
self.resume_marker = self.music.get_position()
self.fade(fade_seconds)
# Reset fade graph
@ -720,7 +764,7 @@ class RowAndTrack:
if self.start_time is None:
return 0
return self.player.get_playtime()
return self.music.get_playtime()
def time_remaining_intro(self) -> int:
"""

View File

@ -316,6 +316,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.action_quicklog.activated.connect(self.quicklog)
self.load_last_playlists()
self.stop_autoplay = False
def about(self) -> None:
"""Get git tag and database name"""
@ -680,6 +681,10 @@ class Window(QMainWindow, Ui_MainWindow):
self.catch_return_key = False
self.show_status_message("Play controls: Enabled", 0)
# autoplay
if not self.stop_autoplay:
self.play_next()
def export_playlist_tab(self) -> None:
"""Export the current playlist to an m3u file"""
@ -1151,6 +1156,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.btnDrop3db.setChecked(False)
# Play (new) current track
log.info(f"Play: {track_sequence.current.title}")
track_sequence.current.play(position)
# Update clocks now, don't wait for next tick
@ -1623,6 +1629,7 @@ class Window(QMainWindow, Ui_MainWindow):
def stop(self) -> None:
"""Stop playing immediately"""
self.stop_autoplay = True
if track_sequence.current:
track_sequence.current.stop()