The audio session's category was set to Playback, which implies that audio is still audible even after setting the device to silent mode or locking the screen. This shouldn't be the default behavior. We now set it to Ambient, which preserves mixing with other apps but makes sure sound is turned off when it should. Task-number: QTBUG-39036 Change-Id: Ic36668d73f3179dc38b41023e380e15f8c8517e0 Reviewed-by: Christian Stromme <christian.stromme@digia.com>
474 lines
16 KiB
Plaintext
474 lines
16 KiB
Plaintext
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/legal
|
|
**
|
|
** This file is part of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and Digia. For licensing terms and
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 2.1 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3.0 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
|
**
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "coreaudiosessionmanager.h"
|
|
|
|
#import <AVFoundation/AVAudioSession.h>
|
|
#import <Foundation/Foundation.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
@interface CoreAudioSessionObserver : NSObject
|
|
{
|
|
CoreAudioSessionManager *m_sessionManager;
|
|
AVAudioSession *m_audioSession;
|
|
}
|
|
|
|
@property (readonly, getter=sessionManager) CoreAudioSessionManager *m_sessionManager;
|
|
@property (readonly, getter=audioSession) AVAudioSession *m_audioSession;
|
|
|
|
-(CoreAudioSessionObserver *)initWithAudioSessionManager:(CoreAudioSessionManager *)sessionManager;
|
|
|
|
-(BOOL)activateAudio;
|
|
-(BOOL)deactivateAudio;
|
|
|
|
//Notification handlers
|
|
-(void)audioSessionInterruption:(NSNotification *)notification;
|
|
-(void)audioSessionRouteChange:(NSNotification *)notification;
|
|
-(void)audioSessionMediaServicesWereReset:(NSNotification *)notification;
|
|
|
|
@end //interface CoreAudioSessionObserver
|
|
|
|
@implementation CoreAudioSessionObserver
|
|
|
|
@synthesize m_sessionManager, m_audioSession;
|
|
|
|
-(CoreAudioSessionObserver *)initWithAudioSessionManager:(CoreAudioSessionManager *)sessionManager
|
|
{
|
|
if (!(self = [super init]))
|
|
return nil;
|
|
|
|
self->m_sessionManager = sessionManager;
|
|
self->m_audioSession = [AVAudioSession sharedInstance];
|
|
|
|
//Set up observers
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(audioSessionInterruption:)
|
|
name:AVAudioSessionInterruptionNotification
|
|
object:self->m_audioSession];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(audioSessionMediaServicesWereReset:)
|
|
name:AVAudioSessionMediaServicesWereResetNotification
|
|
object:self->m_audioSession];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(audioSessionRouteChange:)
|
|
name:AVAudioSessionRouteChangeNotification
|
|
object:self->m_audioSession];
|
|
|
|
return self;
|
|
}
|
|
|
|
-(void)dealloc
|
|
{
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug() << Q_FUNC_INFO;
|
|
#endif
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self
|
|
name:AVAudioSessionInterruptionNotification
|
|
object:self->m_audioSession];
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self
|
|
name:AVAudioSessionMediaServicesWereResetNotification
|
|
object:self->m_audioSession];
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self
|
|
name:AVAudioSessionRouteChangeNotification
|
|
object:self->m_audioSession];
|
|
[super dealloc];
|
|
}
|
|
|
|
-(BOOL)activateAudio
|
|
{
|
|
NSError *error = nil;
|
|
BOOL success = [self->m_audioSession setActive:YES error:&error];
|
|
if (![self->m_audioSession setActive:YES error:&error]) {
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audio session activation failed: %s", [[error localizedDescription] UTF8String]);
|
|
} else {
|
|
qDebug("audio session activated");
|
|
#endif
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
-(BOOL)deactivateAudio
|
|
{
|
|
NSError *error = nil;
|
|
BOOL success = [m_audioSession setActive:NO error:&error];
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
if (!success) {
|
|
qDebug("%s", [[error localizedDescription] UTF8String]);
|
|
}
|
|
#endif
|
|
return success;
|
|
}
|
|
|
|
-(void)audioSessionInterruption:(NSNotification *)notification
|
|
{
|
|
NSNumber *type = [[notification userInfo] valueForKey:AVAudioSessionInterruptionTypeKey];
|
|
if ([type intValue] == AVAudioSessionInterruptionTypeBegan) {
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audioSession Interuption begain");
|
|
#endif
|
|
} else if ([type intValue] == AVAudioSessionInterruptionTypeEnded) {
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audioSession Interuption ended");
|
|
#endif
|
|
NSNumber *option = [[notification userInfo] valueForKey:AVAudioSessionInterruptionOptionKey];
|
|
if ([option intValue] == AVAudioSessionInterruptionOptionShouldResume) {
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audioSession is active and immediately ready to be used.");
|
|
#endif
|
|
} else {
|
|
[self activateAudio];
|
|
}
|
|
}
|
|
}
|
|
|
|
-(void)audioSessionMediaServicesWereReset:(NSNotification *)notification
|
|
{
|
|
Q_UNUSED(notification)
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audioSession Media Services were reset");
|
|
#endif
|
|
//Reactivate audio when this occurs
|
|
[self activateAudio];
|
|
}
|
|
|
|
-(void)audioSessionRouteChange:(NSNotification *)notification
|
|
{
|
|
NSNumber *reason = [[notification userInfo] valueForKey:AVAudioSessionRouteChangeReasonKey];
|
|
NSUInteger reasonEnum = [reason intValue];
|
|
|
|
if (reasonEnum == AVAudioSessionRouteChangeReasonUnknown) {
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audioSession route changed. reason: unknown");
|
|
#endif
|
|
} else if (reasonEnum == AVAudioSessionRouteChangeReasonNewDeviceAvailable) {
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audioSession route changed. reason: new device available");
|
|
#endif
|
|
} else if (reasonEnum == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audioSession route changed. reason: old device unavailable");
|
|
#endif
|
|
} else if (reasonEnum == AVAudioSessionRouteChangeReasonCategoryChange) {
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audioSession route changed. reason: category changed");
|
|
#endif
|
|
} else if (reasonEnum == AVAudioSessionRouteChangeReasonOverride) {
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audioSession route changed. reason: override");
|
|
#endif
|
|
} else if (reasonEnum == AVAudioSessionRouteChangeReasonWakeFromSleep) {
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audioSession route changed. reason: woken from sleep");
|
|
#endif
|
|
} else if (reasonEnum == AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory) {
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug("audioSession route changed. reason: no suitable route for category");
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
@end //implementation CoreAudioSessionObserver
|
|
|
|
CoreAudioSessionManager::CoreAudioSessionManager() :
|
|
QObject(0)
|
|
{
|
|
m_sessionObserver = [[CoreAudioSessionObserver alloc] initWithAudioSessionManager:this];
|
|
setActive(true);
|
|
// Set default category to Ambient (implies MixWithOthers). This makes sure audio stops playing
|
|
// if the screen is locked or if the Silent switch is toggled.
|
|
setCategory(CoreAudioSessionManager::Ambient, CoreAudioSessionManager::None);
|
|
}
|
|
|
|
CoreAudioSessionManager::~CoreAudioSessionManager()
|
|
{
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
qDebug() << Q_FUNC_INFO;
|
|
#endif
|
|
[m_sessionObserver release];
|
|
}
|
|
|
|
|
|
CoreAudioSessionManager &CoreAudioSessionManager::instance()
|
|
{
|
|
static CoreAudioSessionManager instance;
|
|
return instance;
|
|
}
|
|
|
|
bool CoreAudioSessionManager::setActive(bool active)
|
|
{
|
|
if (active) {
|
|
return [m_sessionObserver activateAudio];
|
|
} else {
|
|
return [m_sessionObserver deactivateAudio];
|
|
}
|
|
}
|
|
|
|
bool CoreAudioSessionManager::setCategory(CoreAudioSessionManager::AudioSessionCategorys category, CoreAudioSessionManager::AudioSessionCategoryOptions options)
|
|
{
|
|
NSString *targetCategory = nil;
|
|
|
|
switch (category) {
|
|
case CoreAudioSessionManager::Ambient:
|
|
targetCategory = AVAudioSessionCategoryAmbient;
|
|
break;
|
|
case CoreAudioSessionManager::SoloAmbient:
|
|
targetCategory = AVAudioSessionCategorySoloAmbient;
|
|
break;
|
|
case CoreAudioSessionManager::Playback:
|
|
targetCategory = AVAudioSessionCategoryPlayback;
|
|
break;
|
|
case CoreAudioSessionManager::Record:
|
|
targetCategory = AVAudioSessionCategoryRecord;
|
|
break;
|
|
case CoreAudioSessionManager::PlayAndRecord:
|
|
targetCategory = AVAudioSessionCategoryPlayAndRecord;
|
|
break;
|
|
case CoreAudioSessionManager::AudioProcessing:
|
|
targetCategory = AVAudioSessionCategoryAudioProcessing;
|
|
break;
|
|
case CoreAudioSessionManager::MultiRoute:
|
|
targetCategory = AVAudioSessionCategoryMultiRoute;
|
|
break;
|
|
}
|
|
|
|
if (targetCategory == nil)
|
|
return false;
|
|
|
|
return [[m_sessionObserver audioSession] setCategory:targetCategory
|
|
withOptions:(AVAudioSessionCategoryOptions)options
|
|
error:nil];
|
|
}
|
|
|
|
bool CoreAudioSessionManager::setMode(CoreAudioSessionManager::AudioSessionModes mode)
|
|
{
|
|
NSString *targetMode = nil;
|
|
switch (mode) {
|
|
case CoreAudioSessionManager::Default:
|
|
targetMode = AVAudioSessionModeDefault;
|
|
break;
|
|
case CoreAudioSessionManager::VoiceChat:
|
|
targetMode = AVAudioSessionModeVoiceChat;
|
|
break;
|
|
case CoreAudioSessionManager::GameChat:
|
|
targetMode = AVAudioSessionModeGameChat;
|
|
break;
|
|
case CoreAudioSessionManager::VideoRecording:
|
|
targetMode = AVAudioSessionModeVideoRecording;
|
|
break;
|
|
case CoreAudioSessionManager::Measurement:
|
|
targetMode = AVAudioSessionModeMeasurement;
|
|
break;
|
|
case CoreAudioSessionManager::MoviePlayback:
|
|
targetMode = AVAudioSessionModeMoviePlayback;
|
|
break;
|
|
}
|
|
|
|
if (targetMode == nil)
|
|
return false;
|
|
|
|
return [[m_sessionObserver audioSession] setMode:targetMode error:nil];
|
|
|
|
}
|
|
|
|
CoreAudioSessionManager::AudioSessionCategorys CoreAudioSessionManager::category()
|
|
{
|
|
NSString *category = [[m_sessionObserver audioSession] category];
|
|
AudioSessionCategorys localCategory = Ambient;
|
|
|
|
if (category == AVAudioSessionCategoryAmbient) {
|
|
localCategory = Ambient;
|
|
} else if (category == AVAudioSessionCategorySoloAmbient) {
|
|
localCategory = SoloAmbient;
|
|
} else if (category == AVAudioSessionCategoryPlayback) {
|
|
localCategory = Playback;
|
|
} else if (category == AVAudioSessionCategoryRecord) {
|
|
localCategory = Record;
|
|
} else if (category == AVAudioSessionCategoryPlayAndRecord) {
|
|
localCategory = PlayAndRecord;
|
|
} else if (category == AVAudioSessionCategoryAudioProcessing) {
|
|
localCategory = AudioProcessing;
|
|
} else if (category == AVAudioSessionCategoryMultiRoute) {
|
|
localCategory = MultiRoute;
|
|
}
|
|
|
|
return localCategory;
|
|
}
|
|
|
|
CoreAudioSessionManager::AudioSessionModes CoreAudioSessionManager::mode()
|
|
{
|
|
NSString *mode = [[m_sessionObserver audioSession] mode];
|
|
AudioSessionModes localMode = Default;
|
|
|
|
if (mode == AVAudioSessionModeDefault) {
|
|
localMode = Default;
|
|
} else if (mode == AVAudioSessionModeVoiceChat) {
|
|
localMode = VoiceChat;
|
|
} else if (mode == AVAudioSessionModeGameChat) {
|
|
localMode = GameChat;
|
|
} else if (mode == AVAudioSessionModeVideoRecording) {
|
|
localMode = VideoRecording;
|
|
} else if (mode == AVAudioSessionModeMeasurement) {
|
|
localMode = Measurement;
|
|
} else if (mode == AVAudioSessionModeMoviePlayback) {
|
|
localMode = MoviePlayback;
|
|
}
|
|
|
|
return localMode;
|
|
}
|
|
|
|
QList<QByteArray> CoreAudioSessionManager::inputDevices()
|
|
{
|
|
//TODO: Add support for USB input devices
|
|
//Right now the default behavior on iOS is to have only one input route
|
|
//at a time.
|
|
QList<QByteArray> inputDevices;
|
|
inputDevices << "default";
|
|
return inputDevices;
|
|
}
|
|
|
|
QList<QByteArray> CoreAudioSessionManager::outputDevices()
|
|
{
|
|
//TODO: Add support for USB output devices
|
|
//Right now the default behavior on iOS is to have only one output route
|
|
//at a time.
|
|
QList<QByteArray> outputDevices;
|
|
outputDevices << "default";
|
|
return outputDevices;
|
|
}
|
|
|
|
float CoreAudioSessionManager::currentIOBufferDuration()
|
|
{
|
|
return [[m_sessionObserver audioSession] IOBufferDuration];
|
|
}
|
|
|
|
float CoreAudioSessionManager::preferredSampleRate()
|
|
{
|
|
return [[m_sessionObserver audioSession] preferredSampleRate];
|
|
}
|
|
|
|
#ifdef QT_DEBUG_COREAUDIO
|
|
QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategorys category)
|
|
{
|
|
QDebug output = dbg.nospace();
|
|
switch (category) {
|
|
case CoreAudioSessionManager::Ambient:
|
|
output << "AudioSessionCategoryAmbient";
|
|
break;
|
|
case CoreAudioSessionManager::SoloAmbient:
|
|
output << "AudioSessionCategorySoloAmbient";
|
|
break;
|
|
case CoreAudioSessionManager::Playback:
|
|
output << "AudioSessionCategoryPlayback";
|
|
break;
|
|
case CoreAudioSessionManager::Record:
|
|
output << "AudioSessionCategoryRecord";
|
|
break;
|
|
case CoreAudioSessionManager::PlayAndRecord:
|
|
output << "AudioSessionCategoryPlayAndRecord";
|
|
break;
|
|
case CoreAudioSessionManager::AudioProcessing:
|
|
output << "AudioSessionCategoryAudioProcessing";
|
|
break;
|
|
case CoreAudioSessionManager::MultiRoute:
|
|
output << "AudioSessionCategoryMultiRoute";
|
|
break;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategoryOptions option)
|
|
{
|
|
QDebug output = dbg.nospace();
|
|
switch (option) {
|
|
case CoreAudioSessionManager::None:
|
|
output << "AudioSessionCategoryOptionNone";
|
|
break;
|
|
case CoreAudioSessionManager::MixWithOthers:
|
|
output << "AudioSessionCategoryOptionMixWithOthers";
|
|
break;
|
|
case CoreAudioSessionManager::DuckOthers:
|
|
output << "AudioSessionCategoryOptionDuckOthers";
|
|
break;
|
|
case CoreAudioSessionManager::AllowBluetooth:
|
|
output << "AudioSessionCategoryOptionAllowBluetooth";
|
|
break;
|
|
case CoreAudioSessionManager::DefaultToSpeaker:
|
|
output << "AudioSessionCategoryOptionDefaultToSpeaker";
|
|
break;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionModes mode)
|
|
{
|
|
QDebug output = dbg.nospace();
|
|
switch (mode) {
|
|
case CoreAudioSessionManager::Default:
|
|
output << "AudioSessionModeDefault";
|
|
break;
|
|
case CoreAudioSessionManager::VoiceChat:
|
|
output << "AudioSessionModeVoiceChat";
|
|
break;
|
|
case CoreAudioSessionManager::GameChat:
|
|
output << "AudioSessionModeGameChat";
|
|
break;
|
|
case CoreAudioSessionManager::VideoRecording:
|
|
output << "AudioSessionModeVideoRecording";
|
|
break;
|
|
case CoreAudioSessionManager::Measurement:
|
|
output << "AudioSessionModeMeasurement";
|
|
break;
|
|
case CoreAudioSessionManager::MoviePlayback:
|
|
output << "AudioSessionModeMoviePlayback";
|
|
break;
|
|
}
|
|
return output;
|
|
}
|
|
#endif
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#include "moc_coreaudiosessionmanager.cpp"
|