iOS,AudioUnits 记录到本地 URL
Posted
技术标签:
【中文标题】iOS,AudioUnits 记录到本地 URL【英文标题】:iOS, AudioUnits record to local URL 【发布时间】:2013-08-05 22:07:11 【问题描述】:在我的 iPhone 应用程序中,我想录制我自己的应用程序内部产生的声音,v.s.录制麦克风捕获的外部声音。另一种说法是,我想在播放时直接从声卡上录制声音。从那里我想将新录制的声音文件保存到指定的本地 URL。 here 发布了一个类似的问题。我已经阅读了一些教程并有一些代码,但是有一些事情我需要帮助。这是我的代码:
头文件
OSStatus status;
实现文件
#define kOutputBus 0
#define kInputBus 1
static AudioComponentInstance audioUnit;
static OSStatus recordingCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
// TODO: Use inRefCon to access our interface object to do stuff
// Then, use inNumberFrames to figure out how much data is available, and make
// that much space available in buffers in an AudioBufferList.
AudioBufferList *bufferList; // <- Fill this up with buffers (you will want to malloc it, as it's a dynamic-length list)
// Then:
// Obtain recorded samples
OSStatus status;
status = AudioUnitRender([audioInterface audioUnit],
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
bufferList);
checkStatus(status);
// Now, we have the samples we just read sitting in buffers in bufferList
DoStuffWithTheRecordedAudio(bufferList);
return noErr;
static OSStatus playbackCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
// Notes: ioData contains buffers (may be more than one!)
// Fill them up as much as you can. Remember to set the size value in each buffer to match how
// much data is in the buffer.
return noErr;
void initializeInternalAudioRecorder()
AudiostreamBasicDescription audioFormat; //this is currently being called as a local variable, try calling it as a golbal variable if it doesnt work
OSStatus status;
AudioComponentInstance audioUnit;
// Describe audio component
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
// Get component
AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
// Get audio units
status = AudioComponentInstanceNew(inputComponent, &audioUnit);
checkStatus(status);
// Enable IO for recording
UInt32 flag = 1;
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
kInputBus,
&flag,
sizeof(flag));
checkStatus(status);
// Enable IO for playback
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
kOutputBus,
&flag,
sizeof(flag));
checkStatus(status);
// Describe format
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;
// Apply format
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kInputBus,
&audioFormat,
sizeof(audioFormat));
checkStatus(status);
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kOutputBus,
&audioFormat,
sizeof(audioFormat));
checkStatus(status);
// Set input callback
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = recordingCallback;
callbackStruct.inputProcRefCon = self;
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
kInputBus,
&callbackStruct,
sizeof(callbackStruct));
checkStatus(status);
// Set output callback
callbackStruct.inputProc = playbackCallback;
callbackStruct.inputProcRefCon = self;
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
kOutputBus,
&callbackStruct,
sizeof(callbackStruct));
checkStatus(status);
// Disable buffer allocation for the recorder (optional - do this if we want to pass in our own)
flag = 0;
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_ShouldAllocateBuffer,
kAudioUnitScope_Output,
kInputBus,
&flag,
sizeof(flag));
// TODO: Allocate our own buffers if we want
// Initialise
status = AudioUnitInitialize(audioUnit);
checkStatus(status);
-(void)startInternalRecorder
OSStatus status = AudioOutputUnitStart(audioUnit);
checkStatus(status);
-(void)stopInternalRecorder
OSStatus status = AudioOutputUnitStop(audioUnit);
checkStatus(status);
AudioComponentInstanceDispose(audioUnit);
现在,我在实现中遇到以下错误
'audioInterface' 未声明, 'self' 未声明,所以我的问题是如何修复这些错误,以及如何指定一个 URL 来保存录制的声音文件。
这是我的代码:http://atastypixel.com/blog/using-remoteio-audio-unit/comment-page-6/#comment-6734
我知道我的很多问题都与我对音频单元缺乏了解有关,但我会非常感谢任何提供帮助的人。谢谢。
【问题讨论】:
根据您在下面的评论,听起来您想录制音频并将其保存到设备上的本地文件中。以这种方式表达它是很自然的,因为这就是 API 声明它的方式,但就您的问题而言,它可能有点误导,因为它有点暗示您想将它保存在某个服务器上。如果您只想在本地保存文件,AVAudioRecorder
是一种更简单的方法。
我只是想将文件保存在本地,但我不能使用 AVAudioRecorder 的原因是它只能录制外部声音(例如通过麦克风)。我想立即录制内部声音iPhone 上的声卡。例如,如果有人戴着耳机听东西。我希望能够将应用程序播放的任何内容录制到他们的耳机中,而无需摘下耳机
抱歉给您带来了困惑
我的错,我没有仔细阅读你的问题。
【参考方案1】:
嗯,您复制/粘贴的代码似乎非常不完整。我会小心的。 :) 此外,您似乎已经复制/粘贴而没有保留它应该具有的结构。
无论如何audioFormat
应该被声明为一个局部变量,它的类型是AudioStreamBasicDescription
。代码的顶部(即recordingCallback
函数声明之上的所有内容)实际上是一个初始化函数,尽管原作者对此并没有那么明确。所以代码需要被包装成这样的东西:
void initializeMyStuff()
// Describe audio component
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
... lots more code ...
// Initialise
status = AudioUnitInitialize(audioUnit);
checkStatus(status);
// <-- you were missing this end bracket, which caused the compilation errors
static OSStatus recordingCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp, ... etc
...并在您启动应用程序的音频部分时调用它。如果将此代码的第一部分包装在适当的 C 函数中,则嵌套函数错误将消失。至于未声明的函数,要么将recordingCallback
和playbackCallback
移到initializeMyStuff
之上,要么在文件顶部声明。
您还应该摆脱 -(void)testMethod
并只调用 C 函数 initializeMyStuff()
而不是那个。这有意义吗?
【讨论】:
是的,它确实有道理。那么如何将我通过录制创建的音频文件保存到我在磁盘上指定的 URL 中呢?我可以创建 URL,并且使用 AVAudioRecorder 我只需使用我也想保存文件的 URL 初始化录音机,但看起来我不能在这里这样做...... 所以我声明OSStatus状态还是可以的; AudioComponentInstance 音频单元;在头文件中,还是我需要在 initializeMyStuff() 的顶部声明它们?audioUnit
可能需要是全局的(在这种情况下,它应该在标头中声明为 extern
,然后在 .c 文件的顶部实际声明)。
好的,我已经在我的问题中更新了我的代码。我在头文件中声明了 audioUnit extern,但我不明白如何在 .c 文件的顶部实际声明它。我实际上没有使用 C 文件。除了这一部分,我所有的程序都在 Objective -C 中。
在头文件中,你有extern AudioComponentInstance audioUnit;
,在实现文件的顶部(我猜它不一定是纯C),你有AudioComponentInstance audioUnit;
。如果这不起作用,请尝试从头文件中删除声明,并在实现文件的顶部简单地添加 static AudioComponentInstance audioUnit;
。以上是关于iOS,AudioUnits 记录到本地 URL的主要内容,如果未能解决你的问题,请参考以下文章
如何从 AudioUnits C 函数调用 Objective-C 方法? (iOS)