Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 30 additions & 23 deletions Multiplatform/Controllers/AppContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ import SwiftUI
final class AppContext: NSObject, ObservableObject {
private let store: ValueStore<Preferences>

private var audioPlayer: AVAudioPlayer?
@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<Void, Never>?

@Published var videoViewVisible: Bool = true {
Expand Down Expand Up @@ -115,6 +118,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 isRecordingAlwaysPreparedMode: Bool = false {
didSet {
Task {
Expand Down Expand Up @@ -245,39 +252,39 @@ private extension AppContext {
// MARK: - AudioClips

extension AppContext {
func playSampleAudio() {
func prepareSampleAudio() {
guard let url = Bundle.main.url(forResource: "livekit_clip01", withExtension: "m4a") else {
print("Audio file not found")
return
}
do {
if let prevPlayer = audioPlayer {
prevPlayer.stop()
}

guard let url = Bundle.main.url(forResource: "livekit_clip01", withExtension: "m4a") else {
print("Audio file not found")
return
}
try SoundPlayer.shared.prepare(url: url, withId: "sample01")
isSampleAudioPrepared = true
} catch {
print("Failed to prepare sample audio clip: \(error)")
}
}

let player = try AVAudioPlayer(contentsOf: url)
player.delegate = self
player.play()
audioPlayer = player
func playSampleAudio() {
let options = PlaybackOptions(mode: playbackMode,
loop: playbackLoop,
destination: playbackDestination)
do {
try SoundPlayer.shared.play(id: "sample01", options: options)
isSampleAudioPlaying = true
} catch {
print("Failed to sample audio clip")
print("Failed to play sample audio clip: \(error)")
}
}

func stopSampleAudio() {
if let prevPlayer = audioPlayer {
prevPlayer.stop()
}

audioPlayer = nil
SoundPlayer.shared.stop(id: "sample01")
isSampleAudioPlaying = false
}
}

extension AppContext: @MainActor AVAudioPlayerDelegate {
func audioPlayerDidFinishPlaying(_: AVAudioPlayer, successfully _: Bool) {
func releaseSampleAudio() {
SoundPlayer.shared.release(id: "sample01")
isSampleAudioPrepared = false
isSampleAudioPlaying = false
}
}
46 changes: 36 additions & 10 deletions Multiplatform/Views/AudioControlsPanel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")) {
Expand Down Expand Up @@ -119,20 +123,42 @@ struct AudioControlsPanel: View {
}
}

Section(header: Text("Sample audio clip")) {
if appCtx.isSampleAudioPlaying {
Button {
appCtx.stopSampleAudio()
} label: {
Text("Stop")
Section(header: Text("Sound Player")) {
Picker("Mode", selection: $appCtx.playbackMode) {
Text("Concurrent").tag(PlaybackOptions.Mode.concurrent)
Text("Replace").tag(PlaybackOptions.Mode.replace)
}

Picker("Destination", selection: $appCtx.playbackDestination) {
Text("Local").tag(PlaybackOptions.Destination.local)
Text("Remote").tag(PlaybackOptions.Destination.remote)
Text("Local + Remote").tag(PlaybackOptions.Destination.localAndRemote)
}

Toggle("Loop", isOn: $appCtx.playbackLoop)

HStack {
Button("Prepare") {
appCtx.prepareSampleAudio()
}
} else {
Button {
.disabled(appCtx.isSampleAudioPrepared)

Button("Play") {
appCtx.playSampleAudio()
} label: {
Text("Play")
}
.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")) {
Expand Down
Loading