@@ -39,34 +39,55 @@ @implementation SuperpoweredIOSAudioIO {
3939 NSString *externalAudioDeviceName, *audioSessionCategory;
4040 NSTimer *stopTimer;
4141 NSMutableString *audioSystemInfo;
42- audioProcessingCallback processingCallback;
42+ audioProcessingCallbackNonInterleaved processingCallback;
4343 void *processingClientdata;
44+ float **inputBufs, **outputBufs;
4445 AudioBufferList *inputBuffer;
4546 AudioComponentInstance audioUnit;
4647 multiOutputChannelMap outputChannelMap;
4748 multiInputChannelMap inputChannelMap;
4849 audioDeviceType RemoteIOOutputChannelMap[64 ];
4950 uint64_t lastCallbackTime;
5051 int numberOfChannels, silenceFrames, samplerate, minimumNumberOfFrames, maximumNumberOfFrames;
51- bool audioUnitRunning, background, inputEnabled;
52+ bool audioUnitRunning, background, inputEnabled, interleaved ;
5253}
5354
5455@synthesize preferredBufferSizeMs, preferredSamplerate, saveBatteryInBackground, started;
5556
5657- (void )createInputBuffer {
57- inputBuffer = (AudioBufferList *)malloc (offsetof (AudioBufferList, mBuffers [0 ]) + sizeof (AudioBuffer));
58- if (!inputBuffer) abort ();
59- inputBuffer->mBuffers [0 ].mData = calloc (1 , MAXFRAMES * 4 * numberOfChannels);
60- if (!inputBuffer->mBuffers [0 ].mData ) abort ();
61- inputBuffer->mBuffers [0 ].mDataByteSize = MAXFRAMES * 4 * numberOfChannels;
62- inputBuffer->mBuffers [0 ].mNumberChannels = numberOfChannels;
63- inputBuffer->mNumberBuffers = 1 ;
58+ if (interleaved) {
59+ inputBuffer = (AudioBufferList *)malloc (offsetof (AudioBufferList, mBuffers [0 ]) + sizeof (AudioBuffer));
60+ if (!inputBuffer) abort (); else inputBuffer->mNumberBuffers = 1 ;
61+ inputBuffer->mBuffers [0 ].mData = calloc (1 , MAXFRAMES * 4 * numberOfChannels);
62+ if (!inputBuffer->mBuffers [0 ].mData ) abort ();
63+ inputBuffer->mBuffers [0 ].mDataByteSize = MAXFRAMES * 4 * numberOfChannels;
64+ inputBuffer->mBuffers [0 ].mNumberChannels = numberOfChannels;
65+ } else {
66+ inputBuffer = (AudioBufferList *)malloc (offsetof (AudioBufferList, mBuffers [0 ]) + sizeof (AudioBuffer) * numberOfChannels);
67+ inputBufs = (float **)malloc (sizeof (float *) * numberOfChannels);
68+ if (!inputBuffer || !inputBufs) abort (); else inputBuffer->mNumberBuffers = numberOfChannels;
69+ for (int n = 0 ; n < numberOfChannels; n++) {
70+ inputBuffer->mBuffers [n].mData = inputBufs[n] = (float *)calloc (1 , MAXFRAMES * 4 );
71+ if (!inputBufs[n]) abort ();
72+ inputBuffer->mBuffers [n].mDataByteSize = MAXFRAMES * 4 ;
73+ inputBuffer->mBuffers [n].mNumberChannels = 1 ;
74+ }
75+ }
6476}
6577
6678- (id )initWithDelegate : (NSObject <SuperpoweredIOSAudioIODelegate> *)d preferredBufferSize : (unsigned int )preferredBufferSize preferredSamplerate : (unsigned int )prefsamplerate audioSessionCategory : (NSString *)category channels : (int )channels audioProcessingCallback : (audioProcessingCallback)callback clientdata : (void *)clientdata {
79+ return [self initWithDelegateNonInterleaved: d preferredBufferSize: preferredBufferSize preferredSamplerate: prefsamplerate audioSessionCategory: category channels: -channels audioProcessingCallback: (audioProcessingCallbackNonInterleaved)callback clientdata: clientdata];
80+ }
81+
82+ - (id )initWithDelegateNonInterleaved : (NSObject <SuperpoweredIOSAudioIODelegate> *)d preferredBufferSize : (unsigned int )preferredBufferSize preferredSamplerate : (unsigned int )prefsamplerate audioSessionCategory : (NSString *)category channels : (int )channels audioProcessingCallback : (audioProcessingCallbackNonInterleaved)callback clientdata : (void *)clientdata {
6783 self = [super init ];
6884 if (self) {
69- numberOfChannels = channels;
85+ interleaved = channels < 0 ;
86+ numberOfChannels = abs (channels);
87+ if (!interleaved) {
88+ outputBufs = (float **)malloc (sizeof (float *) * numberOfChannels);
89+ if (!outputBufs) abort ();
90+ }
7091#if !__has_feature(objc_arc)
7192 audioSessionCategory = [category retain ];
7293#else
@@ -160,9 +181,12 @@ - (void)dealloc {
160181 AudioComponentInstanceDispose (audioUnit);
161182 };
162183 if (inputBuffer) {
163- free (inputBuffer->mBuffers [0 ].mData );
184+ if (interleaved) for (int n = 0 ; n < numberOfChannels; n++) free (inputBuffer->mBuffers [n].mData );
185+ else free (inputBuffer->mBuffers [0 ].mData );
164186 free (inputBuffer);
165187 };
188+ if (inputBufs) free (inputBufs);
189+ if (outputBufs) free (outputBufs);
166190 [[AVAudioSession sharedInstance ] setActive: NO error: nil ];
167191 [[NSNotificationCenter defaultCenter ] removeObserver: self ];
168192#if !__has_feature(objc_arc)
@@ -436,39 +460,44 @@ static OSStatus coreAudioProcessingCallback(void *inRefCon, AudioUnitRenderActio
436460 }
437461 }
438462
439- BOOL isiOSAppOnMac = false ;
440463#if !TARGET_OS_MACCATALYST // iOS or Mac (Designed for iPad)
441- if (@ available (iOS 14.0 , *) ) {
442- isiOSAppOnMac = NSProcessInfo . processInfo . isiOSAppOnMac ;
443- }
464+ if (NSProcessInfo . processInfo . isiOSAppOnMac ) {
465+ if ((( int )inNumberFrames < self-> minimumNumberOfFrames ) || (( int )inNumberFrames > self-> maximumNumberOfFrames ) || ( int (self-> interleaved ? ioData-> mBuffers [ 0 ]. mNumberChannels : ioData-> mNumberBuffers ) != self-> numberOfChannels )) return kAudioUnitErr_InvalidParameter ;
466+ } else
444467#endif
445- if (isiOSAppOnMac) {
446- if (((int )inNumberFrames < self->minimumNumberOfFrames ) || ((int )inNumberFrames > self->maximumNumberOfFrames ) || ((int )ioData->mBuffers [0 ].mNumberChannels != self->numberOfChannels )) {
447- return kAudioUnitErr_InvalidParameter ;
448- };
449- } else {
450- if ((d.rem != 0 ) || (inNumberFrames < 32 ) || (inNumberFrames > MAXFRAMES) || ((int )ioData->mBuffers [0 ].mNumberChannels != self->numberOfChannels )) {
451- return kAudioUnitErr_InvalidParameter ;
452- };
453- }
468+ if ((d.rem != 0 ) || (inNumberFrames < 32 ) || (inNumberFrames > MAXFRAMES) || (int (self->interleaved ? ioData->mBuffers [0 ].mNumberChannels : ioData->mNumberBuffers ) != self->numberOfChannels )) return kAudioUnitErr_InvalidParameter ;
454469
455- // Get audio input.
456- float *inputBuf = NULL ;
457- if (self->inputEnabled ) {
458- self->inputBuffer ->mBuffers [0 ].mDataByteSize = MAXFRAMES * 4 * self->numberOfChannels ;
459- self->inputBuffer ->mBuffers [0 ].mNumberChannels = self->numberOfChannels ;
460- self->inputBuffer ->mNumberBuffers = 1 ;
461- if (!AudioUnitRender (self->audioUnit , ioActionFlags, inTimeStamp, 1 , inNumberFrames, self->inputBuffer )) inputBuf = (float *)self->inputBuffer ->mBuffers [0 ].mData ;
462- }
463470 bool silence = true ;
464-
465- // Make audio output.
466- silence = !self->processingCallback (self->processingClientdata , inputBuf, (float *)ioData->mBuffers [0 ].mData , inNumberFrames, self->samplerate , inTimeStamp->mHostTime );
471+ if (self->interleaved ) {
472+ // Get audio input.
473+ float *inputBuf = NULL ;
474+ if (self->inputEnabled ) {
475+ self->inputBuffer ->mBuffers [0 ].mDataByteSize = MAXFRAMES * 4 * self->numberOfChannels ;
476+ self->inputBuffer ->mBuffers [0 ].mNumberChannels = self->numberOfChannels ;
477+ self->inputBuffer ->mNumberBuffers = 1 ;
478+ if (!AudioUnitRender (self->audioUnit , ioActionFlags, inTimeStamp, 1 , inNumberFrames, self->inputBuffer )) inputBuf = (float *)self->inputBuffer ->mBuffers [0 ].mData ;
479+ }
480+ // Make audio output.
481+ silence = !((audioProcessingCallback)self->processingCallback )(self->processingClientdata , inputBuf, (float *)ioData->mBuffers [0 ].mData , inNumberFrames, self->samplerate , inTimeStamp->mHostTime );
482+ } else {
483+ // Get audio input.
484+ float **inputs = NULL ;
485+ if (self->inputEnabled ) {
486+ for (int n = 0 ; n < self->numberOfChannels ; n++) {
487+ self->inputBuffer ->mBuffers [n].mDataByteSize = MAXFRAMES * 4 ;
488+ self->inputBuffer ->mBuffers [n].mNumberChannels = 1 ;
489+ }
490+ self->inputBuffer ->mNumberBuffers = self->numberOfChannels ;
491+ if (!AudioUnitRender (self->audioUnit , ioActionFlags, inTimeStamp, 1 , inNumberFrames, self->inputBuffer )) inputs = self->inputBufs ;
492+ }
493+ // Make audio output.
494+ for (int n = 0 ; n < self->numberOfChannels ; n++) self->outputBufs [n] = (float *)ioData->mBuffers [n].mData ;
495+ silence = !self->processingCallback (self->processingClientdata , inputs, self->outputBufs , inNumberFrames, self->samplerate , inTimeStamp->mHostTime );
496+ }
467497
468498 if (silence) { // Despite of ioActionFlags, it outputs garbage sometimes, so must zero the buffers:
469499 *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence ;
470- memset (ioData->mBuffers [0 ].mData , 0 , inNumberFrames * sizeof (float ) * self->numberOfChannels );
471-
500+ for (unsigned int n = 0 ; n < ioData->mNumberBuffers ; n++) memset (ioData->mBuffers [n].mData , 0 , ioData->mBuffers [n].mDataByteSize );
472501 // If the app is in the background, check if we don't output anything.
473502 if (self->background && self->saveBatteryInBackground ) self->silenceFrames += inNumberFrames; else self->silenceFrames = 0 ;
474503 } else self->silenceFrames = 0 ;
@@ -478,7 +507,6 @@ static OSStatus coreAudioProcessingCallback(void *inRefCon, AudioUnitRenderActio
478507
479508- (AudioUnit)createRemoteIO {
480509 AudioUnit au;
481-
482510 AudioComponentDescription desc;
483511 desc.componentType = kAudioUnitType_Output ;
484512 desc.componentSubType = kAudioUnitSubType_RemoteIO ;
@@ -505,9 +533,10 @@ - (AudioUnit)createRemoteIO {
505533
506534 format.mFormatID = kAudioFormatLinearPCM ;
507535 format.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian ;
536+ if (!interleaved) format.mFormatFlags |= kAudioFormatFlagIsNonInterleaved ;
508537 format.mBitsPerChannel = 32 ;
509538 format.mFramesPerPacket = 1 ;
510- format.mBytesPerFrame = format.mBytesPerPacket = numberOfChannels * 4 ;
539+ format.mBytesPerFrame = format.mBytesPerPacket = interleaved ? ( numberOfChannels * 4 ) : 4 ;
511540 format.mChannelsPerFrame = numberOfChannels;
512541 if (AudioUnitSetProperty (au, kAudioUnitProperty_StreamFormat , kAudioUnitScope_Input , 0 , &format, sizeof (format))) { AudioComponentInstanceDispose (au); return NULL ; };
513542 if (AudioUnitSetProperty (au, kAudioUnitProperty_StreamFormat , kAudioUnitScope_Output , 1 , &format, sizeof (format))) { AudioComponentInstanceDispose (au); return NULL ; };
0 commit comments