Phase 1: dual-track audio capture → mixed-mono 16 kHz WAV + mic VAD

AudioRecorder captures system audio (ScreenCaptureKit) + mic (AVAudioEngine) on a
single serial ioQueue, one shared monotonic t0, time-driven writers (pad gaps /
trim overlaps) so tracks stay aligned, and an energy mic-VAD for 'self' spans.
AudioMixer sums the aligned tracks into mixed_mono_16k.wav. SessionController
drives a serialized start/stop state machine, writes the session folder +
self_vad.json, exposes live level meters, and finalizes on quit.

Hardening from review: ioQueue single-domain (no races), stop() never hangs
(mic-first teardown + bounded stopCapture), layout-agnostic mic deep-copy,
discard-only video output to keep SCStream alive, VAD lockstep on committed
frames, stable signing team in project.yml, single-instance enforcement.
This commit is contained in:
Grant Gilliam
2026-06-05 21:30:11 -05:00
parent b2ae3a62b9
commit fd7e1a5907
12 changed files with 1018 additions and 10 deletions
+10 -2
View File
@@ -10,9 +10,16 @@ import SwiftUI
struct Ten31TranscriptsApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
@StateObject private var settings = AppSettings()
@StateObject private var settings: AppSettings
@StateObject private var permissions = PermissionsManager()
@StateObject private var health = SparkControlHealth()
@StateObject private var session: SessionController
init() {
let settings = AppSettings()
_settings = StateObject(wrappedValue: settings)
_session = StateObject(wrappedValue: SessionController(settings: settings))
}
var body: some Scene {
MenuBarExtra {
@@ -20,8 +27,9 @@ struct Ten31TranscriptsApp: App {
.environmentObject(settings)
.environmentObject(permissions)
.environmentObject(health)
.environmentObject(session)
} label: {
Image(systemName: "waveform.circle")
Image(systemName: session.state == .recording ? "waveform.circle.fill" : "waveform.circle")
}
.menuBarExtraStyle(.window)
}