Audio Processing Recipes¶
Practical recipes for common audio processing tasks.
Normalize Audio Volume¶
Normalize audio to target peak level.
import numpy as np
from coremusic import capi
from coremusic.audio import AudioFile, AudioFormat, ExtendedAudioFile
def normalize_audio(input_path, output_path, target_peak=0.9):
"""Normalize audio to target peak level"""
with AudioFile(input_path) as input_file:
# Read the whole file, as float in [-1, 1]
samples = input_file.read_as_numpy().astype(np.float32) / 32768.0
source_format = input_file.format
# Find current peak
current_peak = np.max(np.abs(samples))
# Calculate and apply gain
if current_peak > 0:
gain = target_peak / current_peak
samples *= gain
print(f"Applied gain: {gain:.3f}x ({20 * np.log10(gain):.2f}dB)")
# Write output as float32, which is what the samples now are
out_format = AudioFormat.pcm(
source_format.sample_rate,
channels=source_format.channels_per_frame,
bits=32,
is_float=True,
)
with ExtendedAudioFile.create(
output_path, capi.fourchar_to_int('WAVE'), out_format
) as output_file:
output_file.write(len(samples), samples.tobytes())
# Usage
normalize_audio("input.wav", "normalized.wav", target_peak=0.9)
Apply Fade In/Out¶
Add smooth fade effects to audio.
import numpy as np
from coremusic import capi
from coremusic.audio import AudioFile, AudioFormat, ExtendedAudioFile
def apply_fades(input_path, output_path, fade_in=0.5, fade_out=0.5):
"""Apply fade in and fade out to audio"""
with AudioFile(input_path) as input_file:
samples = input_file.read_as_numpy().astype(np.float32) / 32768.0
sample_rate = input_file.format.sample_rate
channels = input_file.format.channels_per_frame
# read_as_numpy returns (frames, channels) for multichannel files
audio = samples if samples.ndim == 2 else samples.reshape(-1, 1)
fade_in_frames = min(int(fade_in * sample_rate), len(audio))
fade_out_frames = min(int(fade_out * sample_rate), len(audio))
# Apply fades
audio[:fade_in_frames] *= np.linspace(0, 1, fade_in_frames)[:, np.newaxis]
audio[len(audio) - fade_out_frames:] *= (
np.linspace(1, 0, fade_out_frames)[:, np.newaxis]
)
out_format = AudioFormat.pcm(
sample_rate, channels=channels, bits=32, is_float=True
)
with ExtendedAudioFile.create(
output_path, capi.fourchar_to_int('WAVE'), out_format
) as output_file:
output_file.write(len(audio), audio.tobytes())
# Usage
apply_fades("input.wav", "faded.wav", fade_in=0.5, fade_out=0.5)
Change Sample Rate¶
Resample audio to different sample rate using ExtendedAudioFile.
from coremusic.audio import AudioFormat, convert_audio_file
def resample_audio(input_path, output_path, target_sample_rate=48000.0):
"""Resample audio with automatic conversion"""
out_format = AudioFormat.pcm(
sample_rate=target_sample_rate,
channels=2,
bits=16,
)
convert_audio_file(input_path, output_path, out_format)
resample_audio("input.wav", "resampled.wav", target_sample_rate=48000.0)
convert_audio_file wraps the converter. To drive it yourself - to process
while you resample, say - set a client format on the input and copy:
from coremusic import capi
from coremusic.audio import AudioFormat, ExtendedAudioFile
def resample_manually(input_path, output_path, target_sample_rate=48000.0):
"""Resample by setting a client format and copying block by block.
ExtendedAudioFile converts to the client format as it reads, so the copy
loop below is doing the resampling.
"""
with ExtendedAudioFile(input_path) as input_file:
in_format = input_file.file_format
out_format = AudioFormat.pcm(
sample_rate=target_sample_rate,
channels=in_format.channels_per_frame,
bits=in_format.bits_per_channel,
)
# Set client format for automatic conversion
input_file.client_format = out_format
with ExtendedAudioFile.create(
output_path, capi.fourchar_to_int('WAVE'), out_format
) as output_file:
chunk_size = 8192
while True:
data, count = input_file.read(chunk_size)
if count == 0:
break
output_file.write(count, data)
resample_manually("input.wav", "resampled_manual.wav")
Mix Multiple Tracks¶
Mix multiple audio tracks into stereo output.
import numpy as np
from coremusic import capi
from coremusic.audio import AudioFile, AudioFormat, ExtendedAudioFile
def mix_tracks(track_files, output_path, levels=None):
"""Mix multiple audio tracks with individual levels"""
if levels is None:
levels = [1.0] * len(track_files)
# Load all tracks
tracks = []
max_frames = 0
source_format = None
for file_path, level in zip(track_files, levels, strict=True):
with AudioFile(file_path) as audio:
samples = audio.read_as_numpy().astype(np.float32) / 32768.0
samples *= level # Apply level
tracks.append(samples)
max_frames = max(max_frames, len(samples))
source_format = source_format or audio.format
# Pad tracks to same length
for i, track in enumerate(tracks):
if len(track) < max_frames:
pad = [(0, max_frames - len(track))] + [(0, 0)] * (track.ndim - 1)
tracks[i] = np.pad(track, pad)
# Mix (sum all tracks)
mixed = np.sum(tracks, axis=0)
# Normalize to prevent clipping
peak = np.max(np.abs(mixed))
if peak > 1.0:
mixed /= peak
out_format = AudioFormat.pcm(
source_format.sample_rate,
channels=source_format.channels_per_frame,
bits=32,
is_float=True,
)
with ExtendedAudioFile.create(
output_path, capi.fourchar_to_int('WAVE'), out_format
) as output_file:
output_file.write(len(mixed), mixed.tobytes())
# Usage
tracks = ["drums.wav", "input.wav"]
levels = [1.0, 0.8]
mix_tracks(tracks, "mixed.wav", levels=levels)
Split Audio into Chunks¶
Split long audio file into smaller segments.
from pathlib import Path
from coremusic.audio import AudioFile, trim_audio
def split_audio(input_path, output_dir, chunk_duration=1.0):
"""Split audio file into fixed-duration chunks"""
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
with AudioFile(input_path) as audio:
duration = audio.duration
index = 0
start = 0.0
while start < duration:
end = min(start + chunk_duration, duration)
trim_audio(input_path, str(output_dir / f"chunk_{index:03d}.wav"), start, end)
start = end
index += 1
print(f"Wrote {index} chunks to {output_dir}")
split_audio("input.wav", "chunks", chunk_duration=1.0)
To cut on musical boundaries rather than a fixed grid, let AudioSlicer
detect onsets:
from coremusic.audio import AudioSlicer
# To cut on musical boundaries instead of a fixed grid, let AudioSlicer find
# the onsets and export what it found.
slicer = AudioSlicer("input.wav", method="onset")
slices = slicer.detect_slices()
print(f"Detected {len(slices)} slices")
paths = slicer.export_slices("slices", name_template="slice_{index:03d}.wav")
print(f"Exported {len(paths)} files")
Merge Audio Files¶
Concatenate multiple audio files into one.
from coremusic import capi
from coremusic.audio import AudioFile, AudioFormat, ExtendedAudioFile
def merge_audio_files(input_files, output_path):
"""Merge multiple audio files sequentially.
Every input must share the first file's format; convert first if they do
not.
"""
with AudioFile(str(input_files[0])) as first_file:
source_format = first_file.format
out_format = AudioFormat.pcm(
source_format.sample_rate,
channels=source_format.channels_per_frame,
bits=source_format.bits_per_channel,
)
with ExtendedAudioFile.create(
output_path, capi.fourchar_to_int('WAVE'), out_format
) as output_file:
for input_path in input_files:
with AudioFile(str(input_path)) as input_file:
data, count = input_file.read_packets(0, input_file.packet_count)
output_file.write(count, data)
# Usage
files = ["audio.wav", "input.wav", "drums.wav"]
merge_audio_files(files, "complete.wav")
See Also¶
- File Operations - File I/O recipes
- Performance Guide - Performance optimization
- AudioFile API - AudioFile API reference