1*d9f75844SAndroid Build Coastguard Worker/* 2*d9f75844SAndroid Build Coastguard Worker * Copyright 2016 The WebRTC Project Authors. All rights reserved. 3*d9f75844SAndroid Build Coastguard Worker * 4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license 5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source 6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found 7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may 8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree. 9*d9f75844SAndroid Build Coastguard Worker */ 10*d9f75844SAndroid Build Coastguard Worker 11*d9f75844SAndroid Build Coastguard Worker#import "RTCAudioSessionConfiguration.h" 12*d9f75844SAndroid Build Coastguard Worker#import "RTCAudioSession.h" 13*d9f75844SAndroid Build Coastguard Worker 14*d9f75844SAndroid Build Coastguard Worker#import "helpers/RTCDispatcher.h" 15*d9f75844SAndroid Build Coastguard Worker#import "helpers/UIDevice+RTCDevice.h" 16*d9f75844SAndroid Build Coastguard Worker 17*d9f75844SAndroid Build Coastguard Worker// Try to use mono to save resources. Also avoids channel format conversion 18*d9f75844SAndroid Build Coastguard Worker// in the I/O audio unit. Initial tests have shown that it is possible to use 19*d9f75844SAndroid Build Coastguard Worker// mono natively for built-in microphones and for BT headsets but not for 20*d9f75844SAndroid Build Coastguard Worker// wired headsets. Wired headsets only support stereo as native channel format 21*d9f75844SAndroid Build Coastguard Worker// but it is a low cost operation to do a format conversion to mono in the 22*d9f75844SAndroid Build Coastguard Worker// audio unit. Hence, we will not hit a RTC_CHECK in 23*d9f75844SAndroid Build Coastguard Worker// VerifyAudioParametersForActiveAudioSession() for a mismatch between the 24*d9f75844SAndroid Build Coastguard Worker// preferred number of channels and the actual number of channels. 25*d9f75844SAndroid Build Coastguard Workerconst int kRTCAudioSessionPreferredNumberOfChannels = 1; 26*d9f75844SAndroid Build Coastguard Worker 27*d9f75844SAndroid Build Coastguard Worker// Preferred hardware sample rate (unit is in Hertz). The client sample rate 28*d9f75844SAndroid Build Coastguard Worker// will be set to this value as well to avoid resampling the the audio unit's 29*d9f75844SAndroid Build Coastguard Worker// format converter. Note that, some devices, e.g. BT headsets, only supports 30*d9f75844SAndroid Build Coastguard Worker// 8000Hz as native sample rate. 31*d9f75844SAndroid Build Coastguard Workerconst double kRTCAudioSessionHighPerformanceSampleRate = 48000.0; 32*d9f75844SAndroid Build Coastguard Worker 33*d9f75844SAndroid Build Coastguard Worker// A lower sample rate will be used for devices with only one core 34*d9f75844SAndroid Build Coastguard Worker// (e.g. iPhone 4). The goal is to reduce the CPU load of the application. 35*d9f75844SAndroid Build Coastguard Workerconst double kRTCAudioSessionLowComplexitySampleRate = 16000.0; 36*d9f75844SAndroid Build Coastguard Worker 37*d9f75844SAndroid Build Coastguard Worker// Use a hardware I/O buffer size (unit is in seconds) that matches the 10ms 38*d9f75844SAndroid Build Coastguard Worker// size used by WebRTC. The exact actual size will differ between devices. 39*d9f75844SAndroid Build Coastguard Worker// Example: using 48kHz on iPhone 6 results in a native buffer size of 40*d9f75844SAndroid Build Coastguard Worker// ~10.6667ms or 512 audio frames per buffer. The FineAudioBuffer instance will 41*d9f75844SAndroid Build Coastguard Worker// take care of any buffering required to convert between native buffers and 42*d9f75844SAndroid Build Coastguard Worker// buffers used by WebRTC. It is beneficial for the performance if the native 43*d9f75844SAndroid Build Coastguard Worker// size is as an even multiple of 10ms as possible since it results in "clean" 44*d9f75844SAndroid Build Coastguard Worker// callback sequence without bursts of callbacks back to back. 45*d9f75844SAndroid Build Coastguard Workerconst double kRTCAudioSessionHighPerformanceIOBufferDuration = 0.02; 46*d9f75844SAndroid Build Coastguard Worker 47*d9f75844SAndroid Build Coastguard Worker// Use a larger buffer size on devices with only one core (e.g. iPhone 4). 48*d9f75844SAndroid Build Coastguard Worker// It will result in a lower CPU consumption at the cost of a larger latency. 49*d9f75844SAndroid Build Coastguard Worker// The size of 60ms is based on instrumentation that shows a significant 50*d9f75844SAndroid Build Coastguard Worker// reduction in CPU load compared with 10ms on low-end devices. 51*d9f75844SAndroid Build Coastguard Worker// TODO(henrika): monitor this size and determine if it should be modified. 52*d9f75844SAndroid Build Coastguard Workerconst double kRTCAudioSessionLowComplexityIOBufferDuration = 0.06; 53*d9f75844SAndroid Build Coastguard Worker 54*d9f75844SAndroid Build Coastguard Workerstatic RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *gWebRTCConfiguration = nil; 55*d9f75844SAndroid Build Coastguard Worker 56*d9f75844SAndroid Build Coastguard Worker@implementation RTC_OBJC_TYPE (RTCAudioSessionConfiguration) 57*d9f75844SAndroid Build Coastguard Worker 58*d9f75844SAndroid Build Coastguard Worker@synthesize category = _category; 59*d9f75844SAndroid Build Coastguard Worker@synthesize categoryOptions = _categoryOptions; 60*d9f75844SAndroid Build Coastguard Worker@synthesize mode = _mode; 61*d9f75844SAndroid Build Coastguard Worker@synthesize sampleRate = _sampleRate; 62*d9f75844SAndroid Build Coastguard Worker@synthesize ioBufferDuration = _ioBufferDuration; 63*d9f75844SAndroid Build Coastguard Worker@synthesize inputNumberOfChannels = _inputNumberOfChannels; 64*d9f75844SAndroid Build Coastguard Worker@synthesize outputNumberOfChannels = _outputNumberOfChannels; 65*d9f75844SAndroid Build Coastguard Worker 66*d9f75844SAndroid Build Coastguard Worker- (instancetype)init { 67*d9f75844SAndroid Build Coastguard Worker if (self = [super init]) { 68*d9f75844SAndroid Build Coastguard Worker // Use a category which supports simultaneous recording and playback. 69*d9f75844SAndroid Build Coastguard Worker // By default, using this category implies that our app’s audio is 70*d9f75844SAndroid Build Coastguard Worker // nonmixable, hence activating the session will interrupt any other 71*d9f75844SAndroid Build Coastguard Worker // audio sessions which are also nonmixable. 72*d9f75844SAndroid Build Coastguard Worker _category = AVAudioSessionCategoryPlayAndRecord; 73*d9f75844SAndroid Build Coastguard Worker _categoryOptions = AVAudioSessionCategoryOptionAllowBluetooth; 74*d9f75844SAndroid Build Coastguard Worker 75*d9f75844SAndroid Build Coastguard Worker // Specify mode for two-way voice communication (e.g. VoIP). 76*d9f75844SAndroid Build Coastguard Worker _mode = AVAudioSessionModeVoiceChat; 77*d9f75844SAndroid Build Coastguard Worker 78*d9f75844SAndroid Build Coastguard Worker // Set the session's sample rate or the hardware sample rate. 79*d9f75844SAndroid Build Coastguard Worker // It is essential that we use the same sample rate as stream format 80*d9f75844SAndroid Build Coastguard Worker // to ensure that the I/O unit does not have to do sample rate conversion. 81*d9f75844SAndroid Build Coastguard Worker // Set the preferred audio I/O buffer duration, in seconds. 82*d9f75844SAndroid Build Coastguard Worker NSUInteger processorCount = [NSProcessInfo processInfo].processorCount; 83*d9f75844SAndroid Build Coastguard Worker // Use best sample rate and buffer duration if the CPU has more than one 84*d9f75844SAndroid Build Coastguard Worker // core. 85*d9f75844SAndroid Build Coastguard Worker if (processorCount > 1 && [UIDevice deviceType] != RTCDeviceTypeIPhone4S) { 86*d9f75844SAndroid Build Coastguard Worker _sampleRate = kRTCAudioSessionHighPerformanceSampleRate; 87*d9f75844SAndroid Build Coastguard Worker _ioBufferDuration = kRTCAudioSessionHighPerformanceIOBufferDuration; 88*d9f75844SAndroid Build Coastguard Worker } else { 89*d9f75844SAndroid Build Coastguard Worker _sampleRate = kRTCAudioSessionLowComplexitySampleRate; 90*d9f75844SAndroid Build Coastguard Worker _ioBufferDuration = kRTCAudioSessionLowComplexityIOBufferDuration; 91*d9f75844SAndroid Build Coastguard Worker } 92*d9f75844SAndroid Build Coastguard Worker 93*d9f75844SAndroid Build Coastguard Worker // We try to use mono in both directions to save resources and format 94*d9f75844SAndroid Build Coastguard Worker // conversions in the audio unit. Some devices does only support stereo; 95*d9f75844SAndroid Build Coastguard Worker // e.g. wired headset on iPhone 6. 96*d9f75844SAndroid Build Coastguard Worker // TODO(henrika): add support for stereo if needed. 97*d9f75844SAndroid Build Coastguard Worker _inputNumberOfChannels = kRTCAudioSessionPreferredNumberOfChannels; 98*d9f75844SAndroid Build Coastguard Worker _outputNumberOfChannels = kRTCAudioSessionPreferredNumberOfChannels; 99*d9f75844SAndroid Build Coastguard Worker } 100*d9f75844SAndroid Build Coastguard Worker return self; 101*d9f75844SAndroid Build Coastguard Worker} 102*d9f75844SAndroid Build Coastguard Worker 103*d9f75844SAndroid Build Coastguard Worker+ (void)initialize { 104*d9f75844SAndroid Build Coastguard Worker gWebRTCConfiguration = [[self alloc] init]; 105*d9f75844SAndroid Build Coastguard Worker} 106*d9f75844SAndroid Build Coastguard Worker 107*d9f75844SAndroid Build Coastguard Worker+ (instancetype)currentConfiguration { 108*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; 109*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *config = 110*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCAudioSessionConfiguration) alloc] init]; 111*d9f75844SAndroid Build Coastguard Worker config.category = session.category; 112*d9f75844SAndroid Build Coastguard Worker config.categoryOptions = session.categoryOptions; 113*d9f75844SAndroid Build Coastguard Worker config.mode = session.mode; 114*d9f75844SAndroid Build Coastguard Worker config.sampleRate = session.sampleRate; 115*d9f75844SAndroid Build Coastguard Worker config.ioBufferDuration = session.IOBufferDuration; 116*d9f75844SAndroid Build Coastguard Worker config.inputNumberOfChannels = session.inputNumberOfChannels; 117*d9f75844SAndroid Build Coastguard Worker config.outputNumberOfChannels = session.outputNumberOfChannels; 118*d9f75844SAndroid Build Coastguard Worker return config; 119*d9f75844SAndroid Build Coastguard Worker} 120*d9f75844SAndroid Build Coastguard Worker 121*d9f75844SAndroid Build Coastguard Worker+ (instancetype)webRTCConfiguration { 122*d9f75844SAndroid Build Coastguard Worker @synchronized(self) { 123*d9f75844SAndroid Build Coastguard Worker return (RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *)gWebRTCConfiguration; 124*d9f75844SAndroid Build Coastguard Worker } 125*d9f75844SAndroid Build Coastguard Worker} 126*d9f75844SAndroid Build Coastguard Worker 127*d9f75844SAndroid Build Coastguard Worker+ (void)setWebRTCConfiguration:(RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *)configuration { 128*d9f75844SAndroid Build Coastguard Worker @synchronized(self) { 129*d9f75844SAndroid Build Coastguard Worker gWebRTCConfiguration = configuration; 130*d9f75844SAndroid Build Coastguard Worker } 131*d9f75844SAndroid Build Coastguard Worker} 132*d9f75844SAndroid Build Coastguard Worker 133*d9f75844SAndroid Build Coastguard Worker@end 134