Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start refactoring out a common base class for audio backend drivers, …
…migrate the CAPlayThrough-derived backend over to it, and add one based on dlubin's help around AVFoundation's capture system
- Loading branch information
Showing
10 changed files
with
346 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// | ||
// THMBackEndAVFCapture.h | ||
// HotMic | ||
// | ||
// Created by Chris Jones on 27/04/2019. | ||
// Copyright © 2019 Chris Jones. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
#import <AVFoundation/AVFoundation.h> | ||
#import <CoreAudio/CoreAudio.h> | ||
|
||
#import "THMBackEndBase.h" | ||
#import "THMSingleton.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface THMBackEndAVFCapture : THMBackEndBase <AVCaptureAudioDataOutputSampleBufferDelegate> { | ||
AVCaptureDevice *audioInputDevice; | ||
AVCaptureDeviceInput *audioSessionInput; | ||
AVCaptureAudioPreviewOutput *audioSessionOutput; | ||
AVCaptureConnection *audioSessionConnection; | ||
AVCaptureSession *session; | ||
} | ||
|
||
- (void)cleanupAVSession; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// | ||
// THMBackEndAVFCapture.m | ||
// HotMic | ||
// | ||
// Created by Chris Jones on 27/04/2019. | ||
// Copyright © 2019 Chris Jones. All rights reserved. | ||
// | ||
|
||
#import "THMBackEndAVFCapture.h" | ||
|
||
@implementation THMBackEndAVFCapture | ||
|
||
- (id)initWithInputDevice:(THMAudioDevice *)input andOutputDevice:(THMAudioDevice *)output { | ||
self = [super initWithInputDevice:input andOutputDevice:output]; | ||
if (self) { | ||
[self addUIObservers:YES]; | ||
} | ||
return self; | ||
} | ||
|
||
- (void)dealloc { | ||
[self removeUIObservers]; | ||
[self stop]; | ||
} | ||
|
||
- (BOOL)start { | ||
NSError *error; | ||
|
||
if ([self isRunning]) { | ||
return YES; | ||
} | ||
|
||
audioInputDevice = [AVCaptureDevice deviceWithUniqueID:inputDevice.UID]; | ||
if (!audioInputDevice) { | ||
return NO; | ||
} | ||
|
||
audioSessionInput = [AVCaptureDeviceInput deviceInputWithDevice:audioInputDevice error:&error]; | ||
if (error) { | ||
NSLog(@"Error initialising AVCaptureDeviceInput: %@", error); | ||
audioInputDevice = nil; | ||
audioSessionInput = nil; | ||
return NO; | ||
} | ||
|
||
audioSessionOutput = [[AVCaptureAudioPreviewOutput alloc] init]; | ||
audioSessionOutput.outputDeviceUniqueID = outputDevice.UID; | ||
audioSessionOutput.volume = 0.5; // FIXME: Is this required? Will it inherit the device's volume if we don't do this? If not, how should we solve this? | ||
|
||
session = [[AVCaptureSession alloc] init]; | ||
[session stopRunning]; | ||
[session beginConfiguration]; | ||
if ([session canAddInput:audioSessionInput]) { | ||
[session addInputWithNoConnections:audioSessionInput]; | ||
} else { | ||
NSLog(@"Unable to add %@ as an AVCaptureSession input device", inputDevice); | ||
return NO; | ||
} | ||
|
||
if ([session canAddOutput:audioSessionOutput]) { | ||
[session addOutputWithNoConnections:audioSessionOutput]; | ||
} else { | ||
NSLog(@"Unable to add %@ as an AVCaptureSession output device", outputDevice); | ||
return NO; | ||
} | ||
|
||
audioSessionConnection = [AVCaptureConnection connectionWithInputPorts:audioSessionInput.ports output:audioSessionOutput]; | ||
if ([session canAddConnection:audioSessionConnection]) { | ||
[session addConnection:audioSessionConnection]; | ||
} else { | ||
NSLog(@"Unable to add AVCaptureConnection between %@ and %@", inputDevice, outputDevice); | ||
return NO; | ||
} | ||
|
||
[session commitConfiguration]; | ||
[session startRunning]; | ||
|
||
return YES; | ||
} | ||
|
||
- (void)updateAmplitude { | ||
float averagePowerDB = 0.0; | ||
|
||
NSArray *channels = audioSessionConnection.audioChannels; | ||
|
||
for (AVCaptureAudioChannel *channel in channels) { | ||
averagePowerDB += channel.averagePowerLevel; | ||
} | ||
|
||
averagePowerDB = averagePowerDB / channels.count; | ||
//NSLog(@"Calculated average power for %lu channels as %f", (unsigned long)channels.count, averagePowerDB); | ||
|
||
// FIXME: This isn't technically correct, the range appears to be -212db -> 0dB | ||
lastAmplitude = (averagePowerDB + 200) / 2 / 100; | ||
} | ||
|
||
- (BOOL) stop { | ||
if (![self isRunning]) { | ||
return YES; | ||
} | ||
|
||
[self cleanupAVSession]; | ||
return YES; | ||
} | ||
|
||
- (void)cleanupAVSession { | ||
if (session) { | ||
[session stopRunning]; | ||
|
||
if (audioSessionConnection) { | ||
[session removeConnection:audioSessionConnection]; | ||
} | ||
|
||
if (audioSessionOutput) { | ||
[session removeOutput:audioSessionOutput]; | ||
} | ||
|
||
if (audioSessionInput) { | ||
[session removeInput:audioSessionInput]; | ||
} | ||
} | ||
|
||
audioSessionOutput = nil; | ||
audioSessionInput = nil; | ||
audioInputDevice = nil; | ||
session = nil; | ||
|
||
return; | ||
} | ||
|
||
- (BOOL)isRunning { | ||
return session.running; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// | ||
// THMBackEndBase.h | ||
// HotMic | ||
// | ||
// Created by Chris Jones on 27/04/2019. | ||
// Copyright © 2019 Chris Jones. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
#import "THMAudioDevice.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
|
||
// FIXME: Should we maybe have the ivars here and the method calls in a protocol? Or can we somehow mark "must-override" on the relevant methods? | ||
@interface THMBackEndBase : NSObject { | ||
@public | ||
THMAudioDevice *inputDevice; | ||
THMAudioDevice *outputDevice; | ||
AudioDeviceID inputDeviceID; | ||
AudioDeviceID outputDeviceID; | ||
|
||
BOOL isUIVisible; | ||
Float32 lastAmplitude; | ||
|
||
id uiDidAppearObserver; | ||
id uiDidDisappearObserver; | ||
BOOL pollForAmplitude; | ||
NSTimer *amplitudePollingTimer; | ||
} | ||
|
||
- (id)initWithInputDevice:(THMAudioDevice *)input andOutputDevice:(THMAudioDevice *)output; | ||
- (BOOL)start; | ||
- (BOOL)stop; | ||
- (BOOL)isRunning; | ||
|
||
- (void)addUIObservers:(BOOL)withAmplitudePolling; | ||
- (void)removeUIObservers; | ||
|
||
- (void)updateAmplitude; | ||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
Oops, something went wrong.