Skip to content

Commit 11519f1

Browse files
authored
Merge pull request #1819 from HaishinKit/feature/supress-muted
Fixed an issue where the properties of AudioMixerTrackSettings were unintentionally applied to AVAudioConverter each time.
2 parents b68e9a0 + 3f5bd01 commit 11519f1

File tree

4 files changed

+105
-69
lines changed

4 files changed

+105
-69
lines changed

Examples/iOS/PublishView.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,17 @@ struct PublishView: View {
7676
.foregroundColor(.white)
7777
.frame(width: 30, height: 30)
7878
})
79+
Button(action: {
80+
model.toggleAudioMuted()
81+
}, label: {
82+
Image(systemName: model.isAudioMuted ?
83+
"microphone.slash.circle" :
84+
"microphone.circle")
85+
.resizable()
86+
.scaledToFit()
87+
.foregroundColor(.white)
88+
.frame(width: 30, height: 30)
89+
})
7990
Button(action: {
8091
model.flipCamera()
8192
}, label: {

Examples/iOS/PublishViewModel.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ final class PublishViewModel: ObservableObject {
1616
}
1717
}
1818
@Published var isShowError = false
19+
@Published private(set) var isAudioMuted = false
1920
@Published private(set) var isTorchEnabled = false
2021
@Published private(set) var readyState: SessionReadyState = .closed
2122
@Published var audioSource: AudioSource = .empty {
@@ -127,6 +128,26 @@ final class PublishViewModel: ObservableObject {
127128
}
128129
}
129130

131+
func toggleAudioMuted() {
132+
Task {
133+
if isAudioMuted {
134+
var settings = await mixer.audioMixerSettings
135+
var track = settings.tracks[0] ?? .init()
136+
track.isMuted = false
137+
settings.tracks[0] = track
138+
await mixer.setAudioMixerSettings(settings)
139+
isAudioMuted = false
140+
} else {
141+
var settings = await mixer.audioMixerSettings
142+
var track = settings.tracks[0] ?? .init()
143+
track.isMuted = true
144+
settings.tracks[0] = track
145+
await mixer.setAudioMixerSettings(settings)
146+
isAudioMuted = true
147+
}
148+
}
149+
}
150+
130151
func makeSession(_ preference: PreferenceViewModel) async {
131152
// Make session.
132153
do {

HaishinKit/Sources/Mixer/AudioMixerTrack.swift

Lines changed: 17 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,22 @@
11
import Accelerate
22
import AVFoundation
33

4-
private let kIOAudioMixerTrack_frameCapacity: AVAudioFrameCount = 1024
4+
private let kAudioMixerTrack_frameCapacity: AVAudioFrameCount = 1024
55

66
protocol AudioMixerTrackDelegate: AnyObject {
77
func track(_ track: AudioMixerTrack<Self>, didOutput audioPCMBuffer: AVAudioPCMBuffer, when: AVAudioTime)
88
func track(_ track: AudioMixerTrack<Self>, errorOccurred error: AudioMixerError)
99
}
1010

11-
/// Constraints on the audio mixier track's settings.
12-
public struct AudioMixerTrackSettings: Codable, Sendable {
13-
/// The default value.
14-
public static let `default` = AudioMixerTrackSettings()
15-
16-
/// Specifies the volume for output.
17-
public var volume: Float
18-
19-
/// Specifies the muted that indicates whether the audio output is muted.
20-
public var isMuted = false
21-
22-
/// Specifies the mixes the channels or not. Currently, it supports input sources with 4, 5, 6, and 8 channels.
23-
public var downmix = true
24-
25-
/// Specifies the map of the output to input channels.
26-
/// ## Example code:
27-
/// ```
28-
/// // If you want to use the 3rd and 4th channels from a 4-channel input source for a 2-channel output, you would specify it like this.
29-
/// channelMap = [2, 3]
30-
/// ```
31-
public var channelMap: [Int]?
32-
33-
/// Creates a new instance.
34-
public init(volume: Float = 1.0, isMuted: Bool = false, downmix: Bool = true, channelMap: [Int]? = nil) {
35-
self.volume = volume
36-
self.isMuted = isMuted
37-
self.downmix = downmix
38-
self.channelMap = channelMap
39-
}
40-
41-
func apply(_ converter: AVAudioConverter?, oldValue: AudioMixerTrackSettings?) {
42-
guard let converter else {
43-
return
44-
}
45-
if converter.downmix != downmix {
46-
converter.downmix = downmix
47-
}
48-
if let channelMap = validatedChannelMap(converter) {
49-
converter.channelMap = channelMap.map { NSNumber(value: $0) }
50-
} else {
51-
switch converter.outputFormat.channelCount {
52-
case 1:
53-
converter.channelMap = [0]
54-
case 2:
55-
converter.channelMap = (converter.inputFormat.channelCount == 1) ? [0, 0] : [0, 1]
56-
default:
57-
break
58-
}
59-
}
60-
}
61-
62-
private func validatedChannelMap(_ converter: AVAudioConverter) -> [Int]? {
63-
guard let channelMap, channelMap.count == converter.outputFormat.channelCount else {
64-
return nil
65-
}
66-
for inputChannel in channelMap where converter.inputFormat.channelCount <= inputChannel {
67-
return nil
68-
}
69-
return channelMap
70-
}
71-
}
72-
7311
final class AudioMixerTrack<T: AudioMixerTrackDelegate> {
7412
let id: UInt8
7513
let outputFormat: AVAudioFormat
76-
14+
weak var delegate: T?
7715
var settings: AudioMixerTrackSettings = .init() {
7816
didSet {
7917
settings.apply(audioConverter, oldValue: oldValue)
8018
}
8119
}
82-
weak var delegate: T?
83-
8420
var inputFormat: AVAudioFormat? {
8521
return audioConverter?.inputFormat
8622
}
@@ -101,7 +37,19 @@ final class AudioMixerTrack<T: AudioMixerTrackDelegate> {
10137
guard let audioConverter else {
10238
return
10339
}
104-
settings.apply(audioConverter, oldValue: nil)
40+
audioConverter.downmix = settings.downmix
41+
if let channelMap = settings.validatedChannelMap(audioConverter) {
42+
audioConverter.channelMap = channelMap.map { NSNumber(value: $0) }
43+
} else {
44+
switch audioConverter.outputFormat.channelCount {
45+
case 1:
46+
audioConverter.channelMap = [0]
47+
case 2:
48+
audioConverter.channelMap = (audioConverter.inputFormat.channelCount == 1) ? [0, 0] : [0, 1]
49+
default:
50+
break
51+
}
52+
}
10553
audioConverter.primeMethod = .normal
10654
}
10755
}
@@ -167,9 +115,9 @@ final class AudioMixerTrack<T: AudioMixerTrackDelegate> {
167115
delegate?.track(self, errorOccurred: .failedToCreate(from: inputFormat, to: outputFormat))
168116
return
169117
}
170-
inputBuffer = .init(pcmFormat: inputFormat, frameCapacity: 1024 * 4)
171118
ringBuffer = .init(inputFormat)
172-
outputBuffer = .init(pcmFormat: outputFormat, frameCapacity: kIOAudioMixerTrack_frameCapacity)
119+
inputBuffer = .init(pcmFormat: inputFormat, frameCapacity: kAudioMixerTrack_frameCapacity * 4)
120+
outputBuffer = .init(pcmFormat: outputFormat, frameCapacity: kAudioMixerTrack_frameCapacity)
173121
if logger.isEnabledFor(level: .info) {
174122
logger.info("inputFormat:", inputFormat, ", outputFormat:", outputFormat)
175123
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import AVFoundation
2+
3+
/// Constraints on the audio mixier track's settings.
4+
public struct AudioMixerTrackSettings: Codable, Sendable {
5+
/// The default value.
6+
public static let `default` = AudioMixerTrackSettings()
7+
8+
/// Specifies the volume for output.
9+
public var volume: Float
10+
11+
/// Specifies the muted that indicates whether the audio output is muted.
12+
public var isMuted = false
13+
14+
/// Specifies the mixes the channels or not. Currently, it supports input sources with 4, 5, 6, and 8 channels.
15+
public var downmix = true
16+
17+
/// Specifies the map of the output to input channels.
18+
/// ## Example code:
19+
/// ```swift
20+
/// // If you want to use the 3rd and 4th channels from a 4-channel input source for a 2-channel output, you would specify it like this.
21+
/// channelMap = [2, 3]
22+
/// ```
23+
public var channelMap: [Int]?
24+
25+
/// Creates a new instance.
26+
public init(volume: Float = 1.0, isMuted: Bool = false, downmix: Bool = true, channelMap: [Int]? = nil) {
27+
self.volume = volume
28+
self.isMuted = isMuted
29+
self.downmix = downmix
30+
self.channelMap = channelMap
31+
}
32+
33+
func apply(_ converter: AVAudioConverter?, oldValue: AudioMixerTrackSettings) {
34+
guard let converter else {
35+
return
36+
}
37+
if downmix != oldValue.downmix {
38+
converter.downmix = downmix
39+
}
40+
if channelMap != oldValue.channelMap {
41+
if let channelMap = validatedChannelMap(converter) {
42+
converter.channelMap = channelMap.map { NSNumber(value: $0) }
43+
}
44+
}
45+
}
46+
47+
func validatedChannelMap(_ converter: AVAudioConverter) -> [Int]? {
48+
guard let channelMap, channelMap.count == converter.outputFormat.channelCount else {
49+
return nil
50+
}
51+
for inputChannel in channelMap where converter.inputFormat.channelCount <= inputChannel {
52+
return nil
53+
}
54+
return channelMap
55+
}
56+
}

0 commit comments

Comments
 (0)