У меня возникают проблемы с интерпретацией поведения обратных вызовов удаленного аудиоустройства в iOS. Я настраиваю модуль remoteIO с двумя обратными вызовами, один как обратный вызов ввода, а другой как обратный вызов «рендеринга». Я использую настройку RemoteIO, очень похожую на ту, что рекомендована в этом вкусном пикселе руководство. Это довольно длинный метод установки:
- (void)setup {
AudioUnit ioUnit;
AudioComponentDescription audioCompDesc;
audioCompDesc.componentType = kAudioUnitType_Output;
audioCompDesc.componentSubType = kAudioUnitSubType_RemoteIO;
audioCompDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
audioCompDesc.componentFlags = 0;
audioCompDesc.componentFlagsMask = 0;
AudioComponent rioComponent = AudioComponentFindNext(NULL, &audioCompDesc);
CheckError(AudioComponentInstanceNew(rioComponent, &ioUnit), "Couldn't get RIO unit instance");
// i/o
UInt32 oneFlag = 1;
CheckError(AudioUnitSetProperty(ioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
kOutputBus,
&oneFlag,
sizeof(oneFlag)), "Couldn't enable RIO output");
CheckError(AudioUnitSetProperty(ioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
kInputBus,
&oneFlag,
sizeof(oneFlag)), "Couldn't enable RIO input");
AudioStreamBasicDescription myASBD;
memset (&myASBD, 0, sizeof(myASBD));
myASBD.mSampleRate = 44100;
myASBD.mFormatID = kAudioFormatLinearPCM;
myASBD.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
myASBD.mFramesPerPacket = 1;
myASBD.mChannelsPerFrame = 1;
myASBD.mBitsPerChannel = 16;
myASBD.mBytesPerPacket = 2 * myASBD.mChannelsPerFrame;
myASBD.mBytesPerFrame = 2 * myASBD.mChannelsPerFrame;
// set stream format for both busses
CheckError(AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kOutputBus,
&myASBD,
sizeof(myASBD)), "Couldn't set ASBD for RIO on input scope / bus 0");
CheckError(AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kInputBus,
&myASBD,
sizeof(myASBD)), "Couldn't set ASBD for RIO on output scope / bus 1");
// set arbitrarily high for now
UInt32 bufferSizeBytes = 10000 * sizeof(int);
int offset = offsetof(AudioBufferList, mBuffers[0]);
int bufferListSizeInBytes = offset + (sizeof(AudioBuffer) * myASBD.mChannelsPerFrame);
// why need to cast to audioBufferList * ?
self.inputBuffer = (AudioBufferList *)malloc(bufferListSizeInBytes);
self.inputBuffer->mNumberBuffers = myASBD.mChannelsPerFrame;
for (UInt32 i = 0; i < myASBD.mChannelsPerFrame; i++) {
self.inputBuffer->mBuffers[i].mNumberChannels = 1;
self.inputBuffer->mBuffers[i].mDataByteSize = bufferSizeBytes;
self.inputBuffer->mBuffers[i].mData = malloc(bufferSizeBytes);
}
self.remoteIOUnit = ioUnit;
/////////////////////////////////////////////// callback setup
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = inputCallback;
callbackStruct.inputProcRefCon = (__bridge void * _Nullable)self;
CheckError(AudioUnitSetProperty(ioUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
kInputBus,
&callbackStruct,
sizeof(callbackStruct)), "Couldn't set input callback");
AURenderCallbackStruct callbackStruct2;
callbackStruct2.inputProc = playbackCallback;
callbackStruct2.inputProcRefCon = (__bridge void * _Nullable)self;
CheckError(AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
kOutputBus,
&callbackStruct,
sizeof(callbackStruct)), "Couldn't set input callback");
CheckError(AudioUnitInitialize(ioUnit), "Couldn't initialize input unit");
CheckError(AudioOutputUnitStart(ioUnit), "AudioOutputUnitStart failed");
}
Я наблюдаю странное поведение при обратном вызове. Во-первых, функция playbackCallback
не вызывается вообще, несмотря на то, что ее свойство задается таким же образом, как и в учебнике (учебник принадлежит парню, который написал приложение Loopy).
Во-вторых, входной обратный вызов имеет параметр ioData (audioBufferList), который должен иметь значение null (согласно документации), но переключается между нулем и значением, отличным от нуля, при каждом втором обратном вызове. Есть ли в этом смысл для кого-то?
Кроме того, вызов audiounitRender
во входном обратном вызове (семантика которого я до сих пор не понимаю с точки зрения логики API, жизненного цикла и т. Д.) Приводит к ошибке -50, что является очень типичным "плохими параметрами". Скорее всего, это связано с неправильной «топологией» audiobufferlist
, то есть с чередованием / деинтерлиброванием, номером канала и т. Д. Однако я пробовал различные топологии, и ни одна из них не привела к ошибке. И это также не объясняет странного поведения ioData. ЗДЕСЬ функция для справки:
OSStatus inputCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
MicController *myRefCon = (__bridge MicController *)inRefCon;
CheckError(AudioUnitRender(myRefCon.remoteIOUnit,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
myRefCon.inputBuffer), "audio unit render");
return noErr;
}
Я считаю, что мой опыт может быть связан с некоторыми простыми ошибками при форматировании или, возможно, с использованием неправильной шины в неправильной области видимости или с какой-либо другой тривиальной (и легко выполнимой в основной ошибке звукового контекста). Однако, поскольку у меня принципиально нет интуиции в отношении семантики и потока жизненного цикла (схемы? Я даже не знаю, какое слово использовать), я не могу адекватно отладить это. Я был бы очень признателен за помощь более опытного программиста основного звука, который мог бы пролить свет на эту ситуацию.
kOutputBus
равен нулю 0 другой - 1. Я не устанавливал...shouldAllocateBuffer
и не видел того, что требуется для кода remoteIO, который я видел до сих пор. Я также не знал, что вам нужен сеанс не по умолчанию. Я взял AudioSession по умолчанию из AVFoundation и установил категориюPlayAndRecord
, но это не помогло. Я тестирую это на iphone 6+. - person Alex Bollbach   schedule 14.02.2016