1/* 2 * Copyright (C) 2014 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "AudioHardwareListenerMac.h" 28 29#if PLATFORM(MAC) 30 31#include <algorithm> 32 33enum { 34 kAudioHardwarePropertyProcessIsRunning = 'prun' 35}; 36 37namespace WebCore { 38 39static AudioHardwareActivityType isAudioHardwareProcessRunning() 40{ 41 AudioObjectPropertyAddress propertyAddress = { 42 kAudioHardwarePropertyProcessIsRunning, 43 kAudioObjectPropertyScopeGlobal, 44 kAudioObjectPropertyElementMaster 45 }; 46 47 if (!AudioObjectHasProperty(kAudioObjectSystemObject, &propertyAddress)) 48 return AudioHardwareActivityType::Unknown; 49 50 UInt32 result = 0; 51 UInt32 resultSize = sizeof(UInt32); 52 53 if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, 0, &resultSize, &result)) 54 return AudioHardwareActivityType::Unknown; 55 56 if (result) 57 return AudioHardwareActivityType::IsActive; 58 else 59 return AudioHardwareActivityType::IsInactive; 60} 61 62static bool currentDeviceSupportsLowPowerBufferSize() 63{ 64 AudioDeviceID deviceID = kAudioDeviceUnknown; 65 UInt32 descriptorSize = sizeof(deviceID); 66 AudioObjectPropertyAddress defaultOutputDeviceDescriptor = { 67 kAudioHardwarePropertyDefaultOutputDevice, 68 kAudioObjectPropertyScopeGlobal, 69 kAudioObjectPropertyElementMaster }; 70 71 if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultOutputDeviceDescriptor, 0, 0, &descriptorSize, (void*)&deviceID)) 72 return false; 73 74 UInt32 transportType = kAudioDeviceTransportTypeUnknown; 75 descriptorSize = sizeof(transportType); 76 AudioObjectPropertyAddress tranportTypeDescriptor = { 77 kAudioDevicePropertyTransportType, 78 kAudioObjectPropertyScopeGlobal, 79 kAudioObjectPropertyElementMaster, 80 }; 81 82 if (AudioObjectGetPropertyData(deviceID, &tranportTypeDescriptor, 0, 0, &descriptorSize, &transportType)) 83 return false; 84 85 // Only allow low-power buffer size when using built-in output device, many external devices perform 86 // poorly with a large output buffer. 87 return kAudioDeviceTransportTypeBuiltIn == transportType; 88} 89 90static const AudioObjectPropertyAddress& processIsRunningPropertyDescriptor() 91{ 92 static AudioObjectPropertyAddress processIsRunningProperty = { 93 kAudioHardwarePropertyProcessIsRunning, 94 kAudioObjectPropertyScopeGlobal, 95 kAudioObjectPropertyElementMaster 96 }; 97 98 return processIsRunningProperty; 99}; 100 101static const AudioObjectPropertyAddress& outputDevicePropertyDescriptor() 102{ 103 static AudioObjectPropertyAddress outputDeviceProperty = { 104 kAudioHardwarePropertyDefaultOutputDevice, 105 kAudioObjectPropertyScopeGlobal, 106 kAudioObjectPropertyElementMaster 107 }; 108 109 return outputDeviceProperty; 110}; 111 112PassRefPtr<AudioHardwareListener> AudioHardwareListener::create(Client& client) 113{ 114 return AudioHardwareListenerMac::create(client); 115} 116 117PassRefPtr<AudioHardwareListenerMac> AudioHardwareListenerMac::create(Client& client) 118{ 119 return adoptRef(new AudioHardwareListenerMac(client)); 120} 121 122AudioHardwareListenerMac::AudioHardwareListenerMac(Client& client) 123 : AudioHardwareListener(client) 124 , m_weakFactory(this) 125{ 126 setHardwareActivity(isAudioHardwareProcessRunning()); 127 setOutputDeviceSupportsLowPowerMode(currentDeviceSupportsLowPowerBufferSize()); 128 129 auto weakThis = m_weakFactory.createWeakPtr(); 130 m_block = Block_copy(^(UInt32 count, const AudioObjectPropertyAddress properties[]) { 131 if (weakThis) 132 weakThis->propertyChanged(count, properties); 133 }); 134 135 AudioObjectAddPropertyListenerBlock(kAudioObjectSystemObject, &processIsRunningPropertyDescriptor(), dispatch_get_main_queue(), m_block); 136 AudioObjectAddPropertyListenerBlock(kAudioObjectSystemObject, &outputDevicePropertyDescriptor(), dispatch_get_main_queue(), m_block); 137} 138 139AudioHardwareListenerMac::~AudioHardwareListenerMac() 140{ 141 AudioObjectRemovePropertyListenerBlock(kAudioObjectSystemObject, &processIsRunningPropertyDescriptor(), dispatch_get_main_queue(), m_block); 142 AudioObjectRemovePropertyListenerBlock(kAudioObjectSystemObject, &outputDevicePropertyDescriptor(), dispatch_get_main_queue(), m_block); 143 Block_release(m_block); 144} 145 146void AudioHardwareListenerMac::propertyChanged(UInt32 propertyCount, const AudioObjectPropertyAddress properties[]) 147{ 148 const AudioObjectPropertyAddress& deviceRunning = processIsRunningPropertyDescriptor(); 149 const AudioObjectPropertyAddress& outputDevice = outputDevicePropertyDescriptor(); 150 151 for (UInt32 i = 0; i < propertyCount; ++i) { 152 const AudioObjectPropertyAddress& property = properties[i]; 153 154 if (!memcmp(&property, &deviceRunning, sizeof(AudioObjectPropertyAddress))) 155 processIsRunningChanged(); 156 else if (!memcmp(&property, &outputDevice, sizeof(AudioObjectPropertyAddress))) 157 outputDeviceChanged(); 158 } 159} 160 161void AudioHardwareListenerMac::processIsRunningChanged() 162{ 163 AudioHardwareActivityType activity = isAudioHardwareProcessRunning(); 164 if (activity == hardwareActivity()) 165 return; 166 setHardwareActivity(activity); 167 168 if (hardwareActivity() == AudioHardwareActivityType::IsActive) 169 m_client.audioHardwareDidBecomeActive(); 170 else if (hardwareActivity() == AudioHardwareActivityType::IsInactive) 171 m_client.audioHardwareDidBecomeInactive(); 172} 173 174void AudioHardwareListenerMac::outputDeviceChanged() 175{ 176 setOutputDeviceSupportsLowPowerMode(currentDeviceSupportsLowPowerBufferSize()); 177 m_client.audioOutputDeviceChanged(); 178} 179 180} 181 182#endif 183