swift - 音频流格式和数据类型与 Core Audio 的混淆

标签 swift audio buffer core-audio

我正在使用 Core Audio(带有快速包装器)播放一些音频样本(用于记录冲动的短刺激)。我坚持使用核心音频而不是较新的 AVFoundation,因为我需要一些严格的时间和多设备输入,而较新的框架目前还没有涵盖(我通过苹果代码请求让他们告诉我我必须使用核心音频)。

我现在使用以下方法创建了一个非常简单的正弦波:

func createSine()->[Float]{
    var timeArray = makeArray(from: ((1.0/Float(sampleRate))*((-0.5)*Float(kernelLength))), to: ((1.0/Float(sampleRate))*((0.5)*Float(kernelLength))), increment: 1/sampleRate)
    var sineArray = Array(repeating:0, count: timeArray.count)

    for i in 0..<timeArray.count {
            let x = 2 * Float.pi * 1000 * testTimeArray[i]
            sineArray[i] = cos(x)
    }
}

当以采样率(在我的例子中为 44,100Hz)播放时,这会为 1000Hz 的正弦波创建一个浮点(我认为是 32 位)数组

如果我将其写入 wav 文件并播放,音调将按预期创建。

但是,我实际上想在应用程序中触发此声音。我已经设置了我的 AUGraph 并用音频单元填充了它。我创建了一个 AURenderCallback,它在输入混音器时被调用。每一次,这个输入需要信号它调用这个回调函数。

let genCallback: AURenderCallback = { (
    inRefCon,
    ioActionFlags,
    inTimeStamp,
    inBusNumber,
    frameCount,
    ioData) -> OSStatus in

        let audioObject = unsafeBitCast(inRefCon, to: AudioEngine.self)

        for buffer in UnsafeMutableAudioBufferListPointer(ioData!) {
            var frames = buffer.mData!.assumingMemoryBound(to: Float.self)

            var j = 0

             for i in stride(from: 0, to: Int(frameCount), by: 2) {

                frames[i] = Float((audioObject.Stimulus[j + audioObject.stimulusReadIndex]))

                j += 1

            }

            audioObject.stimulusReadIndex += Int(frameCount/2)
        }
    }

   return noErr;
}

其中 audioObject.Stimulus 是我的 SineArray,而 audioObject.stimulusReadIndex 只是一个计数器,用于记住数组中已读取的内容。

现在,这就是我遇到麻烦的地方。如果我启动 AUGraph,我会听到我的正弦波,但我也会听到很多谐波(噪音)。看来这不是正确的格式。

如果我将每组帧复制到另一个数组中以测试写入的内容是否正确,则输出与输入刺激匹配,因此没有丢失样本。

如果我去查看混音器单元的 AudioStreamBasicDescription(因为这是调用渲染回调,我有以下信息:

var audioFormat = AudioStreamBasicDescription()
    audioFormat.mSampleRate            = 44100.00;
    audioFormat.mFormatID            = kAudioFormatLinearPCM;
    audioFormat.mFormatFlags        = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
    audioFormat.mFramesPerPacket    = 1;
    audioFormat.mChannelsPerFrame    = 2;
    audioFormat.mBitsPerChannel        = 16;
    audioFormat.mBytesPerPacket        = 4;
    audioFormat.mBytesPerFrame        = 4;
    audioFormat.mReserved             = 0;

  status = AudioUnitSetProperty(mixerUnit!,
                                  kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Input,
                                  1,
                                  &stimFormat,
                                  UInt32(MemoryLayout<AudioStreamBasicDescription>.size));
    checkStatus(status: status!);

所以这告诉了我一些事情。它需要两个 channel ,并且是交错的(因为不存在非交错标志)。在我的回调函数中,我将帧跨 2 以仅使用样本填充第一个 channel 。如果我改为从 1 开始播放,则写入音频并播放到右侧。

采样率是正确的,但是比特率是 16(Float 不是),我可以看到有一个标志用于“isSignedInteger”,所以这需要不同的格式。

现在,我尝试使用以下方法将 Float Array 转换为 Int16:

for i in 0..<sineArray.count{
       sineArray[i] =  Int16.init((32767 * sweepSamples[i]))
    }

然而,这仍然会导致不同的噪音,尽管有所不同。如果我检查数组,我可以确认结果是有符号的 int16,落在数据范围内。

我看不出如何以核心音频期望看到的格式来表示这些数据。我尝试将格式标志更改为 kAudioFormatFlagIsFloat 但仍然没有成功。

最佳答案

鉴于您的 [Float] 数据,而不是 kAudioFormatFlagIsSignedInteger 和每个 channel 16 位,您可能想要使用 kAudioFormatFlagIsFloat 和 32 位(每个数据包和帧 8 个字节)。

请注意,对于所有最新的 iOS 设备, native 音频格式为 32 位 float ,而不是 16 位整数,使用的 native (硬件?)采样率为 48000,而不是 44100。

此外,请注意,Apple 建议不要在音频回调上下文中使用 Swift(请参阅 2017 年或 2018 年 WWDC 音频 session ),因此您的音频单元渲染回调可能应该调用 C 函数来完成所有工作(任何涉及 ioData 或在 RefCon)。

您可能还想检查以确保您的数组索引没有超出数组边界。

关于swift - 音频流格式和数据类型与 Core Audio 的混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55784712/

相关文章:

swift - Segue 未从 Swift 中的 TableViewCell 触发

ios - NSObject 类中的委托(delegate)

audio - 基本的WAV数据 block

C Windows 缓冲区大小

python - 使用 CFFI 在 Python 中创建 CData 类型的缓冲区

ios - 为不同设备获取相同的 uibutton 中心值

ios - Swift - 同时播放音频文件和 Itunes 音乐

audio - 对渲染器执行Upnp stop命令立即停止

html - Chrome-在预加载后不会查找音频

c++ - 如何从 char [] 创建指向缓冲区的指针