Единственный 100% надежный способ сделать это, который я нашел, — это настроить либо через CoreAudio, либо через AudioToolbox: https://developer.apple.com/documentation/audiotoolbox поставщик данных аудиопотока, который вызывается iOS через регулярные фиксированные интервалы для предоставления аудиосистеме образцов аудио.
Сначала это может показаться пугающим, но как только вы его настроите, у вас будет полный и точный контроль над тем, что генерируется для звука.
Это код, который я использовал для настройки AudioUnit с помощью AudioToolbox:
static AudioComponentInstance _audioUnit;
static int _outputAudioBus;
...
#pragma mark - Audio Unit
+(void)_activateAudioUnit
{
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
if([self _createAudioUnitInstance]
&& [self _setupAudioUnitOutput]
&& [self _setupAudioUnitFormat]
&& [self _setupAudioUnitRenderCallback]
&& [self _initializeAudioUnit]
&& [self _startAudioUnit]
)
{
[self _adjustOutputLatency];
// NSLog(@"Audio unit initialized");
}
}
+(BOOL)_createAudioUnitInstance
{
// Describe audio component
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
// Get audio units
OSStatus status = AudioComponentInstanceNew(inputComponent, &_audioUnit);
[self _logStatus:status step:@"instantiate"];
return (status == noErr );
}
+(BOOL)_setupAudioUnitOutput
{
UInt32 flag = 1;
OSStatus status = AudioUnitSetProperty(_audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
_outputAudioBus,
&flag,
sizeof(flag));
[self _logStatus:status step:@"set output bus"];
return (status == noErr );
}
+(BOOL)_setupAudioUnitFormat
{
AudioStreamBasicDescription audioFormat = {0};
audioFormat.mSampleRate = 44100.00;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 2;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 4;
audioFormat.mBytesPerFrame = 4;
OSStatus status = AudioUnitSetProperty(_audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
_outputAudioBus,
&audioFormat,
sizeof(audioFormat));
[self _logStatus:status step:@"set audio format"];
return (status == noErr );
}
+(BOOL)_setupAudioUnitRenderCallback
{
AURenderCallbackStruct audioCallback;
audioCallback.inputProc = playbackCallback;
audioCallback.inputProcRefCon = (__bridge void *)(self);
OSStatus status = AudioUnitSetProperty(_audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
_outputAudioBus,
&audioCallback,
sizeof(audioCallback));
[self _logStatus:status step:@"set render callback"];
return (status == noErr);
}
+(BOOL)_initializeAudioUnit
{
OSStatus status = AudioUnitInitialize(_audioUnit);
[self _logStatus:status step:@"initialize"];
return (status == noErr);
}
+(void)start
{
[self clearFeeds];
[self _startAudioUnit];
}
+(void)stop
{
[self _stopAudioUnit];
}
+(BOOL)_startAudioUnit
{
OSStatus status = AudioOutputUnitStart(_audioUnit);
[self _logStatus:status step:@"start"];
return (status == noErr);
}
+(BOOL)_stopAudioUnit
{
OSStatus status = AudioOutputUnitStop(_audioUnit);
[self _logStatus:status step:@"stop"];
return (status == noErr);
}
+(void)_logStatus:(OSStatus)status step:(NSString *)step
{
if( status != noErr )
{
NSLog(@"AudioUnit failed to %@, error: %d", step, (int)status);
}
}
Наконец, как только это будет запущено, мой зарегистрированный звуковой обратный вызов будет тем, который обеспечивает звук:
static OSStatus playbackCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
@autoreleasepool {
AudioBuffer *audioBuffer = ioData->mBuffers;
// .. fill in audioBuffer with Metronome sample data, fill the in-between ticks with 0s
}
return noErr;
}
Вы можете использовать звуковой редактор, например Audacity: https://www.audacityteam.org/download/mac/, чтобы отредактировать и сохранить файл в файле моно/стерео данных RAW PCM, или вы можете использовать одну из библиотек AVFoundation для извлечения аудиосэмплов из любого из поддерживаемых форматов аудиофайлов. Загрузите сэмплы в буфер, отслеживайте, где вы остановились между кадрами аудио обратного вызова, и подайте сэмпл метронома, чередующийся с 0.
Прелесть этого заключается в том, что теперь вы можете положиться на AudioToolbox iOS для определения приоритетов вашего кода, чтобы аудио и анимация просмотра не мешали друг другу.
Здоровья и удачи!
person
ekscrypto
schedule
02.08.2018