@@ -3,6 +3,8 @@ import SwiftUI
33struct SourceCardView : View {
44 @Binding var source : AudioSource
55 @EnvironmentObject var routingState : RoutingState
6+ @EnvironmentObject var audioDeviceManager : AudioDeviceManager
7+ @EnvironmentObject var audioRouter : AudioRouter
68 @State private var isHovering = false
79 @State private var showOptions = false
810
@@ -155,24 +157,19 @@ struct SourceCardView: View {
155157 Divider ( )
156158 . background ( LoopbackerTheme . border)
157159
158- // Pass-Through toggle
159- HStack {
160- Text ( " Pass-Thru " )
161- . font ( . system( size: 11 ) )
160+ // Monitor output picker
161+ HStack ( spacing : 6 ) {
162+ Image ( systemName : " speaker.wave.2 " )
163+ . font ( . system( size: 10 ) )
162164 . foregroundColor ( LoopbackerTheme . textSecondary)
163- . tooltip ( " Also play this source through your speakers for monitoring " )
164165
165- Spacer ( )
166+ Text ( " Monitor: " )
167+ . font ( . system( size: 11 ) )
168+ . foregroundColor ( LoopbackerTheme . textSecondary)
166169
167- Toggle ( " " , isOn: $source. isPassThrough)
168- . toggleStyle ( . switch)
169- . controlSize ( . mini)
170- . tint ( LoopbackerTheme . accent)
171- . onChange ( of: source. isPassThrough) { _, _ in
172- routingState. save ( )
173- }
174- . tooltip ( " Pass audio through without processing (monitor mode) " )
170+ monitorPicker
175171 }
172+ . tooltip ( " Hear this source through a physical output (speakers/headphones) " )
176173
177174 // Mute toggle
178175 HStack {
@@ -245,6 +242,36 @@ struct SourceCardView: View {
245242 . padding ( . bottom, 4 )
246243 }
247244
245+ // MARK: - Monitor output picker
246+
247+ @ViewBuilder
248+ private var monitorPicker : some View {
249+ let devices = audioDeviceManager. outputDevices
250+
251+ Picker ( " " , selection: Binding (
252+ get: { source. monitorOutputUID } ,
253+ set: { newUID in
254+ if !source. monitorOutputUID. isEmpty {
255+ audioRouter. stopOutputRouting ( virtualDeviceUID: " monitor: \( source. deviceUID) " )
256+ }
257+ source. monitorOutputUID = newUID
258+ source. monitorOutputName = devices. first ( where: { $0. uid == newUID } ) ? . name ?? " "
259+ routingState. save ( )
260+ if !newUID. isEmpty && source. isEnabled && !source. isMuted {
261+ audioRouter. startMonitoring ( sourceDeviceUID: source. deviceUID, outputDeviceUID: newUID)
262+ }
263+ }
264+ ) ) {
265+ Text ( " None " ) . tag ( " " )
266+ ForEach ( devices) { device in
267+ Text ( device. name) . tag ( device. uid)
268+ }
269+ }
270+ . pickerStyle ( . menu)
271+ . frame ( maxWidth: . infinity, alignment: . leading)
272+ . tint ( LoopbackerTheme . accent)
273+ }
274+
248275 // MARK: - Options toggle button
249276
250277 private var optionsToggle : some View {
0 commit comments