|
|
|
@ -1,6 +1,7 @@ |
|
|
|
from __future__ import annotations |
|
|
|
from __future__ import annotations |
|
|
|
|
|
|
|
|
|
|
|
import logging |
|
|
|
import logging |
|
|
|
|
|
|
|
import shutil |
|
|
|
import subprocess |
|
|
|
import subprocess |
|
|
|
import threading |
|
|
|
import threading |
|
|
|
from pathlib import Path |
|
|
|
from pathlib import Path |
|
|
|
@ -86,12 +87,11 @@ class AudioPlayer: |
|
|
|
raise ValueError("end_seconds must be greater than start_seconds") |
|
|
|
raise ValueError("end_seconds must be greater than start_seconds") |
|
|
|
|
|
|
|
|
|
|
|
volume_factor = max(0.0, min(1.0, float(volume) / 100.0)) |
|
|
|
volume_factor = max(0.0, min(1.0, float(volume) / 100.0)) |
|
|
|
cmd = [ |
|
|
|
base_cmd = [ |
|
|
|
"play", |
|
|
|
"play", |
|
|
|
"-q", |
|
|
|
"-q", |
|
|
|
"-v", |
|
|
|
"-v", |
|
|
|
str(volume_factor), |
|
|
|
str(volume_factor), |
|
|
|
str(music_path), |
|
|
|
|
|
|
|
] |
|
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
effects: list[str] = [] |
|
|
|
effects: list[str] = [] |
|
|
|
@ -134,14 +134,14 @@ class AudioPlayer: |
|
|
|
effects += ["repeat", str(int(repeat_count))] |
|
|
|
effects += ["repeat", str(int(repeat_count))] |
|
|
|
|
|
|
|
|
|
|
|
# If sox lacks mp3 handlers, decode with ffmpeg and stream wav to sox. |
|
|
|
# If sox lacks mp3 handlers, decode with ffmpeg and stream wav to sox. |
|
|
|
if music_path.suffix.lower() == ".mp3": |
|
|
|
if music_path.suffix.lower() == ".mp3" and shutil.which("ffmpeg"): |
|
|
|
decoder_cmd = ["ffmpeg", "-v", "error", "-i", str(music_path), "-f", "wav", "-"] |
|
|
|
decoder_cmd = ["ffmpeg", "-v", "error", "-i", str(music_path), "-f", "wav", "-"] |
|
|
|
self._decoder_proc = subprocess.Popen( |
|
|
|
self._decoder_proc = subprocess.Popen( |
|
|
|
decoder_cmd, |
|
|
|
decoder_cmd, |
|
|
|
stdout=subprocess.PIPE, |
|
|
|
stdout=subprocess.PIPE, |
|
|
|
stderr=subprocess.DEVNULL, |
|
|
|
stderr=subprocess.DEVNULL, |
|
|
|
) |
|
|
|
) |
|
|
|
cmd += ["-t", "wav", "-"] + effects |
|
|
|
cmd = base_cmd + ["-t", "wav", "-"] + effects |
|
|
|
self._proc = subprocess.Popen( |
|
|
|
self._proc = subprocess.Popen( |
|
|
|
cmd, |
|
|
|
cmd, |
|
|
|
stdin=self._decoder_proc.stdout, |
|
|
|
stdin=self._decoder_proc.stdout, |
|
|
|
@ -151,7 +151,9 @@ class AudioPlayer: |
|
|
|
if self._decoder_proc.stdout is not None: |
|
|
|
if self._decoder_proc.stdout is not None: |
|
|
|
self._decoder_proc.stdout.close() |
|
|
|
self._decoder_proc.stdout.close() |
|
|
|
else: |
|
|
|
else: |
|
|
|
cmd += [str(music_path)] + effects |
|
|
|
if music_path.suffix.lower() == ".mp3": |
|
|
|
|
|
|
|
logger.warning("ffmpeg unavailable, falling back to direct sox playback for %s", music_path) |
|
|
|
|
|
|
|
cmd = base_cmd + [str(music_path)] + effects |
|
|
|
self._proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
|
|
|
self._proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
|
|
|
|
|
|
|
|
|
|
|
def stop(self) -> None: |
|
|
|
def stop(self) -> None: |
|
|
|
|