Плохая производительность на iPhoneXs/iPhone XS Max при записи звука с использованием аудиоустройства
Я пишу код для аудиозаписи с аудиоустройством, но боюсь, что производительность API аудиоустройства на iPhoneXs/iPhoneXs Max намного хуже, чем на других устройствах iOS, таких как iPhone6s Plus.
Вот некоторые данные о производительности, протестированные с iPhoneXs и iPhone6s Plus для API AudioOutputUnitStart. На самом деле это метод VoiceProcessingAudioUnit:: Start ().
iPhone6s Plus: 150 мс iPhoneXs:500 мс
Я пробовал частоту дискретизации 48 кГц и 16 кГц для аудио сессии, это не имеет значения.
bool VoiceProcessingAudioUnit::Init() {
//RTC_DCHECK_EQ(state_, kInitRequired);
// Create an audio component description to identify the Voice Processing
// I/O audio unit.
AudioComponentDescription vpio_unit_description;
vpio_unit_description.componentType = kAudioUnitType_Output;
vpio_unit_description.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
vpio_unit_description.componentManufacturer = kAudioUnitManufacturer_Apple;
vpio_unit_description.componentFlags = 0;
vpio_unit_description.componentFlagsMask = 0;
// Obtain an audio unit instance given the description.
AudioComponent found_vpio_unit_ref =
AudioComponentFindNext(nullptr, &vpio_unit_description);
// Create a Voice Processing IO audio unit.
OSStatus result = noErr;
result = AudioComponentInstanceNew(found_vpio_unit_ref, &vpio_unit_);
if (result != noErr) {
vpio_unit_ = nullptr;
return false;
}
// Enable input on the input scope of the input element.
UInt32 enable_input = 1;
result = AudioUnitSetProperty(vpio_unit_, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input, kInputBus, &enable_input,
sizeof(enable_input));
if (result != noErr) {
DisposeAudioUnit();
return false;
}
// Enable output on the output scope of the output element.
UInt32 enable_output = 0;
result = AudioUnitSetProperty(vpio_unit_, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output, kOutputBus,
&enable_output, sizeof(enable_output));
if (result != noErr) {
DisposeAudioUnit();
return false;
}
// Specify the callback function that provides audio samples to the audio
// unit.
AURenderCallbackStruct render_callback;
render_callback.inputProc = OnGetPlayoutData;
render_callback.inputProcRefCon = this;
result = AudioUnitSetProperty(
vpio_unit_, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
kOutputBus, &render_callback, sizeof(render_callback));
if (result != noErr) {
DisposeAudioUnit();
APM_INFO(@"Failed to specify the render callback on the output bus. "
"Error=%ld.",
(long)result);
return false;
}
// Disable AU buffer allocation for the recorder, we allocate our own.
// TODO(henrika): not sure that it actually saves resource to make this call.
UInt32 flag = 0;
result = AudioUnitSetProperty(
vpio_unit_, kAudioUnitProperty_ShouldAllocateBuffer,
kAudioUnitScope_Output, kInputBus, &flag, sizeof(flag));
if (result != noErr) {
DisposeAudioUnit();
return false;
}
// Specify the callback to be called by the I/O thread to us when input audio
// is available. The recorded samples can then be obtained by calling the
// AudioUnitRender() method.
AURenderCallbackStruct input_callback;
input_callback.inputProc = OnDeliverRecordedData;
input_callback.inputProcRefCon = this;
result = AudioUnitSetProperty(vpio_unit_,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global, kInputBus,
&input_callback, sizeof(input_callback));
if (result != noErr) {
DisposeAudioUnit();
return false;
}
state_ = kUninitialized;
return true;
}
bool VoiceProcessingAudioUnit::Initialize(AudioStreamBasicDescription desc){
AudioStreamBasicDescription format = desc;
OSStatus result = noErr;
UInt32 size = sizeof(format);
#if !defined(NDEBUG)
APMLogStreamDescription(format);
#endif
// Set the format on the output scope of the input element/bus.
result =
AudioUnitSetProperty(vpio_unit_, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output, kInputBus, &format, size);
if (result != noErr) {
return false;
}
result = AudioUnitInitialize(vpio_unit_);
state_ = kInitialized;
return true;
}
bool VoiceProcessingAudioUnit::Initialize(Float64 sample_rate) {
AudioStreamBasicDescription format = GetFormat(sample_rate);
return Initialize(format);
}
bool VoiceProcessingAudioUnit::Start() {
OSStatus result = AudioOutputUnitStart(vpio_unit_);
state_ = kStarted;
return true;
}
AudioStreamBasicDescription VoiceProcessingAudioUnit::GetFormat(
Float64 sample_rate) const {
// Set the application formats for input and output:
// - use same format in both directions
// - avoid resampling in the I/O unit by using the hardware sample rate
// - linear PCM => noncompressed audio data format with one frame per packet
// - no need to specify interleaving since only mono is supported
AudioStreamBasicDescription format = {0};
//RTC_DCHECK_EQ(1, kAPMAudioSessionPreferredNumberOfChannels);
format.mSampleRate = sample_rate;
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags =
kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
format.mBytesPerPacket = 2;
format.mFramesPerPacket = 1; // uncompressed.
format.mBytesPerFrame = 2;
format.mChannelsPerFrame = kAPMAudioSessionPreferredNumberOfChannels;
format.mBitsPerChannel = 8 * 2;
return format;
}
//audio session's config
+ (instancetype)asrConfiguration {
APMAudioSessionConfig *config = [[APMAudioSessionConfig alloc] init];
config.category = AVAudioSessionCategoryPlayAndRecord;
config.categoryOptions = AVAudioSessionCategoryOptionAllowBluetooth;
config.mode = AVAudioSessionModeVoiceChat;
config.sampleRate =48000;
config.ioBufferDuration = 0.01;
config.inputNumberOfChannels = 1;
config.outputNumberOfChannels = 1;
return config;
}
Производительность iPhoneXs самого устройства намного выше, чем у iPhone6s plus.so, что делает такую большую разницу в производительности с тем же кодом - это смутило меня.