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
create(AudioSourceProvider & provider,double sampleRate)43 PassOwnPtr<AudioDestination> AudioDestination::create(AudioSourceProvider& provider, double sampleRate)
44 {
45 return adoptPtr(new AudioDestinationMac(provider, sampleRate));
46 }
47
hardwareSampleRate()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
AudioDestinationMac(AudioSourceProvider & provider,double sampleRate)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
~AudioDestinationMac()99 AudioDestinationMac::~AudioDestinationMac()
100 {
101 if (m_outputUnit)
102 CloseComponent(m_outputUnit);
103 }
104
configure()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
start()134 void AudioDestinationMac::start()
135 {
136 OSStatus result = AudioOutputUnitStart(m_outputUnit);
137
138 if (!result)
139 m_isPlaying = true;
140 }
141
stop()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.
render(UInt32 numberOfFrames,AudioBufferList * ioData)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
inputProc(void * userData,AudioUnitRenderActionFlags *,const AudioTimeStamp *,UInt32,UInt32 numberOfFrames,AudioBufferList * ioData)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