swift AVAudioEngine преобразует многоканальный сигнал без чередования в один канал - PullRequest
0 голосов
/ 09 ноября 2019

Я использую AVAudioEngine для измерения. Я играю стимулирующий звук из моего интерфейса и использую micTap для записи возвращенного сигнала.

Сейчас я смотрю на различные аудиоинтерфейсы, которые поддерживают множество различных форматов. Я преобразую формат ввода inputNode через микшер по двум различным причинам:

  1. , чтобы уменьшить частоту с предпочтительного значения sampleRate для интерфейсов до sampleRate, с которым работает мое приложение

  2. для преобразования количества входящих каналов в один моноканал

Я пытаюсь это сделать, однако, кажется, что он не всегда работает должным образом. Если мой интерфейс работает 96k, а мое приложение работает 48k, изменение формата через микшер заканчивается следующим: enter image description here

Похоже, что это только одна сторонастерео чередуемого канала. Ниже приведен мой код audioEngine:

func initializeEngine(inputSweep:SweepFilter)  {
    buf1current = 0
    buf2current = 0
    in1StartTime = 0
    in2startTime = 0
    in1firstRun = true
    in2firstRun = true
    in1Buf = Array(repeating:0, count:1000000)
    in2Buf = Array(repeating:0, count:1000000)
    engine.stop()
    engine.reset()
    engine = AVAudioEngine()
    numberOfSamples = 0

    var time:Int = 0
    do {
        try AVAudioSession.sharedInstance().setCategory(.playAndRecord)
        try AVAudioSession.sharedInstance()
        .setPreferredSampleRate(Double(sampleRate))    
    } catch {
        assertionFailure("AVAudioSession setup failed")
    }

    let format = engine.outputNode.inputFormat(forBus: 0)
    let stimulusFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32,
        sampleRate: Double(sampleRate),
        channels: 1,
        interleaved: false)

    let outputFormat = engine.outputNode.inputFormat(forBus: 0)
    let inputFormat = engine.inputNode.outputFormat(forBus: 0)

    let srcNode = AVAudioSourceNode { _, timeStamp, frameCount, AudioBufferList -> OSStatus in
            let ablPointer = UnsafeMutableAudioBufferListPointer(AudioBufferList)
            if self.in2firstRun == true {
                let start2 = CACurrentMediaTime()
                self.in2startTime = Double(CACurrentMediaTime())
                self.in2firstRun = false
            }

            if Int(frameCount) + time >= inputSweep.stimulus.count{
            self.running = false
            print("AUDIO ENGINE STOPPED")
        }

        if (Int(frameCount) + time) <= inputSweep.stimulus.count {
            for frame in 0..<Int(frameCount) {
                let value = inputSweep.stimulus[frame + time] * Float(outputVolume)
                for buffer in ablPointer {
                    let buf: UnsafeMutableBufferPointer<Float> = UnsafeMutableBufferPointer(buffer)
                    buf[frame] = value
                }
            }

            time += Int(frameCount)
        } else {
            for frame in 0..<Int(frameCount) {
                let value = 0
                for buffer in ablPointer {
                    let buf: UnsafeMutableBufferPointer<Float> = UnsafeMutableBufferPointer(buffer)
                    buf[frame] = Float(value)
                }
            }
        }
    return noErr
    }

    engine.attach(srcNode)
    engine.connect(srcNode, to: engine.mainMixerNode, format: stimulusFormat)
    engine.connect(engine.mainMixerNode, to: engine.outputNode, format: format)

    let requiredFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32,
        sampleRate: Double(sampleRate),
        channels: 1,
        interleaved: false)  

    let formatMixer = AVAudioMixerNode()
    engine.attach(formatMixer)
    engine.connect(engine.inputNode, to: formatMixer, format: inputFormat)

    let MicSinkNode = AVAudioSinkNode() { (timeStamp, frames, audioBufferList) ->
        OSStatus in
            if self.in1firstRun == true {
                let start1 = CACurrentMediaTime()
                self.in1StartTime = Double(start1)
                self.in1firstRun = false

           }

            let ptr = audioBufferList.pointee.mBuffers.mData?.assumingMemoryBound(to: Float.self)
            var monoSamples = [Float]()
            monoSamples.append(contentsOf: UnsafeBufferPointer(start: ptr, count: Int(frames)))
        if self.buf1current >= 100000 {
            self.running = false
        }
            for frame in 0..<frames {
                self.in1Buf[self.buf1current + Int(frame)] = monoSamples[Int(frame)]
            }
            self.buf1current = self.buf1current + Int(frames)



       return noErr
    }

    engine.attach(MicSinkNode)
    engine.connect(formatMixer, to: MicSinkNode, format: requiredFormat)

    engine.prepare()
    assert(engine.inputNode != nil)
    running = true
    try! engine.start()
}

Мой sourceNode - это массив чисел с плавающей точкой, синтезированных для использования в формате стимул. Если я слушаю этот audioEngine с моим интерфейсом на 96k, выходной стимул звучит совершенно чисто. Однако этот разбитый сигнал - это то, что исходит от micTap. Физически выход интерфейса маршрутизируется. непосредственно на вход, поэтому не проходя через какое-либо другое устройство.

В дополнение к этому у меня есть следующая функция, которая записывает мои массивы в файлы WAV, чтобы я мог визуально проверить их в DAW.

func writetoFile(buff:[Float], name:String){
let SAMPLE_RATE =  sampleRate

let outputFormatSettings = [
    AVFormatIDKey:kAudioFormatLinearPCM,
    AVLinearPCMBitDepthKey:32,
    AVLinearPCMIsFloatKey: true,
    AVLinearPCMIsBigEndianKey: true,
    AVSampleRateKey: SAMPLE_RATE,
    AVNumberOfChannelsKey: 1
    ] as [String : Any]

let fileName = name
let DocumentDirURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)


let url = DocumentDirURL.appendingPathComponent(fileName).appendingPathExtension("wav")
//print("FilePath: \(url.path)")

let audioFile = try? AVAudioFile(forWriting: url, settings: outputFormatSettings, commonFormat: AVAudioCommonFormat.pcmFormatFloat32, interleaved: false)

let bufferFormat = AVAudioFormat(settings: outputFormatSettings)

let outputBuffer = AVAudioPCMBuffer(pcmFormat: bufferFormat!, frameCapacity: AVAudioFrameCount(buff.count))

for i in 0..<buff.count {
    outputBuffer?.floatChannelData!.pointee[i] = Float(( buff[i] ))
}
outputBuffer!.frameLength = AVAudioFrameCount( buff.count )

do{
    try audioFile?.write(from: outputBuffer!)

} catch let error as NSError {
    print("error:", error.localizedDescription)
}

}

Если я установлю свой интерфейс на 48 Кб, а мое приложение будет работать на 48 Кб, если я проверю свой опорный сигнал и. мой измерительный сигнал, я получаю следующее:

enter image description here

Измеренный сигнал явно намного длиннее исходного стимула. Физический размер файла. такой же, как он инициализируется как пустой массив фиксированного размера. Однако в какой-то момент, делая преобразование формата, это не правильно. Если я установлю свой интерфейс на 44.1k, а мое приложение будет работать на 48k, я смогу увидеть обычные «глюки» в аудио. Таким образом, преобразование формата здесь не работает так, как должно.

Кто-нибудь может увидеть что-то явно не так?

...