Skip to content

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