Skip to content

Migration Guide

Guide for migrating from other Python audio libraries to CoreMusic, porting CoreAudio C/Objective-C code to Python, and updating code written against older CoreMusic releases.

From CoreMusic 0.2.2 and Earlier

0.2.3 dissolved the coremusic.objects package into domain subpackages, and there is no compatibility shim: import coremusic.objects raises ModuleNotFoundError. The top-level package exports only __version__, so coremusic.AudioFile and similar flat names do not resolve either.

Old import New import
from coremusic.objects import AudioFile, AudioFormat from coremusic.audio import AudioFile, AudioFormat
from coremusic.objects import AudioQueue, AudioUnit, AUGraph from coremusic.audio import AudioQueue, AudioUnit, AUGraph
from coremusic.objects import AudioDevice, AudioDeviceManager from coremusic.audio import AudioDevice, AudioDeviceManager
from coremusic.objects import AudioClock, ClockTimeFormat from coremusic.audio import AudioClock, ClockTimeFormat
from coremusic.objects import MIDIClient, MIDIPort from coremusic.midi import MIDIClient, MIDIPort
from coremusic.objects import MusicPlayer, MusicSequence, MusicTrack from coremusic.midi import MusicPlayer, MusicSequence, MusicTrack
from coremusic.objects import AudioFileError, MIDIError from coremusic.exceptions import AudioFileError, MIDIError
from coremusic.objects import CoreAudioObject, AudioPlayer, NUMPY_AVAILABLE from coremusic.base import CoreAudioObject, AudioPlayer, NUMPY_AVAILABLE
import coremusic as cm; cm.AudioFile(...) from coremusic.audio import AudioFile

The MIDI CLI was restructured at the same time - midi device list, midi input monitor, and midi output send became midi list, midi monitor, and midi send. See the CLI Guide.

The Import Guide has the full package map.

From pydub

pydub is a high-level audio library focused on simplicity. CoreMusic provides similar ease-of-use with native performance.

Loading Audio Files

pydub:

from pydub import AudioSegment

# Load audio
audio = AudioSegment.from_wav("audio.wav")
audio = AudioSegment.from_mp3("audio.mp3")

# Get properties
duration = len(audio)  # milliseconds
sample_rate = audio.frame_rate
channels = audio.channels

CoreMusic:

from coremusic.audio import AudioFile, ExtendedAudioFile

# Load audio (supports WAV, MP3, AAC, AIFF, etc.)
with AudioFile("audio.wav") as audio:
    # Get properties
    duration = audio.duration  # seconds
    sample_rate = audio.format.sample_rate
    channels = audio.format.channels_per_frame

# Or, to decode a compressed file to PCM as you read it
with ExtendedAudioFile("audio.wav") as audio:
    file_format = audio.file_format
    frame_count = audio.frame_count

Basic Operations

pydub:

from pydub import AudioSegment

# Load
audio = AudioSegment.from_wav("input.wav")

# Volume adjustment
louder = audio + 10  # Increase by 10dB
quieter = audio - 5  # Decrease by 5dB

# Slicing
first_10_seconds = audio[:10000]  # milliseconds

# Concatenation
combined = audio1 + audio2

# Export
audio.export("output.mp3", format="mp3")

CoreMusic:

import numpy as np

from coremusic import capi
from coremusic.audio import AudioFile, ExtendedAudioFile
from coremusic.audio.slicing import AudioSlicer

# Load the whole file as samples
with AudioFile("input.wav") as audio:
    samples = audio.read_as_numpy().astype(np.float32) / 32768.0
    audio_format = audio.format

# Volume adjustment
samples *= 3.16   # +10 dB
samples *= 0.56   # -5 dB

# Slicing: AudioSlicer finds musical boundaries rather than cutting blindly
slicer = AudioSlicer("input.wav")
slices = slicer.detect_slices()

# Export
out_format = capi.fourchar_to_int('WAVE')
float_format = audio_format.pcm(
    audio_format.sample_rate,
    channels=audio_format.channels_per_frame,
    bits=32,
    is_float=True,
)
with ExtendedAudioFile.create("output.wav", out_format, float_format) as output:
    output.write(len(samples), samples.tobytes())

Key Differences

Feature pydub CoreMusic
Performance Relies on ffmpeg Native CoreAudio
Memory Usage High (loads all) Low (streaming)
Platform Cross-platform macOS only
Real-time Audio No Yes (AudioUnit)
MIDI Support No Yes (CoreMIDI)
Dependencies ffmpeg required No external deps
Type Immutable segments Mutable buffers

From soundfile / libsndfile

soundfile provides NumPy-based audio I/O. CoreMusic offers similar functionality with deeper macOS integration.

Reading Audio

soundfile:

import soundfile as sf

# Read entire file
data, sample_rate = sf.read("audio.wav")

# Read with specific dtype
data, sample_rate = sf.read("audio.wav", dtype='float32')

# Get info without reading
info = sf.info("audio.wav")
print(f"Duration: {info.duration}s")
print(f"Channels: {info.channels}")

CoreMusic:

import numpy as np

from coremusic.audio import AudioFile

# Read entire file
with AudioFile("audio.wav") as audio:
    samples = audio.read_as_numpy()
    sample_rate = audio.format.sample_rate

# Get info without reading the samples
with AudioFile("audio.wav") as audio:
    duration = audio.duration
    channels = audio.format.channels_per_frame
    sample_rate = audio.format.sample_rate

Writing Audio

soundfile:

import soundfile as sf
import numpy as np

# Generate audio
data = np.random.randn(44100 * 2)  # 2 seconds

# Write
sf.write("output.wav", data, 44100)

CoreMusic:

import numpy as np

from coremusic import capi
from coremusic.audio import AudioFormat, ExtendedAudioFile

# Generate audio
data = (np.random.randn(44100) * 0.1).astype(np.float32)

# Describe it
audio_format = AudioFormat.pcm(
    sample_rate=44100.0,
    channels=1,
    bits=32,
    is_float=True,
)

# Write
with ExtendedAudioFile.create(
    "output.wav",
    capi.fourchar_to_int('WAVE'),
    audio_format,
) as audio:
    audio.write(len(data), data.tobytes())

Streaming

soundfile:

import soundfile as sf

# Read in blocks
with sf.SoundFile("audio.wav") as file:
    while True:
        data = file.read(1024)
        if len(data) == 0:
            break
        # Process block

CoreMusic:

from coremusic.audio import ExtendedAudioFile

# Read in blocks
with ExtendedAudioFile("audio.wav") as audio:
    while True:
        data, count = audio.read(1024)
        if count == 0:
            break
        # Process block

From wave / audioread

wave is Python's built-in WAV module. CoreMusic provides more features and better performance.

Reading WAV

wave:

import wave

with wave.open("audio.wav", 'rb') as wav:
    # Get parameters
    channels = wav.getnchannels()
    sample_width = wav.getsampwidth()
    framerate = wav.getframerate()
    n_frames = wav.getnframes()

    # Read frames
    frames = wav.readframes(n_frames)

CoreMusic:

from coremusic.audio import ExtendedAudioFile

with ExtendedAudioFile("audio.wav") as audio:
    # Get parameters
    file_format = audio.file_format
    channels = file_format.channels_per_frame
    sample_rate = file_format.sample_rate
    bits = file_format.bits_per_channel
    n_frames = audio.frame_count

    # Read frames
    data, count = audio.read(n_frames)

Writing WAV

wave:

import wave
import numpy as np

data = np.random.randint(-32768, 32767, 44100, dtype=np.int16)

with wave.open("output.wav", 'wb') as wav:
    wav.setnchannels(1)
    wav.setsampwidth(2)
    wav.setframerate(44100)
    wav.writeframes(data.tobytes())

CoreMusic:

import numpy as np

from coremusic import capi
from coremusic.audio import AudioFormat, ExtendedAudioFile

data = np.random.randint(-32768, 32767, 44100, dtype=np.int16)

audio_format = AudioFormat.pcm(sample_rate=44100.0, channels=1, bits=16)

with ExtendedAudioFile.create(
    "output16.wav",
    capi.fourchar_to_int('WAVE'),
    audio_format,
) as audio:
    audio.write(len(data), data.tobytes())

From mido (MIDI)

mido is a popular MIDI library. CoreMusic provides CoreMIDI access for macOS.

Opening MIDI Ports

mido:

import mido

# List ports
print(mido.get_output_names())

# Open output port
with mido.open_output('IAC Driver Bus 1') as port:
    msg = mido.Message('note_on', note=60, velocity=100)
    port.send(msg)

CoreMusic:

from coremusic.midi import MIDIClient, get_destinations, note_on

# List ports
for destination in get_destinations():
    print(destination.name)

# Send MIDI
client = MIDIClient("MyApp")
port = client.create_output_port("Output")

destinations = get_destinations()
dest = destinations[0] if destinations else client.create_virtual_destination("Out")

# Send note on
message = note_on("C4", 100)  # Channel 0, note 60, velocity 100
port.send_data(dest, message)

client.dispose()

MIDI Files

mido:

import mido

# Load MIDI file
mid = mido.MidiFile("song.mid")

# Iterate through messages
for track in mid.tracks:
    for msg in track:
        print(msg)

# Create new file
mid = mido.MidiFile()
track = mido.MidiTrack()
mid.tracks.append(track)

track.append(mido.Message('note_on', note=60, time=0))
track.append(mido.Message('note_off', note=60, time=480))

mid.save("output.mid")

CoreMusic:

from coremusic.midi import MIDISequence

# Load a MIDI file
sequence = MIDISequence.load("song.mid")
for track in sequence.tracks:
    print(f"{track.name}: {len(track.events)} events")

# Create a new one
sequence = MIDISequence()
track = sequence.add_track("Melody")
track.add_note(time=0.0, note=60, velocity=100, duration=1.0)
sequence.save("output.mid")

From CoreAudio C/Objective-C

Migrating existing CoreAudio code to Python with CoreMusic.

AudioFile Operations

C/Objective-C:

// Open audio file
AudioFileID fileID;
CFURLRef fileURL = CFURLCreateFromFileSystemRepresentation(
    NULL, (const UInt8 *)"/path/to/audio.wav", strlen("/path/to/audio.wav"), false
);
OSStatus status = AudioFileOpenURL(fileURL, kAudioFileReadPermission, 0, &fileID);

// Get format
AudioStreamBasicDescription format;
UInt32 size = sizeof(format);
AudioFileGetProperty(fileID, kAudioFilePropertyDataFormat, &size, &format);

// Read packets
UInt32 numPackets = 1024;
void *buffer = malloc(numPackets * format.mBytesPerPacket);
AudioFileReadPacketData(fileID, false, &size, NULL, 0, &numPackets, buffer);

// Cleanup
AudioFileClose(fileID);
free(buffer);

CoreMusic:

from coremusic.audio import AudioFile

# Open audio file
with AudioFile("audio.wav") as audio:
    # Get format
    audio_format = audio.format

    # Read packets
    data, count = audio.read_packets(0, 1024)

# Automatic cleanup via context manager

Or using functional API for closer C mapping:

from coremusic import capi

# Open
file_id = capi.audio_file_open_url("audio.wav")

# Get format
format_data = capi.audio_file_get_property(
    file_id,
    capi.get_audio_file_property_data_format()
)

# Read
data, count = capi.audio_file_read_packets(file_id, 0, 1024)

# Close
capi.audio_file_close(file_id)

AudioUnit Operations

C/Objective-C:

// Find output unit
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;

AudioComponent comp = AudioComponentFindNext(NULL, &desc);
AudioUnit unit;
AudioComponentInstanceNew(comp, &unit);

// Initialize and start
AudioUnitInitialize(unit);
AudioOutputUnitStart(unit);

CoreMusic:

from coremusic.audio import AudioUnit

# Find and create the default output unit
unit = AudioUnit.default_output()
unit.initialize()
unit.start()
unit.stop()
unit.dispose()

MIDI Operations

C/Objective-C:

// Create MIDI client
MIDIClientRef client;
MIDIClientCreate(CFSTR("MyClient"), NULL, NULL, &client);

// Create output port
MIDIPortRef outputPort;
MIDIOutputPortCreate(client, CFSTR("Output"), &outputPort);

// Get destination
MIDIEndpointRef dest = MIDIGetDestination(0);

// Send note
Byte packet[3] = {0x90, 60, 100};  // Note on
MIDISend(outputPort, dest, packet, 3);

CoreMusic:

from coremusic import capi
from coremusic.midi import note_on

# Create MIDI client
client = capi.midi_client_create("MyClient")

# Create output port
output_port = capi.midi_output_port_create(client, "Output")

# Get a destination
dest = capi.midi_destination_create(client, "MyClient Destination")

# Send note
message = note_on("C4", 100)
capi.midi_send_data(output_port, dest, message)

capi.midi_client_dispose(client)

From AudioKit (Swift)

AudioKit is a powerful Swift framework. CoreMusic provides similar capabilities in Python.

Audio Playback

AudioKit (Swift):

import AudioKit

let file = try AVAudioFile(forReading: URL(fileURLWithPath: "audio.wav"))
let player = AudioPlayer(file: file)
AudioKit.output = player
try AudioKit.start()
player.play()

CoreMusic:

    from coremusic.base import AudioPlayer

    # High-level player
    player = AudioPlayer()
    player.load_file("audio.wav")
    player.setup_output()
    player.play()
    player.stop()

Audio Effects

AudioKit (Swift):

import AudioKit

let player = AudioPlayer(file: file)
let reverb = Reverb(player)
reverb.dryWetMix = 0.5

AudioKit.output = reverb
try AudioKit.start()

CoreMusic:

from coremusic.audio.audiounit_host import AudioUnitPlugin

input_data = bytes(512 * 2 * 4)  # 512 stereo frames of float32 silence

# Load a reverb AudioUnit
with AudioUnitPlugin.from_name("AUMatrixReverb") as reverb:
    reverb['Dry/Wet Mix'] = 50.0

    # Process audio
    output = reverb.process(input_data)

Feature Comparison Matrix

Feature pydub soundfile wave mido CoreAudio CoreMusic
Audio File I/O Yes Yes Yes No Yes Yes
Format Conversion Yes No No No Yes Yes
Real-time Audio No No No No Yes Yes
AudioUnit Support No No No No Yes Yes
MIDI I/O No No No Yes Yes Yes
MIDI Files No No No Yes Yes Yes
Hardware Control No No No No Yes Yes
Streaming Limited Yes Limited N/A Yes Yes
NumPy Integration Limited Yes No No No Yes
Cross-platform Yes Yes Yes Yes No No
External Dependencies ffmpeg libsndfile None None None None
Performance Medium High Low High Native Native

Migration Checklist

When migrating to CoreMusic:

  1. Identify Dependencies

  2. Check if your code relies on cross-platform support

  3. Verify macOS version compatibility (10.13+)
  4. List external tools (ffmpeg, etc.)

  5. Update Imports

  6. Replace library imports with CoreMusic

  7. Update API calls to CoreMusic equivalents
  8. Add NumPy if processing audio data

  9. Adapt Audio Operations

  10. Convert high-level operations to CoreMusic patterns

  11. Update file I/O to use AudioFile/ExtendedAudioFile
  12. Migrate streaming code to chunked processing

  13. Update MIDI Code

  14. Replace MIDI library calls with CoreMIDI via CoreMusic

  15. Adapt port discovery and device enumeration
  16. Update message sending/receiving patterns

  17. Test Thoroughly

  18. Verify audio quality and correctness

  19. Check resource cleanup and memory usage
  20. Test error handling and edge cases
  21. Benchmark performance improvements

Common Migration Patterns

Pattern 1: Simple Audio Processing

Before (pydub):

from pydub import AudioSegment

audio = AudioSegment.from_wav("input.wav")
audio = audio + 6  # Increase volume
audio.export("output.wav", format="wav")

After (CoreMusic):

import numpy as np

from coremusic import capi
from coremusic.audio import AudioFile, AudioFormat, ExtendedAudioFile

with AudioFile("input.wav") as audio:
    samples = audio.read_as_numpy().astype(np.float32) / 32768.0
    samples *= 2.0  # Increase volume (~6dB)

    out_format = AudioFormat.pcm(
        audio.format.sample_rate,
        channels=audio.format.channels_per_frame,
        bits=32,
        is_float=True,
    )

with ExtendedAudioFile.create(
    "output.wav",
    capi.fourchar_to_int('WAVE'),
    out_format,
) as output:
    output.write(len(samples), samples.tobytes())

Pattern 2: MIDI Processing

Before (mido):

import mido

with mido.open_output() as port:
    for note in [60, 64, 67]:
        msg = mido.Message('note_on', note=note)
        port.send(msg)

After (CoreMusic):

from coremusic.midi import MIDIClient, get_destinations, note_on

client = MIDIClient("App")
port = client.create_output_port("Out")

destinations = get_destinations()
dest = destinations[0] if destinations else client.create_virtual_destination("Out")

for note in [60, 64, 67]:
    port.send_data(dest, note_on(note, 100))

client.dispose()

See Also

Note

Every snippet on this page is a runnable program under examples/guides/migration/.