【问题标题】:ios Core audio: how to get samples from AudioBuffer with interleaved audioios核心音频:如何从AudioBuffer中获取带有交错音频的样本
【发布时间】:2016-06-26 13:12:22
【问题描述】:

我已使用ExtAudioFileRead 函数将音频文件读入AudioBufferList
这是音频的 ASBD:

AudioStreamBasicDescription importFormat;

importFormat.mFormatID          = kAudioFormatLinearPCM;
importFormat.mFormatFlags       = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
importFormat.mBytesPerPacket    = 4;
importFormat.mFramesPerPacket   = 1;
importFormat.mBytesPerFrame     = 4;
importFormat.mChannelsPerFrame  = 2;
importFormat.mBitsPerChannel    = 16;
importFormat.mSampleRate = [[AVAudioSession sharedInstance] sampleRate];

所以我们得到并交错了 2 个通道的音频,每个通道有 16 位有符号 int
AudioBufferList init:

UInt32 *audioData = (UInt32 *) calloc (totalFramesInFile, sizeof (UInt32));

AudioBufferList *bufferList;
bufferList = (AudioBufferList *) malloc (sizeof (AudioBufferList));

// buffers amount is 1 because audio is interleaved
bufferList->mNumberBuffers = 1;

bufferList->mBuffers[0].mNumberChannels  = 2;
bufferList->mBuffers[0].mDataByteSize    = totalFramesInFile * sizeof(UInt32);
bufferList->mBuffers[0].mData            = audioData;

并读入缓冲区:

CheckError(ExtAudioFileRead (
                             audioFileObject,
                             &numberOfPacketsToRead,
                             bufferList), "error ExtAudioFileRead");

audioFileObjectExtAudioFileRef 的实例,它是在前面的代码中启动的,为了节省空间我没有粘贴到这里。
我想要完成的是在我的渲染回调中修改音频样本。

OSStatus MyCallback (void *inRefCon,
                 AudioUnitRenderActionFlags *ioActionFlags,
                 const AudioTimeStamp *inTimeStamp,
                 UInt32 inBusNumber,
                 UInt32 inNumberFrames,
                 AudioBufferList *ioData){


    ViewController *view = (__bridge ViewController *) inRefCon;

    soundStruct *soundStruct = (soundStruct *) &view->mys;

    SInt64            frameTotalForSound        = soundStruct->frameCount;

    soundStruct->isPlaying = true;

    UInt32 *audioData   = soundStruct->audioData;

    UInt32 sampleNumber = soundStruct->sampleNumber;

    for( int i = 0; i < ioData->mNumberBuffers; i++){

        AudioBuffer buffer = ioData->mBuffers[i];
        UInt32 *frameBuffer = buffer.mData;

        for(UInt32 frame = 0; frame < inNumberFrames; frame++) {

            // here I fill the buffer with my audio data.
            // i need to get left and right channel samples 
            // from  audioData[sampleNumber], modify them
            // and write into frameBuffer 

            frameBuffer[frame] = audioData[sampleNumber];

            sampleNumber++;

            if(sampleNumber > frameTotalForSound) {
                soundStruct->isPlaying = false;
                AudioOutputUnitStop(soundStruct->outputUnit);
            }
        }
    }

    soundStruct->sampleNumber = sampleNumber;

    return noErr;

}

是否可以从 UInt32 音频数据数组中获取 Sint16 左右声道样本?

【问题讨论】:

  • 我不确定我是否理解您的问题:您的程序正在生成交错式UInt32 格式的音频数据,并希望将其实时转换为非交错式SInt16播放?
  • @user3078414,不完全是。我根本不想转换。我有一个音频文件,其中包含交错音频和每个通道的 SInt16 值。目标是将其写入AudioBufferList 并保存在内存中。当 RemoteIO AudioUnit 请求新的声音部分时会触发此渲染回调,并且我希望实时修改每个通道的样本。因为有 2 个通道 - 我将样本存储在 UInt32 数组中。我不确定这是否是正确的调用,因为我不知道如何在稍后的回调中提取样本。
  • 这是我的answer 与类似但略有不同的问题:输出 - 写入文件需要单个交错缓冲区,而 AU 产生非交错缓冲区。我也看到了您的问题的答案 - 对最里面的实时循环中的索引乘法要小心……[2*(n-1)+1] 有利于演示清晰 - 我宁愿使用 [n+n-1] 代替。 (-:

标签: ios core-audio audiobuffer


【解决方案1】:

audioDataframeBuffer 都是SInt16s:

SInt16 *audioData;
// ...
SInt16 *frameBuffer;

您的缓冲区大小计算应该是 n * 2 * sizeof(SInt16) and you'll either need to changesoundStruct` 或添加类型转换。

然后你可以像这样访问交错的样本:

frameBuffer[0] = modify(audioData[0]);    // left sample 1
frameBuffer[1] = modify(audioData[1]);    // right sample 1
frameBuffer[2] = modify(audioData[2]);    // left sample 2
frameBuffer[3] = modify(audioData[3]);    // right sample 2
// ...
frameBuffer[2*(n-1)] = modify(audioData[2*(n-1)]);    // left sample n
frameBuffer[2*(n-1)+1] = modify(audioData[2*(n-1)+1]); // right sample n

【讨论】:

    【解决方案2】:

    @Rhythmic Fistman,非常感谢 - 它有帮助。
    我无法设置 frameBuffer 以这种方式工作。声音在输出处失真。
    我猜这是因为 AudioUnit 期望两个通道数据在一帧中。或者也许还有其他解释。
    这是我修改的代码,希望对大家有所帮助:

    audioData初始化:

    SInt16 *audioData = (SInt16 *) malloc (sizeof(SInt16) * totalFramesInFile * 2);
    

    修改渲染回调:

    OSStatus MyCallback (void *inRefCon,
                 AudioUnitRenderActionFlags *ioActionFlags,
                 const AudioTimeStamp *inTimeStamp,
                 UInt32 inBusNumber,
                 UInt32 inNumberFrames,
                 AudioBufferList *ioData)
    {
        ViewController *view = (__bridge ViewController *) inRefCon;
    
        soundStruct *soundStruct  = (soundStruct *) &view->mys;
    
        SInt64 frameTotalForSound = soundStruct->frameCount;
    
        soundStruct->isPlaying = true;
    
        SInt16 *audioData   = soundStruct->audioData;
    
        UInt32 sampleNumber = soundStruct->sampleNumber;
    
        for( int i = 0; i < ioData->mNumberBuffers; i++){
            AudioBuffer buffer = ioData->mBuffers[i];
            SInt16 *frameBuffer = (SInt16*) ioData->mBuffers[0].mData;
    
            for(UInt32 frame = 0; frame < inNumberFrames * 2; frame+=2) {
    
                /* .. some samples modification code .. */
    
                // left channel
                frameBuffer[frame] = audioData[sampleNumber];
                // right channel
                frameBuffer[frame + 1] = audioData[sampleNumber + 1];
    
                sampleNumber +=2;
    
                if(sampleNumber > frameTotalForSound * 2) {
                    soundStruct->isPlaying = false;
                    AudioOutputUnitStop(soundStruct->outputUnit);
                }
            }
        }
    
        soundStruct->sampleNumber = sampleNumber;
        return noErr;
    }
    

    【讨论】:

    • 为什么不把frameBuffer改成SInt16呢?然后你就不需要随便创建对了。
    • 完成 :) 我修改了源代码。现在没有失真,一切似乎都正常。感谢您的帮助
    • 您非常无私,发布了您问题的完整解决方案。时不时会出现类似的问题。我相信这会有所帮助,@jangofett
    猜你喜欢
    • 2013-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-10
    • 1970-01-01
    • 2017-09-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多