Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 在音频单元渲染周期中处理不同数量的采样_Ios_Iphone_Audio_Core Audio - Fatal编程技术网

Ios 在音频单元渲染周期中处理不同数量的采样

Ios 在音频单元渲染周期中处理不同数量的采样,ios,iphone,audio,core-audio,Ios,Iphone,Audio,Core Audio,在iPhone 6s和6s+推出后,我的应用程序中出现了这个问题,我几乎可以肯定这是因为新款的内置麦克风在48kHz的频率下无法录制(你可以阅读更多关于这方面的信息)。澄清一下,我测试过的以前的手机型号从来没有出现过这个问题。下面我将详细介绍我的音频引擎实现,以及根据手机型号在不同点上的不同结果 下面是发生的情况-当我的代码在以前的设备上运行时,我在AVCaptureDevice返回的每个CMSampleBuffer中获得了一致数量的音频样本,通常是1024个样本。my audio unit g

在iPhone 6s和6s+推出后,我的应用程序中出现了这个问题,我几乎可以肯定这是因为新款的内置麦克风在48kHz的频率下无法录制(你可以阅读更多关于这方面的信息)。澄清一下,我测试过的以前的手机型号从来没有出现过这个问题。下面我将详细介绍我的音频引擎实现,以及根据手机型号在不同点上的不同结果

下面是发生的情况-当我的代码在以前的设备上运行时,我在AVCaptureDevice返回的每个CMSampleBuffer中获得了一致数量的音频样本,通常是1024个样本。my audio unit graph的渲染回调提供了一个适当的缓冲区,其空间可容纳1024帧。一切都很好,听起来也很好

然后苹果不得不去制造这个该死的iphone6s(只是开玩笑,太棒了,这个bug刚刚进入我的头脑),现在我得到了一些非常不一致和混乱的结果。AVCaptureDevice现在在捕获940或941个样本之间变化,渲染回调现在开始在第一次调用时为940或941个样本帧创建一个缓冲区,但随后立即开始将其在后续调用中保留的空间增加到1010、1012或1024个样本帧,然后保持不变。它最终保留的空间因会话而异。老实说,我不知道这个渲染回调是如何决定它为渲染准备了多少帧的,但我猜它与渲染回调打开的音频单元的采样率有关

无论设备是什么,CMSampleBuffer的格式都是以44.1kHz的采样率提供的,因此我猜在我从6s上的AVCaptureDevice接收CMSampleBuffer之前,会发生某种隐式采样率转换。唯一的区别是6s的首选硬件采样率为48kHz,而早期版本为44.1kHz

我已经读到,对于6s,您必须准备好为返回的不同数量的样本留出空间,但是我上面描述的这种行为正常吗?如果是,如何调整渲染周期以处理此问题

下面是处理音频缓冲区的代码,如果您想进一步了解:

音频样本缓冲区通过麦克风输入,并发送到我的音频处理功能,该功能对捕获的名为audioBuffer的CMSampleBufferRef执行以下操作

CMBlockBufferRef buffer = CMSampleBufferGetDataBuffer(audioBuffer);
        
CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples(audioBuffer);
AudioBufferList audioBufferList;
        
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(audioBuffer,
                                                            NULL,
                                                            &audioBufferList,
                                                            sizeof(audioBufferList),
                                                            NULL,
                                                            NULL,
                                                            kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
                                                            &buffer
                                                            );

self.audioProcessingCallback(&audioBufferList, numSamplesInBuffer, audioBuffer);
CFRelease(buffer);
这是将音频样本放入AudioBufferList,并将其连同样本数和保留的CMSampleBuffer一起发送到下面我用于音频处理的函数。TL;DR以下代码设置音频图形中的一些音频单元,使用CMSampleBuffer的格式设置ASBD作为输入,通过一个转换器单元、一个newTimePitch单元和另一个转换器单元运行音频采样。然后,我使用从CMSampleBufferRef接收到的样本数在输出转换器单元上启动渲染调用,并将渲染样本放回AudioBufferList,随后写入到电影文件中,有关Audio单元渲染回调的更多信息,请参见下文

movieWriter.audioProcessingCallback = {(audioBufferList, numSamplesInBuffer, CMSampleBuffer) -> () in

        var ASBDSize = UInt32(sizeof(AudioStreamBasicDescription))
        self.currentInputAudioBufferList = audioBufferList.memory
        
        let formatDescription = CMSampleBufferGetFormatDescription(CMSampleBuffer)
        let sampleBufferASBD = CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription!)
        
        if (sampleBufferASBD.memory.mFormatID != kAudioFormatLinearPCM) {
            print("Bad ASBD")
        }
        
        if(sampleBufferASBD.memory.mChannelsPerFrame != self.currentInputASBD.mChannelsPerFrame || sampleBufferASBD.memory.mSampleRate != self.currentInputASBD.mSampleRate){
            
            
            // Set currentInputASBD to format of data coming IN from camera
            self.currentInputASBD = sampleBufferASBD.memory
            print("New IN ASBD: \(self.currentInputASBD)")
            
            // set the ASBD for converter in's input to currentInputASBD
            var err = AudioUnitSetProperty(self.converterInAudioUnit,
                kAudioUnitProperty_StreamFormat,
                kAudioUnitScope_Input,
                0,
                &self.currentInputASBD,
                UInt32(sizeof(AudioStreamBasicDescription)))
            self.checkErr(err, "Set converter in's input stream format")
            
            // Set currentOutputASBD to the in/out format for newTimePitch unit
            err = AudioUnitGetProperty(self.newTimePitchAudioUnit,
                kAudioUnitProperty_StreamFormat,
                kAudioUnitScope_Input,
                0,
                &self.currentOutputASBD,
                &ASBDSize)
            self.checkErr(err, "Get NewTimePitch ASBD stream format")
            
            print("New OUT ASBD: \(self.currentOutputASBD)")
            
            //Set the ASBD for the convert out's input to currentOutputASBD
            err = AudioUnitSetProperty(self.converterOutAudioUnit,
                kAudioUnitProperty_StreamFormat,
                kAudioUnitScope_Input,
                0,
                &self.currentOutputASBD,
                ASBDSize)
            self.checkErr(err, "Set converter out's input stream format")
            
            //Set the ASBD for the converter out's output to currentInputASBD
            err = AudioUnitSetProperty(self.converterOutAudioUnit,
                kAudioUnitProperty_StreamFormat,
                kAudioUnitScope_Output,
                0,
                &self.currentInputASBD,
                ASBDSize)
            self.checkErr(err, "Set converter out's output stream format")
            
            //Initialize the graph
            err = AUGraphInitialize(self.auGraph)
            self.checkErr(err, "Initialize audio graph")
            
            self.checkAllASBD()
            
        }
        
        self.currentSampleTime += Double(numSamplesInBuffer)
        
        var timeStamp = AudioTimeStamp()
        memset(&timeStamp, 0, sizeof(AudioTimeStamp))
        timeStamp.mSampleTime = self.currentSampleTime
        timeStamp.mFlags = AudioTimeStampFlags.SampleTimeValid
        
        var flags = AudioUnitRenderActionFlags(rawValue: 0)
        
        err = AudioUnitRender(self.converterOutAudioUnit,
            &flags,
            &timeStamp,
            0,
            UInt32(numSamplesInBuffer),
            audioBufferList)
        self.checkErr(err, "Render Call on converterOutAU")
        
    }
AudioUnitRender调用到达输入转换器单元后调用的AudioUnitRender回调如下所示

func pushCurrentInputBufferIntoAudioUnit(inRefCon : UnsafeMutablePointer<Void>, ioActionFlags : UnsafeMutablePointer<AudioUnitRenderActionFlags>, inTimeStamp : UnsafePointer<AudioTimeStamp>, inBusNumber : UInt32, inNumberFrames : UInt32, ioData : UnsafeMutablePointer<AudioBufferList>) -> OSStatus {

let bufferRef = UnsafeMutablePointer<AudioBufferList>(inRefCon)
ioData.memory = bufferRef.memory
print(inNumberFrames);

return noErr
}
func pushCurrentInputBufferIntoAudioUnit(inRefCon:Unsafemutable指针,ioActionFlags:Unsafemutable指针,inTimeStamp:UnsafePointer,inBunsNumber:UInt32,inNumberFrames:UInt32,ioData:Unsafemutable指针)->OSStatus{
让bufferRef=UnsafemeutablePointer(inRefCon)
ioData.memory=bufferRef.memory
打印(数字框);
返回noErr
}

诸如此类,这是一个巨大的脑力消耗,但我真的非常感谢任何帮助。如果您需要任何其他信息,请告诉我。

通常,您可以通过将传入样本放入无锁循环fifo来处理缓冲区大小的细微变化(但输入和输出的采样率不变),在你有一个完整大小的块加上一些潜在的安全填充物来覆盖未来的大小抖动之前,不要从循环fifo中移除任何样本块

大小的变化可能与采样率转换器比率(不是简单的倍数)、所需的重采样滤波器以及重采样过程所需的任何缓冲有关

1024*(44100/48000)=940.8


因此,速率转换可以解释940和941样本之间的抖动。如果硬件总是以48 kHz的固定速率输出1024个样本块,并且您需要将该块重新采样到44100,以便尽快回调,转换后的样本中有一小部分最终只需要在一些输出回调中输出。

从您的回答中,我几乎觉得我的最佳选择是尝试消除样本率转换。就像我说的,我不知道它发生在哪里/为什么,我也不相信我需要它发生。对此有何想法?如果应用程序中的其余处理可以处理44.1k或48k的硬件采样率,则可能不需要从该给定速率进行转换。您是如何解决此问题的?提前谢谢