Skip to content

Commit

Permalink
Show error when no microphone is connected
Browse files Browse the repository at this point in the history
  • Loading branch information
CominAtYou committed Nov 9, 2024
1 parent a5954a4 commit 60f0a93
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 53 deletions.
8 changes: 4 additions & 4 deletions AirMute.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@
CODE_SIGN_ENTITLEMENTS = AirMute/AirMute.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 839U72R654;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
Expand All @@ -512,7 +512,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0.1;
MARKETING_VERSION = 1.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.cominatyou.AirMute;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -528,7 +528,7 @@
CODE_SIGN_ENTITLEMENTS = AirMute/AirMute.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 839U72R654;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
Expand All @@ -542,7 +542,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0.1;
MARKETING_VERSION = 1.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.cominatyou.AirMute;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
69 changes: 60 additions & 9 deletions AirMute/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import Cocoa
import AVFAudio
import Combine
import AVFoundation

@main
class AppDelegate: NSObject, NSApplicationDelegate {
var controller = AudioInputController()!
var controller: AudioInputController?
var cancellable: AnyCancellable?
var clientInitiatedAction = false
var isMicrophoneConnected = false

/// This exists as a secondary buffer for status text that isn't the "no microphone connected" text, as that must be displayed over all other text.
var statusItemTitle = "Inactive — Discord Not Open"

var statusBarMenuItem: NSStatusItem!
var statusItem: NSMenuItem!
Expand All @@ -21,8 +26,27 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let clientId = UserDefaults.standard.string(forKey: "client_id")
let clientSecret = UserDefaults.standard.string(forKey: "client_secret")

Task {
while true {
if !isMicrophoneConnected && self.statusItem.title != "Inactive — No Microphone Connected" {
self.statusItem.title = "Inactive — No Microphone Connected"
}
else if (isMicrophoneConnected && self.statusItem.title != statusItemTitle) {
self.statusItem.title = self.statusItemTitle
}

try? await Task.sleep(nanoseconds: 250_000_000)
}
}

if clientId == nil || clientId!.isEmpty || clientSecret == nil || clientSecret!.isEmpty {
statusItem.title = "Inactive — Missing Settings Values"
statusItemTitle = "Inactive — Missing Settings Values"
return
}

if AVCaptureDevice.default(for: .audio) != nil {
isMicrophoneConnected = true
controller = AudioInputController()
}

let rpc = RPC(clientId: clientId!, clientSecret: clientSecret!)
Expand All @@ -33,7 +57,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.didLaunchApplicationNotification, object: nil, queue: nil) { notif in
if let app = notif.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication {
if app.bundleIdentifier == "com.hnc.Discord" {
self.statusItem.title = "Trying to connect..."
self.statusItemTitle = "Trying to connect..."
Task {
while (true) {
do {
Expand All @@ -52,27 +76,55 @@ class AppDelegate: NSObject, NSApplicationDelegate {
NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.didTerminateApplicationNotification, object: nil, queue: nil) { notif in
if let app = notif.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication {
if app.bundleIdentifier == "com.hnc.Discord" {
self.statusItem.title = "Inactive — Discord Not Open"
self.controller.stop()
self.statusItemTitle = "Inactive — Discord Not Open"
self.controller?.stop()
self.rpc?.closeSocket()
}
}
}

if !NSRunningApplication.runningApplications(withBundleIdentifier: "com.hnc.Discord").isEmpty {
self.statusItem.title = "Trying to connect..."
self.statusItemTitle = "Trying to connect..."
Task {
do {
try rpc.connect()
}
catch {
DispatchQueue.main.async {
self.statusItem.title = "Inactive — Can't Connect to Dicord"
self.statusItemTitle = "Inactive — Can't Connect to Dicord"
}
logger.log("Couldn't establish connection: \(String(describing: error))")
}
}
}

NotificationCenter.default.addObserver(self, selector: #selector(audioCaptureDeviceConnected), name: AVCaptureDevice.wasConnectedNotification, object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(audioCaptureDeviceWasDisconnected), name: AVCaptureDevice.wasDisconnectedNotification, object: nil)
}

@objc func audioCaptureDeviceConnected(notification: Notification) {
guard let device = notification.object as? AVCaptureDevice, device.hasMediaType(.audio) else {
return
}

if (isMicrophoneConnected) { return }

NSLog("An audio capture device was connected.")
isMicrophoneConnected = true
controller = AudioInputController()
}

@objc func audioCaptureDeviceWasDisconnected(notification: Notification) {
guard let device = notification.object as? AVCaptureDevice, device.hasMediaType(.audio) else {
return
}

if AVCaptureDevice.default(for: .audio) == nil {
NSLog("An audio capture device was disconnected, and none are left.")
isMicrophoneConnected = false
controller = nil
}
}

@objc func launchPreferences() {
Expand All @@ -89,7 +141,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
window.styleMask = [.titled, .closable, .miniaturizable]
window.title = "AirMute — Settings"


window.delegate = windowDelegate

let controller = NSWindowController(window: window)
Expand All @@ -114,7 +165,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {

func makeMenu() {
let menu = NSMenu()
statusItem = NSMenuItem(title: "Inactive — Discord Not Open", action: nil, keyEquivalent: "")
statusItem = NSMenuItem(title: statusItemTitle, action: nil, keyEquivalent: "")
statusItem.isEnabled = false
menu.addItem(statusItem)

Expand Down
68 changes: 28 additions & 40 deletions AirMute/RPCEvents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ extension AppDelegate {

NSLog("Connected to @\(authentication.data.user.username)!")

DispatchQueue.main.async {
self.statusItem.title = "Inactive — Not in Voice"
}
self.statusItemTitle = "Inactive — Not in Voice"

_ = try rpcParam.subscribe(event: .voiceConnectionStatus)
_ = try rpcParam.subscribe(event: .voiceSettingsUpdate)
Expand All @@ -24,26 +22,22 @@ extension AppDelegate {
}
}
catch HTTPError.failed(let code, let error) {
DispatchQueue.main.async {
if (code == 401) {
self.statusItem.title = "Error — Invalid Client Secret"
rpc.closeSocket()
}
else {
self.statusItem.title = "Error — Unable to Connect"
logger.error("Got HTTP error \(code ?? -1): \(String(describing: error))")
}
if (code == 401) {
self.statusItemTitle = "Error — Invalid Client Secret"
rpc.closeSocket()
}
else {
self.statusItemTitle = "Error — Unable to Connect"
logger.error("Got HTTP error \(code ?? -1): \(String(describing: error))")
}
}
catch CommandError.failed(let code, let errorMessage) {
DispatchQueue.main.async {
if code == .oAuth2Error {
self.statusItem.title = "Error — Couldn't Obtain Authorization"
}
else {
self.statusItem.title = "Error — Command Failed"
logger.error("Got command error \(String(describing: code)): \(errorMessage)")
}
if code == .oAuth2Error {
self.statusItemTitle = "Error — Couldn't Obtain Authorization"
}
else {
self.statusItemTitle = "Error — Command Failed"
logger.error("Got command error \(String(describing: code)): \(errorMessage)")
}
}
catch {
Expand All @@ -61,34 +55,28 @@ extension AppDelegate {
else if eventType == .voiceConnectionStatus {
if let eventData = try? EventVoiceConnectionStatus.from(data: event) {
if eventData.data.state == .disconnected {
DispatchQueue.main.async {
self.statusItem.title = "Inactive — Not in Voice"
}
self.controller.stop()
self.statusItemTitle = "Inactive — Not in Voice"
self.controller?.stop()
}
else {
DispatchQueue.main.async {
self.statusItem.title = "Active — In Voice"
}
self.controller.start()
self.statusItemTitle = "Active — In Voice"
self.controller?.start()
}
}
}
}

rpc.onDisconnect { rpcParam, event in
DispatchQueue.main.async {
switch event.code {
case .invalidClientID:
self.statusItem.title = "Error — Invalid Client ID"
case .invalidOrigin:
self.statusItem.title = "Error — Invalid RPC Origin"
case .socketDisconnected:
break
default:
self.statusItem.title = "Error — Unable to Connect"
logger.error("Got disocnnect OpCode: \(String(describing: event.code)) \(event.message)")
}
switch event.code {
case .invalidClientID:
self.statusItemTitle = "Error — Invalid Client ID"
case .invalidOrigin:
self.statusItemTitle = "Error — Invalid RPC Origin"
case .socketDisconnected:
break
default:
self.statusItemTitle = "Error — Unable to Connect"
logger.error("Got disocnnect OpCode: \(String(describing: event.code)) \(event.message)")
}
}
}
Expand Down

0 comments on commit 60f0a93

Please sign in to comment.