2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2011, 2014 Apple Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #if ENABLE(WEB_AUDIO) && PLATFORM(IOS)
34 #include "AudioDestinationIOS.h"
36 #include "AudioIOCallback.h"
37 #include "AudioSession.h"
38 #include "FloatConversion.h"
40 #include "RuntimeApplicationChecks.h"
41 #include "SoftLinking.h"
42 #include <AudioToolbox/AudioServices.h>
43 #include <wtf/HashSet.h>
44 #include <wtf/NeverDestroyed.h>
46 SOFT_LINK_FRAMEWORK(AudioToolbox)
47 SOFT_LINK(AudioToolbox, AudioComponentFindNext, AudioComponent, (AudioComponent inComponent, const AudioComponentDescription *inDesc), (inComponent, inDesc))
48 SOFT_LINK(AudioToolbox, AudioComponentInstanceDispose, OSStatus, (AudioComponentInstance inInstance), (inInstance))
49 SOFT_LINK(AudioToolbox, AudioComponentInstanceNew, OSStatus, (AudioComponent inComponent, AudioComponentInstance *outInstance), (inComponent, outInstance))
50 SOFT_LINK(AudioToolbox, AudioOutputUnitStart, OSStatus, (AudioUnit ci), (ci))
51 SOFT_LINK(AudioToolbox, AudioOutputUnitStop, OSStatus, (AudioUnit ci), (ci))
52 SOFT_LINK(AudioToolbox, AudioUnitAddPropertyListener, OSStatus, (AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void *inProcUserData), (inUnit, inID, inProc, inProcUserData))
53 SOFT_LINK(AudioToolbox, AudioUnitGetProperty, OSStatus, (AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void *outData, UInt32 *ioDataSize), (inUnit, inID, inScope, inElement, outData, ioDataSize))
54 SOFT_LINK(AudioToolbox, AudioUnitInitialize, OSStatus, (AudioUnit inUnit), (inUnit))
55 SOFT_LINK(AudioToolbox, AudioUnitSetProperty, OSStatus, (AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void *inData, UInt32 inDataSize), (inUnit, inID, inScope, inElement, inData, inDataSize))
59 const int kRenderBufferSize = 128;
60 const int kPreferredBufferSize = 256;
62 typedef HashSet<AudioDestinationIOS*> AudioDestinationSet;
63 static AudioDestinationSet& audioDestinations()
65 static NeverDestroyed<AudioDestinationSet> audioDestinationSet;
66 return audioDestinationSet;
69 // Factory method: iOS-implementation
70 std::unique_ptr<AudioDestination> AudioDestination::create(AudioIOCallback& callback, const String&, unsigned numberOfInputChannels, unsigned numberOfOutputChannels, float sampleRate)
72 // FIXME: make use of inputDeviceId as appropriate.
74 // FIXME: Add support for local/live audio input.
75 if (numberOfInputChannels)
76 LOG(Media, "AudioDestination::create(%u, %u, %f) - unhandled input channels", numberOfInputChannels, numberOfOutputChannels, sampleRate);
78 // FIXME: Add support for multi-channel (> stereo) output.
79 if (numberOfOutputChannels != 2)
80 LOG(Media, "AudioDestination::create(%u, %u, %f) - unhandled output channels", numberOfInputChannels, numberOfOutputChannels, sampleRate);
82 return std::make_unique<AudioDestinationIOS>(callback, sampleRate);
85 float AudioDestination::hardwareSampleRate()
87 return AudioSession::sharedSession().sampleRate();
90 unsigned long AudioDestination::maxChannelCount()
92 // FIXME: query the default audio hardware device to return the actual number
93 // of channels of the device. Also see corresponding FIXME in create().
94 // There is a small amount of code which assumes stereo in AudioDestinationIOS which
99 AudioDestinationIOS::AudioDestinationIOS(AudioIOCallback& callback, double sampleRate)
101 , m_callback(callback)
102 , m_renderBus(AudioBus::create(2, kRenderBufferSize, false))
103 , m_spareBus(AudioBus::create(2, kRenderBufferSize, true))
104 , m_sampleRate(sampleRate)
107 audioDestinations().add(this);
109 // Open and initialize DefaultOutputUnit
111 AudioComponentDescription desc;
113 desc.componentType = kAudioUnitType_Output;
114 desc.componentSubType = kAudioUnitSubType_RemoteIO;
115 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
116 desc.componentFlags = 0;
117 desc.componentFlagsMask = 0;
118 comp = AudioComponentFindNext(0, &desc);
122 OSStatus result = AudioComponentInstanceNew(comp, &m_outputUnit);
126 result = AudioUnitSetProperty(m_outputUnit,
127 kAudioOutputUnitProperty_EnableIO,
128 kAudioUnitScope_Output,
134 result = AudioUnitAddPropertyListener(m_outputUnit, kAudioUnitProperty_MaximumFramesPerSlice, frameSizeChangedProc, this);
137 result = AudioUnitInitialize(m_outputUnit);
143 AudioDestinationIOS::~AudioDestinationIOS()
145 audioDestinations().remove(this);
148 AudioComponentInstanceDispose(m_outputUnit);
151 void AudioDestinationIOS::configure()
153 // Set render callback
154 AURenderCallbackStruct input;
155 input.inputProc = inputProc;
156 input.inputProcRefCon = this;
157 OSStatus result = AudioUnitSetProperty(m_outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(input));
161 AudioStreamBasicDescription streamFormat;
163 UInt32 size = sizeof(AudioStreamBasicDescription);
164 result = AudioUnitGetProperty(m_outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, (void*)&streamFormat, &size);
167 const int bytesPerFloat = sizeof(Float32);
168 const int bitsPerByte = 8;
169 streamFormat.mSampleRate = m_sampleRate;
170 streamFormat.mFormatID = kAudioFormatLinearPCM;
171 streamFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
172 streamFormat.mBytesPerPacket = bytesPerFloat;
173 streamFormat.mFramesPerPacket = 1;
174 streamFormat.mBytesPerFrame = bytesPerFloat;
175 streamFormat.mChannelsPerFrame = 2;
176 streamFormat.mBitsPerChannel = bitsPerByte * bytesPerFloat;
178 result = AudioUnitSetProperty(m_outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, (void*)&streamFormat, sizeof(AudioStreamBasicDescription));
181 AudioSession::sharedSession().setPreferredBufferSize(kPreferredBufferSize);
184 void AudioDestinationIOS::start()
186 LOG(Media, "AudioDestinationIOS::start");
188 OSStatus result = AudioOutputUnitStart(m_outputUnit);
193 void AudioDestinationIOS::stop()
195 LOG(Media, "AudioDestinationIOS::stop");
197 OSStatus result = AudioOutputUnitStop(m_outputUnit);
202 static void assignAudioBuffersToBus(AudioBuffer* buffers, AudioBus& bus, UInt32 numberOfBuffers, UInt32 numberOfFrames, UInt32 frameOffset, UInt32 framesThisTime)
204 for (UInt32 i = 0; i < numberOfBuffers; ++i) {
205 UInt32 bytesPerFrame = buffers[i].mDataByteSize / numberOfFrames;
206 UInt32 byteOffset = frameOffset * bytesPerFrame;
207 float* memory = reinterpret_cast<float*>(reinterpret_cast<char*>(buffers[i].mData) + byteOffset);
208 bus.setChannelMemory(i, memory, framesThisTime);
212 // Pulls on our provider to get rendered audio stream.
213 OSStatus AudioDestinationIOS::render(UInt32 numberOfFrames, AudioBufferList* ioData)
215 AudioBuffer* buffers = ioData->mBuffers;
216 UInt32 numberOfBuffers = ioData->mNumberBuffers;
217 UInt32 framesRemaining = numberOfFrames;
218 UInt32 frameOffset = 0;
219 while (framesRemaining > 0) {
220 if (m_startSpareFrame < m_endSpareFrame) {
221 ASSERT(m_startSpareFrame < m_endSpareFrame);
222 UInt32 framesThisTime = std::min<UInt32>(m_endSpareFrame - m_startSpareFrame, numberOfFrames);
223 assignAudioBuffersToBus(buffers, *m_renderBus, numberOfBuffers, numberOfFrames, frameOffset, framesThisTime);
224 m_renderBus->copyFromRange(*m_spareBus, m_startSpareFrame, m_endSpareFrame);
225 frameOffset += framesThisTime;
226 framesRemaining -= framesThisTime;
227 m_startSpareFrame += framesThisTime;
230 UInt32 framesThisTime = std::min<UInt32>(kRenderBufferSize, framesRemaining);
231 assignAudioBuffersToBus(buffers, *m_renderBus, numberOfBuffers, numberOfFrames, frameOffset, framesThisTime);
235 if (framesThisTime < kRenderBufferSize) {
236 m_callback.render(0, m_spareBus.get(), kRenderBufferSize);
237 m_renderBus->copyFromRange(*m_spareBus, 0, framesThisTime);
238 m_startSpareFrame = framesThisTime;
239 m_endSpareFrame = kRenderBufferSize;
241 m_callback.render(0, m_renderBus.get(), framesThisTime);
242 frameOffset += framesThisTime;
243 framesRemaining -= framesThisTime;
249 void AudioDestinationIOS::setIsPlaying(bool isPlaying)
251 if (m_isPlaying == isPlaying)
254 m_isPlaying = isPlaying;
255 m_callback.isPlayingDidChange();
258 // DefaultOutputUnit callback
259 OSStatus AudioDestinationIOS::inputProc(void* userData, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32 /*busNumber*/, UInt32 numberOfFrames, AudioBufferList* ioData)
261 AudioDestinationIOS* audioOutput = static_cast<AudioDestinationIOS*>(userData);
262 return audioOutput->render(numberOfFrames, ioData);
265 void AudioDestinationIOS::frameSizeChangedProc(void *inRefCon, AudioUnit, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement)
267 AudioDestinationIOS* audioOutput = static_cast<AudioDestinationIOS*>(inRefCon);
268 UInt32 bufferSize = 0;
269 UInt32 dataSize = sizeof(bufferSize);
270 AudioUnitGetProperty(audioOutput->m_outputUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (void*)&bufferSize, &dataSize);
271 fprintf(stderr, ">>>> frameSizeChanged = %lu\n", static_cast<unsigned long>(bufferSize));
274 } // namespace WebCore
276 #endif // ENABLE(WEB_AUDIO) && PLATFORM(IOS)