我正在读取一个输入文件,在脱机手动渲染模式下,我想执行振幅调制,并将结果写入输出文件。
为了便于测试,我产生了纯正弦波-这对于低于6.000 Hz的频率很好。对于更高的频率(我的目标是使用大约20.000赫兹),信号(因此收听输出文件)失真,频谱在8.000赫兹结束-不再是纯频谱,在0到8.000赫兹之间有多个峰值。
下面是我的代码片段:
let outputFile: AVAudioFile
do {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let outputURL = documentsURL.appendingPathComponent("output.caf")
outputFile = try AVAudioFile(forWriting: outputURL, settings: sourceFile.fileFormat.settings)
} catch {
fatalError("Unable to open output audio file: \(error).")
}
var sampleTime: Float32 = 0
while engine.manualRenderingSampleTime < sourceFile.length {
do {
let frameCount = sourceFile.length - engine.manualRenderingSampleTime
let framesToRender = min(AVAudioFrameCount(frameCount), buffer.frameCapacity)
let status = try engine.renderOffline(framesToRender, to: buffer)
switch status {
case .success:
// The data rendered successfully. Write it to the output file.
let sampleRate:Float = Float((mixer.outputFormat(forBus: 0).sampleRate))
let modulationFrequency: Float = 20000.0
for i in stride(from:0, to: Int(buffer.frameLength), by: 1) {
let val = sinf(2.0 * .pi * modulationFrequency * Float(sampleTime) / Float(sampleRate))
// TODO: perform modulation later
buffer.floatChannelData?.pointee[Int(i)] = val
sampleTime = sampleTime + 1.0
}
try outputFile.write(from: buffer)
case .insufficientDataFromInputNode:
// Applicable only when using the input node as one of the sources.
break
case .cannotDoInCurrentContext:
// The engine couldn't render in the current render call.
// Retry in the next iteration.
break
case .error:
// An error occurred while rendering the audio.
fatalError("The manual rendering failed.")
@unknown default:
fatalError("unknown error")
}
} catch {
fatalError("The manual rendering failed: \(error).")
}
}或者有谁知道如何产生具有更高频率的正弦波的输出文件?
我认为手动渲染模式不足以处理更高的频率。
更新:在此期间,我使用Audacity分析了输出文件。上面是1.000 Hz的波形,下面是20.000 Hz的波形:

当我放大时,我看到以下内容:

比较两个输出文件的频谱,我得到以下结果:

奇怪的是,频率越高,振幅就越接近于零。此外,我在第二个频谱中看到更多的频率。
与结果相关的一个新问题是以下算法的正确性:
// Process the audio in `renderBuffer` here
for i in 0..<Int(renderBuffer.frameLength) {
let val = sinf(1000.0*Float(index) *2 * .pi / Float(sampleRate))
renderBuffer.floatChannelData?.pointee[i] = val
index += 1
}我确实检查了采样率,它是48000 -我知道当采样频率大于被采样信号的最大频率的两倍时,原始信号可以被忠实地重建。
更新2:
settings[AVFormatIDKey] = kAudioFormatAppleLossless
settings[AVAudioFileTypeKey] = kAudioFileCAFType
settings[AVSampleRateKey] = readBuffer.format.sampleRate
settings[AVNumberOfChannelsKey] = 1
settings[AVLinearPCMIsFloatKey] = (readBuffer.format.commonFormat == .pcmFormatInt32)
settings[AVSampleRateConverterAudioQualityKey] = AVAudioQuality.max
settings[AVLinearPCMBitDepthKey] = 32
settings[AVEncoderAudioQualityKey] = AVAudioQuality.max现在输出信号的质量更好了,但并不完美。我得到了更高的振幅,但在频谱分析仪中总是不止一个频率。也许解决方法可以包括应用高通滤波器?
在此期间,我确实使用了一种SignalGenerator,将处理过的缓冲区(使用正弦波)直接传输到扬声器-在这种情况下,输出是完美的。我认为将信号路由到文件会导致这样的问题。
发布于 2020-11-22 00:36:52
手动渲染模式的速度不是问题,因为手动渲染上下文中的速度有点无关紧要。
下面是从源文件到输出文件的手动渲染框架代码:
// Open the input file
let file = try! AVAudioFile(forReading: URL(fileURLWithPath: "/tmp/test.wav"))
let engine = AVAudioEngine()
let player = AVAudioPlayerNode()
engine.attach(player)
engine.connect(player, to:engine.mainMixerNode, format: nil)
// Run the engine in manual rendering mode using chunks of 512 frames
let renderSize: AVAudioFrameCount = 512
// Use the file's processing format as the rendering format
let renderFormat = AVAudioFormat(commonFormat: file.processingFormat.commonFormat, sampleRate: file.processingFormat.sampleRate, channels: file.processingFormat.channelCount, interleaved: true)!
let renderBuffer = AVAudioPCMBuffer(pcmFormat: renderFormat, frameCapacity: renderSize)!
try! engine.enableManualRenderingMode(.offline, format: renderFormat, maximumFrameCount: renderBuffer.frameCapacity)
try! engine.start()
player.play()
// The render format is also the output format
let output = try! AVAudioFile(forWriting: URL(fileURLWithPath: "/tmp/foo.wav"), settings: renderFormat.settings, commonFormat: renderFormat.commonFormat, interleaved: renderFormat.isInterleaved)
// Read using a buffer sized to produce `renderSize` frames of output
let readBuffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: renderSize)!
// Process the file
while true {
do {
// Processing is finished if all frames have been read
if file.framePosition == file.length {
break
}
try file.read(into: readBuffer)
player.scheduleBuffer(readBuffer, completionHandler: nil)
let result = try engine.renderOffline(readBuffer.frameLength, to: renderBuffer)
// Process the audio in `renderBuffer` here
// Write the audio
try output.write(from: renderBuffer)
if result != .success {
break
}
}
catch {
break
}
}
player.stop()
engine.stop()下面的代码片段显示了如何在整个引擎中设置相同的采样率:
// Replace:
//engine.connect(player, to:engine.mainMixerNode, format: nil)
// With:
let busFormat = AVAudioFormat(standardFormatWithSampleRate: file.fileFormat.sampleRate, channels: file.fileFormat.channelCount)
engine.disconnectNodeInput(engine.outputNode, bus: 0)
engine.connect(engine.mainMixerNode, to: engine.outputNode, format: busFormat)
engine.connect(player, to:engine.mainMixerNode, format: busFormat)使用以下命令验证采样率是否始终相同:
NSLog("%@", engine)________ GraphDescription ________
AVAudioEngineGraph 0x7f8194905af0: initialized = 0, running = 0, number of nodes = 3
******** output chain ********
node 0x600001db9500 {'auou' 'ahal' 'appl'}, 'U'
inputs = 1
(bus0, en1) <- (bus0) 0x600001d80b80, {'aumx' 'mcmx' 'appl'}, [ 2 ch, 48000 Hz, 'lpcm' (0x00000029) 32-bit little-endian float, deinterleaved]
node 0x600001d80b80 {'aumx' 'mcmx' 'appl'}, 'U'
inputs = 1
(bus0, en1) <- (bus0) 0x600000fa0200, {'augn' 'sspl' 'appl'}, [ 2 ch, 48000 Hz, 'lpcm' (0x00000029) 32-bit little-endian float, deinterleaved]
outputs = 1
(bus0, en1) -> (bus0) 0x600001db9500, {'auou' 'ahal' 'appl'}, [ 2 ch, 48000 Hz, 'lpcm' (0x00000029) 32-bit little-endian float, deinterleaved]
node 0x600000fa0200 {'augn' 'sspl' 'appl'}, 'U'
outputs = 1
(bus0, en1) -> (bus0) 0x600001d80b80, {'aumx' 'mcmx' 'appl'}, [ 2 ch, 48000 Hz, 'lpcm' (0x00000029) 32-bit little-endian float, deinterleaved]
______________________________________https://stackoverflow.com/questions/64937978
复制相似问题