Skip to content

Commit

Permalink
Add a workaround for connection format bug (AudioKit#2811)
Browse files Browse the repository at this point in the history
* Add a workaround for connection format bug

There is a bug when using destination point API and providing only one
point:
http://openradar.appspot.com/radar?id=5490575180562432

* Add a note about channel layout when using matrix mixer

* Disable test on tvOS
  • Loading branch information
jcavar authored Jan 2, 2023
1 parent 4837b9a commit 40dfa59
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 4 deletions.
9 changes: 8 additions & 1 deletion Sources/AudioKit/Internals/Engine/AudioEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ public extension AVAudioMixerNode {
var points = engine.outputConnectionPoints(for: input, outputBus: 0)
if points.contains(where: { $0.node === self }) { return }
points.append(AVAudioConnectionPoint(node: self, bus: nextAvailableInputBus))
engine.connect(input, to: points, fromBus: 0, format: format)
if points.count == 1 {
// If we only have 1 connection point, use connect API
// Workaround for a bug where specified format is not correctly applied
// http://openradar.appspot.com/radar?id=5490575180562432
engine.connect(input, to: self, format: format)
} else {
engine.connect(input, to: points, fromBus: 0, format: format)
}
}
}
}
Expand Down
16 changes: 13 additions & 3 deletions Sources/AudioKit/Nodes/Mixing/MatrixMixer.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKit/

/// Matrix Mixer allows you to map X input channels to Y output channels
/// Matrix Mixer allows you to map X input channels to Y output channels.
/// There is almost no documentation about how matrix mixer audio unit works.
/// This implementation is a result of consolidating various online resources:
/// - https://stackoverflow.com/questions/48059405/how-should-an-aumatrixmixer-be-configured-in-an-avaudioengine-graph
Expand All @@ -9,11 +9,21 @@
/// - https://lists.apple.com/archives/coreaudio-api/2006/Jul/msg00047.html
/// - https://lists.apple.com/archives/coreaudio-api/2008/Jun/msg00116.html
///
/// In order to be able to use Matrix Mixer upstream connections will need to have
/// In order to be able to use Matrix Mixer, upstream connections will need to have
/// different format then downstream. Downstream connections are determined by
/// output node's channel count. But for matrix mixer to be able to count input channels
/// output node's channel count. But, for matrix mixer to be able to count input channels
/// correctly, upstream connections need to preserve source number of channels.
/// This can be done using `Node.outputFormat`.
///
/// Additionally, you might need to set audio format channel layout.
/// Even though it seems like `kAudioChannelLayoutTag_DiscreteInOrder` should be used, you will likely need `kAudioChannelLayoutTag_Unknown`
/// See:
/// https://www.mail-archive.com/[email protected]/msg01143.html
/// ```
/// let multiChannelLayout = AVAudioChannelLayout(
/// layoutTag: kAudioChannelLayoutTag_Unknown | outputFormat.channelCount
/// )!
/// ```

import AVFAudio

Expand Down
27 changes: 27 additions & 0 deletions Tests/AudioKitTests/Node Tests/NodeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,33 @@ class NodeTests: XCTestCase {
// XCTAssertFalse(engine.avEngine.description.contains("other nodes"))
}
}

// This is a test for workaround for:
// http://openradar.appspot.com/radar?id=5490575180562432
// Connection format is not correctly applied when adding a node to paused engine
// This is only happening when using destination point API with one point
#if !os(tvOS)
func testConnectionFormatAppliedWhenAddingNode() throws {
let engine = AudioEngine()
let previousFormat = Settings.audioFormat

var settings = Settings.audioFormat.settings
settings[AVSampleRateKey] = 48000
Settings.audioFormat = AVAudioFormat(settings: settings)!

let mixer = Mixer(MIDISampler())
engine.output = mixer
try engine.start()
engine.pause()

let sampler = MIDISampler()
mixer.addInput(sampler)

XCTAssertEqual(sampler.avAudioNode.outputFormat(forBus: 0).sampleRate, 48000)

Settings.audioFormat = previousFormat
}
#endif
}

private extension NodeTests {
Expand Down

0 comments on commit 40dfa59

Please sign in to comment.