从IO单元将audio写入磁盘

重写这个问题会更有成效。

我的问题是,我不能成功地写一个audio文件从远程IO单元磁盘。

我采取的步骤是

打开一个mp3文件并将其audio提取到缓冲区。 我根据图的属性设置了一个asbd以用于我的图。 我安装并运行我的graphics循环提取的audio和声音成功地出来扬声器!

我有困难的是从远程IOcallback采取audio采样,并将其写入磁盘上的audio文件,我正在使用ExtAudioFileWriteASync。

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

我不确定问题是否存在

A)ExtAudioFileWriteAsync不能像io单元callback提供的那样快写样本。

  • 要么 –

B)我已经为extaudiofile refeferencebuild立了ASBD。 我想先保存一个wav文件。 我不确定是否在下面的ASBD中正确描述了这一点。

其次,我不确定创buildaudio文件时传递给inChannelLayout属性的值。

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

我意识到这个问题有很多,但是我有很多不确定因素,我似乎无法自行清理。

设置立体声stream格式

- (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 :"); } 

使用立体声stream格式设置remoteiocallback

 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

我真的在这里黑暗刺中,但是我需要使用audio转换器还是kExtAudioFileProperty_ClientDataFormat照顾?

编辑2

我附上2个audio样本。 第一个是Im循环和尝试复制的原始audio。 第二个是该循环的录制audio。 希望这可能会给人一些线索,看看出了什么问题。

原始的MP3

MP3录音问题

经过几天的眼泪和头发拉我有一个解决scheme。

在我的代码和其他例子中,我看到extaudiofilewriteasync被调用remoteio单元的callback像这样。

** 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; } 

在这个callback中,我从另一个应用eqs的audio单元中提取audio数据并混合audio。

我从remoteiocallback中删除了extaudiofilewriteasync调用这个遥控器拉动和文件写入成功的其他callback!

* equnitscallback函数*

 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; } 

为了充分理解为什么我的解决scheme能奏效,有人可以向我解释为什么从remoteio的iodata缓冲区列表中写入数据会导致audio失真,但是将数据写入链中会导致完美的audio?