O.K,所以我使用核心音频从10个不同的样本源中提取音频,然后在我的回调函数中将它们混合在一起。
它在模拟器中运行完美,一切都很顺利。当我试图在4.2 iphone设备上运行时,我遇到了麻烦。
如果我在回调中混合2个音频文件,一切正常。 如果我混合使用5或6个音频文件,音频会播放但是在很短的时间后音频会降级,最终没有音频会进入扬声器。 (回调不会停止)。
如果我尝试混合10个音频文件,则回调会运行,但根本不会发出音频。
这几乎就像回调时间不多了,这可能解释了我混合5或6的情况,但不会解释混合10个音频源的最后一个案例,其中根本没有播放音频。
我不确定以下内容是否有任何影响,但在调试时此消息始终打印到控制台。这可以说明问题是什么吗?
mem 0x1000 0x3fffffff cache
mem 0x40000000 0xffffffff none
mem 0x00000000 0x0fff none
run
Running…
[Switching to thread 11523]
[Switching to thread 11523]
Re-enabling shared library breakpoint 1
continue
warning: Unable to read symbols for /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.2.1 (8C148)/Symbols/usr/lib/info/dns.so (file not found).
**设置我的回调**
#pragma mark -
#pragma mark Callback setup & control
- (void) setupCallback
{
OSStatus status;
// 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);
UInt32 flag = 1;
// Enable IO for playback
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
kOutputBus,
&flag,
sizeof(flag));
//Apply format
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kOutputBus,
&stereoStreamFormat,
sizeof(stereoStreamFormat));
// Set up the playback callback
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = playbackCallback; //!!****assignment from incompatible pointer warning here *****!!!!!!
//set the reference to "self" this becomes *inRefCon in the playback callback
callbackStruct.inputProcRefCon = self;
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
kOutputBus,
&callbackStruct,
sizeof(callbackStruct));
// Initialise
status = AudioUnitInitialize(audioUnit); // error check this status
}
CallBack
static OSStatus playbackCallback (
void *inRefCon, // A pointer to a struct containing the complete audio data
// to play, as well as state information such as the
// first sample to play on this invocation of the callback.
AudioUnitRenderActionFlags *ioActionFlags, // Unused here. When generating audio, use ioActionFlags to indicate silence
// between sounds; for silence, also memset the ioData buffers to 0.
AudioTimeStamp *inTimeStamp, // Unused here.
UInt32 inBusNumber, // The mixer unit input bus that is requesting some new
// frames of audio data to play.
UInt32 inNumberFrames, // The number of frames of audio to provide to the buffer(s)
// pointed to by the ioData parameter.
AudioBufferList *ioData // On output, the audio data to play. The callback's primary
// responsibility is to fill the buffer(s) in the
// AudioBufferList.
) {
Engine *remoteIOplayer = (Engine *)inRefCon;
AudioUnitSampleType *outSamplesChannelLeft;
AudioUnitSampleType *outSamplesChannelRight;
outSamplesChannelLeft = (AudioUnitSampleType *) ioData->mBuffers[0].mData;
outSamplesChannelRight = (AudioUnitSampleType *) ioData->mBuffers[1].mData;
int thetime =0;
thetime=remoteIOplayer.sampletime;
for (int frameNumber = 0; frameNumber < inNumberFrames; ++frameNumber)
{
// get NextPacket returns a 32 bit value, one frame.
AudioUnitSampleType *suml=0;
AudioUnitSampleType *sumr=0;
//NSLog (@"frame number - %i", frameNumber);
for(int j=0;j<10;j++)
{
AudioUnitSampleType valuetoaddl=0;
AudioUnitSampleType valuetoaddr=0;
//valuetoadd = [remoteIOplayer getSample:j ];
valuetoaddl = [remoteIOplayer getNonInterleavedSample:j currenttime:thetime channel:0 ];
//valuetoaddl = [remoteIOplayer getSample:j];
valuetoaddr = [remoteIOplayer getNonInterleavedSample:j currenttime:thetime channel:1 ];
suml = suml+(valuetoaddl/10);
sumr = sumr+(valuetoaddr/10);
}
outSamplesChannelLeft[frameNumber]=(AudioUnitSampleType) suml;
outSamplesChannelRight[frameNumber]=(AudioUnitSampleType) sumr;
remoteIOplayer.sampletime +=1;
}
return noErr;
}
我的音频提取功能
-(AudioUnitSampleType) getNonInterleavedSample:(int) index currenttime:(int)time channel:(int)ch;
{
AudioUnitSampleType returnvalue= 0;
soundStruct snd=soundStructArray[index];
UInt64 sn= snd.frameCount;
UInt64 st=sampletime;
UInt64 read= (UInt64)(st%sn);
if(ch==0)
{
if (snd.sendvalue==1) {
returnvalue = snd.audioDataLeft[read];
}else {
returnvalue=0;
}
}else if(ch==1)
{
if (snd.sendvalue==1) {
returnvalue = snd.audioDataRight[read];
}else {
returnvalue=0;
}
soundStructArray[index].sampleNumber=read;
}
if(soundStructArray[index].sampleNumber >soundStructArray[index].frameCount)
{
soundStructArray[index].sampleNumber=0;
}
return returnvalue;
}
编辑1
为了回应@andre,我将回调更改为以下内容,但仍然没有帮助。
static OSStatus playbackCallback (
void *inRefCon, // A pointer to a struct containing the complete audio data
// to play, as well as state information such as the
// first sample to play on this invocation of the callback.
AudioUnitRenderActionFlags *ioActionFlags, // Unused here. When generating audio, use ioActionFlags to indicate silence
// between sounds; for silence, also memset the ioData buffers to 0.
AudioTimeStamp *inTimeStamp, // Unused here.
UInt32 inBusNumber, // The mixer unit input bus that is requesting some new
// frames of audio data to play.
UInt32 inNumberFrames, // The number of frames of audio to provide to the buffer(s)
// pointed to by the ioData parameter.
AudioBufferList *ioData // On output, the audio data to play. The callback's primary
// responsibility is to fill the buffer(s) in the
// AudioBufferList.
) {
Engine *remoteIOplayer = (Engine *)inRefCon;
AudioUnitSampleType *outSamplesChannelLeft;
AudioUnitSampleType *outSamplesChannelRight;
outSamplesChannelLeft = (AudioUnitSampleType *) ioData->mBuffers[0].mData;
outSamplesChannelRight = (AudioUnitSampleType *) ioData->mBuffers[1].mData;
int thetime =0;
thetime=remoteIOplayer.sampletime;
for (int frameNumber = 0; frameNumber < inNumberFrames; ++frameNumber)
{
// get NextPacket returns a 32 bit value, one frame.
AudioUnitSampleType suml=0;
AudioUnitSampleType sumr=0;
//NSLog (@"frame number - %i", frameNumber);
for(int j=0;j<16;j++)
{
soundStruct snd=remoteIOplayer->soundStructArray[j];
UInt64 sn= snd.frameCount;
UInt64 st=remoteIOplayer.sampletime;
UInt64 read= (UInt64)(st%sn);
suml+= snd.audioDataLeft[read];
suml+= snd.audioDataRight[read];
}
outSamplesChannelLeft[frameNumber]=(AudioUnitSampleType) suml;
outSamplesChannelRight[frameNumber]=(AudioUnitSampleType) sumr;
remoteIOplayer.sampletime +=1;
}
return noErr;
}
答案 0 :(得分:3)
就像Andre说的那样,最好不要在回调中进行任何Objective-C函数调用。您还应该将inputProcRefCon更改为C-Struct而不是Objective-C对象。
此外,您可能会逐帧“手动”将数据复制到缓冲区中。相反,使用memcopy复制一大块数据。
另外,我很确定你在回调中没有进行磁盘I / O,但如果你是,你也不应该这样做。
答案 1 :(得分:2)
根据我的经验,尽量不要在RemoteIO Callback中使用Objective-C函数调用。他们会放慢速度。尝试使用C结构在Callback中移动“getNonInterleavedSample”函数以访问音频数据。
答案 2 :(得分:1)
我认为你是CPU限制的;模拟器在处理速度方面比各种设备更强大。
回调可能无法跟上它被调用的频率。
编辑:你能“预先计算”混音(提前完成或在另一个线程中),以便在回调触发时它已经被混合,并且回调的工作量较少吗?