diff --git a/Sources/AudioTeeCLI/AudioTee.swift b/Sources/AudioTeeCLI/AudioTee.swift index 12b5b63..13ff419 100644 --- a/Sources/AudioTeeCLI/AudioTee.swift +++ b/Sources/AudioTeeCLI/AudioTee.swift @@ -9,6 +9,7 @@ struct AudioTee { var stereo: Bool = false var sampleRate: Double? var chunkDuration: Double = 0.2 + var flush: Bool = false init() {} @@ -32,6 +33,7 @@ struct AudioTee { audiotee --include-processes 1234 5678 9012 # Tap only these processes audiotee --exclude-processes 1234 5678 # Tap everything except these audiotee --mute # Mute processes being tapped + audiotee --flush # Flush stdout after each chunk """ ) @@ -43,6 +45,7 @@ struct AudioTee { name: "exclude-processes", help: "Process IDs to exclude (space-separated)") parser.addFlag(name: "mute", help: "Mute processes being tapped") parser.addFlag(name: "stereo", help: "Records in stereo") + parser.addFlag(name: "flush", help: "Flush stdout after each audio chunk (reduces latency when piping)") parser.addOption( name: "sample-rate", help: "Target sample rate (8000, 16000, 22050, 24000, 32000, 44100, 48000)") @@ -60,6 +63,7 @@ struct AudioTee { audioTee.excludeProcesses = try parser.getArrayValue("exclude-processes", as: Int32.self) audioTee.mute = parser.getFlag("mute") audioTee.stereo = parser.getFlag("stereo") + audioTee.flush = parser.getFlag("flush") audioTee.sampleRate = try parser.getOptionalValue("sample-rate", as: Double.self) audioTee.chunkDuration = try parser.getValue("chunk-duration", as: Double.self) @@ -137,7 +141,7 @@ struct AudioTee { throw ExitCode.failure } - let outputHandler = BinaryAudioOutputHandler() + let outputHandler = BinaryAudioOutputHandler(flushAfterWrite: flush) let recorder = AudioRecorder( deviceID: deviceID, outputHandler: outputHandler, convertToSampleRate: sampleRate, chunkDuration: chunkDuration) diff --git a/Sources/AudioTeeCore/Output/BinaryOutputHandler.swift b/Sources/AudioTeeCore/Output/BinaryOutputHandler.swift index 883a75b..703026c 100644 --- a/Sources/AudioTeeCore/Output/BinaryOutputHandler.swift +++ b/Sources/AudioTeeCore/Output/BinaryOutputHandler.swift @@ -1,12 +1,17 @@ import Foundation public class BinaryAudioOutputHandler: AudioOutputHandler { - public init() {} - + private let flushAfterWrite: Bool + + public init(flushAfterWrite: Bool = false) { + self.flushAfterWrite = flushAfterWrite + } public func handleAudioPacket(_ packet: AudioPacket) { - // TODO: should we use a DispatchQueue instead of writing directly? // Write raw binary audio data directly to stdout FileHandle.standardOutput.write(packet.data) + if flushAfterWrite { + fflush(stdout) + } } public func handleMetadata(_ metadata: AudioStreamMetadata) {