Skip to content

Latest commit

 

History

History
593 lines (468 loc) · 16.8 KB

File metadata and controls

593 lines (468 loc) · 16.8 KB

BitPerfectCore Architecture

This document describes the technical architecture and design of BitPerfectCore, the bit-perfect audio playback engine for macOS.

Overview

BitPerfectCore is designed as a modular, high-performance audio engine that provides true bit-perfect playback on macOS. The architecture prioritizes audio quality, performance, and reliability while maintaining a clean, Swift-idiomatic API.

Design Principles

1. Bit-Perfect Fidelity

  • No audio processing or modification in the playback path
  • Direct hardware access via CoreAudio HAL
  • Sample rate matching to avoid resampling
  • Exclusive device access when needed

2. Performance

  • Minimal CPU overhead
  • Efficient buffer management
  • Low-latency audio path
  • Optimized for modern Apple Silicon and Intel Macs

3. Reliability

  • Robust error handling
  • Graceful degradation
  • Thread-safe operations
  • Comprehensive testing

4. Modularity

  • Clean separation of concerns
  • Protocol-oriented design
  • Pluggable components
  • Easy to extend and maintain

5. Swift-Native

  • Modern Swift idioms
  • Type safety
  • Async/await support
  • SwiftUI integration ready

System Architecture

┌─────────────────────────────────────────────────────────────┐
│                    Client Application                        │
│                      (Perfecta)                              │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                  BitPerfectCore Public API                   │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │ BitPerfect   │  │   Audio      │  │   Format     │     │
│  │   Engine     │  │   Device     │  │   Decoder    │     │
│  │              │  │   Manager    │  │   Protocol   │     │
│  └──────────────┘  └──────────────┘  └──────────────┘     │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                    Core Components Layer                     │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │   Audio      │  │   Sample     │  │    Buffer    │     │
│  │  Renderer    │  │    Rate      │  │   Manager    │     │
│  │              │  │   Matcher    │  │              │     │
│  └──────────────┘  └──────────────┘  └──────────────┘     │
│                                                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │   Format     │  │   Metadata   │  │   Gapless    │     │
│  │  Decoders    │  │  Extractor   │  │   Player     │     │
│  │              │  │              │  │              │     │
│  └──────────────┘  └──────────────┘  └──────────────┘     │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                   CoreAudio HAL Layer                        │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │   Device     │  │   Stream     │  │   Hardware   │     │
│  │   Access     │  │   Config     │  │   Control    │     │
│  │              │  │              │  │              │     │
│  └──────────────┘  └──────────────┘  └──────────────┘     │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                    Audio Hardware (DAC)                      │
└─────────────────────────────────────────────────────────────┘

Core Components

1. BitPerfectEngine

Purpose: Main entry point and orchestrator for bit-perfect audio playback.

Responsibilities:

  • Initialize and configure the audio pipeline
  • Manage playback state (play, pause, stop)
  • Coordinate between components
  • Handle errors and state transitions

Key APIs:

class BitPerfectEngine {
    func configure(device: AudioDevice, format: AudioFormat) throws
    func play(url: URL) async throws
    func pause()
    func stop()
    func seek(to position: TimeInterval) throws
    
    var isPlaying: Bool { get }
    var currentTime: TimeInterval { get }
    var duration: TimeInterval { get }
}

2. AudioDeviceManager

Purpose: Manage audio devices and their configurations.

Responsibilities:

  • Enumerate available audio devices
  • Query device capabilities
  • Configure device settings
  • Monitor device changes (hot-plug)

Key APIs:

class AudioDeviceManager {
    func enumerateDevices() -> [AudioDevice]
    func defaultDevice() -> AudioDevice?
    func device(withID id: AudioDeviceID) -> AudioDevice?
    func setDefaultDevice(_ device: AudioDevice) throws
    
    var deviceChangePublisher: AnyPublisher<AudioDeviceChange, Never> { get }
}

struct AudioDevice {
    let id: AudioDeviceID
    let name: String
    let manufacturer: String
    let supportedSampleRates: [Double]
    let supportedBitDepths: [Int]
    let channels: Int
    let isDefault: Bool
}

3. AudioRenderer

Purpose: Direct audio rendering to hardware.

Responsibilities:

  • Interface with CoreAudio HAL
  • Manage audio buffers
  • Handle exclusive device access
  • Ensure bit-perfect output

Key APIs:

class AudioRenderer {
    func initialize(device: AudioDevice, format: AudioFormat) throws
    func start() throws
    func stop()
    func render(buffer: AudioBuffer) throws
    
    var isExclusiveMode: Bool { get }
    var actualSampleRate: Double { get }
}

4. SampleRateManager

Purpose: Match device sample rate to source material.

Responsibilities:

  • Detect source sample rate
  • Configure device sample rate
  • Validate sample rate compatibility
  • Handle sample rate changes

Key APIs:

class SampleRateManager {
    func detectSourceRate(from url: URL) throws -> Double
    func matchDeviceRate(device: AudioDevice, to sampleRate: Double) throws
    func validateCompatibility(device: AudioDevice, sampleRate: Double) -> Bool
}

5. FormatDecoder Protocol

Purpose: Unified interface for audio format decoding.

Responsibilities:

  • Decode audio files to PCM
  • Extract audio metadata
  • Support multiple formats
  • Efficient streaming

Key APIs:

protocol FormatDecoder {
    func canDecode(url: URL) -> Bool
    func decode(url: URL) throws -> AudioStream
    func metadata(from url: URL) throws -> AudioMetadata
}

// Implementations:
class FLACDecoder: FormatDecoder { }
class ALACDecoder: FormatDecoder { }
class WAVDecoder: FormatDecoder { }
class AIFFDecoder: FormatDecoder { }
class DSDDecoder: FormatDecoder { }

6. BufferManager

Purpose: Efficient audio buffer management.

Responsibilities:

  • Allocate and manage audio buffers
  • Handle buffer queuing
  • Optimize memory usage
  • Prevent buffer underruns

Key APIs:

class BufferManager {
    func allocateBuffer(size: Int) -> AudioBuffer
    func enqueueBuffer(_ buffer: AudioBuffer)
    func dequeueBuffer() -> AudioBuffer?
    func clear()
    
    var bufferCount: Int { get }
    var isBufferReady: Bool { get }
}

7. GaplessPlayer

Purpose: Seamless track transitions.

Responsibilities:

  • Pre-buffer next track
  • Seamless transitions
  • Handle format changes
  • Maintain playback continuity

Key APIs:

class GaplessPlayer {
    func enqueueTrack(url: URL) throws
    func prepareNextTrack() async throws
    func transitionToNext() throws
    
    var currentTrack: URL? { get }
    var nextTrack: URL? { get }
}

8. MetadataExtractor

Purpose: Extract audio file metadata.

Responsibilities:

  • Read ID3, Vorbis, APE tags
  • Extract embedded artwork
  • Parse audio properties
  • Support multiple formats

Key APIs:

class MetadataExtractor {
    func extract(from url: URL) throws -> AudioMetadata
}

struct AudioMetadata {
    let title: String?
    let artist: String?
    let album: String?
    let albumArtist: String?
    let genre: String?
    let year: Int?
    let trackNumber: Int?
    let artwork: Data?
    
    let duration: TimeInterval
    let sampleRate: Double
    let bitDepth: Int
    let channels: Int
    let bitrate: Int?
}

Data Flow

Playback Pipeline

┌──────────────┐
│  Audio File  │
└──────┬───────┘
       │
       ▼
┌──────────────┐
│   Format     │ ─── Detect format
│   Decoder    │ ─── Decode to PCM
└──────┬───────┘
       │
       ▼
┌──────────────┐
│   Sample     │ ─── Match device rate
│    Rate      │ ─── Configure hardware
│   Matcher    │
└──────┬───────┘
       │
       ▼
┌──────────────┐
│   Buffer     │ ─── Queue audio buffers
│   Manager    │ ─── Prevent underruns
└──────┬───────┘
       │
       ▼
┌──────────────┐
│   Audio      │ ─── Render to hardware
│  Renderer    │ ─── Bit-perfect output
└──────┬───────┘
       │
       ▼
┌──────────────┐
│   Hardware   │
│     DAC      │
└──────────────┘

Threading Model

Main Thread

  • Public API calls
  • State management
  • UI updates (via callbacks/publishers)

Audio Thread (Real-time)

  • Audio rendering
  • Buffer management
  • CoreAudio callbacks
  • Must be lock-free and deterministic

Decoder Thread

  • File I/O
  • Audio decoding
  • Metadata extraction
  • Buffer preparation

Device Monitor Thread

  • Device enumeration
  • Hot-plug detection
  • Configuration changes

Error Handling

Error Types

enum BitPerfectError: Error {
    case deviceNotFound
    case deviceNotAvailable
    case unsupportedFormat
    case unsupportedSampleRate
    case exclusiveModeUnavailable
    case bufferUnderrun
    case decodingError(underlying: Error)
    case hardwareError(underlying: Error)
}

Error Recovery Strategy

  1. Graceful Degradation: Fall back to non-exclusive mode if needed
  2. Retry Logic: Attempt recovery for transient errors
  3. User Notification: Report errors via callbacks/publishers
  4. Logging: Comprehensive error logging for debugging

Memory Management

Principles

  • Use ARC for Swift objects
  • Manual memory management for audio buffers (performance)
  • Pool audio buffers to avoid allocations
  • Release resources promptly

Buffer Allocation Strategy

  • Pre-allocate buffer pool at initialization
  • Reuse buffers to avoid allocations during playback
  • Use page-aligned memory for optimal performance
  • Monitor memory usage and adjust pool size

Performance Considerations

Optimization Targets

  • CPU Usage: < 5% on modern Macs during playback
  • Latency: < 10ms from decode to output
  • Memory: < 50MB for typical playback
  • Buffer Underruns: Zero under normal conditions

Optimization Techniques

  • Lock-free audio thread
  • SIMD optimizations where applicable
  • Efficient buffer management
  • Lazy initialization
  • Minimal allocations in hot paths

Testing Strategy

Unit Tests

  • Individual component testing
  • Mock CoreAudio interfaces
  • Edge case coverage
  • Performance benchmarks

Integration Tests

  • End-to-end playback tests
  • Multi-format validation
  • Device switching tests
  • Error recovery tests

Validation Tests

  • Bit-perfect verification (loopback)
  • Sample rate accuracy
  • Latency measurements
  • CPU profiling

Platform Integration

macOS CoreAudio HAL

Key APIs Used:

  • AudioObjectGetPropertyData - Query device properties
  • AudioDeviceCreateIOProcID - Create audio callback
  • AudioDeviceStart/Stop - Control playback
  • AudioObjectAddPropertyListener - Monitor changes

Exclusive Mode:

  • Use kAudioDevicePropertyHogMode to take exclusive control
  • Bypass system audio processing
  • Direct hardware access

Sample Rate Matching:

  • Query kAudioDevicePropertyNominalSampleRate
  • Set device sample rate to match source
  • Verify actual rate after configuration

Future Considerations

Potential Enhancements

  • Multi-channel audio support (5.1, 7.1)
  • DSD native playback
  • Network audio streaming
  • Room correction integration
  • Plugin architecture for custom processors
  • Cross-platform support (iOS, Linux)

Research Areas

  • MQA decoding
  • High sample rate optimization (384kHz+)
  • Low-latency monitoring
  • Audio analysis and visualization
  • Hardware-accelerated decoding

Dependencies

Core Frameworks

  • CoreAudio - Audio hardware access
  • AVFoundation - Audio format support
  • Accelerate - SIMD optimizations

Third-Party Libraries (Potential)

  • libFLAC - FLAC decoding
  • libsndfile - Multi-format support
  • TagLib - Metadata extraction

API Design Philosophy

Principles

  1. Swift-First: Idiomatic Swift API
  2. Type-Safe: Leverage Swift's type system
  3. Async-Ready: Support async/await
  4. Publisher-Based: Use Combine for reactive updates
  5. Error-Explicit: Clear error handling
  6. Documentation: Comprehensive inline docs

Example API Usage

import BitPerfectCore

// Initialize engine
let engine = BitPerfectEngine()

// Configure device
let deviceManager = AudioDeviceManager()
let devices = deviceManager.enumerateDevices()
let dac = devices.first { $0.name.contains("DAC") }

try engine.configure(device: dac!)

// Play audio
try await engine.play(url: audioFileURL)

// Monitor state
engine.statePublisher
    .sink { state in
        print("Playback state: \(state)")
    }
    .store(in: &cancellables)

Versioning & Compatibility

Semantic Versioning

  • MAJOR: Breaking API changes
  • MINOR: New features, backward compatible
  • PATCH: Bug fixes, backward compatible

API Stability

  • Public API marked with @available
  • Deprecation warnings before removal
  • Migration guides for breaking changes

Documentation Standards

Code Documentation

  • All public APIs documented with DocC
  • Usage examples in documentation
  • Parameter descriptions
  • Return value descriptions
  • Error conditions documented

Architecture Documentation

  • This document (ARCHITECTURE.md)
  • Component diagrams
  • Sequence diagrams
  • API reference (generated)

Last Updated: June 2026
Version: 0.5.0
Author: Mario Alberto Arce