Browse files

MKAudio: rework device management to improve support for iOS 6's new …

…VoiceProcessingIO behavior.
  • Loading branch information...
1 parent 805e1e8 commit c130614650a92b4555b7ce4e9edb321e19423022 @mkrautz mkrautz committed Oct 4, 2012
View
34 MumbleKit.xcodeproj/project.pbxproj
@@ -99,11 +99,21 @@
28492365132ED67E00B4EAAC /* ProtocolBuffers.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 28492360132ED67E00B4EAAC /* ProtocolBuffers.dylib */; };
28492366132ED67E00B4EAAC /* Speex.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 28492361132ED67E00B4EAAC /* Speex.dylib */; };
28492367132ED67E00B4EAAC /* SpeexDSP.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 28492362132ED67E00B4EAAC /* SpeexDSP.dylib */; };
+ 284C1ABC161CABCC00B87340 /* MKVoiceProcessingDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 284C1ABA161CABCC00B87340 /* MKVoiceProcessingDevice.h */; };
+ 284C1ABD161CABCC00B87340 /* MKVoiceProcessingDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 284C1ABB161CABCC00B87340 /* MKVoiceProcessingDevice.m */; };
285B426414E6E1420045E282 /* libOpus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 285B426114E6E1200045E282 /* libOpus.a */; };
2879526414C1BAB900567430 /* MKTextMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 2879526114C1BAB900567430 /* MKTextMessage.m */; };
2879526514C1BAB900567430 /* MKTextMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 2879526114C1BAB900567430 /* MKTextMessage.m */; };
2879526814C1BDD800567430 /* MKTextMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 2879526714C1BDD800567430 /* MKTextMessage.h */; };
2879526914C1BDD800567430 /* MKTextMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 2879526714C1BDD800567430 /* MKTextMessage.h */; };
+ 288211C0161CE44B00E72F91 /* MKAudioDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 288211BE161CE44B00E72F91 /* MKAudioDevice.h */; };
+ 288211C1161CE44B00E72F91 /* MKAudioDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 288211BE161CE44B00E72F91 /* MKAudioDevice.h */; };
+ 288211C2161CE44B00E72F91 /* MKAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 288211BF161CE44B00E72F91 /* MKAudioDevice.m */; };
+ 288211C3161CE44B00E72F91 /* MKAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 288211BF161CE44B00E72F91 /* MKAudioDevice.m */; };
+ 288211C7161CECD000E72F91 /* MKiOSAudioDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 288211C5161CECD000E72F91 /* MKiOSAudioDevice.h */; };
+ 288211C8161CECD000E72F91 /* MKiOSAudioDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 288211C5161CECD000E72F91 /* MKiOSAudioDevice.h */; };
+ 288211C9161CECD000E72F91 /* MKiOSAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 288211C6161CECD000E72F91 /* MKiOSAudioDevice.m */; };
+ 288211CA161CECD000E72F91 /* MKiOSAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 288211C6161CECD000E72F91 /* MKiOSAudioDevice.m */; };
28CC36EB132ED87100241269 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CC36EA132ED87000241269 /* Security.framework */; };
28CC36ED132ED88200241269 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CC36EC132ED88200241269 /* Foundation.framework */; };
28CC36EF132ED88A00241269 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CC36EE132ED88A00241269 /* Cocoa.framework */; };
@@ -391,9 +401,15 @@
28492360132ED67E00B4EAAC /* ProtocolBuffers.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = ProtocolBuffers.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
28492361132ED67E00B4EAAC /* Speex.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = Speex.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
28492362132ED67E00B4EAAC /* SpeexDSP.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = SpeexDSP.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+ 284C1ABA161CABCC00B87340 /* MKVoiceProcessingDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MKVoiceProcessingDevice.h; path = src/MKVoiceProcessingDevice.h; sourceTree = SOURCE_ROOT; };
+ 284C1ABB161CABCC00B87340 /* MKVoiceProcessingDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MKVoiceProcessingDevice.m; path = src/MKVoiceProcessingDevice.m; sourceTree = SOURCE_ROOT; };
285B425814E6E1200045E282 /* Opus.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Opus.xcodeproj; path = 3rdparty/opusbuild/Opus.xcodeproj; sourceTree = "<group>"; };
2879526114C1BAB900567430 /* MKTextMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MKTextMessage.m; path = src/MKTextMessage.m; sourceTree = SOURCE_ROOT; };
2879526714C1BDD800567430 /* MKTextMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MKTextMessage.h; path = src/MumbleKit/MKTextMessage.h; sourceTree = SOURCE_ROOT; };
+ 288211BE161CE44B00E72F91 /* MKAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MKAudioDevice.h; path = src/MKAudioDevice.h; sourceTree = SOURCE_ROOT; };
+ 288211BF161CE44B00E72F91 /* MKAudioDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MKAudioDevice.m; path = src/MKAudioDevice.m; sourceTree = SOURCE_ROOT; };
+ 288211C5161CECD000E72F91 /* MKiOSAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MKiOSAudioDevice.h; path = src/MKiOSAudioDevice.h; sourceTree = SOURCE_ROOT; };
+ 288211C6161CECD000E72F91 /* MKiOSAudioDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MKiOSAudioDevice.m; path = src/MKiOSAudioDevice.m; sourceTree = SOURCE_ROOT; };
28BCF2C1132AE3B40003AEC1 /* libMumbleKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMumbleKit.a; sourceTree = BUILT_PRODUCTS_DIR; };
28CC36EA132ED87000241269 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = SDKs/MacOSX10.6.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
28CC36EC132ED88200241269 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = SDKs/MacOSX10.6.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
@@ -505,9 +521,12 @@
children = (
28CC36F8132ED92500241269 /* Mumble.pb.m */,
28CC36F9132ED92500241269 /* ObjectivecDescriptor.pb.m */,
- 2845A781132D9C220034D631 /* CryptState.cpp */,
BCD11E06158F40FA00321E06 /* MKAccessControl.m */,
+ 2845A781132D9C220034D631 /* CryptState.cpp */,
2845A782132D9C220034D631 /* MKAudio.m */,
+ 288211BF161CE44B00E72F91 /* MKAudioDevice.m */,
+ 288211C6161CECD000E72F91 /* MKiOSAudioDevice.m */,
+ 284C1ABB161CABCC00B87340 /* MKVoiceProcessingDevice.m */,
2845A783132D9C220034D631 /* MKAudioInput.m */,
2845A784132D9C220034D631 /* MKAudioOutput.m */,
2845A785132D9C220034D631 /* MKAudioOutputSpeech.m */,
@@ -562,6 +581,9 @@
2845A7BB132D9C400034D631 /* MKUserPrivate.h */,
2845A7BC132D9C400034D631 /* MulticastDelegate.h */,
28F2FAD413E37BA000034BF2 /* MKAudioOutputUserPrivate.h */,
+ 288211BE161CE44B00E72F91 /* MKAudioDevice.h */,
+ 288211C5161CECD000E72F91 /* MKiOSAudioDevice.h */,
+ 284C1ABA161CABCC00B87340 /* MKVoiceProcessingDevice.h */,
2845A7C8132D9C520034D631 /* MKAudioInput.h */,
2845A7C9132D9C520034D631 /* MKAudioOutput.h */,
2845A7CA132D9C520034D631 /* MKAudioOutputSpeech.h */,
@@ -717,6 +739,8 @@
BCD11DF8158F40A900321E06 /* MKAccessControl.h in Headers */,
BCD11DFA158F40A900321E06 /* MKChannelACL.h in Headers */,
BCD11DFC158F40A900321E06 /* MKChannelGroup.h in Headers */,
+ 288211C1161CE44B00E72F91 /* MKAudioDevice.h in Headers */,
+ 288211C8161CECD000E72F91 /* MKiOSAudioDevice.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -753,6 +777,9 @@
BCD11DF7158F40A900321E06 /* MKAccessControl.h in Headers */,
BCD11DF9158F40A900321E06 /* MKChannelACL.h in Headers */,
BCD11DFB158F40A900321E06 /* MKChannelGroup.h in Headers */,
+ 284C1ABC161CABCC00B87340 /* MKVoiceProcessingDevice.h in Headers */,
+ 288211C0161CE44B00E72F91 /* MKAudioDevice.h in Headers */,
+ 288211C7161CECD000E72F91 /* MKiOSAudioDevice.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -987,6 +1014,8 @@
BCD11E0A158F40FA00321E06 /* MKAccessControl.m in Sources */,
BCD11E0C158F40FA00321E06 /* MKChannelACL.m in Sources */,
BCD11E0E158F40FA00321E06 /* MKChannelGroup.m in Sources */,
+ 288211C3161CE44B00E72F91 /* MKAudioDevice.m in Sources */,
+ 288211CA161CECD000E72F91 /* MKiOSAudioDevice.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1020,6 +1049,9 @@
BCD11E09158F40FA00321E06 /* MKAccessControl.m in Sources */,
BCD11E0B158F40FA00321E06 /* MKChannelACL.m in Sources */,
BCD11E0D158F40FA00321E06 /* MKChannelGroup.m in Sources */,
+ 284C1ABD161CABCC00B87340 /* MKVoiceProcessingDevice.m in Sources */,
+ 288211C2161CE44B00E72F91 /* MKAudioDevice.m in Sources */,
+ 288211C9161CECD000E72F91 /* MKiOSAudioDevice.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
48 src/MKAudio.m
@@ -4,22 +4,31 @@
#import <MumbleKit/MKAudio.h>
#import "MKUtils.h"
+#import "MKAudioDevice.h"
#import "MKAudioInput.h"
#import "MKAudioOutput.h"
#import "MKAudioOutputSidetone.h"
+#import "MKVoiceProcessingDevice.h"
+#import "MKiOSAudioDevice.h"
+
+#import <AudioUnit/AudioUnit.h>
+#import <AudioUnit/AUComponent.h>
+#import <AudioToolbox/AudioToolbox.h>
+
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#import <UIKit/UIKit.h>
#endif
NSString *MKAudioDidRestartNotification = @"MKAudioDidRestartNotification";
@interface MKAudio () {
- MKAudioInput *_audioInput;
- MKAudioOutput *_audioOutput;
- MKAudioOutputSidetone *_sidetoneOutput;
- MKAudioSettings _audioSettings;
- BOOL _running;
+ MKAudioDevice *_audioDevice;
+ MKAudioInput *_audioInput;
+ MKAudioOutput *_audioOutput;
+ MKAudioOutputSidetone *_sidetoneOutput;
+ MKAudioSettings _audioSettings;
+ BOOL _running;
}
@end
@@ -233,6 +242,9 @@ - (void) stop {
_audioInput = nil;
[_audioOutput release];
_audioOutput = nil;
+ [_audioDevice teardownDevice];
+ [_audioDevice release];
+ _audioDevice = nil;
[_sidetoneOutput release];
_sidetoneOutput = nil;
_running = NO;
@@ -248,13 +260,21 @@ - (void) start {
AudioSessionSetActive(YES);
#endif
@synchronized(self) {
- _audioInput = [[MKAudioInput alloc] initWithSettings:&_audioSettings];
- _audioOutput = [[MKAudioOutput alloc] initWithSettings:&_audioSettings];
+#if TARGET_OS_IPHONE == 1
+ if ([[MKAudio sharedAudio] echoCancellationAvailable] && _audioSettings.enableEchoCancellation) {
+ _audioDevice = [[MKVoiceProcessingDevice alloc] initWithSettings:&_audioSettings];
+ } else {
+ _audioDevice = [[MKiOSAudioDevice alloc] initWithSettings:&_audioSettings];
+ }
+#else
+# error Missing MKAudioDevice
+#endif
+ [_audioDevice setupDevice];
+ _audioInput = [[MKAudioInput alloc] initWithDevice:_audioDevice andSettings:&_audioSettings];
+ _audioOutput = [[MKAudioOutput alloc] initWithDevice:_audioDevice andSettings:&_audioSettings];
if (_audioSettings.enableSideTone) {
_sidetoneOutput = [[MKAudioOutputSidetone alloc] initWithSettings:&_audioSettings];
}
- [_audioInput setupDevice];
- [_audioOutput setupDevice];
_running = YES;
}
}
@@ -327,16 +347,6 @@ - (void) setMuted:(BOOL)muted {
- (BOOL) echoCancellationAvailable {
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
- // Disable Echo Cancellation on iOS 6 due to its faulty VoiceProcessingIO.
- NSString *systemVersion = [[UIDevice currentDevice] systemVersion];
- NSArray *versionComponents = [systemVersion componentsSeparatedByString:@"."];
- if (versionComponents.count == 0) {
- return NO;
- }
- if ([[versionComponents objectAtIndex:0] integerValue] >= 6) {
- return NO;
- }
-
NSDictionary *dict = nil;
UInt32 valSize = sizeof(NSDictionary *);
OSStatus err = AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &valSize, &dict);
View
22 src/MKAudioDevice.h
@@ -0,0 +1,22 @@
+// Copyright 2012 The MumbleKit Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import <MumbleKit/MKAudio.h>
+
+typedef BOOL (^MKAudioDeviceOutputFunc)(short *frames, unsigned int nsamp);
+typedef BOOL (^MKAudioDeviceInputFunc)(short *frames, unsigned int nsamp);
+
+@interface MKAudioDevice : NSObject
+- (id) initWithSettings:(MKAudioSettings *)settings;
+- (BOOL) setupDevice;
+- (BOOL) teardownDevice;
+
+- (void) setupInput:(MKAudioDeviceInputFunc)inf;
+- (void) setupOutput:(MKAudioDeviceOutputFunc)outf;
+
+- (int) inputSampleRate;
+- (int) outputSampleRate;
+- (int) numberOfInputChannels;
+- (int) numberOfOutputChannels;
+@end
View
43 src/MKAudioDevice.m
@@ -0,0 +1,43 @@
+// Copyright 2012 The MumbleKit Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import "MKAudioDevice.h"
+
+@implementation MKAudioDevice
+
+- (id) initWithSettings:(MKAudioSettings *)settings {
+ return nil;
+}
+
+- (BOOL) setupDevice {
+ return NO;
+}
+
+- (BOOL) teardownDevice {
+ return NO;
+}
+
+- (void) setupOutput:(MKAudioDeviceOutputFunc)outf {
+}
+
+- (void) setupInput:(MKAudioDeviceInputFunc)inf {
+}
+
+- (int) inputSampleRate {
+ return 0;
+}
+
+- (int) outputSampleRate {
+ return 0;
+}
+
+- (int) numberOfInputChannels {
+ return 0;
+}
+
+- (int) numberOfOutputChannels {
+ return 0;
+}
+
+@end
View
9 src/MKAudioInput.h
@@ -4,18 +4,13 @@
#import <MumbleKit/MKAudio.h>
#import <MumbleKit/MKConnection.h>
-#import <AudioUnit/AudioUnit.h>
-#import <AudioUnit/AUComponent.h>
-#import <AudioToolbox/AudioToolbox.h>
+#import "MKAudioDevice.h"
@interface MKAudioInput : NSObject
-- (id) initWithSettings:(MKAudioSettings *)settings;
+- (id) initWithDevice:(MKAudioDevice *)device andSettings:(MKAudioSettings *)settings;
- (void) dealloc;
-- (BOOL) setupDevice;
-- (BOOL) teardownDevice;
-
- (void) initializeMixer;
- (void) resetPreprocessor;
View
350 src/MKAudioInput.m
@@ -8,6 +8,7 @@
#import "MKPacketDataStream.h"
#import "MKAudioInput.h"
#import "MKAudioOutputSidetone.h"
+#import "MKAudioDevice.h"
#include <speex/speex.h>
#include <speex/speex_preprocess.h>
@@ -20,12 +21,11 @@
@interface MKAudioInput () {
@public
- AudioUnit audioUnit;
- AudioBufferList buflist;
int micSampleSize;
int numMicChannels;
@private
+ MKAudioDevice *_device;
MKAudioSettings _settings;
SpeexPreprocessState *_preprocessorState;
@@ -70,52 +70,17 @@ @interface MKAudioInput () {
NSMutableData *_opusBuffer;
}
-- (BOOL) setupMacDevice;
-- (BOOL) setupiOSDevice;
@end
-static OSStatus inputCallback(void *udata, AudioUnitRenderActionFlags *flags, const AudioTimeStamp *ts,
- UInt32 busnum, UInt32 nframes, AudioBufferList *buflist) {
- MKAudioInput *i = (MKAudioInput *)udata;
- OSStatus err;
-
- if (! i->buflist.mBuffers->mData) {
- NSLog(@"MKAudioInput: No buffer allocated.");
- i->buflist.mNumberBuffers = 1;
- AudioBuffer *b = i->buflist.mBuffers;
- b->mNumberChannels = i->numMicChannels;
- b->mDataByteSize = i->micSampleSize * nframes;
- b->mData = calloc(1, b->mDataByteSize);
- }
-
- if (i->buflist.mBuffers->mDataByteSize < (nframes/i->micSampleSize)) {
- NSLog(@"MKAudioInput: Buffer too small. Allocating more space.");
- AudioBuffer *b = i->buflist.mBuffers;
- free(b->mData);
- b->mDataByteSize = i->micSampleSize * nframes;
- b->mData = calloc(1, b->mDataByteSize);
- }
-
- err = AudioUnitRender(i->audioUnit, flags, ts, busnum, nframes, &i->buflist);
- if (err != noErr) {
- NSLog(@"MKAudioInput: AudioUnitRender failed. err = %ld", err);
- return err;
- }
-
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- short *buf = (short *)i->buflist.mBuffers->mData;
- [i addMicrophoneDataWithBuffer:buf amount:nframes];
- [pool release];
-
- return noErr;
-}
-
@implementation MKAudioInput
-- (id) initWithSettings:(MKAudioSettings *)settings {
+- (id) initWithDevice:(MKAudioDevice *)device andSettings:(MKAudioSettings *)settings {
self = [super init];
if (self == nil)
return nil;
+
+ // Set device
+ _device = [device retain];
// Copy settings
memcpy(&_settings, settings, sizeof(MKAudioSettings));
@@ -184,13 +149,22 @@ - (id) initWithSettings:(MKAudioSettings *)settings {
frameList = [[NSMutableArray alloc] initWithCapacity:_settings.audioPerPacket];
udpMessageType = ~0;
+
+ [_device setupInput:^BOOL(short *frames, unsigned int nsamp) {
+ [self addMicrophoneDataWithBuffer:frames amount:nsamp];
+ return YES;
+ }];
+
+ micFrequency = [_device inputSampleRate];
+ numMicChannels = [_device numberOfInputChannels];
+
+ [self initializeMixer];
return self;
}
- (void) dealloc {
- // fixme(mkrautz): Return value?
- [self teardownDevice];
+ [_device release];
[frameList release];
[_opusBuffer release];
@@ -242,295 +216,6 @@ - (void) initializeMixer {
NSLog(@"MKAudioInput: Initialized mixer for %i channel %i Hz and %i channel %i Hz echo", numMicChannels, micFrequency, 0, 0);
}
-- (BOOL) setupDevice {
-#if TARGET_IPHONE_SIMULATOR
- return [self setupiOSDevice];
-#else
-#if TARGET_OS_MAC == 1 && TARGET_OS_IPHONE == 0
- return [self setupMacDevice];
-#elif TARGET_OS_MAC == 1 && TARGET_OS_IPHONE == 1
- return [self setupiOSDevice];
-#endif
-#endif
-}
-
-- (BOOL) setupMacDevice {
-#if TARGET_OS_MAC == 1 && TARGET_OS_IPHONE == 0
- UInt32 len;
- UInt32 val;
- OSStatus err;
- AudioComponent comp;
- AudioComponentDescription desc;
- AudioStreamBasicDescription fmt;
- AudioDeviceID devId;
-
- // Get default device
- len = sizeof(AudioDeviceID);
- err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &len, &devId);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to query for default device.");
- return NO;
- }
-
- desc.componentType = kAudioUnitType_Output;
- desc.componentSubType = kAudioUnitSubType_HALOutput;
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
-
- comp = AudioComponentFindNext(NULL, &desc);
- if (! comp) {
- NSLog(@"MKAudioInput: Unable to find AudioUnit.");
- return NO;
- }
-
- err = AudioComponentInstanceNew(comp, (AudioComponentInstance *) &audioUnit);
- if (err != noErr) {
- NSLog(@"AudioInput: Unable to instantiate new AudioUnit.");
- return NO;
- }
-
- err = AudioUnitInitialize(audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to initialize AudioUnit.");
- return NO;
- }
-
- val = 1;
- err = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &val, sizeof(UInt32));
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to configure input scope on AudioUnit.");
- return NO;
- }
-
- val = 0;
- err = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &val, sizeof(UInt32));
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to configure output scope on AudioUnit.");
- return NO;
- }
-
- // Set default device
- len = sizeof(AudioDeviceID);
- err = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &devId, len);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to set default device.");
- return NO;
- }
-
- len = sizeof(AudioStreamBasicDescription);
- err = AudioUnitGetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &fmt, &len);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to query device for stream info.");
- return NO;
- }
-
- if (fmt.mChannelsPerFrame > 1) {
- NSLog(@"MKAudioInput: Input device with more than one channel detected. Defaulting to 1.");
- }
-
- micFrequency = (int) fmt.mSampleRate;
- numMicChannels = 1;
- [self initializeMixer];
-
- fmt.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
- fmt.mBitsPerChannel = sizeof(short) * 8;
- fmt.mFormatID = kAudioFormatLinearPCM;
- fmt.mSampleRate = micFrequency;
- fmt.mChannelsPerFrame = numMicChannels;
- fmt.mBytesPerFrame = micSampleSize;
- fmt.mBytesPerPacket = micSampleSize;
- fmt.mFramesPerPacket = 1;
-
- len = sizeof(AudioStreamBasicDescription);
- err = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &fmt, len);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to set stream format for output device. (output scope)");
- return NO;
- }
-
- AURenderCallbackStruct cb;
- cb.inputProc = inputCallback;
- cb.inputProcRefCon = self;
- len = sizeof(AURenderCallbackStruct);
- err = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &cb, len);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to setup callback.");
- return NO;
- }
-
- err = AudioOutputUnitStart(audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to start AudioUnit.");
- return NO;
- }
-
- return YES;
-#else
- return NO;
-#endif
-}
-
-- (BOOL) setupiOSDevice {
-#if TARGET_OS_IPHONE == 1
- UInt32 len;
- UInt32 val;
- OSStatus err;
- AudioComponent comp;
- AudioComponentDescription desc;
- AudioStreamBasicDescription fmt;
-
- BOOL shouldCancelEcho = NO;
- if ([[MKAudio sharedAudio] echoCancellationAvailable]) {
- shouldCancelEcho = _settings.enableEchoCancellation;
- }
-
- desc.componentType = kAudioUnitType_Output;
- if (shouldCancelEcho)
- desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
- else
- desc.componentSubType = kAudioUnitSubType_RemoteIO;
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
-
- comp = AudioComponentFindNext(NULL, &desc);
- if (! comp) {
- NSLog(@"MKAudioInput: Unable to find AudioUnit.");
- return NO;
- }
-
- err = AudioComponentInstanceNew(comp, (AudioComponentInstance *) &audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to instantiate new AudioUnit.");
- return NO;
- }
-
- /* fixme(mkrautz): Backport some of this to the desktop CoreAudio backend? */
-
- val = 1;
- err = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &val, sizeof(UInt32));
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to configure input scope on AudioUnit.");
- return NO;
- }
-
- val = 0;
- err = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &val, sizeof(UInt32));
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to configure output scope on AudioUnit.");
- return NO;
- }
-
- AURenderCallbackStruct cb;
- cb.inputProc = inputCallback;
- cb.inputProcRefCon = self;
- len = sizeof(AURenderCallbackStruct);
- err = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &cb, len);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to setup callback.");
- return NO;
- }
-
- len = sizeof(AudioStreamBasicDescription);
- err = AudioUnitGetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &fmt, &len);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to query device for stream info.");
- return NO;
- }
-
- if (fmt.mChannelsPerFrame > 1) {
- NSLog(@"MKAudioInput: Input device with more than one channel detected. Defaulting to 1.");
- }
-
- micFrequency = (int) 48000;
- numMicChannels = 1;
- [self initializeMixer];
-
- fmt.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
- fmt.mBitsPerChannel = sizeof(short) * 8;
- fmt.mFormatID = kAudioFormatLinearPCM;
- fmt.mSampleRate = micFrequency;
- fmt.mChannelsPerFrame = numMicChannels;
- fmt.mBytesPerFrame = micSampleSize;
- fmt.mBytesPerPacket = micSampleSize;
- fmt.mFramesPerPacket = 1;
-
- len = sizeof(AudioStreamBasicDescription);
- err = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &fmt, len);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to set stream format for output device. (output scope)");
- return NO;
- }
-
- if (shouldCancelEcho) {
- val = 0;
- len = sizeof(UInt32);
- err = AudioUnitSetProperty(audioUnit, kAUVoiceIOProperty_BypassVoiceProcessing, kAudioUnitScope_Global, 0, &val, len);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to disable VPIO voice processing.");
- return NO;
- }
-
- val = 0;
- len = sizeof(UInt32);
- err = AudioUnitSetProperty(audioUnit, kAUVoiceIOProperty_VoiceProcessingEnableAGC, kAudioUnitScope_Global, 0, &val, len);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to disable VPIO AGC.");
- return NO;
- }
-
- // It's sufficient to set the quality to 0 for our use case; we do our own preprocessing
- // after this, and the job of the VPIO is only to do echo cancellation.
- val = 0;
- len = sizeof(UInt32);
- err = AudioUnitSetProperty(audioUnit, kAUVoiceIOProperty_VoiceProcessingQuality, kAudioUnitScope_Global, 0, &val, len);
- if (err != noErr) {
- NSLog(@"MKAudioInput: unable to set VPIO quality.");
- return NO;
- }
- }
-
- err = AudioUnitInitialize(audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to initialize AudioUnit.");
- return NO;
- }
-
- err = AudioOutputUnitStart(audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioInput: Unable to start AudioUnit.");
- return NO;
- }
-
- return YES;
-#endif
- return NO;
-}
-
-- (BOOL) teardownDevice {
- OSStatus err;
-
- err = AudioOutputUnitStop(audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioInput: unable to stop AudioUnit.");
- return NO;
- }
-
- err = AudioComponentInstanceDispose(audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioInput: unable to dispose of AudioUnit.");
- return NO;
- }
-
- AudioBuffer *b = buflist.mBuffers;
- if (b && b->mData)
- free(b->mData);
-
- NSLog(@"MKAudioInput: teardown finished.");
- return YES;
-}
-
- (void) addMicrophoneDataWithBuffer:(short *)input amount:(NSUInteger)nsamp {
int i;
@@ -718,7 +403,6 @@ - (void) processAndEncodeAudioFrame {
buf[i] = val * 32767.0f;
}
}
-
float sum = 1.0f;
int i;
View
6 src/MKAudioOutput.h
@@ -6,17 +6,15 @@
#import <MumbleKit/MKUser.h>
#import <MumbleKit/MKConnection.h>
#import "MKAudioOutputUser.h"
+#import "MKAudioDevice.h"
@class MKUser;
@interface MKAudioOutput : NSObject
-- (id) initWithSettings:(MKAudioSettings *)settings;
+- (id) initWithDevice:(MKAudioDevice *)device andSettings:(MKAudioSettings *)settings;
- (void) dealloc;
-- (BOOL) setupDevice;
-- (BOOL) teardownDevice;
-
- (void) removeBuffer:(MKAudioOutputUser *)u;
- (BOOL) mixFrames: (void *)frames amount:(unsigned int)nframes;
- (void) addFrameToBufferWithSession:(NSUInteger)session data:(NSData *)data sequence:(NSUInteger)seq type:(MKUDPMessageType)msgType;
View
162 src/MKAudioOutput.m
@@ -6,35 +6,14 @@
#import "MKAudioOutput.h"
#import "MKAudioOutputSpeech.h"
#import "MKAudioOutputUser.h"
+#import "MKAudioDevice.h"
#import <AudioUnit/AudioUnit.h>
#import <AudioUnit/AUComponent.h>
#import <AudioToolbox/AudioToolbox.h>
-static OSStatus outputCallback(void *udata, AudioUnitRenderActionFlags *flags, const AudioTimeStamp *ts,
- UInt32 busnum, UInt32 nframes, AudioBufferList *buflist) {
- MKAudioOutput *ao = (MKAudioOutput *) udata;
- AudioBuffer *buf = buflist->mBuffers;
- MK_UNUSED OSStatus err;
- BOOL done;
-
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- done = [ao mixFrames:buf->mData amount:nframes];
- if (! done) {
- // Not very obvious from the documentation, but CoreAudio simply wants you to set your buffer
- // size to 0, and return a non-zero value when you don't have anything to feed it. It will call
- // you back later.
- buf->mDataByteSize = 0;
- [pool release];
- return -1;
- }
-
- [pool release];
- return noErr;
-}
-
@interface MKAudioOutput () {
+ MKAudioDevice *_device;
MKAudioSettings _settings;
AudioUnit _audioUnit;
int _sampleSize;
@@ -49,138 +28,45 @@ @interface MKAudioOutput () {
@implementation MKAudioOutput
-- (id) initWithSettings:(MKAudioSettings *)settings {
+- (id) initWithDevice:(MKAudioDevice *)device andSettings:(MKAudioSettings *)settings {
if ((self = [super init])) {
memcpy(&_settings, settings, sizeof(MKAudioSettings));
+ _device = [device retain];
_sampleSize = 0;
_frameSize = SAMPLE_RATE / 100;
_mixerFrequency = 0;
_outputLock = [[NSLock alloc] init];
_outputs = [[NSMutableDictionary alloc] init];
+
+ _mixerFrequency = [_device inputSampleRate];
+ _numChannels = [_device numberOfInputChannels];
+ _sampleSize = _numChannels * sizeof(short);
+
+ if (_speakerVolume) {
+ free(_speakerVolume);
+ }
+ _speakerVolume = malloc(sizeof(float)*_numChannels);
+
+ int i;
+ for (i = 0; i < _numChannels; ++i) {
+ _speakerVolume[i] = 1.0f;
+ }
+
+ [_device setupOutput:^BOOL(short *frames, unsigned int nsamp) {
+ return [self mixFrames:frames amount:nsamp];
+ }];
}
return self;
}
- (void) dealloc {
- [self teardownDevice];
+ [_device setupOutput:NULL];
+ [_device release];
[_outputLock release];
[_outputs release];
[super dealloc];
}
-- (BOOL) setupDevice {
- UInt32 len;
- OSStatus err;
- AudioComponent comp;
- AudioComponentDescription desc;
- AudioStreamBasicDescription fmt;
-
- desc.componentType = kAudioUnitType_Output;
-#if TARGET_OS_IPHONE == 1
- desc.componentSubType = kAudioUnitSubType_RemoteIO;
-#elif TARGET_OS_MAC == 1
- desc.componentSubType = kAudioUnitSubType_HALOutput;
-#endif
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
-
- comp = AudioComponentFindNext(NULL, &desc);
- if (! comp) {
- NSLog(@"MKAudioOutput: Unable to find AudioUnit.");
- return NO;
- }
-
- err = AudioComponentInstanceNew(comp, (AudioComponentInstance *) &_audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioOutput: Unable to instantiate new AudioUnit.");
- return NO;
- }
-
- err = AudioUnitInitialize(_audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioOutput: Unable to initialize AudioUnit.");
- return NO;
- }
-
- len = sizeof(AudioStreamBasicDescription);
- err = AudioUnitGetProperty(_audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &fmt, &len);
- if (err != noErr) {
- NSLog(@"MKAudioOuptut: Unable to get output stream format from AudioUnit.");
- return NO;
- }
-
- _mixerFrequency = (int) 48000;
- _numChannels = (int) fmt.mChannelsPerFrame;
- _sampleSize = _numChannels * sizeof(short);
-
- if (_speakerVolume) {
- free(_speakerVolume);
- }
- _speakerVolume = malloc(sizeof(float)*_numChannels);
-
- int i;
- for (i = 0; i < _numChannels; ++i) {
- _speakerVolume[i] = 1.0f;
- }
-
- fmt.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
- fmt.mBitsPerChannel = sizeof(short) * 8;
-
- NSLog(@"MKAudioOutput: Output device currently configured as %iHz sample rate, %i channels, %i sample size", _mixerFrequency, _numChannels, _sampleSize);
-
- fmt.mFormatID = kAudioFormatLinearPCM;
- fmt.mSampleRate = _mixerFrequency;
- fmt.mBytesPerFrame = _sampleSize;
- fmt.mBytesPerPacket = _sampleSize;
- fmt.mFramesPerPacket = 1;
-
- len = sizeof(AudioStreamBasicDescription);
- err = AudioUnitSetProperty(_audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &fmt, len);
- if (err != noErr) {
- NSLog(@"MKAudioOutput: Unable to set stream format for output device.");
- return NO;
- }
-
- AURenderCallbackStruct cb;
- cb.inputProc = outputCallback;
- cb.inputProcRefCon = self;
- len = sizeof(AURenderCallbackStruct);
- err = AudioUnitSetProperty(_audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &cb, len);
- if (err != noErr) {
- NSLog(@"MKAudioOutput: Could not set render callback.");
- return NO;
- }
-
- // On desktop we call AudioDeviceSetProperty() with kAudioDevicePropertyBufferFrameSize
- // to setup our frame size.
-
- err = AudioOutputUnitStart(_audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioOutput: Unable to start AudioUnit");
- return NO;
- }
-
- return YES;
-}
-
-- (BOOL) teardownDevice {
- OSStatus err = AudioOutputUnitStop(_audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioOutput: unable to stop AudioUnit.");
- return NO;
- }
-
- err = AudioComponentInstanceDispose(_audioUnit);
- if (err != noErr) {
- NSLog(@"MKAudioOutput: unable to dispose of AudioUnit.");
- return NO;
- }
-
- NSLog(@"MKAudioOuptut: teardown finished.");
- return YES;
-}
-
- (BOOL) mixFrames:(void *)frames amount:(unsigned int)nsamp {
unsigned int i, s;
BOOL retVal = NO;
View
9 src/MKVoiceProcessingDevice.h
@@ -0,0 +1,9 @@
+// Copyright 2012 The MumbleKit Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import <MumbleKit/MKAudio.h>
+#import "MKAudioDevice.h"
+
+@interface MKVoiceProcessingDevice : MKAudioDevice
+@end
View
299 src/MKVoiceProcessingDevice.m
@@ -0,0 +1,299 @@
+// Copyright 2012 The MumbleKit Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import "MKVoiceProcessingDevice.h"
+
+#import <MumbleKit/MKAudio.h>
+
+#import <AudioUnit/AudioUnit.h>
+#import <AudioUnit/AUComponent.h>
+#import <AudioToolbox/AudioToolbox.h>
+
+@interface MKVoiceProcessingDevice () {
+@public
+ MKAudioSettings _settings;
+ AudioUnit _audioUnit;
+ AudioBufferList _buflist;
+ int _micFrequency;
+ int _micSampleSize;
+ int _numMicChannels;
+ MKAudioDeviceOutputFunc _outputFunc;
+ MKAudioDeviceInputFunc _inputFunc;
+}
+@end
+
+static OSStatus inputCallback(void *udata, AudioUnitRenderActionFlags *flags, const AudioTimeStamp *ts,
+ UInt32 busnum, UInt32 nframes, AudioBufferList *buflist) {
+ MKVoiceProcessingDevice *dev = (MKVoiceProcessingDevice *)udata;
+ OSStatus err;
+
+ if (! dev->_buflist.mBuffers->mData) {
+ NSLog(@"MKVoiceProcessingDevice: No buffer allocated.");
+ dev->_buflist.mNumberBuffers = 1;
+ AudioBuffer *b = dev->_buflist.mBuffers;
+ b->mNumberChannels = dev->_numMicChannels;
+ b->mDataByteSize = dev->_micSampleSize * nframes;
+ b->mData = calloc(1, b->mDataByteSize);
+ }
+
+ if (dev->_buflist.mBuffers->mDataByteSize < (nframes/dev->_micSampleSize)) {
+ NSLog(@"MKVoiceProcessingDevice: Buffer too small. Allocating more space.");
+ AudioBuffer *b = dev->_buflist.mBuffers;
+ free(b->mData);
+ b->mDataByteSize = dev->_micSampleSize * nframes;
+ b->mData = calloc(1, b->mDataByteSize);
+ }
+
+ err = AudioUnitRender(dev->_audioUnit, flags, ts, busnum, nframes, &dev->_buflist);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: AudioUnitRender failed. err = %ld", err);
+ return err;
+ }
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ short *buf = (short *) dev->_buflist.mBuffers->mData;
+ MKAudioDeviceInputFunc inputFunc = dev->_inputFunc;
+ if (inputFunc) {
+ inputFunc(buf, nframes);
+ }
+ [pool release];
+
+ return noErr;
+}
+
+static OSStatus outputCallback(void *udata, AudioUnitRenderActionFlags *flags, const AudioTimeStamp *ts,
+ UInt32 busnum, UInt32 nframes, AudioBufferList *buflist) {
+ MKVoiceProcessingDevice *dev = (MKVoiceProcessingDevice *) udata;
+ AudioBuffer *buf = buflist->mBuffers;
+ MKAudioDeviceOutputFunc outputFunc = dev->_outputFunc;
+ BOOL done;
+
+ if (outputFunc == NULL) {
+ // No frames available yet.
+ buf->mDataByteSize = 0;
+ return -1;
+ }
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ done = outputFunc(buf->mData, nframes);
+ if (! done) {
+ // No frames available yet.
+ buf->mDataByteSize = 0;
+ [pool release];
+ return -1;
+ }
+
+ [pool release];
+ return noErr;
+}
+
+@implementation MKVoiceProcessingDevice
+
+- (id) initWithSettings:(MKAudioSettings *)settings {
+ if ((self = [super init])) {
+ memcpy(&_settings, settings, sizeof(MKAudioSettings));
+ }
+ return self;
+}
+
+- (void) dealloc {
+ [_inputFunc release];
+ [_outputFunc release];
+ [super dealloc];
+}
+
+- (BOOL) setupDevice {
+ UInt32 len;
+ UInt32 val;
+ OSStatus err;
+ AudioComponent comp;
+ AudioComponentDescription desc;
+ AudioStreamBasicDescription fmt;
+
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ comp = AudioComponentFindNext(NULL, &desc);
+ if (! comp) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to find AudioUnit.");
+ return NO;
+ }
+
+ err = AudioComponentInstanceNew(comp, (AudioComponentInstance *) &_audioUnit);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to instantiate new AudioUnit.");
+ return NO;
+ }
+
+ val = 1;
+ err = AudioUnitSetProperty(_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &val, sizeof(UInt32));
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to configure input scope on AudioUnit.");
+ return NO;
+ }
+
+ val = 1;
+ err = AudioUnitSetProperty(_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &val, sizeof(UInt32));
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to configure output scope on AudioUnit.");
+ return NO;
+ }
+
+ AURenderCallbackStruct cb;
+ cb.inputProc = inputCallback;
+ cb.inputProcRefCon = self;
+ len = sizeof(AURenderCallbackStruct);
+ err = AudioUnitSetProperty(_audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &cb, len);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to setup callback.");
+ return NO;
+ }
+
+ cb.inputProc = outputCallback;
+ cb.inputProcRefCon = self;
+ len = sizeof(AURenderCallbackStruct);
+ err = AudioUnitSetProperty(_audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &cb, len);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Could not set render callback.");
+ return NO;
+ }
+
+ len = sizeof(AudioStreamBasicDescription);
+ err = AudioUnitGetProperty(_audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &fmt, &len);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to query device for stream info.");
+ return NO;
+ }
+
+ if (fmt.mChannelsPerFrame > 1) {
+ NSLog(@"MKVoiceProcessingDevice: Input device with more than one channel detected. Defaulting to 1.");
+ }
+
+ _micFrequency = 48000;
+ _numMicChannels = 1;
+ _micSampleSize = _numMicChannels * sizeof(short);
+
+ fmt.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
+ fmt.mBitsPerChannel = sizeof(short) * 8;
+ fmt.mFormatID = kAudioFormatLinearPCM;
+ fmt.mSampleRate = _micFrequency;
+ fmt.mChannelsPerFrame = _numMicChannels;
+ fmt.mBytesPerFrame = _micSampleSize;
+ fmt.mBytesPerPacket = _micSampleSize;
+ fmt.mFramesPerPacket = 1;
+
+ len = sizeof(AudioStreamBasicDescription);
+ err = AudioUnitSetProperty(_audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &fmt, len);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to set stream format for output device. (output scope)");
+ return NO;
+ }
+
+ len = sizeof(AudioStreamBasicDescription);
+ err = AudioUnitSetProperty(_audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &fmt, len);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to set stream format for input device. (input scope)");
+ return NO;
+ }
+
+ val = 0;
+ len = sizeof(UInt32);
+ err = AudioUnitSetProperty(_audioUnit, kAUVoiceIOProperty_BypassVoiceProcessing, kAudioUnitScope_Global, 0, &val, len);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to disable VPIO voice processing.");
+ return NO;
+ }
+
+ val = 0;
+ len = sizeof(UInt32);
+ err = AudioUnitSetProperty(_audioUnit, kAUVoiceIOProperty_VoiceProcessingEnableAGC, kAudioUnitScope_Global, 0, &val, len);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to disable VPIO AGC.");
+ return NO;
+ }
+
+ // It's sufficient to set the quality to 0 for our use case; we do our own preprocessing
+ // after this, and the job of the VPIO is only to do echo cancellation.
+ val = 0;
+ len = sizeof(UInt32);
+ err = AudioUnitSetProperty(_audioUnit, kAUVoiceIOProperty_VoiceProcessingQuality, kAudioUnitScope_Global, 0, &val, len);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: unable to set VPIO quality.");
+ return NO;
+ }
+
+ val = 0;
+ len = sizeof(UInt32);
+ err = AudioUnitSetProperty(_audioUnit, kAUVoiceIOProperty_MuteOutput, kAudioUnitScope_Global, 0, &val, len);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: unable to unmute output.");
+ return NO;
+ }
+
+ err = AudioUnitInitialize(_audioUnit);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to initialize AudioUnit.");
+ return NO;
+ }
+
+ err = AudioOutputUnitStart(_audioUnit);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: Unable to start AudioUnit.");
+ return NO;
+ }
+
+ return YES;
+}
+
+- (BOOL) teardownDevice {
+ OSStatus err;
+
+ err = AudioOutputUnitStop(_audioUnit);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: unable to stop AudioUnit.");
+ return NO;
+ }
+
+ err = AudioComponentInstanceDispose(_audioUnit);
+ if (err != noErr) {
+ NSLog(@"MKVoiceProcessingDevice: unable to dispose of AudioUnit.");
+ return NO;
+ }
+
+ AudioBuffer *b = _buflist.mBuffers;
+ if (b && b->mData)
+ free(b->mData);
+
+ NSLog(@"MKVoiceProcessingDevice: teardown finished.");
+ return YES;
+}
+
+- (void) setupOutput:(MKAudioDeviceOutputFunc)outf {
+ _outputFunc = [outf copy];
+}
+
+- (void) setupInput:(MKAudioDeviceInputFunc)inf {
+ _inputFunc = [inf copy];
+}
+
+- (int) inputSampleRate {
+ return _micFrequency;
+}
+
+- (int) outputSampleRate {
+ return _micFrequency;
+}
+
+- (int) numberOfInputChannels {
+ return _numMicChannels;
+}
+
+- (int) numberOfOutputChannels {
+ return _numMicChannels;
+}
+
+@end
View
10 src/MKiOSAudioDevice.h
@@ -0,0 +1,10 @@
+// Copyright 2012 The MumbleKit Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import <MumbleKit/MKAudio.h>
+#import "MKAudioDevice.h"
+
+@interface MKiOSAudioDevice : MKAudioDevice
+
+@end
View
266 src/MKiOSAudioDevice.m
@@ -0,0 +1,266 @@
+// Copyright 2012 The MumbleKit Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import <MumbleKit/MKAudio.h>
+#import "MKAudioDevice.h"
+
+#import "MKiOSAudioDevice.h"
+
+#import <AudioUnit/AudioUnit.h>
+#import <AudioUnit/AUComponent.h>
+#import <AudioToolbox/AudioToolbox.h>
+
+@interface MKiOSAudioDevice () {
+@public
+ MKAudioSettings _settings;
+ AudioUnit _audioUnit;
+ AudioBufferList _buflist;
+ int _micFrequency;
+ int _micSampleSize;
+ int _numMicChannels;
+ MKAudioDeviceOutputFunc _outputFunc;
+ MKAudioDeviceInputFunc _inputFunc;
+}
+@end
+
+static OSStatus inputCallback(void *udata, AudioUnitRenderActionFlags *flags, const AudioTimeStamp *ts,
+ UInt32 busnum, UInt32 nframes, AudioBufferList *buflist) {
+ MKiOSAudioDevice *dev = (MKiOSAudioDevice *)udata;
+ OSStatus err;
+
+ if (! dev->_buflist.mBuffers->mData) {
+ NSLog(@"MKiOSAudioDevice: No buffer allocated.");
+ dev->_buflist.mNumberBuffers = 1;
+ AudioBuffer *b = dev->_buflist.mBuffers;
+ b->mNumberChannels = dev->_numMicChannels;
+ b->mDataByteSize = dev->_micSampleSize * nframes;
+ b->mData = calloc(1, b->mDataByteSize);
+ }
+
+ if (dev->_buflist.mBuffers->mDataByteSize < (nframes/dev->_micSampleSize)) {
+ NSLog(@"MKiOSAudioDevice: Buffer too small. Allocating more space.");
+ AudioBuffer *b = dev->_buflist.mBuffers;
+ free(b->mData);
+ b->mDataByteSize = dev->_micSampleSize * nframes;
+ b->mData = calloc(1, b->mDataByteSize);
+ }
+
+ err = AudioUnitRender(dev->_audioUnit, flags, ts, busnum, nframes, &dev->_buflist);
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: AudioUnitRender failed. err = %ld", err);
+ return err;
+ }
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ short *buf = (short *) dev->_buflist.mBuffers->mData;
+ MKAudioDeviceInputFunc inputFunc = dev->_inputFunc;
+ if (inputFunc) {
+ inputFunc(buf, nframes);
+ }
+ [pool release];
+
+ return noErr;
+}
+
+static OSStatus outputCallback(void *udata, AudioUnitRenderActionFlags *flags, const AudioTimeStamp *ts,
+ UInt32 busnum, UInt32 nframes, AudioBufferList *buflist) {
+ MKiOSAudioDevice *dev = (MKiOSAudioDevice *) udata;
+ AudioBuffer *buf = buflist->mBuffers;
+ MKAudioDeviceOutputFunc outputFunc = dev->_outputFunc;
+ BOOL done;
+
+ if (outputFunc == NULL) {
+ // No frames available yet.
+ buf->mDataByteSize = 0;
+ return -1;
+ }
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ done = outputFunc(buf->mData, nframes);
+ if (! done) {
+ // No frames available yet.
+ buf->mDataByteSize = 0;
+ [pool release];
+ return -1;
+ }
+
+ [pool release];
+ return noErr;
+}
+
+@implementation MKiOSAudioDevice
+
+- (id) initWithSettings:(MKAudioSettings *)settings {
+ if ((self = [super init])) {
+ memcpy(&_settings, settings, sizeof(MKAudioSettings));
+ }
+ return self;
+}
+
+- (void) dealloc {
+ [_inputFunc release];
+ [_outputFunc release];
+ [super dealloc];
+}
+
+- (BOOL) setupDevice {
+ UInt32 len;
+ UInt32 val;
+ OSStatus err;
+ AudioComponent comp;
+ AudioComponentDescription desc;
+ AudioStreamBasicDescription fmt;
+
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_RemoteIO;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ comp = AudioComponentFindNext(NULL, &desc);
+ if (! comp) {
+ NSLog(@"MKiOSAudioDevice: Unable to find AudioUnit.");
+ return NO;
+ }
+
+ err = AudioComponentInstanceNew(comp, (AudioComponentInstance *) &_audioUnit);
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: Unable to instantiate new AudioUnit.");
+ return NO;
+ }
+
+ val = 1;
+ err = AudioUnitSetProperty(_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &val, sizeof(UInt32));
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: Unable to configure input scope on AudioUnit.");
+ return NO;
+ }
+
+ val = 1;
+ err = AudioUnitSetProperty(_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &val, sizeof(UInt32));
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: Unable to configure output scope on AudioUnit.");
+ return NO;
+ }
+
+ AURenderCallbackStruct cb;
+ cb.inputProc = inputCallback;
+ cb.inputProcRefCon = self;
+ len = sizeof(AURenderCallbackStruct);
+ err = AudioUnitSetProperty(_audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &cb, len);
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: Unable to setup callback.");
+ return NO;
+ }
+
+ cb.inputProc = outputCallback;
+ cb.inputProcRefCon = self;
+ len = sizeof(AURenderCallbackStruct);
+ err = AudioUnitSetProperty(_audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &cb, len);
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: Could not set render callback.");
+ return NO;
+ }
+
+ len = sizeof(AudioStreamBasicDescription);
+ err = AudioUnitGetProperty(_audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &fmt, &len);
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: Unable to query device for stream info.");
+ return NO;
+ }
+
+ if (fmt.mChannelsPerFrame > 1) {
+ NSLog(@"MKiOSAudioDevice: Input device with more than one channel detected. Defaulting to 1.");
+ }
+
+ _micFrequency = 48000;
+ _numMicChannels = 1;
+ _micSampleSize = _numMicChannels * sizeof(short);
+
+ fmt.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
+ fmt.mBitsPerChannel = sizeof(short) * 8;
+ fmt.mFormatID = kAudioFormatLinearPCM;
+ fmt.mSampleRate = _micFrequency;
+ fmt.mChannelsPerFrame = _numMicChannels;
+ fmt.mBytesPerFrame = _micSampleSize;
+ fmt.mBytesPerPacket = _micSampleSize;
+ fmt.mFramesPerPacket = 1;
+
+ len = sizeof(AudioStreamBasicDescription);
+ err = AudioUnitSetProperty(_audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &fmt, len);
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: Unable to set stream format for output device. (output scope)");
+ return NO;
+ }
+
+ len = sizeof(AudioStreamBasicDescription);
+ err = AudioUnitSetProperty(_audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &fmt, len);
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: Unable to set stream format for input device. (input scope)");
+ return NO;
+ }
+
+ err = AudioUnitInitialize(_audioUnit);
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: Unable to initialize AudioUnit.");
+ return NO;
+ }
+
+ err = AudioOutputUnitStart(_audioUnit);
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: Unable to start AudioUnit.");
+ return NO;
+ }
+
+ return YES;
+}
+
+- (BOOL) teardownDevice {
+ OSStatus err;
+
+ err = AudioOutputUnitStop(_audioUnit);
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: unable to stop AudioUnit.");
+ return NO;
+ }
+
+ err = AudioComponentInstanceDispose(_audioUnit);
+ if (err != noErr) {
+ NSLog(@"MKiOSAudioDevice: unable to dispose of AudioUnit.");
+ return NO;
+ }
+
+ AudioBuffer *b = _buflist.mBuffers;
+ if (b && b->mData)
+ free(b->mData);
+
+ NSLog(@"MKiOSAudioDevice: teardown finished.");
+ return YES;
+}
+
+- (void) setupOutput:(MKAudioDeviceOutputFunc)outf {
+ _outputFunc = [outf copy];
+}
+
+- (void) setupInput:(MKAudioDeviceInputFunc)inf {
+ _inputFunc = [inf copy];
+}
+
+- (int) inputSampleRate {
+ return _micFrequency;
+}
+
+- (int) outputSampleRate {
+ return _micFrequency;
+}
+
+- (int) numberOfInputChannels {
+ return _numMicChannels;
+}
+
+- (int) numberOfOutputChannels {
+ return _numMicChannels;
+}
+
+@end

0 comments on commit c130614

Please sign in to comment.