1 /* 2 * Copyright (C) 2010 Google 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 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "config.h" 30 31 #if ENABLE(WEB_AUDIO) 32 33 #include "AudioDestinationMac.h" 34 35 #include "AudioSourceProvider.h" 36 #include <CoreAudio/AudioHardware.h> 37 38 namespace WebCore { 39 40 const int kBufferSize = 128; 41 42 // Factory method: Mac-implementation 43 PassOwnPtr<AudioDestination> AudioDestination::create(AudioSourceProvider& provider, double sampleRate) 44 { 45 return adoptPtr(new AudioDestinationMac(provider, sampleRate)); 46 } 47 48 double AudioDestination::hardwareSampleRate() 49 { 50 // Determine the default output device's sample-rate. 51 AudioDeviceID deviceID = kAudioDeviceUnknown; 52 UInt32 infoSize = sizeof(deviceID); 53 54 AudioObjectPropertyAddress defaultOutputDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 55 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultOutputDeviceAddress, 0, 0, &infoSize, (void*)&deviceID); 56 if (result) 57 return 0.0; // error 58 59 Float64 nominalSampleRate; 60 infoSize = sizeof(Float64); 61 62 AudioObjectPropertyAddress nominalSampleRateAddress = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 63 result = AudioObjectGetPropertyData(deviceID, &nominalSampleRateAddress, 0, 0, &infoSize, (void*)&nominalSampleRate); 64 if (result) 65 return 0.0; // error 66 67 return nominalSampleRate; 68 } 69 70 AudioDestinationMac::AudioDestinationMac(AudioSourceProvider& provider, double sampleRate) 71 : m_outputUnit(0) 72 , m_provider(provider) 73 , m_renderBus(2, kBufferSize, false) 74 , m_sampleRate(sampleRate) 75 , m_isPlaying(false) 76 { 77 // Open and initialize DefaultOutputUnit 78 Component comp; 79 ComponentDescription desc; 80 81 desc.componentType = kAudioUnitType_Output; 82 desc.componentSubType = kAudioUnitSubType_DefaultOutput; 83 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 84 desc.componentFlags = 0; 85 desc.componentFlagsMask = 0; 86 comp = FindNextComponent(0, &desc); 87 88 ASSERT(comp); 89 90 OSStatus result = OpenAComponent(comp, &m_outputUnit); 91 ASSERT(!result); 92 93 result = AudioUnitInitialize(m_outputUnit); 94 ASSERT(!result); 95 96 configure(); 97 } 98 99 AudioDestinationMac::~AudioDestinationMac() 100 { 101 if (m_outputUnit) 102 CloseComponent(m_outputUnit); 103 } 104 105 void AudioDestinationMac::configure() 106 { 107 // Set render callback 108 AURenderCallbackStruct input; 109 input.inputProc = inputProc; 110 input.inputProcRefCon = this; 111 OSStatus result = AudioUnitSetProperty(m_outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &input, sizeof(input)); 112 ASSERT(!result); 113 114 // Set stream format 115 AudioStreamBasicDescription streamFormat; 116 streamFormat.mSampleRate = m_sampleRate; 117 streamFormat.mFormatID = kAudioFormatLinearPCM; 118 streamFormat.mFormatFlags = kAudioFormatFlagsCanonical | kAudioFormatFlagIsNonInterleaved; 119 streamFormat.mBitsPerChannel = 8 * sizeof(AudioSampleType); 120 streamFormat.mChannelsPerFrame = 2; 121 streamFormat.mFramesPerPacket = 1; 122 streamFormat.mBytesPerPacket = sizeof(AudioSampleType); 123 streamFormat.mBytesPerFrame = sizeof(AudioSampleType); 124 125 result = AudioUnitSetProperty(m_outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, (void*)&streamFormat, sizeof(AudioStreamBasicDescription)); 126 ASSERT(!result); 127 128 // Set the buffer frame size. 129 UInt32 bufferSize = kBufferSize; 130 result = AudioUnitSetProperty(m_outputUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Output, 0, (void*)&bufferSize, sizeof(bufferSize)); 131 ASSERT(!result); 132 } 133 134 void AudioDestinationMac::start() 135 { 136 OSStatus result = AudioOutputUnitStart(m_outputUnit); 137 138 if (!result) 139 m_isPlaying = true; 140 } 141 142 void AudioDestinationMac::stop() 143 { 144 OSStatus result = AudioOutputUnitStop(m_outputUnit); 145 146 if (!result) 147 m_isPlaying = false; 148 } 149 150 // Pulls on our provider to get rendered audio stream. 151 OSStatus AudioDestinationMac::render(UInt32 numberOfFrames, AudioBufferList* ioData) 152 { 153 AudioBuffer* buffers = ioData->mBuffers; 154 m_renderBus.setChannelMemory(0, (float*)buffers[0].mData, numberOfFrames); 155 m_renderBus.setChannelMemory(1, (float*)buffers[1].mData, numberOfFrames); 156 157 m_provider.provideInput(&m_renderBus, numberOfFrames); 158 159 return noErr; 160 } 161 162 // DefaultOutputUnit callback 163 OSStatus AudioDestinationMac::inputProc(void* userData, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32 /*busNumber*/, UInt32 numberOfFrames, AudioBufferList* ioData) 164 { 165 AudioDestinationMac* audioOutput = static_cast<AudioDestinationMac*>(userData); 166 return audioOutput->render(numberOfFrames, ioData); 167 } 168 169 } // namespace WebCore 170 171 #endif // ENABLE(WEB_AUDIO) 172