Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions music_assistant/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,12 +310,12 @@
label="Enable Smart Fades",
options=[
ConfigValueOption("Disabled", "disabled"),
ConfigValueOption("Smart Fades", "smart_fades"),
ConfigValueOption("Smart Crossfade", "smart_crossfade"),
ConfigValueOption("Standard Crossfade", "standard_crossfade"),
],
default_value="disabled",
description="Select the crossfade mode to use when transitioning between tracks.\n\n"
"- 'Smart Fades': Uses beat matching and DJ-like EQ filters to create smooth transitions"
"- 'Smart Crossfade': Uses beat matching and EQ filters to create smooth transitions"
" between tracks.\n"
"- 'Standard Crossfade': Regular crossfade that crossfades the last/first x-seconds of a "
"track.",
Expand Down
6 changes: 3 additions & 3 deletions music_assistant/controllers/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ async def get_queue_flow_stream(
# calculate crossfade buffer size
crossfade_buffer_duration = (
SMART_CROSSFADE_DURATION
if smart_fades_mode == SmartFadesMode.SMART_FADES
if smart_fades_mode == SmartFadesMode.SMART_CROSSFADE
else standard_crossfade_duration
)
crossfade_buffer_duration = min(
Expand Down Expand Up @@ -1362,7 +1362,7 @@ async def get_queue_item_stream_with_smartfade(
self,
queue_item: QueueItem,
pcm_format: AudioFormat,
smart_fades_mode: SmartFadesMode = SmartFadesMode.SMART_FADES,
smart_fades_mode: SmartFadesMode = SmartFadesMode.SMART_CROSSFADE,
standard_crossfade_duration: int = 10,
) -> AsyncGenerator[bytes, None]:
"""Get the audio stream for a single queue item with (smart) crossfade to the next item."""
Expand Down Expand Up @@ -1397,7 +1397,7 @@ async def get_queue_item_stream_with_smartfade(
# calculate crossfade buffer size
crossfade_buffer_duration = (
SMART_CROSSFADE_DURATION
if smart_fades_mode == SmartFadesMode.SMART_FADES
if smart_fades_mode == SmartFadesMode.SMART_CROSSFADE
else standard_crossfade_duration
)
crossfade_buffer_duration = min(
Expand Down
99 changes: 0 additions & 99 deletions music_assistant/helpers/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,105 +80,6 @@
STREAMDETAILS_EXPIRATION: Final[int] = 60 * 15 # 15 minutes


async def crossfade_pcm_parts(
fade_in_part: bytes,
fade_out_part: bytes,
pcm_format: AudioFormat,
fade_out_pcm_format: AudioFormat | None = None,
) -> bytes:
"""Crossfade two chunks of pcm/raw audio using ffmpeg."""
if fade_out_pcm_format is None:
fade_out_pcm_format = pcm_format

# calculate the fade_length from the smallest chunk
fade_length = min(
len(fade_in_part) / pcm_format.pcm_sample_size,
len(fade_out_part) / fade_out_pcm_format.pcm_sample_size,
)
# write the fade_out_part to a temporary file
fadeout_filename = f"/tmp/{shortuuid.random(20)}.pcm" # noqa: S108
async with aiofiles.open(fadeout_filename, "wb") as outfile:
await outfile.write(fade_out_part)

args = [
# generic args
"ffmpeg",
"-hide_banner",
"-loglevel",
"quiet",
# fadeout part (as file)
"-acodec",
fade_out_pcm_format.content_type.name.lower(),
"-ac",
str(fade_out_pcm_format.channels),
"-ar",
str(fade_out_pcm_format.sample_rate),
"-channel_layout",
"mono" if fade_out_pcm_format.channels == 1 else "stereo",
"-f",
fade_out_pcm_format.content_type.value,
"-i",
fadeout_filename,
# fade_in part (stdin)
"-acodec",
pcm_format.content_type.name.lower(),
"-ac",
str(pcm_format.channels),
"-channel_layout",
"mono" if pcm_format.channels == 1 else "stereo",
"-ar",
str(pcm_format.sample_rate),
"-f",
pcm_format.content_type.value,
"-i",
"-",
# filter args
"-filter_complex",
f"[0][1]acrossfade=d={fade_length}",
# output args
"-acodec",
pcm_format.content_type.name.lower(),
"-ac",
str(pcm_format.channels),
"-channel_layout",
"mono" if pcm_format.channels == 1 else "stereo",
"-ar",
str(pcm_format.sample_rate),
"-f",
pcm_format.content_type.value,
"-",
]
_, crossfaded_audio, _ = await communicate(args, fade_in_part)
await remove_file(fadeout_filename)
if crossfaded_audio:
LOGGER.log(
VERBOSE_LOG_LEVEL,
"crossfaded 2 pcm chunks. fade_in_part: %s - "
"fade_out_part: %s - fade_length: %s seconds",
len(fade_in_part),
len(fade_out_part),
fade_length,
)
return crossfaded_audio
# no crossfade_data, return original data instead
LOGGER.debug(
"crossfade of pcm chunks failed: not enough data? - fade_in_part: %s - fade_out_part: %s",
len(fade_in_part),
len(fade_out_part),
)
if fade_out_pcm_format.sample_rate != pcm_format.sample_rate:
# Edge case: the sample rates are different,
# we need to resample the fade_out part to the same sample rate as the fade_in part
async with FFMpeg(
audio_input="-",
input_format=fade_out_pcm_format,
output_format=pcm_format,
) as ffmpeg:
res = await ffmpeg.communicate(fade_out_part)
return res[0] + fade_in_part
return fade_out_part + fade_in_part


async def strip_silence(
mass: MusicAssistant, # noqa: ARG001
audio_data: bytes,
Expand Down
Loading