Skip to content

Commit e669d05

Browse files
committed
Fixed erroneous retain by NSTimer
1 parent 2ffb157 commit e669d05

File tree

1 file changed

+36
-12
lines changed

1 file changed

+36
-12
lines changed

Superpowered/OpenSource/SuperpoweredIOSAudioIO.mm

+36-12
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,35 @@ - (id)initWithDelegate:(NSObject<SuperpoweredIOSAudioIODelegate> *)d preferredBu
9393
externalAudioDeviceName = nil;
9494
audioUnit = NULL;
9595
inputBuffer = NULL;
96-
96+
9797
#if (USES_AUDIO_INPUT == 1)
9898
if (inputEnabled) [self createInputBuffer];
9999
#endif
100-
stopTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(everySecond) userInfo:nil repeats:YES];
101100
#if !__has_feature(objc_arc)
101+
stopTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(everySecond) userInfo:nil repeats:YES];
102102
[self release]; // to prevent NSTimer retaining this
103+
#else
104+
typeof(self) __weak weakSelf = self;
105+
stopTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
106+
__strong typeof(weakSelf) strongSelf = weakSelf;
107+
if (!strongSelf) return;
108+
if (strongSelf->silenceFrames > strongSelf->samplerate) {
109+
[strongSelf beginInterruption];
110+
strongSelf->silenceFrames = 0;
111+
} else if (!strongSelf->background && strongSelf->audioUnitRunning && strongSelf->started) { // If it should run...
112+
mach_timebase_info_data_t timebase;
113+
mach_timebase_info(&timebase);
114+
uint64_t diff = mach_absolute_time() - strongSelf->lastCallbackTime;
115+
diff *= timebase.numer;
116+
diff /= timebase.denom;
117+
if (diff > 1000000000) { // But it didn't call the audio processing callback in the past second.
118+
strongSelf->audioUnitRunning = false;
119+
[[AVAudioSession sharedInstance] setActive:NO error:nil];
120+
[strongSelf resetAudio];
121+
[strongSelf start];
122+
}
123+
}
124+
}];
103125
#endif
104126
[self resetAudio];
105127

@@ -109,7 +131,7 @@ - (id)initWithDelegate:(NSObject<SuperpoweredIOSAudioIODelegate> *)d preferredBu
109131
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMediaServerReset:) name:AVAudioSessionMediaServicesWereResetNotification object:[AVAudioSession sharedInstance]];
110132
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAudioSessionInterrupted:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
111133
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onRouteChange:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
112-
134+
113135
#if (USES_AUDIO_INPUT == 1)
114136
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 170000
115137
if ((recordOnly || [category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) && ([AVAudioApplication sharedInstance].recordPermission != AVAudioApplicationRecordPermissionGranted)) {
@@ -162,6 +184,7 @@ - (void)onMediaServerReset:(NSNotification *)notification { // The mediaserver d
162184
};
163185
}
164186

187+
#if !__has_feature(objc_arc)
165188
- (void)everySecond { // If we waited for more than 1 second with silence, stop RemoteIO to save battery.
166189
if (silenceFrames > samplerate) {
167190
[self beginInterruption];
@@ -180,6 +203,7 @@ - (void)everySecond { // If we waited for more than 1 second with silence, stop
180203
}
181204
}
182205
}
206+
#endif
183207

184208
- (void)beginInterruption { // Phone call, etc.
185209
if (![NSThread isMainThread]) [self performSelectorOnMainThread:@selector(beginInterruption) withObject:nil waitUntilDone:NO];
@@ -242,7 +266,7 @@ - (void)onRouteChange:(NSNotification *)notification {
242266
[self performSelectorOnMainThread:@selector(onRouteChange:) withObject:notification waitUntilDone:NO];
243267
return;
244268
};
245-
269+
246270
bool receiverAvailable = false, usbOrHDMIAvailable = false;
247271
for (AVAudioSessionPortDescription *port in [[[AVAudioSession sharedInstance] currentRoute] outputs]) {
248272
if ([port.portType isEqualToString:AVAudioSessionPortUSBAudio] || [port.portType isEqualToString:AVAudioSessionPortHDMI]) {
@@ -274,7 +298,7 @@ - (void)onRouteChange:(NSNotification *)notification {
274298
audioDeviceType type = NSStringToAudioDeviceType(port.portType);
275299
[audioSystemInfo appendFormat:@"%s%@ (%i out)", first ? "" : ", ", [port.portName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]], channels];
276300
first = false;
277-
301+
278302
if (type == audioDeviceType_headphone) outputChannelMap.headphoneAvailable = true;
279303
else if (type == audioDeviceType_HDMI) {
280304
outputChannelMap.numberOfHDMIChannelsAvailable = channels;
@@ -293,7 +317,7 @@ - (void)onRouteChange:(NSNotification *)notification {
293317
externalAudioDeviceName = port.portName;
294318
#endif
295319
};
296-
320+
297321
while (channels > 0) {
298322
RemoteIOOutputChannelMap[n++] = type;
299323
channels--;
@@ -303,15 +327,15 @@ - (void)onRouteChange:(NSNotification *)notification {
303327
if ([[AVAudioSession sharedInstance] isInputAvailable]) {
304328
[audioSystemInfo appendString:@", Inputs: "];
305329
first = true;
306-
330+
307331
for (AVAudioSessionPortDescription *port in [[[AVAudioSession sharedInstance] currentRoute] inputs]) {
308332
int channels = (int)[port.channels count];
309333
audioDeviceType type = NSStringToAudioDeviceType(port.portType);
310334
[audioSystemInfo appendFormat:@"%s%@ (%i in)", first ? "" : ", ", [port.portName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]], channels];
311335
first = false;
312336
if (type == audioDeviceType_USB) inputChannelMap.numberOfUSBChannelsAvailable = channels;
313337
};
314-
338+
315339
if (first) [audioSystemInfo appendString:@"-"];
316340
};
317341

@@ -401,7 +425,7 @@ static void streamFormatChangedCallback(void *inRefCon, AudioUnit inUnit, AudioU
401425
static OSStatus coreAudioProcessingCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) {
402426
__unsafe_unretained SuperpoweredIOSAudioIO *self = (__bridge SuperpoweredIOSAudioIO *)inRefCon;
403427
self->lastCallbackTime = mach_absolute_time();
404-
428+
405429
if (!ioData) ioData = self->inputBuffer;
406430
div_t d = div(inNumberFrames, 8);
407431
if (d.rem != 0) {
@@ -448,7 +472,7 @@ static OSStatus coreAudioProcessingCallback(void *inRefCon, AudioUnitRenderActio
448472
// If the app is in the background, check if we don't output anything.
449473
if (self->background && self->saveBatteryInBackground) self->silenceFrames += inNumberFrames; else self->silenceFrames = 0;
450474
} else self->silenceFrames = 0;
451-
475+
452476
return noErr;
453477
}
454478

@@ -532,7 +556,7 @@ - (void)mapChannels {
532556
outputChannelMap.deviceChannels[0] = outputChannelMap.deviceChannels[1] = -1;
533557
for (int n = 0; n < 8; n++) outputChannelMap.HDMIChannels[n] = -1;
534558
for (int n = 0; n < 32; n++) outputChannelMap.USBChannels[n] = inputChannelMap.USBChannels[n] = -1;
535-
559+
536560
if ([(NSObject *)self->delegate respondsToSelector:@selector(mapChannels:inputMap:externalAudioDeviceName:outputsAndInputs:)]) [delegate mapChannels:&outputChannelMap inputMap:&inputChannelMap externalAudioDeviceName:externalAudioDeviceName outputsAndInputs:audioSystemInfo];
537561
if (!audioUnit || (numberOfChannels <= 2)) return;
538562

@@ -547,7 +571,7 @@ - (void)mapChannels {
547571
} else outputmap[n] = -1;
548572
inputmap[n] = inputChannelMap.USBChannels[n];
549573
};
550-
574+
551575
#if !TARGET_IPHONE_SIMULATOR
552576
AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 0, outputmap, 128);
553577
AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 1, inputmap, 128);

0 commit comments

Comments
 (0)