From 187a1b7fdf843605dd1e269f4f088d2790434451 Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:15:23 +0800 Subject: [PATCH 1/5] imp --- .../xcshareddata/swiftpm/Package.resolved | 11 +----- Multiplatform/Controllers/AppContext.swift | 35 +++++++++++-------- Multiplatform/Views/AudioControlsPanel.swift | 18 ++++++---- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/LiveKitExample-dev.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LiveKitExample-dev.xcworkspace/xcshareddata/swiftpm/Package.resolved index b104952..eae374f 100644 --- a/LiveKitExample-dev.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/LiveKitExample-dev.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "7211f5ec68d50979bdb0938d0e60396e453aaab524b20b84c083042744b32f68", + "originHash" : "5cff40a09faf62672c6c96ee8f2a9d9299d3b8e62fa5915e4999d02e33ec230c", "pins" : [ { "identity" : "keychainaccess", @@ -63,15 +63,6 @@ "revision" : "2547102afd04fe49f1b286090f13ebce07284980", "version" : "1.31.1" } - }, - { - "identity" : "webrtc-xcframework", - "kind" : "remoteSourceControl", - "location" : "https://github.com/livekit/webrtc-xcframework.git", - "state" : { - "revision" : "0aa6a5ea4031d492d0493e3e4d4fbe08b5a0df78", - "version" : "137.7151.12" - } } ], "version" : 3 diff --git a/Multiplatform/Controllers/AppContext.swift b/Multiplatform/Controllers/AppContext.swift index d0e3115..d9e5430 100644 --- a/Multiplatform/Controllers/AppContext.swift +++ b/Multiplatform/Controllers/AppContext.swift @@ -24,7 +24,6 @@ import SwiftUI final class AppContext: NSObject, ObservableObject { private let store: ValueStore - private var audioPlayer: AVAudioPlayer? @Published var isSampleAudioPlaying: Bool = false @Published var videoViewVisible: Bool = true { @@ -110,6 +109,10 @@ final class AppContext: NSObject, ObservableObject { didSet { AudioManager.shared.mixer.appVolume = appVolume } } + @Published var soundPlayerVolume: Float = 1.0 { + didSet { AudioManager.shared.mixer.soundPlayerVolume = soundPlayerVolume } + } + @Published var isAdvancedDuckingEnabled: Bool = false { didSet { if #available(iOS 17, macOS 14.0, visionOS 1.0, *) { @@ -205,35 +208,37 @@ private extension AppContext { // MARK: - AudioClips extension AppContext { - func playSampleAudio() { - do { - if let prevPlayer = audioPlayer { - prevPlayer.stop() - } + +func prepareSampleAudio() { guard let url = Bundle.main.url(forResource: "livekit_clip01", withExtension: "m4a") else { print("Audio file not found") return } +do { + try SoundPlayer.shared.prepare(url: url, withId: "sample01") + } catch { + print("Failed to prepare sample audio clip") + } +} - let player = try AVAudioPlayer(contentsOf: url) - player.delegate = self - player.play() - audioPlayer = player + func playSampleAudio() { + do { isSampleAudioPlaying = true + try SoundPlayer.shared.play(id: "sample01") } catch { print("Failed to sample audio clip") } } func stopSampleAudio() { - if let prevPlayer = audioPlayer { - prevPlayer.stop() - } - - audioPlayer = nil + SoundPlayer.shared.stop(id: "sample01") isSampleAudioPlaying = false } + + func releaseSampleAudio() { + SoundPlayer.shared.release(id: "sample01") + } } extension AppContext: @MainActor AVAudioPlayerDelegate { diff --git a/Multiplatform/Views/AudioControlsPanel.swift b/Multiplatform/Views/AudioControlsPanel.swift index 80561b0..c8acdae 100644 --- a/Multiplatform/Views/AudioControlsPanel.swift +++ b/Multiplatform/Views/AudioControlsPanel.swift @@ -54,6 +54,10 @@ struct AudioControlsPanel: View { Text("App") Slider(value: $appCtx.appVolume, in: 0.0 ... 1.0) } + HStack { + Text("Sound player") + Slider(value: $appCtx.soundPlayerVolume, in: 0.0 ... 1.0) + } } Section(header: Text("Audio Devices")) { @@ -112,19 +116,21 @@ struct AudioControlsPanel: View { } Section(header: Text("Sample audio clip")) { - if appCtx.isSampleAudioPlaying { + Button { + appCtx.prepareSampleAudio() + } label: { + Text("Prepare") + } Button { appCtx.stopSampleAudio() } label: { Text("Stop") } - } else { - Button { - appCtx.playSampleAudio() + Button { + appCtx.releaseSampleAudio() } label: { - Text("Play") + Text("Release") } - } } Section(header: Text("Audio Engine Availability")) { From 2ea6567c4afb20188471d335e5291d3624dda22d Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:21:36 +0800 Subject: [PATCH 2/5] format --- Multiplatform/Controllers/AppContext.swift | 26 +++++++++----------- Multiplatform/Views/AudioControlsPanel.swift | 26 ++++++++++---------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/Multiplatform/Controllers/AppContext.swift b/Multiplatform/Controllers/AppContext.swift index d9e5430..6856bab 100644 --- a/Multiplatform/Controllers/AppContext.swift +++ b/Multiplatform/Controllers/AppContext.swift @@ -208,19 +208,17 @@ private extension AppContext { // MARK: - AudioClips extension AppContext { - -func prepareSampleAudio() { - - guard let url = Bundle.main.url(forResource: "livekit_clip01", withExtension: "m4a") else { - print("Audio file not found") - return - } -do { - try SoundPlayer.shared.prepare(url: url, withId: "sample01") - } catch { + func prepareSampleAudio() { + guard let url = Bundle.main.url(forResource: "livekit_clip01", withExtension: "m4a") else { + print("Audio file not found") + return + } + do { + try SoundPlayer.shared.prepare(url: url, withId: "sample01") + } catch { print("Failed to prepare sample audio clip") } -} + } func playSampleAudio() { do { @@ -235,10 +233,10 @@ do { SoundPlayer.shared.stop(id: "sample01") isSampleAudioPlaying = false } - + func releaseSampleAudio() { - SoundPlayer.shared.release(id: "sample01") - } + SoundPlayer.shared.release(id: "sample01") + } } extension AppContext: @MainActor AVAudioPlayerDelegate { diff --git a/Multiplatform/Views/AudioControlsPanel.swift b/Multiplatform/Views/AudioControlsPanel.swift index c8acdae..f0868ab 100644 --- a/Multiplatform/Views/AudioControlsPanel.swift +++ b/Multiplatform/Views/AudioControlsPanel.swift @@ -117,20 +117,20 @@ struct AudioControlsPanel: View { Section(header: Text("Sample audio clip")) { Button { - appCtx.prepareSampleAudio() - } label: { - Text("Prepare") - } - Button { - appCtx.stopSampleAudio() - } label: { - Text("Stop") - } + appCtx.prepareSampleAudio() + } label: { + Text("Prepare") + } Button { - appCtx.releaseSampleAudio() - } label: { - Text("Release") - } + appCtx.stopSampleAudio() + } label: { + Text("Stop") + } + Button { + appCtx.releaseSampleAudio() + } label: { + Text("Release") + } } Section(header: Text("Audio Engine Availability")) { From d858b735a622c9d19184291595d2cc1b961f0eed Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Wed, 4 Feb 2026 21:24:39 +0800 Subject: [PATCH 3/5] Update AudioControlsPanel.swift --- Multiplatform/Views/AudioControlsPanel.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Multiplatform/Views/AudioControlsPanel.swift b/Multiplatform/Views/AudioControlsPanel.swift index f0868ab..a560dee 100644 --- a/Multiplatform/Views/AudioControlsPanel.swift +++ b/Multiplatform/Views/AudioControlsPanel.swift @@ -121,6 +121,11 @@ struct AudioControlsPanel: View { } label: { Text("Prepare") } + Button { + appCtx.playSampleAudio() + } label: { + Text("Play") + } Button { appCtx.stopSampleAudio() } label: { From 5286c4aad17e150a77571282cf473d536d9f4771 Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Tue, 24 Mar 2026 17:21:26 +0900 Subject: [PATCH 4/5] Update Package.resolved --- .../xcshareddata/swiftpm/Package.resolved | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/LiveKitExample-dev.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LiveKitExample-dev.xcworkspace/xcshareddata/swiftpm/Package.resolved index eae374f..35d9be9 100644 --- a/LiveKitExample-dev.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/LiveKitExample-dev.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "5cff40a09faf62672c6c96ee8f2a9d9299d3b8e62fa5915e4999d02e33ec230c", + "originHash" : "5a18807be8c0dcfdfa97ef3aaaa85f1f206e48cba749de53fb56c3a304dbe5ec", "pins" : [ { "identity" : "keychainaccess", @@ -63,6 +63,15 @@ "revision" : "2547102afd04fe49f1b286090f13ebce07284980", "version" : "1.31.1" } + }, + { + "identity" : "webrtc-xcframework", + "kind" : "remoteSourceControl", + "location" : "https://github.com/livekit/webrtc-xcframework.git", + "state" : { + "revision" : "14a945724d81cfe5a8ce331e4af15d205d2afb85", + "version" : "144.7559.1" + } } ], "version" : 3 From 0b596c3d1d3a85acc9fc10b7f5fea26adeb2bb7a Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:26:17 +0900 Subject: [PATCH 5/5] improvements --- Multiplatform/Controllers/AppContext.swift | 20 +++++--- Multiplatform/Views/AudioControlsPanel.swift | 51 +++++++++++++------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/Multiplatform/Controllers/AppContext.swift b/Multiplatform/Controllers/AppContext.swift index 5d6e805..8136af0 100644 --- a/Multiplatform/Controllers/AppContext.swift +++ b/Multiplatform/Controllers/AppContext.swift @@ -25,6 +25,10 @@ final class AppContext: NSObject, ObservableObject { private let store: ValueStore @Published var isSampleAudioPlaying: Bool = false + @Published var isSampleAudioPrepared: Bool = false + @Published var playbackMode: PlaybackOptions.Mode = .concurrent + @Published var playbackLoop: Bool = false + @Published var playbackDestination: PlaybackOptions.Destination = .localAndRemote private var mutedSpeechToastHideTask: Task? @Published var videoViewVisible: Bool = true { @@ -255,17 +259,21 @@ extension AppContext { } do { try SoundPlayer.shared.prepare(url: url, withId: "sample01") + isSampleAudioPrepared = true } catch { - print("Failed to prepare sample audio clip") + print("Failed to prepare sample audio clip: \(error)") } } func playSampleAudio() { + let options = PlaybackOptions(mode: playbackMode, + loop: playbackLoop, + destination: playbackDestination) do { + try SoundPlayer.shared.play(id: "sample01", options: options) isSampleAudioPlaying = true - try SoundPlayer.shared.play(id: "sample01") } catch { - print("Failed to sample audio clip") + print("Failed to play sample audio clip: \(error)") } } @@ -276,11 +284,7 @@ extension AppContext { func releaseSampleAudio() { SoundPlayer.shared.release(id: "sample01") - } -} - -extension AppContext: @MainActor AVAudioPlayerDelegate { - func audioPlayerDidFinishPlaying(_: AVAudioPlayer, successfully _: Bool) { + isSampleAudioPrepared = false isSampleAudioPlaying = false } } diff --git a/Multiplatform/Views/AudioControlsPanel.swift b/Multiplatform/Views/AudioControlsPanel.swift index 87386eb..776af2d 100644 --- a/Multiplatform/Views/AudioControlsPanel.swift +++ b/Multiplatform/Views/AudioControlsPanel.swift @@ -123,27 +123,42 @@ struct AudioControlsPanel: View { } } - Section(header: Text("Sample audio clip")) { - Button { - appCtx.prepareSampleAudio() - } label: { - Text("Prepare") + Section(header: Text("Sound Player")) { + Picker("Mode", selection: $appCtx.playbackMode) { + Text("Concurrent").tag(PlaybackOptions.Mode.concurrent) + Text("Replace").tag(PlaybackOptions.Mode.replace) } - Button { - appCtx.playSampleAudio() - } label: { - Text("Play") - } - Button { - appCtx.stopSampleAudio() - } label: { - Text("Stop") + + Picker("Destination", selection: $appCtx.playbackDestination) { + Text("Local").tag(PlaybackOptions.Destination.local) + Text("Remote").tag(PlaybackOptions.Destination.remote) + Text("Local + Remote").tag(PlaybackOptions.Destination.localAndRemote) } - Button { - appCtx.releaseSampleAudio() - } label: { - Text("Release") + + Toggle("Loop", isOn: $appCtx.playbackLoop) + + HStack { + Button("Prepare") { + appCtx.prepareSampleAudio() + } + .disabled(appCtx.isSampleAudioPrepared) + + Button("Play") { + appCtx.playSampleAudio() + } + .disabled(!appCtx.isSampleAudioPrepared) + + Button("Stop") { + appCtx.stopSampleAudio() + } + .disabled(!appCtx.isSampleAudioPlaying) + + Button("Release") { + appCtx.releaseSampleAudio() + } + .disabled(!appCtx.isSampleAudioPrepared) } + .buttonStyle(.bordered) } Section(header: Text("Audio Engine Availability")) {