| 
          import CoreAudio | 
        
        
           | 
          import Foundation | 
        
        
           | 
          
 | 
        
        
           | 
          func deviceName(deviceID: AudioDeviceID) -> String? { | 
        
        
           | 
              var propertySize = UInt32(MemoryLayout<CFString>.size) | 
        
        
           | 
              var name: Unmanaged<CFString>? | 
        
        
           | 
              var address = AudioObjectPropertyAddress( | 
        
        
           | 
                  mSelector: kAudioDevicePropertyDeviceNameCFString, mScope: kAudioObjectPropertyScopeGlobal, | 
        
        
           | 
                  mElement: kAudioObjectPropertyElementMain) | 
        
        
           | 
              guard AudioObjectGetPropertyData(deviceID, &address, 0, nil, &propertySize, &name) == noErr | 
        
        
           | 
              else { return nil } | 
        
        
           | 
              return name?.takeRetainedValue() as String? | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          func inputDevices() -> [AudioDeviceID] { | 
        
        
           | 
              var propertySize: UInt32 = 0 | 
        
        
           | 
              var address = AudioObjectPropertyAddress( | 
        
        
           | 
                  mSelector: kAudioHardwarePropertyDevices, mScope: kAudioObjectPropertyScopeGlobal, | 
        
        
           | 
                  mElement: kAudioObjectPropertyElementMain) | 
        
        
           | 
          
 | 
        
        
           | 
              guard | 
        
        
           | 
                  AudioObjectGetPropertyDataSize( | 
        
        
           | 
                      AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, &propertySize) == noErr | 
        
        
           | 
              else { return [] } | 
        
        
           | 
          
 | 
        
        
           | 
              var deviceIDs = [AudioDeviceID]( | 
        
        
           | 
                  repeating: kAudioObjectUnknown, count: Int(propertySize) / MemoryLayout<AudioDeviceID>.size) | 
        
        
           | 
              guard | 
        
        
           | 
                  AudioObjectGetPropertyData( | 
        
        
           | 
                      AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, &propertySize, &deviceIDs) | 
        
        
           | 
                      == noErr | 
        
        
           | 
              else { return [] } | 
        
        
           | 
              return deviceIDs | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          func setDefaultInput(deviceID: AudioDeviceID) { | 
        
        
           | 
              var deviceID = deviceID | 
        
        
           | 
              var address = AudioObjectPropertyAddress( | 
        
        
           | 
                  mSelector: kAudioHardwarePropertyDefaultInputDevice, | 
        
        
           | 
                  mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMain) | 
        
        
           | 
              let status = AudioObjectSetPropertyData( | 
        
        
           | 
                  AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, | 
        
        
           | 
                  UInt32(MemoryLayout<AudioDeviceID>.size), &deviceID) | 
        
        
           | 
              if status != noErr { print("Error setting default input device: \(status)") } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          func currentInputDevice() -> AudioDeviceID? { | 
        
        
           | 
              var deviceID = kAudioObjectUnknown | 
        
        
           | 
              var propertySize = UInt32(MemoryLayout<AudioDeviceID>.size) | 
        
        
           | 
              var address = AudioObjectPropertyAddress( | 
        
        
           | 
                  mSelector: kAudioHardwarePropertyDefaultInputDevice, | 
        
        
           | 
                  mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMain) | 
        
        
           | 
              guard | 
        
        
           | 
                  AudioObjectGetPropertyData( | 
        
        
           | 
                      AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, &propertySize, &deviceID) | 
        
        
           | 
                      == noErr | 
        
        
           | 
              else { return nil } | 
        
        
           | 
              return deviceID | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          func findDevice(containing substring: String) -> AudioDeviceID? { | 
        
        
           | 
              let devices = inputDevices().filter { | 
        
        
           | 
                  deviceName(deviceID: $0)?.localizedCaseInsensitiveContains(substring) == true | 
        
        
           | 
              } | 
        
        
           | 
          
 | 
        
        
           | 
              switch devices.count { | 
        
        
           | 
              case 0: | 
        
        
           | 
                  print("No matching device found.") | 
        
        
           | 
                  return nil | 
        
        
           | 
              case 1: | 
        
        
           | 
                  return devices.first | 
        
        
           | 
              default: | 
        
        
           | 
                  print("Multiple matching devices found:") | 
        
        
           | 
                  devices.forEach { print("- \(deviceName(deviceID: $0) ?? "Unknown")") } | 
        
        
           | 
                  return nil  // Or handle multiple matches differently, e.g., let the user choose. | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          func monitorInputChanges(deviceNameSubstring: String) { | 
        
        
           | 
              var address = AudioObjectPropertyAddress( | 
        
        
           | 
                  mSelector: kAudioHardwarePropertyDefaultInputDevice, | 
        
        
           | 
                  mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMain) | 
        
        
           | 
              var lastDeviceID: AudioDeviceID? = currentInputDevice() | 
        
        
           | 
          
 | 
        
        
           | 
              AudioObjectAddPropertyListenerBlock(AudioObjectID(kAudioObjectSystemObject), &address, nil) { | 
        
        
           | 
                  _, _ in | 
        
        
           | 
                  guard let targetDeviceID = findDevice(containing: deviceNameSubstring) else { | 
        
        
           | 
                      print("Target device not found.  Stopping monitoring.") | 
        
        
           | 
                      return | 
        
        
           | 
                  } | 
        
        
           | 
          
 | 
        
        
           | 
                  guard let currentDeviceID = currentInputDevice(), currentDeviceID != targetDeviceID, | 
        
        
           | 
                      currentDeviceID != lastDeviceID | 
        
        
           | 
                  else { return } | 
        
        
           | 
          
 | 
        
        
           | 
                  print("Switching back to target device #\(targetDeviceID).") | 
        
        
           | 
                  setDefaultInput(deviceID: targetDeviceID) | 
        
        
           | 
                  lastDeviceID = targetDeviceID | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          func main() { | 
        
        
           | 
              guard CommandLine.argc > 1 else { | 
        
        
           | 
                  print("Please provide a device name substring as a parameter.") | 
        
        
           | 
                  return | 
        
        
           | 
              } | 
        
        
           | 
          
 | 
        
        
           | 
              let deviceNameSubstring = CommandLine.arguments[1] | 
        
        
           | 
          
 | 
        
        
           | 
              guard findDevice(containing: deviceNameSubstring) != nil else { | 
        
        
           | 
                  print("No matching device found.  Exiting.") | 
        
        
           | 
                  return | 
        
        
           | 
              } | 
        
        
           | 
          
 | 
        
        
           | 
              print("Monitoring changes for device matching substring: \(deviceNameSubstring)") | 
        
        
           | 
              monitorInputChanges(deviceNameSubstring: deviceNameSubstring) | 
        
        
           | 
              RunLoop.current.run() | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          main() |