从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?