混淆音频流格式和核心音频数据类型

问题描述 投票:1回答:1

我正在使用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)
    }
}

这会在sampleRate(在我的情况下为44,100Hz)回放时创建一个Float(我相信是32位)阵列,用于1000Hz的正弦波

如果我将其写入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(浮点数不是),我可以看到'isSignedInteger'有一个标志,所以这需要不同的格式。

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

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

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

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

swift audio buffer core-audio
1个回答
0
投票

给定[Float]数据,而不是kAudioFormatFlagIsSignedInteger和每个通道16位,您可能希望使用kAudioFormatFlagIsFloat和32位(每个数据包和帧8个字节)。

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

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

您可能还需要检查以确保数组索引不超过数组边界。

© www.soinside.com 2019 - 2024. All rights reserved.