7

我一直在尝试在 iPad 2 上的 iOS 5 中的 renderCallback 中从 RemoteIO 单元直接录制到 AAC。我看到相互矛盾的信息说这是不可能的,而且它是可能的(在此处的评论中)。我想要这样做的原因是因为录制到 PCM 需要如此多的磁盘空间来录制任何长度的 - 即使它后来被转换为 AAC。

不过我已经准备好放弃了。我已经浏览了 Google、SO、Core Audio 书籍和 Apple Core-Audio 邮件列表和论坛,并且已经达到了没有出现任何错误的地步——并且正在将某些内容录制到磁盘上,但生成的文件无法播放。模拟器和设备上都是这种情况。

所以......如果有人有这方面的经验,我真的很感激朝着正确的方向轻推。设置是 RemoteIO 正在播放 AUSamplers 的输出并且工作正常。

这是我在下面的代码中所做的

  • 指定AudioStreamBasicDescriptionremoteIO 单元的格式kAudioFormatLinearPCM

  • ExtAudioFileRef 通过从 RemoteIO 单元获取指定客户端格式来创建并指定目标格式

  • 为 RemoteID 单元指定 renderCallback

  • 在renderCallback中, kAudioUnitRenderAction_PostRender阶段写入数据

正如我所说,我没有收到任何错误,并且生成的音频文件大小显示正在写入某些内容,但该文件无法播放。也许我的格式搞砸了?

无论如何,这是我在瓶中和/或“Be Here Dragons”旗帜中向任何冒着 Core-Audio 黑暗水域的人传达的信息。


//尝试播放文件时收到的不愉快的消息:

在此处输入图像描述

// remoteIO 设置的一部分

    // Enable IO for recording

UInt32 flag = 1;
result = AudioUnitSetProperty(ioUnit, 
                              kAudioOutputUnitProperty_EnableIO, 
                              kAudioUnitScope_Input, 
                              kInputBus, // == 1
                              &flag, 
                              sizeof(flag));
if (noErr != result) {[self printErrorMessage: @"Enable IO for recording" withStatus: result]; return;}

// Describe format - - - - - - - - - -
size_t bytesPerSample = sizeof (AudioUnitSampleType);
AudioStreamBasicDescription audioFormat;
memset(&audioFormat, 0, sizeof(audioFormat));
audioFormat.mSampleRate   = 44100.00;
audioFormat.mFormatID     = kAudioFormatLinearPCM;
audioFormat.mFormatFlags    = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
audioFormat.mBitsPerChannel   = 16;
audioFormat.mBytesPerPacket   = 2;
audioFormat.mBytesPerFrame    = 2;

result = AudioUnitSetProperty(ioUnit, 
                              kAudioUnitProperty_StreamFormat, 
                              kAudioUnitScope_Output, 
                              kInputBus, // == 1
                              &audioFormat, 
                              sizeof(audioFormat));


result = AudioUnitSetProperty(ioUnit, 
                              kAudioUnitProperty_StreamFormat, 
                              kAudioUnitScope_Input, 
                              kOutputBus, // == 0
                              &audioFormat, 
                              sizeof(audioFormat));

// 设置文件和渲染回调的函数

 - (void)startRecordingAAC
{

OSStatus result;

NSArray  *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *recordFile = [documentsDirectory stringByAppendingPathComponent: @"audio.m4a"];

CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, 
                                                        (__bridge   CFStringRef)recordFile, 
                                                        kCFURLPOSIXPathStyle, 
                                                        false);

AudioStreamBasicDescription destinationFormat;
memset(&destinationFormat, 0, sizeof(destinationFormat));
destinationFormat.mChannelsPerFrame = 2;
destinationFormat.mFormatID = kAudioFormatMPEG4AAC;
UInt32 size = sizeof(destinationFormat);
result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destinationFormat);        
if(result) printf("AudioFormatGetProperty %ld \n", result);


result = ExtAudioFileCreateWithURL(destinationURL, 
                                   kAudioFileM4AType, 
                                   &destinationFormat, 
                                   NULL, 
                                   kAudioFileFlags_EraseFile, 
                                   &extAudioFileRef);
if(result) printf("ExtAudioFileCreateWithURL %ld \n", result);

AudioStreamBasicDescription clientFormat;
memset(&clientFormat, 0, sizeof(clientFormat));


result = AudioUnitGetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, & clientFormat, &size);
 if(result) printf("AudioUnitGetProperty %ld \n", result);

result = ExtAudioFileSetProperty(extAudioFileRef,kExtAudioFileProperty_ClientDataFormat,sizeof(clientFormat),&clientFormat);
if(result) printf("ExtAudioFileSetProperty %ld \n", result);

result =  ExtAudioFileWriteAsync(extAudioFileRef, 0, NULL);
if (result) {[self printErrorMessage: @"ExtAudioFileWriteAsync error" withStatus: result];}

result = AudioUnitAddRenderNotify(ioUnit, renderCallback, (__bridge void*)self);
if (result) {[self printErrorMessage: @"AudioUnitAddRenderNotify" withStatus: result];}
}

// 最后,渲染回调

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

OSStatus result;
if (*ioActionFlags == kAudioUnitRenderAction_PostRender){
    MusicPlayerController* THIS = (__bridge MusicPlayerController *)inRefCon;

       result =  ExtAudioFileWriteAsync(THIS->extAudioFileRef, inNumberFrames, ioData);
       if(result) printf("ExtAudioFileWriteAsync %ld \n", result); 

}
return noErr; 
}
4

3 回答 3

15

所以我终于解决了这个问题!呃,真是个信息寻宝游戏。

无论如何,这是我错过的 ExtAudioFile 文档中的一点(请参阅粗体文本)。我没有设置这个属性。数据正在写入我的 .m4a 文件,但在播放时无法读取。所以总结一下:我有一堆 AUSamplers -> AUMixer -> RemoteIO。RemoteIO 实例上的渲染回调以压缩的 m4a 格式将数据写入磁盘。因此可以动态生成压缩音频(iOS 5/iPad 2)

看起来很健壮 - 我在 rendercallback 中有一些 printf 语句并且写入工作正常。

耶!

ExtAudioFileProperty_CodecManufacturer 扩展音频文件对象要使用的编解码器的制造商。值是读/写 UInt32。您必须在设置 kExtAudioFileProperty_ClientDataFormat (第 20 页)属性之前指定此属性,这反过来会触发编解码器的创建。通过指定 kAppleHardwareAudioCodecManufacturer 或 kAppleSoftwareAudioCodecManufacturer,在 iOS 中使用此属性在硬件或软件编码器之间进行选择。适用于 Mac OS X v10.7 及更高版本。在 ExtendedAudioFile.h 中声明。

// specify codec
UInt32 codec = kAppleHardwareAudioCodecManufacturer;
size = sizeof(codec);
result = ExtAudioFileSetProperty(extAudioFileRef, 
                                 kExtAudioFileProperty_CodecManufacturer, 
                                 size, 
                                 &codec);

if(result) printf("ExtAudioFileSetProperty %ld \n", result);
于 2012-04-12T18:01:53.943 回答
1

您是否在 mpg 4 音频文件的开头编写了所需的魔法 cookie?

您还需要至少在音频单元渲染回调之外写入第一个文件。

添加:

您最后是否正确刷新并关闭了音频文件?(在 AU 回调之外)

于 2012-04-12T06:25:23.340 回答
0

看起来这不仅仅是为输出音频文件 ID 指定编解码器。

根据此线程:ExtAudioFileWrite to m4a/aac 在双核设备(ipad 2、iphone 4s)上失败,例如某些 iPhone 型号(4S),不能很好地与硬件解码器配合使用。

根据我使用 4S 的经验,尝试使用硬件编解码器对 AAC 文件进行编码 a) 有时可以工作,b) 失败并出现错误 -66567 (kExtAudioFileError_MaxPacketSizeUnknown) 或 c) 写出一些样本然后挂起,没有可追溯的错误.

软件编解码器在 iPhone 4S 上运行良好,但性能较慢。

编辑:有些人声称硬件编解码器不喜欢 44.1kHz 采样率。我还没有证实这一点。

于 2013-07-03T14:00:45.837 回答