678557caf7
Adds --capture-mic/--mic-output for a second, independently-captured audio track (mic vs system, written to separate outputs to avoid interleaving corruption). Embeds Info.plist at link time so the binary carries a stable CFBundleIdentifier and the usage-description keys TCC requires, and adds scripts/build-signed.sh + scripts/create-signing-identity.sh so a rebuilt binary keeps the same signing identity instead of losing granted permissions on every rebuild. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
55 lines
1.7 KiB
Swift
55 lines
1.7 KiB
Swift
import AudioTeeCore
|
|
import Foundation
|
|
|
|
/// CLI-specific output handler that writes raw PCM audio to a file descriptor
|
|
/// (stdout by default) and lifecycle messages to stderr via the logger.
|
|
///
|
|
/// `source` tags every stderr message so a consumer running two tracks at
|
|
/// once (e.g. system audio + microphone, each on its own fd) can tell which
|
|
/// track a given metadata/lifecycle message belongs to.
|
|
class BinaryAudioOutputHandler: AudioOutputHandler {
|
|
private let fd: Int32
|
|
private let source: String
|
|
|
|
init(fd: Int32 = STDOUT_FILENO, source: String = "audio") {
|
|
self.fd = fd
|
|
self.source = source
|
|
}
|
|
|
|
func handleAudioData(_ pointer: UnsafeRawPointer, count: Int) {
|
|
var written = 0
|
|
while written < count {
|
|
let result = write(fd, pointer.advanced(by: written), count - written)
|
|
if result >= 0 {
|
|
written += result
|
|
} else if errno == EINTR {
|
|
continue
|
|
} else {
|
|
break // EPIPE, EIO, etc — consumer gone or real error
|
|
}
|
|
}
|
|
}
|
|
|
|
func handleMetadata(_ metadata: AudioStreamMetadata) {
|
|
let taggedMetadata = AudioStreamMetadata(
|
|
sampleRate: metadata.sampleRate,
|
|
channelsPerFrame: metadata.channelsPerFrame,
|
|
bitsPerChannel: metadata.bitsPerChannel,
|
|
isFloat: metadata.isFloat,
|
|
captureMode: source,
|
|
deviceName: metadata.deviceName,
|
|
deviceUID: metadata.deviceUID,
|
|
encoding: metadata.encoding
|
|
)
|
|
AudioTeeLogging.logger.writeMessage(.metadata, data: taggedMetadata)
|
|
}
|
|
|
|
func handleStreamStart() {
|
|
AudioTeeLogging.logger.writeMessage(.streamStart, data: source)
|
|
}
|
|
|
|
func handleStreamStop() {
|
|
AudioTeeLogging.logger.writeMessage(.streamStop, data: source)
|
|
}
|
|
}
|