native first
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
public struct AudioPipelineStats: Equatable {
|
||||
public private(set) var microphoneFrames: Int
|
||||
public private(set) var microphoneBytes: Int
|
||||
public private(set) var systemFrames: Int
|
||||
public private(set) var systemBytes: Int
|
||||
|
||||
public init(microphoneFrames: Int = 0, microphoneBytes: Int = 0, systemFrames: Int = 0, systemBytes: Int = 0) {
|
||||
self.microphoneFrames = microphoneFrames
|
||||
self.microphoneBytes = microphoneBytes
|
||||
self.systemFrames = systemFrames
|
||||
self.systemBytes = systemBytes
|
||||
}
|
||||
|
||||
public mutating func recordFrame(source: AudioSource, byteCount: Int) {
|
||||
switch source {
|
||||
case .microphone:
|
||||
microphoneFrames += 1
|
||||
microphoneBytes += byteCount
|
||||
case .system:
|
||||
systemFrames += 1
|
||||
systemBytes += byteCount
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
public struct OverlayActions {
|
||||
public let openSettings: () -> Void
|
||||
public let hideOverlay: () -> Void
|
||||
public let quitApp: () -> Void
|
||||
|
||||
public init(openSettings: @escaping () -> Void, hideOverlay: @escaping () -> Void, quitApp: @escaping () -> Void) {
|
||||
self.openSettings = openSettings
|
||||
self.hideOverlay = hideOverlay
|
||||
self.quitApp = quitApp
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
public struct OverlaySettings: Equatable {
|
||||
public static let minimumOpacity = 0.35
|
||||
public static let maximumOpacity = 0.95
|
||||
public static let `default` = OverlaySettings(backgroundOpacity: 0.72)
|
||||
|
||||
public let backgroundOpacity: Double
|
||||
|
||||
public init(backgroundOpacity: Double) {
|
||||
self.backgroundOpacity = Self.clamp(backgroundOpacity)
|
||||
}
|
||||
|
||||
public static func clamp(_ opacity: Double) -> Double {
|
||||
min(maximumOpacity, max(minimumOpacity, opacity))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import Foundation
|
||||
|
||||
public enum PCM16LE {
|
||||
public static func encode(samples: [Float]) -> Data {
|
||||
var data = Data()
|
||||
data.reserveCapacity(samples.count * MemoryLayout<Int16>.size)
|
||||
|
||||
for sample in samples {
|
||||
let clamped = max(-1.0, min(1.0, sample))
|
||||
let scaled: Int16
|
||||
|
||||
if clamped >= 1.0 {
|
||||
scaled = Int16.max
|
||||
} else if clamped <= -1.0 {
|
||||
scaled = Int16.min
|
||||
} else {
|
||||
scaled = Int16((clamped * Float(Int16.max)).rounded())
|
||||
}
|
||||
|
||||
var littleEndian = scaled.littleEndian
|
||||
withUnsafeBytes(of: &littleEndian) { bytes in
|
||||
data.append(contentsOf: bytes)
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
public static func resampleLinear(samples: [Float], sourceRate: Int, targetRate: Int = 16_000) -> [Float] {
|
||||
guard sourceRate > 0, targetRate > 0, !samples.isEmpty else {
|
||||
return []
|
||||
}
|
||||
|
||||
guard sourceRate != targetRate else {
|
||||
return samples
|
||||
}
|
||||
|
||||
let ratio = Double(sourceRate) / Double(targetRate)
|
||||
let outputCount = max(1, Int(Double(samples.count) / ratio))
|
||||
|
||||
return (0..<outputCount).map { index in
|
||||
let sourcePosition = Double(index) * ratio
|
||||
let lowerIndex = Int(sourcePosition)
|
||||
let upperIndex = min(lowerIndex + 1, samples.count - 1)
|
||||
let fraction = Float(sourcePosition - Double(lowerIndex))
|
||||
let lower = samples[min(lowerIndex, samples.count - 1)]
|
||||
let upper = samples[upperIndex]
|
||||
return lower + ((upper - lower) * fraction)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import Foundation
|
||||
|
||||
public enum AudioSource: String, Equatable {
|
||||
case microphone
|
||||
case system
|
||||
}
|
||||
|
||||
public struct PCMFrame: Equatable {
|
||||
public let source: AudioSource
|
||||
public let sampleRate: Int
|
||||
public let channels: Int
|
||||
public let pcmS16LE: Data
|
||||
public let timestamp: Date
|
||||
|
||||
public init(source: AudioSource, sampleRate: Int, channels: Int, pcmS16LE: Data, timestamp: Date = Date()) {
|
||||
self.source = source
|
||||
self.sampleRate = sampleRate
|
||||
self.channels = channels
|
||||
self.pcmS16LE = pcmS16LE
|
||||
self.timestamp = timestamp
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
public enum TrustStatus: Equatable {
|
||||
case idle
|
||||
case listening
|
||||
case screenContext
|
||||
case systemAudio
|
||||
case agentWorking
|
||||
case paused
|
||||
case permissionNeeded(String)
|
||||
case error(String)
|
||||
|
||||
public var menuBarTitle: String {
|
||||
switch self {
|
||||
case .idle:
|
||||
return "Mastermind: Idle"
|
||||
case .listening:
|
||||
return "Mastermind: Listening"
|
||||
case .screenContext:
|
||||
return "Mastermind: Screen"
|
||||
case .systemAudio:
|
||||
return "Mastermind: System Audio"
|
||||
case .agentWorking:
|
||||
return "Mastermind: Working"
|
||||
case .paused:
|
||||
return "Mastermind: Paused"
|
||||
case .permissionNeeded:
|
||||
return "Mastermind: Permission"
|
||||
case .error:
|
||||
return "Mastermind: Error"
|
||||
}
|
||||
}
|
||||
|
||||
public var isCapturing: Bool {
|
||||
switch self {
|
||||
case .listening, .screenContext, .systemAudio:
|
||||
return true
|
||||
case .idle, .agentWorking, .paused, .permissionNeeded, .error:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user