165 lines
5.6 KiB
Swift
165 lines
5.6 KiB
Swift
import AppKit
|
|
import Combine
|
|
import Foundation
|
|
import os
|
|
import SwiftUI
|
|
import CoreAudio
|
|
import AudioToolbox
|
|
|
|
/// Controls system audio management during recording
|
|
class MediaController: ObservableObject {
|
|
static let shared = MediaController()
|
|
private var previousVolume: Float = 1.0
|
|
private var didMuteAudio = false
|
|
|
|
private let logger = Logger(subsystem: "com.prakashjoshipax.voiceink", category: "MediaController")
|
|
|
|
@Published var isSystemMuteEnabled: Bool = UserDefaults.standard.bool(forKey: "isSystemMuteEnabled") {
|
|
didSet {
|
|
UserDefaults.standard.set(isSystemMuteEnabled, forKey: "isSystemMuteEnabled")
|
|
}
|
|
}
|
|
|
|
private init() {
|
|
// Set default if not already set
|
|
if !UserDefaults.standard.contains(key: "isSystemMuteEnabled") {
|
|
UserDefaults.standard.set(true, forKey: "isSystemMuteEnabled")
|
|
}
|
|
}
|
|
|
|
/// Mutes system audio during recording
|
|
func muteSystemAudio() async -> Bool {
|
|
guard isSystemMuteEnabled else {
|
|
logger.info("System mute feature is disabled")
|
|
return false
|
|
}
|
|
|
|
// Get current volume before muting
|
|
previousVolume = getSystemVolume()
|
|
logger.info("Muting system audio. Previous volume: \(self.previousVolume)")
|
|
|
|
// Set system volume to 0 (mute)
|
|
setSystemVolume(0.0)
|
|
didMuteAudio = true
|
|
return true
|
|
}
|
|
|
|
/// Restores system audio after recording
|
|
func unmuteSystemAudio() async {
|
|
guard isSystemMuteEnabled, didMuteAudio else {
|
|
return
|
|
}
|
|
|
|
logger.info("Unmuting system audio to previous volume: \(self.previousVolume)")
|
|
setSystemVolume(previousVolume)
|
|
didMuteAudio = false
|
|
}
|
|
|
|
/// Gets the current system output volume (0.0 to 1.0)
|
|
private func getSystemVolume() -> Float {
|
|
var defaultOutputDeviceID = AudioDeviceID(0)
|
|
var defaultOutputDeviceIDSize = UInt32(MemoryLayout.size(ofValue: defaultOutputDeviceID))
|
|
|
|
// Get the default output device
|
|
var getDefaultOutputDeviceProperty = AudioObjectPropertyAddress(
|
|
mSelector: kAudioHardwarePropertyDefaultOutputDevice,
|
|
mScope: kAudioObjectPropertyScopeGlobal,
|
|
mElement: kAudioObjectPropertyElementMain)
|
|
|
|
let status = AudioObjectGetPropertyData(
|
|
AudioObjectID(kAudioObjectSystemObject),
|
|
&getDefaultOutputDeviceProperty,
|
|
0,
|
|
nil,
|
|
&defaultOutputDeviceIDSize,
|
|
&defaultOutputDeviceID)
|
|
|
|
if status != kAudioHardwareNoError {
|
|
logger.error("Failed to get default output device: \(status)")
|
|
return 1.0 // Default to full volume on error
|
|
}
|
|
|
|
// Get the volume
|
|
var volume: Float = 0.0
|
|
var volumeSize = UInt32(MemoryLayout.size(ofValue: volume))
|
|
|
|
var volumeProperty = AudioObjectPropertyAddress(
|
|
mSelector: kAudioDevicePropertyVolumeScalar,
|
|
mScope: kAudioDevicePropertyScopeOutput,
|
|
mElement: kAudioObjectPropertyElementMain)
|
|
|
|
let volumeStatus = AudioObjectGetPropertyData(
|
|
defaultOutputDeviceID,
|
|
&volumeProperty,
|
|
0,
|
|
nil,
|
|
&volumeSize,
|
|
&volume)
|
|
|
|
if volumeStatus != kAudioHardwareNoError {
|
|
logger.error("Failed to get system volume: \(volumeStatus)")
|
|
return 1.0 // Default to full volume on error
|
|
}
|
|
|
|
return volume
|
|
}
|
|
|
|
/// Sets the system output volume (0.0 to 1.0)
|
|
private func setSystemVolume(_ volume: Float) {
|
|
var defaultOutputDeviceID = AudioDeviceID(0)
|
|
var defaultOutputDeviceIDSize = UInt32(MemoryLayout.size(ofValue: defaultOutputDeviceID))
|
|
|
|
// Get the default output device
|
|
var getDefaultOutputDeviceProperty = AudioObjectPropertyAddress(
|
|
mSelector: kAudioHardwarePropertyDefaultOutputDevice,
|
|
mScope: kAudioObjectPropertyScopeGlobal,
|
|
mElement: kAudioObjectPropertyElementMain)
|
|
|
|
let status = AudioObjectGetPropertyData(
|
|
AudioObjectID(kAudioObjectSystemObject),
|
|
&getDefaultOutputDeviceProperty,
|
|
0,
|
|
nil,
|
|
&defaultOutputDeviceIDSize,
|
|
&defaultOutputDeviceID)
|
|
|
|
if status != kAudioHardwareNoError {
|
|
logger.error("Failed to get default output device: \(status)")
|
|
return
|
|
}
|
|
|
|
// Clamp volume to valid range
|
|
var safeVolume = max(0.0, min(1.0, volume))
|
|
|
|
// Set the volume
|
|
var volumeProperty = AudioObjectPropertyAddress(
|
|
mSelector: kAudioDevicePropertyVolumeScalar,
|
|
mScope: kAudioDevicePropertyScopeOutput,
|
|
mElement: kAudioObjectPropertyElementMain)
|
|
|
|
let volumeStatus = AudioObjectSetPropertyData(
|
|
defaultOutputDeviceID,
|
|
&volumeProperty,
|
|
0,
|
|
nil,
|
|
UInt32(MemoryLayout.size(ofValue: safeVolume)),
|
|
&safeVolume)
|
|
|
|
if volumeStatus != kAudioHardwareNoError {
|
|
logger.error("Failed to set system volume: \(volumeStatus)")
|
|
} else {
|
|
logger.info("Set system volume to \(safeVolume)")
|
|
}
|
|
}
|
|
}
|
|
|
|
extension UserDefaults {
|
|
func contains(key: String) -> Bool {
|
|
return object(forKey: key) != nil
|
|
}
|
|
|
|
var isSystemMuteEnabled: Bool {
|
|
get { bool(forKey: "isSystemMuteEnabled") }
|
|
set { set(newValue, forKey: "isSystemMuteEnabled") }
|
|
}
|
|
} |