Skip to content

Getting Started

This guide will help you get started with coremusic, from installation through your first audio and MIDI applications.

Prerequisites

Before installing coremusic, ensure you have:

  • macOS: CoreAudio and CoreMIDI frameworks are macOS-specific
  • Python 3.10+: Python 3.10 or higher is required
  • Xcode Command Line Tools: Required for framework headers

Install Xcode Command Line Tools:

xcode-select --install

Installation

pip install coremusic

Or with uv:

uv add coremusic

From Source

  1. Clone the repository:
git clone https://github.com/shakfu/coremusic.git
cd coremusic
  1. Install with uv (recommended):
uv sync
  1. Build the extension:
make
  1. Verify installation:
make test

Understanding the Dual API

coremusic provides two complementary APIs that can be used together or independently:

Functional API (Traditional)

The functional API provides direct access to CoreAudio C functions:

Advantages:

  • Direct mapping to CoreAudio C APIs
  • Maximum performance and control
  • Familiar interface for CoreAudio developers
  • Fine-grained resource management

Use when:

  • Maximum performance is critical
  • Porting existing CoreAudio C code
  • Need fine-grained control over resource lifetimes
  • Building low-level audio processing components

Example:

from coremusic import capi

# Open audio file (manual resource management)
audio_file = capi.audio_file_open_url("audio.wav")
try:
    format_data = capi.audio_file_get_property(
        audio_file,
        capi.get_audio_file_property_data_format()
    )
    data, count = capi.audio_file_read_packets(audio_file, 0, 1000)
finally:
    capi.audio_file_close(audio_file)

Object-Oriented API (Modern)

The object-oriented API provides Pythonic wrappers with automatic resource management:

Advantages:

  • Automatic cleanup with context managers
  • Type safety with proper Python classes
  • Pythonic patterns (properties, iteration, operators)
  • Resource safety preventing memory leaks
  • IDE autocompletion and type hints

Use when:

  • Building new applications
  • Rapid prototyping and development
  • Team development where code safety is important
  • Working with complex audio workflows

Example:

from coremusic.audio import AudioFile

# Automatic resource management with context manager
with AudioFile("audio.wav") as audio_file:
    print(f"Duration: {audio_file.duration:.2f}s")
    print(f"Sample rate: {audio_file.format.sample_rate}Hz")
    data, count = audio_file.read_packets(0, 1000)

Your First Audio Application

Audio File Information Tool

Let's create a simple tool to display audio file information:

import sys

from coremusic.audio import AudioFile


def display_audio_info(filepath):
    """Display comprehensive audio file information."""
    with AudioFile(filepath) as audio:
        fmt = audio.format

        print(f"File: {filepath}")
        print(f"Duration: {audio.duration:.2f} seconds")
        print(f"Sample Rate: {fmt.sample_rate} Hz")
        print(f"Channels: {fmt.channels_per_frame}")
        print(f"Bits per Channel: {fmt.bits_per_channel}")
        print(f"Format: {fmt.format_id}")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python audio_info.py <audio_file>")
        sys.exit(1)

    display_audio_info(sys.argv[1])

Save this as audio_info.py and run:

python audio_info.py path/to/audio.wav

Simple Audio Player

Create a basic audio player:

import sys
import time

from coremusic.base import AudioPlayer


def play_audio(filepath):
    """Play an audio file."""
    # Create audio player
    player = AudioPlayer()

    # Load and setup
    player.load_file(filepath)
    player.setup_output()

    # Start playback
    print(f"Playing: {filepath}")
    player.play()

    # Wait for playback to complete
    while player.is_playing():
        progress = player.get_progress()
        print(f"Progress: {progress:.1%}", end='\r')
        time.sleep(0.1)

    print("\nPlayback complete!")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python play_audio.py <audio_file>")
        sys.exit(1)

    play_audio(sys.argv[1])

Audio/MIDI Synchronization

Use AudioClock for synchronizing audio and MIDI with precise timing:

import time

from coremusic.audio import AudioClock


def clock_demo():
    """Demonstrate AudioClock timing and synchronization."""
    with AudioClock() as clock:
        # Set playback rate
        clock.play_rate = 1.0  # Normal speed

        # Start the clock
        clock.start()
        print("Clock started at normal speed")

        # Monitor time in different formats
        for _i in range(5):
            seconds = clock.get_time_seconds()
            beats = clock.get_time_beats()
            samples = clock.get_time_samples()
            print(f"Time: {seconds:.3f}s, {beats:.2f} beats, {samples:.0f} samples")
            time.sleep(0.2)

        # Change playback rate
        clock.play_rate = 0.5  # Half speed
        print("\nChanged to half speed")

        start_time = clock.get_time_seconds()
        time.sleep(1.0)
        end_time = clock.get_time_seconds()

        print("Real time elapsed: 1.0s")
        print(f"Clock time elapsed: {end_time - start_time:.3f}s")

        clock.stop()
        print("Clock stopped")


if __name__ == "__main__":
    clock_demo()

Your First MIDI Application

MIDI Device Lister

Create a tool to list all MIDI devices:

from coremusic import capi
from coremusic.midi import get_destinations, get_sources


def list_midi_devices():
    """List all available MIDI devices, sources, and destinations."""
    device_count = capi.midi_get_number_of_devices()
    sources = get_sources()
    destinations = get_destinations()

    print(f"MIDI Devices: {device_count}")
    print(f"MIDI Sources: {len(sources)}")
    print(f"MIDI Destinations: {len(destinations)}")
    print()

    # A device is the physical unit; its entities carry the endpoints that
    # sources and destinations refer to.
    for i in range(device_count):
        device = capi.midi_get_device(i)
        try:
            print(f"Device {i}: {capi.midi_object_get_name(device)}")
        except Exception:
            print(f"Device {i}: <error reading name>")


if __name__ == "__main__":
    list_midi_devices()

Simple MIDI Monitor

Create a MIDI monitor that displays incoming messages:

import time

from coremusic.midi import MIDIClient, get_sources


def monitor_midi(seconds):
    """Monitor MIDI input from all sources."""
    client = MIDIClient("MIDI Monitor")

    try:
        input_port = client.create_input_port(
            "Monitor Input",
            callback=lambda data, host_time: print(f"Received {data.hex()}"),
        )

        sources = get_sources()
        for source in sources:
            input_port.connect_source(source)

        print(f"Monitoring {len(sources)} MIDI sources...")
        print("Press Ctrl+C to stop")

        deadline = time.monotonic() + seconds
        while time.monotonic() < deadline:
            time.sleep(0.1)

    except KeyboardInterrupt:
        print("\nStopping monitor...")
    finally:
        client.dispose()


if __name__ == "__main__":
    monitor_midi(seconds=1.0)

Next Steps

Now that you've created your first applications, explore:

Common Patterns

Context Managers

Always use context managers for automatic resource cleanup:

from coremusic.audio import AudioFile

# Good - automatic cleanup
with AudioFile("audio.wav") as audio:
    data = audio.read_packets(0, 1000)

# Also good - explicit but safe
audio = AudioFile("audio.wav")
try:
    audio.open()
    data = audio.read_packets(0, 1000)
finally:
    audio.close()

Error Handling

Handle errors appropriately:

from coremusic.audio import AudioFile
from coremusic.exceptions import AudioFileError

try:
    with AudioFile("audio.wav") as audio:
        data = audio.read_packets(0, 1000)
except AudioFileError as e:
    print(f"Audio file error: {e}")
except FileNotFoundError:
    print("File not found")

Resource Management

When using the functional API, always clean up resources:

from coremusic import capi

# Functional API - manual cleanup required
audio_file = capi.audio_file_open_url("audio.wav")
try:
    # Use the file
    data = capi.audio_file_read_packets(audio_file, 0, 1000)
finally:
    # Always close, even if errors occur
    capi.audio_file_close(audio_file)

Troubleshooting

Build Errors

If you encounter build errors:

  1. Ensure Xcode Command Line Tools are installed:
xcode-select --install
  1. Clean and rebuild:
make clean
make
  1. If using uv, ensure dependencies are synced:
uv sync --reinstall-package coremusic

Runtime Errors

"Module not found" errors:

  • Ensure you're running Python from the project directory
  • Verify the extension was built: ls src/coremusic/*.so

Audio playback issues:

  • Check audio file format is supported (WAV, AIFF, MP3, etc.)
  • Verify audio file exists and is not corrupted
  • Ensure macOS audio system is working

MIDI issues:

  • Check MIDI devices are connected and powered on
  • Verify MIDI devices appear in Audio MIDI Setup app
  • Ensure no other application is exclusively using MIDI devices

Getting Help

If you encounter issues:

  1. Check the API Reference for detailed function documentation
  2. Review the Examples for working code samples
  3. Search existing issues on GitHub
  4. Create a new issue with:
  5. Your macOS version
  6. Python version
  7. Complete error message
  8. Minimal code to reproduce the issue