【问题标题】:Confusion With Audio Stream Formats and Data Types with Core Audio与核心音频的音频流格式和数据类型混淆
【发布时间】:2019-04-21 16:46:50
【问题描述】:

我正在使用 Core Audio(带有 swift 包装器)播放一些音频样本(用于记录冲动的短刺激)。我坚持使用核心音频而不是较新的 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)
    }
}

当以 sampleRate(在我的情况下为 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!);

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

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

所以现在,我尝试使用以下方法将我的浮点数组转换为 Int16:

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

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

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

【问题讨论】:

    标签: swift audio buffer core-audio


    【解决方案1】:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2017-09-28
      • 1970-01-01
      • 2014-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多