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:
-
Identify Dependencies
-
Check if your code relies on cross-platform support
- Verify macOS version compatibility (10.13+)
-
List external tools (ffmpeg, etc.)
-
Update Imports
-
Replace library imports with CoreMusic
- Update API calls to CoreMusic equivalents
-
Add NumPy if processing audio data
-
Adapt Audio Operations
-
Convert high-level operations to CoreMusic patterns
- Update file I/O to use AudioFile/ExtendedAudioFile
-
Migrate streaming code to chunked processing
-
Update MIDI Code
-
Replace MIDI library calls with CoreMIDI via CoreMusic
- Adapt port discovery and device enumeration
-
Update message sending/receiving patterns
-
Test Thoroughly
-
Verify audio quality and correctness
- Check resource cleanup and memory usage
- Test error handling and edge cases
- 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/.