Background Music sur macOS : techniques avancées pour orchestrer vos flux audio

La gestion granulaire du son sous macOS représente un défi technique que Background Music résout élégamment. Cette solution libre exploite une architecture à base de périphérique audio virtuel pour offrir un contrôle fin sur chaque source sonore, sans modifier le noyau système.

Les limitations natives de Core Audio

Le framework Core Audio d'Apple impose une architecture où le mixer système agit comme point unique de contrôle. Conséquence : impossibilité d'atténuer Spotify tout en conservant les notifications Slack à plein volume. Background Music contourne cette restriction en insérant un Audio Server Plug-in entre les applications et la sortie matérielle.

Architecture technique du projet

Le dépôt se structure en trois couches distinctes :

Module Responsabilité Langage
BGMApp Interfcae utilisateur et orchestration Objective-C/Swift
BGMDriver Pilote audio virtuel et routage C++
BGMXPCHelper Communication inter-processus sécurisée C

Méthodes d'installatoin comparées

Approche par gestionnaire de paquets

Pour une intégration automatique avec Homebrew :

brew tap homebrew/cask-drivers
brew install --cask background-music

Compilation depuis les sources

Les contributeurs peuvent générer une build personnalisée :

# Clonage avec les sous-modules requis
git clone --recursive https://github.com/kyleneideck/BackgroundMusic.git
cd BackgroundMusic

# Génération du projet Xcode
xcodebuild -project BGMDriver/BGMDriver.xcodeproj \
           -target BGMDriver \
           -configuration Release

# Installation avec privilèges administrateur
sudo installer -pkg build/BGMDriver.pkg -target /

Configuration des flux audio par application

Une fois le pilote actif, chaque processus émettant du son apparaît comme entrée distincte. Le diagramme suivant illustre le flux de données :


┌─────────────┐     ┌──────────────┐     ┌─────────────┐     ┌──────────┐
│   Spotify   │────→│              │     │             │     │          │
├─────────────┤     │   Périphérique   │     │   Mixer     │────→│ Sortie   │
│    Zoom     │────→│   virtuel BGMApp │────→│   interne   │     │ matérielle│
├─────────────┤     │   (2 canaux)     │     │             │     │          │
│   Safari    │────→│              │     │             │     │          │
└─────────────┘     └──────────────┘     └─────────────┘     └──────────┘

Implémentation de la pause intelligente

Le mécanisme d'auto-pause repose sur l'observation des propriétés kAudioDevicePropertyDeviceIsRunningSomewhere. Lorsqu'un second processus déclenche une lecture, Background Music émet un événement AppleScript vers le lecteur musical actif.

Exemple de configuration pour un lecteur personnalisé :

// BGMCustomPlayer.m
#import "BGMMusicPlayer.h"

@interface BGMCustomPlayer : NSObject <BGMMusicPlayer>
@end

@implementation BGMCustomPlayer

- (NSString *)bundleID {
    return @"com.example.customplayer";
}

- (void)pause {
    NSAppleScript *script = [[NSAppleScript alloc] initWithSource:
        @"tell application \"CustomPlayer\" to pause"];
    [script executeAndReturnError:nil];
}

- (BOOL)isRunning {
    return [[NSWorkspace sharedWorkspace] 
            runningApplicationsWithBundleIdentifier:[self bundleID]].count > 0;
}

@end

Capture du son système

La fonctionnalité d'enregistrement exploite la même infrastructure virtuelle. En sélectionnant Background Music comme entrée dans QuickTime ou OBS, on capture l'ensemble du mixage sans câble physique.

Script d'automatisation pour lancer un enregistrement :

#!/bin/zsh
# record_system_audio.sh

DEVICE_UID="com.bearisdriving.BGM.virtual"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
OUTPUT="$HOME/Enregistrements/capture_${TIMESTAMP}.m4a"

# Sélection du périphérique virtuel
SwitchAudioSource -t input -u "$DEVICE_UID"

# Lancement de l'enregistrement avec ffmpeg
ffmpeg -f avfoundation -i ":$DEVICE_UID" \
       -c:a aac_at -b:a 256k \
       -t 300 "$OUTPUT" &

echo $! > /tmp/bgm_recorder.pid

Optimisation de la latence

Le tampon audio par défaut entroduit une latence perceptible. Pour les applications temps-réel (DAW, visioconférence), éditez BGMDriver/BGM_Types.h :

// Valeurs originales
#define kSampleRate 48000.0
#define kRingBufferFrameSize 16384

// Configuration basse latence
#define kSampleRate 48000.0
#define kRingBufferFrameSize 2048  // Réduction par facteur 8

Attention : une taille de tampon trop faible provoque des artefacts audio (crépitements) sous charge CPU élevée.

Résolution des conflits avec Audio Hijack

L'empilement de pilotes audio virtuels crée fréquemment des boucles de feedback. Si Background Music et Audio Hijack coexistent :

  1. Désactivez Instant On dans Audio Hijack
  2. Configurez Background Music comme source unique dans Audio Hijack
  3. Redémarrez le daemon coreaudiod : sudo killall -9 coreaudiod

Extension : création d'un contrôleur MIDI

Pour les utilisateurs de contrôleurs matériels, ce pont OSC permet d'ajuster les volumes via TouchOSC ou similaire :

# bgm_osc_bridge.py
import asyncio
from pythonosc import dispatcher, osc_server
from Foundation import NSAppleScript

VOLUME_SCRIPT = '''
tell application "System Events"
    tell application process "Background Music"
        set value of slider {} of group {} of window 1 to {}
    end tell
end tell
'''

def handle_volume(address, *args):
    app_index = int(address.split('/')[-1])
    volume = min(max(float(args[0]), 0.0), 1.0)
    
    script = NSAppleScript.alloc().initWithSource_(
        VOLUME_SCRIPT.format(app_index, 1, volume * 100)
    )
    script.executeAndReturnError_(None)

dispatcher = dispatcher.Dispatcher()
dispatcher.map("/bgm/volume/*", handle_volume)

server = osc_server.AsyncIOOSCUDPServer(
    ('0.0.0.0', 9000), dispatcher, asyncio.get_event_loop()
)
server.serve()

Intégration dans un workflow de développement

Pour les développeurs souhaitant isoler les alertes sonores de leur IDE :

// .bgm_profile.json
{
  "profiles": {
    "focus_dev": {
      "com.apple.dt.Xcode": 0.3,
      "com.tinyspeck.slackmacgap": 0.1,
      "com.spotify.client": 0.0,
      "system_sounds": 0.15
    },
    "pair_programming": {
      "com.apple.dt.Xcode": 0.5,
      "us.zoom.xos": 0.8,
      "com.spotify.client": 0.2
    }
  },
  "auto_switch": {
    "trigger": "frontmost_application",
    "delay_ms": 500
  }
}

Ce profil JSON peut être chargé via un raccourci clavier configuré dans Hammerspoon ou BetterTouchTool.

Étiquettes: macOS Core Audio Objective-C Homebrew AppleScript

Publié le 28 août à 08h05