gpt4 book ai didi

swift AVAudioEngine 和 AVAudioSinkNode sampleRate 转换

转载 作者:行者123 更新时间:2023-12-05 07:17:32 25 4
gpt4 key购买 nike

一段时间以来我一直遇到这个问题,并编写了以下可以作为应用程序的主视图 Controller 文件运行的 swift 文件。执行后,它将播放一段短促的 1kHz 正弦波。它将同时从音频接口(interface)的输入进行记录。

目前我已经将输出插入到输入中进行测试。但这也可能是内置扬声器和内置麦克风的计算机(运行应用程序前只需检查系统设置中的音量,因为它会自动播放)

我无法得到准确的结果:

import UIKit
import AVFoundation

var globalSampleRate = 48000


class ViewController: UIViewController {
var micBuffer:[Float] = Array(repeating:0, count:10000)
var referenceBuffer:[Float] = Array(repeating:0, count:10000)
var running:Bool = false
var engine = AVAudioEngine()

override func viewDidLoad() {
super.viewDidLoad()

let syncQueue = DispatchQueue(label:"Audio Engine")
syncQueue.sync{
initializeAudioEngine()
while running == true {
}
engine.stop()
writetoFile(buff: micBuffer, name: "Mic Input")
writetoFile(buff: referenceBuffer, name: "Reference")

}
}

func initializeAudioEngine(){

var micBufferPosition:Int = 0
var refBufferPosition:Int = 0
let frequency:Float = 1000.0
let amplitude:Float = 1.0
let signal = { (time: Float) -> Float in
return amplitude * sin(2.0 * Float.pi * frequency * time)
}

let deltaTime = 1.0 / Float(globalSampleRate)
var time: Float = 0

let micSinkNode = AVAudioSinkNode() { (timeStamp, frames, audioBufferList) ->
OSStatus in

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

if micBufferPosition > 8000 {
self.running = false
}

return noErr
}


let srcNode = AVAudioSourceNode { _, _, frameCount, audioBufferList -> OSStatus in
let ablPointer = UnsafeMutableAudioBufferListPointer(audioBufferList)
for frame in 0..<Int(frameCount) {
let value = signal(time)
time += deltaTime
for buffer in ablPointer {
let buf: UnsafeMutableBufferPointer<Float> = UnsafeMutableBufferPointer(buffer)
buf[frame] = value
self.referenceBuffer[refBufferPosition + frame] = value
}

}
refBufferPosition += Int(frameCount)
return noErr
}

let inputFormat = engine.inputNode.inputFormat(forBus: 0)
let outputFormat = engine.outputNode.outputFormat(forBus: 0)
let nativeFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32,
sampleRate: Double(globalSampleRate),
channels: 1,
interleaved: false)

let formatMixer = AVAudioMixerNode()
engine.attach(formatMixer)
engine.attach(micSinkNode)
engine.attach(srcNode)
//engine.connect(engine.inputNode, to: micSinkNode, format: inputFormat)
engine.connect(engine.inputNode, to: formatMixer, format: inputFormat)
engine.connect(formatMixer, to: micSinkNode, format: nativeFormat)
engine.connect(srcNode, to: engine.mainMixerNode, format: nativeFormat)
engine.connect(engine.mainMixerNode, to: engine.outputNode, format: outputFormat)
print("micSinkNode Format is \(micSinkNode.inputFormat(forBus: 0))")
print("inputNode Format is \(engine.inputNode.inputFormat(forBus: 0))")
print("outputNode Format is \(engine.outputNode.outputFormat(forBus: 0))")
print("formatMixer Format is \(formatMixer.outputFormat(forBus: 0))")

engine.prepare()
running = true
do {
try engine.start()
} catch {
print("Error")
}
}

}


func writetoFile(buff:[Float], name:String){

let outputFormatSettings = [
AVFormatIDKey:kAudioFormatLinearPCM,
AVLinearPCMBitDepthKey:32,
AVLinearPCMIsFloatKey: true,
AVLinearPCMIsBigEndianKey: true,
AVSampleRateKey: globalSampleRate,
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)
}
}

如果我运行这个应用程序,控制台将打印出创建的两个波形的 url(一个是生成的正弦波,另一个是记录的麦克风输入)。如果我在 daw 中检查这些,我会得到以下信息。您可以看到两个正弦波不同步。这让我相信采样率是不同的,但是打印到控制台的格式告诉我它们没有不同。

最初 inputNode 直接连接到 micSinkNode,但是我插入了一个 AVAudioMixerNode 以尝试在使用 AVAudioSinkNode 之前转换格式。

目标是能够使用任何使用自己的设置运行的 sampleRate 硬件,并将样本保存到应用程序首选的“ native 设置”。 (即应用程序将以 48kHz 的频率进行数字压缩。我希望能够使用 96k 硬件和不同的 channel 数)。

任何人都可以建议为什么这不起作用?

enter image description here

最佳答案

AVAudioSinkNode 支持格式转换。

它必须处理硬件输入格式的数据。

引用:来自 WWDC 2019 的演讲,此处约 3 分 55 秒: https://developer.apple.com/videos/play/wwdc2019/510/

附言我目前正在努力在设备上设置一个非默认采样率(我假设硬件支持它,因为我从可用采样率列表中选择它),但即使在这种情况下非默认采样率,接收器完全停止工作(没有崩溃,只是没有被调用)。

关于swift AVAudioEngine 和 AVAudioSinkNode sampleRate 转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58781271/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com