Skip to content

Commit

Permalink
Merge pull request #1280 from stream-labs/bugfix/audio-channel-map
Browse files Browse the repository at this point in the history
Improve audio channel mapping
  • Loading branch information
shogo4405 committed Sep 2, 2023
2 parents 2aa1489 + 507eed3 commit bc4615a
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 13 deletions.
27 changes: 15 additions & 12 deletions Sources/Codec/AudioCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,17 @@ public class AudioCodec {
}

/// Creates a channel map for specific input and output format
/// - Examples:
/// - Input channel count is 4 and 2, result: [0, 1, -1, -1]
/// - Input channel count is 2 and 2, result: [0, 1]
static func makeChannelMap(from fromFormat: AVAudioFormat, to toFormat: AVAudioFormat) -> [NSNumber] {
let inChannels = Int(fromFormat.channelCount)
let outChannels = Int(toFormat.channelCount)
let channelIndexes = Array(0...inChannels - 1)
return channelIndexes
.prefix(outChannels)
.map { NSNumber(value: $0) }
+ Array(repeating: -1, count: max(0, inChannels - outChannels))
static func makeChannelMap(inChannels: Int, outChannels: Int, outputChannelsMap: [Int: Int]) -> [NSNumber] {
var result = Array(repeating: -1, count: outChannels)
for inputIndex in 0..<min(inChannels, outChannels) {
result[inputIndex] = inputIndex
}
for currentIndex in 0..<outChannels {
if let inputIndex = outputChannelsMap[currentIndex], inputIndex < inChannels {
result[currentIndex] = inputIndex
}
}
return result.map { NSNumber(value: $0) }
}

/// Specifies the delegate.
Expand Down Expand Up @@ -194,7 +194,10 @@ public class AudioCodec {
logger.debug("inputFormat: \(inputFormat)")
logger.debug("outputFormat: \(outputFormat)")
let converter = AVAudioConverter(from: inputFormat, to: outputFormat)
converter?.channelMap = Self.makeChannelMap(from: inputFormat, to: outputFormat)
let channelMap = Self.makeChannelMap(inChannels: Int(inputFormat.channelCount), outChannels: Int(outputFormat.channelCount),
outputChannelsMap: settings.outputChannelsMap)
logger.debug("channelMap: \(channelMap)")
converter?.channelMap = channelMap
settings.apply(converter, oldValue: nil)
if converter == nil {
delegate?.audioCodec(self, errorOccurred: .failedToCreate(from: inputFormat, to: outputFormat))
Expand Down
11 changes: 10 additions & 1 deletion Sources/Codec/AudioCodecSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,23 @@ public struct AudioCodecSettings: Codable {

/// Specifies the output format.
public var format: AudioCodecSettings.Format

/// Map of the output to input channels
public var outputChannelsMap: [Int: Int]

/// Create an new AudioCodecSettings instance.
public init(
bitRate: Int = 64 * 1000,
format: AudioCodecSettings.Format = .aac
format: AudioCodecSettings.Format = .aac,
outputChannelsMap: [Int: Int] = [0: 0, 1: 1]
) {
self.bitRate = bitRate
self.format = format
self.outputChannelsMap = outputChannelsMap
}

func recreateConverter(_ rhs: AudioCodecSettings) -> Bool {
!(outputChannelsMap == rhs.outputChannelsMap)
}

func apply(_ converter: AVAudioConverter?, oldValue: AudioCodecSettings?) {
Expand Down
31 changes: 31 additions & 0 deletions Tests/Codec/AudioCodecTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,36 @@ final class AudioCodecTests: XCTestCase {
encoder.appendSampleBuffer(sampleBuffer)
}
}

func testChannelMaps() {
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 1, outChannels: 1, outputChannelsMap: [:]), [0])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 1, outChannels: 1, outputChannelsMap: [0: 0]), [0])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 1, outChannels: 1, outputChannelsMap: [0: 0, 1: 1]), [0])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 1, outChannels: 1, outputChannelsMap: [0: -1]), [-1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 1, outChannels: 1, outputChannelsMap: [Int.max: Int.max]), [0])

XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 1, outChannels: 2, outputChannelsMap: [:]), [0, -1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 1, outChannels: 2, outputChannelsMap: [0: 0]), [0, -1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 1, outChannels: 2, outputChannelsMap: [0: 1]), [0, -1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 1, outChannels: 2, outputChannelsMap: [0: -1, 1: -1]), [-1, -1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 1, outChannels: 2, outputChannelsMap: [0: 1, 1: Int.max]), [0, 1])

XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 2, outChannels: 1, outputChannelsMap: [:]), [0])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 2, outChannels: 1, outputChannelsMap: [0: 0]), [0])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 2, outChannels: 1, outputChannelsMap: [0: 1]), [1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 1, outChannels: 1, outputChannelsMap: [0: -1]), [-1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 2, outChannels: 1, outputChannelsMap: [Int.max: 0]), [0])

XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 2, outChannels: 2, outputChannelsMap: [:]), [0, 1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 2, outChannels: 2, outputChannelsMap: [0: 0]), [0, 1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 2, outChannels: 2, outputChannelsMap: [0: 0, 1: 1]), [0, 1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 2, outChannels: 2, outputChannelsMap: [0: -1, 1: -1]), [-1, -1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 2, outChannels: 2, outputChannelsMap: [0: -1, 1: 1]), [-1, 1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 2, outChannels: 2, outputChannelsMap: [0: 0, 1: 1, Int.max: Int.max]), [0, 1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 2, outChannels: 2, outputChannelsMap: [0: 0, 1: Int.max]), [0, 1])

XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 12, outChannels: 2, outputChannelsMap: [:]), [0, 1])
XCTAssertEqual(AudioCodec.makeChannelMap(inChannels: 12, outChannels: 2, outputChannelsMap: [0: -1, 1: 11]), [-1, 11])
}
}

0 comments on commit bc4615a

Please sign in to comment.