fix: make !volume apply immediately during playback
stream_audio now accepts a callable for volume, re-read on each PCM frame instead of capturing a static float at track start. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -556,8 +556,8 @@ HTML stripped on receive, escaped on send. IRC-only commands are no-ops.
|
|||||||
!volume 75 # Set volume (0-100, default 50)
|
!volume 75 # Set volume (0-100, default 50)
|
||||||
```
|
```
|
||||||
|
|
||||||
Requires: `yt-dlp`, `ffmpeg`, `libopus.so.0` on the host.
|
Requires: `yt-dlp`, `ffmpeg`, `libopus` on the host.
|
||||||
Max 50 tracks in queue. Volume applies on next track.
|
Max 50 tracks in queue. Volume changes take effect immediately.
|
||||||
Mumble-only: `!play` replies with error on other adapters, others silently no-op.
|
Mumble-only: `!play` replies with error on other adapters, others silently no-op.
|
||||||
|
|
||||||
## Plugin Template
|
## Plugin Template
|
||||||
|
|||||||
@@ -1576,7 +1576,7 @@ and voice transmission.
|
|||||||
```
|
```
|
||||||
|
|
||||||
- Queue holds up to 50 tracks
|
- Queue holds up to 50 tracks
|
||||||
- Volume applies from the next track (default: 50%)
|
- Volume takes effect immediately during playback (default: 50%)
|
||||||
- Title resolved via `yt-dlp --get-title` before playback
|
- Title resolved via `yt-dlp --get-title` before playback
|
||||||
- Audio pipeline: `yt-dlp | ffmpeg` subprocess, PCM fed to pymumble
|
- Audio pipeline: `yt-dlp | ffmpeg` subprocess, PCM fed to pymumble
|
||||||
- Commands are Mumble-only; `!play` on other adapters replies with an error,
|
- Commands are Mumble-only; `!play` on other adapters replies with an error,
|
||||||
|
|||||||
@@ -78,10 +78,11 @@ async def _play_loop(bot) -> None:
|
|||||||
done = asyncio.Event()
|
done = asyncio.Event()
|
||||||
ps["done_event"] = done
|
ps["done_event"] = done
|
||||||
|
|
||||||
volume = ps["volume"] / 100.0
|
|
||||||
try:
|
try:
|
||||||
await bot.stream_audio(
|
await bot.stream_audio(
|
||||||
track.url, volume=volume, on_done=done,
|
track.url,
|
||||||
|
volume=lambda: ps["volume"] / 100.0,
|
||||||
|
on_done=done,
|
||||||
)
|
)
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
raise
|
raise
|
||||||
@@ -269,7 +270,7 @@ async def cmd_volume(bot, message):
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
!volume Show current volume
|
!volume Show current volume
|
||||||
!volume <0-100> Set volume (applies on next track)
|
!volume <0-100> Set volume (takes effect immediately)
|
||||||
"""
|
"""
|
||||||
if not _is_mumble(bot):
|
if not _is_mumble(bot):
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -421,7 +421,7 @@ class MumbleBot:
|
|||||||
self,
|
self,
|
||||||
url: str,
|
url: str,
|
||||||
*,
|
*,
|
||||||
volume: float = 0.5,
|
volume=0.5,
|
||||||
on_done=None,
|
on_done=None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Stream audio from URL through yt-dlp|ffmpeg to voice channel.
|
"""Stream audio from URL through yt-dlp|ffmpeg to voice channel.
|
||||||
@@ -432,12 +432,16 @@ class MumbleBot:
|
|||||||
|
|
||||||
Feeds raw PCM to pymumble's sound_output which handles Opus
|
Feeds raw PCM to pymumble's sound_output which handles Opus
|
||||||
encoding, packetization, and timing.
|
encoding, packetization, and timing.
|
||||||
|
|
||||||
|
``volume`` may be a float (static) or a callable returning float
|
||||||
|
(dynamic, re-read each frame).
|
||||||
"""
|
"""
|
||||||
if self._mumble is None:
|
if self._mumble is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
_get_vol = volume if callable(volume) else lambda: volume
|
||||||
log.info("stream_audio: starting pipeline for %s (vol=%.0f%%)",
|
log.info("stream_audio: starting pipeline for %s (vol=%.0f%%)",
|
||||||
url, volume * 100)
|
url, _get_vol() * 100)
|
||||||
|
|
||||||
proc = await asyncio.create_subprocess_exec(
|
proc = await asyncio.create_subprocess_exec(
|
||||||
"sh", "-c",
|
"sh", "-c",
|
||||||
@@ -457,8 +461,9 @@ class MumbleBot:
|
|||||||
if len(pcm) < _FRAME_BYTES:
|
if len(pcm) < _FRAME_BYTES:
|
||||||
pcm += b"\x00" * (_FRAME_BYTES - len(pcm))
|
pcm += b"\x00" * (_FRAME_BYTES - len(pcm))
|
||||||
|
|
||||||
if volume != 1.0:
|
vol = _get_vol()
|
||||||
pcm = _scale_pcm(pcm, volume)
|
if vol != 1.0:
|
||||||
|
pcm = _scale_pcm(pcm, vol)
|
||||||
|
|
||||||
self._mumble.sound_output.add_sound(pcm)
|
self._mumble.sound_output.add_sound(pcm)
|
||||||
frames += 1
|
frames += 1
|
||||||
|
|||||||
Reference in New Issue
Block a user