10

重写这个问题更成功一点。

我的问题是我无法从远程 IO 单元成功地将音频文件写入磁盘。

我采取的步骤是

打开一个 mp3 文件并将其音频提取到缓冲区。我根据图表的属性设置了一个 asbd 以与我的图表一起使用。我设置并运行我的图表循环提取的音频和声音成功地从扬声器中出来!

我遇到的困难是从远程 IO 回调中获取音频样本并将它们写入我正在使用 ExtAudioFileWriteASync 的磁盘上的音频文件。

音频文件确实被写入并且与原始 mp3 有一些可听的相似之处,但听起来非常失真。

我不确定问题是否存在

A) ExtAudioFileWriteAsync 无法像 io 单元回调提供的那样快地写入样本。

  • 或者 -

B) 我为 extaudiofile 参考错误设置了 ASBD。我想先保存一个 wav 文件。我不确定我是否在下面的 ASBD 中正确描述了这一点。

其次,我不确定在创建音频文件时为 inChannelLayout 属性传递什么值。

最后,我非常不确定将什么 asbd 用于 kExtAudioFileProperty_ClientDataFormat。我一直在使用我的立体声流格式,但仔细查看文档说这一定是 pcm。这应该与 remoteio 的输出格式相同吗?如果是这样我将远程io的输出格式设置为stereostreamformat是错误的吗?

我意识到这个问题有很多,但我有很多不确定性,我似乎无法自己解决。

设置立体声流格式

- (void) setupStereoStreamFormat
{

    size_t bytesPerSample = sizeof (AudioUnitSampleType);
    stereoStreamFormat.mFormatID          = kAudioFormatLinearPCM;
    stereoStreamFormat.mFormatFlags       = kAudioFormatFlagsAudioUnitCanonical;
    stereoStreamFormat.mBytesPerPacket    = bytesPerSample;
    stereoStreamFormat.mFramesPerPacket   = 1;
    stereoStreamFormat.mBytesPerFrame     = bytesPerSample;
    stereoStreamFormat.mChannelsPerFrame  = 2;                    // 2 indicates stereo
    stereoStreamFormat.mBitsPerChannel    = 8 * bytesPerSample;
    stereoStreamFormat.mSampleRate        = engineDescribtion.samplerate;

    NSLog (@"The stereo stereo format :");


}

使用立体声流格式设置 remoteio 回调

AudioUnitSetProperty(engineDescribtion.masterChannelMixerUnit, 
                             kAudioUnitProperty_StreamFormat, 
                             kAudioUnitScope_Output, 
                             masterChannelMixerUnitloop, 
                             &stereoStreamFormat, 
                             sizeof(stereoStreamFormat));

        AudioUnitSetProperty(engineDescribtion.masterChannelMixerUnit, 
                             kAudioUnitProperty_StreamFormat, 
                             kAudioUnitScope_Input, 
                             masterChannelMixerUnitloop, 
                             &stereoStreamFormat, 
                             sizeof(stereoStreamFormat));




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

{

   // ref.equnit;
    //AudioUnitRender(engineDescribtion.channelMixers[inBusNumber], ioActionFlags, inTimeStamp, 0, inNumberFrames, ioData);
    Engine *engine= (Engine *) inRefCon;
    AudioUnitRender(engineDescribtion.equnit, ioActionFlags, inTimeStamp, 0, inNumberFrames, ioData);

    if(engine->isrecording)
    {
        ExtAudioFileWriteAsync(engine->recordingfileref, inNumberFrames, ioData);

    }


    return 0;

}

**录音设置**

-(void)startrecording

{

    NSArray  *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    destinationFilePath = [[NSString alloc] initWithFormat: @"%@/testrecording.wav", documentsDirectory];
    destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)destinationFilePath, kCFURLPOSIXPathStyle, false);

    OSStatus status;

    // prepare a 16-bit int file format, sample channel count and sample rate
    AudioStreamBasicDescription dstFormat;
    dstFormat.mSampleRate=44100.0;
    dstFormat.mFormatID=kAudioFormatLinearPCM;
    dstFormat.mFormatFlags=kAudioFormatFlagsNativeEndian|kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked;
    dstFormat.mBytesPerPacket=4;
    dstFormat.mBytesPerFrame=4;
    dstFormat.mFramesPerPacket=1;
    dstFormat.mChannelsPerFrame=2;
    dstFormat.mBitsPerChannel=16;
    dstFormat.mReserved=0;

    // create the capture file
  status=  ExtAudioFileCreateWithURL(destinationURL, kAudioFileWAVEType, &dstFormat, NULL, kAudioFileFlags_EraseFile, &recordingfileref);
    CheckError( status ,"couldnt create audio file");
        // set the capture file's client format to be the canonical format from the queue
 status=ExtAudioFileSetProperty(recordingfileref, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &stereoStreamFormat);

    CheckError( status ,"couldnt set input format");

    ExtAudioFileSeek(recordingfileref, 0);
 isrecording=YES;

  // [documentsDirectory release];


}

编辑 1

我现在真的在黑暗中刺伤,但我需要使用音频转换器还是 kExtAudioFileProperty_ClientDataFormat 会处理这个问题?

编辑 2

我附上了2个音频样本。第一个是我循环播放并尝试复制的原始音频。第二个是该循环的录制音频。希望它可以给某人一个关于出了什么问题的线索。

原版mp3

mp3录音问题

4

1 回答 1

9

经过几天的眼泪和头发拉扯,我有一个解决方案。

在我的代码和其他示例中,我看到在 remoteio 单元的回调中调用了 extaudiofilewriteasync,就像这样。

** remoteiounit 回调 **

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

{


    AudioUnitRender(engineDescribtion.equnit, ioActionFlags, inTimeStamp, 0, inNumberFrames, ioData);


    if(isrecording)
    {
        ExtAudioFileWriteAsync(engine->recordingfileref, inNumberFrames, ioData);


    }



    return 0;

}

在这个回调中,我从另一个应用 eqs 并混合音频的音频单元中提取音频数据。

我从 remoteio 回调中删除了 extaudiofilewriteasync 调用到 remoteio 拉动并且文件成功写入的另一个回调!

* equunits 回调函数 *

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


    AudioUnitRender(engineDescribtion.masterChannelMixerUnit, ioActionFlags, inTimeStamp, 0, inNumberFrames, ioData);

   //process audio here    

    Engine *engine= (Engine *) inRefCon;


    OSStatus s;

    if(engine->isrecording)
    {
        s=ExtAudioFileWriteAsync(engine->recordingfileref, inNumberFrames, ioData);


    }


    return noErr;

}

为了充分理解为什么我的解决方案有效,有人可以向我解释为什么将数据从 remoteio 的 iodata 缓冲区列表写入文件会导致音频失真,但将数据进一步写入链会导致完美的音频?

于 2011-08-05T16:08:16.017 回答