[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebCore / platform / audio / ios / AudioDestinationIOS.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2011, 2014 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
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.
17  *
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.
28  */
29
30 #include "config.h"
31
32 #if ENABLE(WEB_AUDIO) && PLATFORM(IOS_FAMILY)
33
34 #include "AudioDestinationIOS.h"
35
36 #include "AudioIOCallback.h"
37 #include "AudioSession.h"
38 #include "FloatConversion.h"
39 #include "Logging.h"
40 #include "RuntimeApplicationChecks.h"
41 #include <AudioToolbox/AudioServices.h>
42 #include <pal/spi/cocoa/AudioToolboxSPI.h>
43 #include <wtf/HashSet.h>
44 #include <wtf/NeverDestroyed.h>
45 #include <wtf/SoftLinking.h>
46
47 SOFT_LINK_FRAMEWORK(AudioToolbox)
48 SOFT_LINK(AudioToolbox, AudioComponentFindNext, AudioComponent, (AudioComponent inComponent, const AudioComponentDescription *inDesc), (inComponent, inDesc))
49 SOFT_LINK(AudioToolbox, AudioComponentInstanceDispose, OSStatus, (AudioComponentInstance inInstance), (inInstance))
50 SOFT_LINK(AudioToolbox, AudioComponentInstanceNew, OSStatus, (AudioComponent inComponent, AudioComponentInstance *outInstance), (inComponent, outInstance))
51 SOFT_LINK(AudioToolbox, AudioOutputUnitStart, OSStatus, (AudioUnit ci), (ci))
52 SOFT_LINK(AudioToolbox, AudioOutputUnitStop, OSStatus, (AudioUnit ci), (ci))
53 SOFT_LINK(AudioToolbox, AudioUnitAddPropertyListener, OSStatus, (AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void *inProcUserData), (inUnit, inID, inProc, inProcUserData))
54 SOFT_LINK(AudioToolbox, AudioUnitGetProperty, OSStatus, (AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void *outData, UInt32 *ioDataSize), (inUnit, inID, inScope, inElement, outData, ioDataSize))
55 SOFT_LINK(AudioToolbox, AudioUnitInitialize, OSStatus, (AudioUnit inUnit), (inUnit))
56 SOFT_LINK(AudioToolbox, AudioUnitSetProperty, OSStatus, (AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void *inData, UInt32 inDataSize), (inUnit, inID, inScope, inElement, inData, inDataSize))
57
58 namespace WebCore {
59
60 const int kRenderBufferSize = 128;
61 const int kPreferredBufferSize = 256;
62
63 typedef HashSet<AudioDestinationIOS*> AudioDestinationSet;
64 static AudioDestinationSet& audioDestinations()
65 {
66     static NeverDestroyed<AudioDestinationSet> audioDestinationSet;
67     return audioDestinationSet;
68 }
69
70 // Factory method: iOS-implementation
71 std::unique_ptr<AudioDestination> AudioDestination::create(AudioIOCallback& callback, const String&, unsigned numberOfInputChannels, unsigned numberOfOutputChannels, float sampleRate)
72 {
73     // FIXME: make use of inputDeviceId as appropriate.
74
75     // FIXME: Add support for local/live audio input.
76     if (numberOfInputChannels)
77         LOG(Media, "AudioDestination::create(%u, %u, %f) - unhandled input channels", numberOfInputChannels, numberOfOutputChannels, sampleRate);
78
79     // FIXME: Add support for multi-channel (> stereo) output.
80     if (numberOfOutputChannels != 2)
81         LOG(Media, "AudioDestination::create(%u, %u, %f) - unhandled output channels", numberOfInputChannels, numberOfOutputChannels, sampleRate);
82
83     return makeUnique<AudioDestinationIOS>(callback, sampleRate);
84 }
85
86 float AudioDestination::hardwareSampleRate()
87 {
88     return AudioSession::sharedSession().sampleRate();
89 }
90
91 unsigned long AudioDestination::maxChannelCount()
92 {
93     // FIXME: query the default audio hardware device to return the actual number
94     // of channels of the device. Also see corresponding FIXME in create().
95     // There is a small amount of code which assumes stereo in AudioDestinationIOS which
96     // can be upgraded.
97     return 0;
98 }
99
100 AudioDestinationIOS::AudioDestinationIOS(AudioIOCallback& callback, double sampleRate)
101     : m_outputUnit(0)
102     , m_callback(callback)
103     , m_renderBus(AudioBus::create(2, kRenderBufferSize, false))
104     , m_spareBus(AudioBus::create(2, kRenderBufferSize, true))
105     , m_sampleRate(sampleRate)
106     , m_isPlaying(false)
107 {
108     audioDestinations().add(this);
109
110     // Open and initialize DefaultOutputUnit
111     AudioComponent comp;
112     AudioComponentDescription desc;
113
114     desc.componentType = kAudioUnitType_Output;
115     desc.componentSubType = kAudioUnitSubType_RemoteIO;
116     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
117     desc.componentFlags = 0;
118     desc.componentFlagsMask = 0;
119     comp = AudioComponentFindNext(0, &desc);
120
121     ASSERT(comp);
122
123     OSStatus result = AudioComponentInstanceNew(comp, &m_outputUnit);
124     ASSERT(!result);
125
126     UInt32 flag = 1;
127     result = AudioUnitSetProperty(m_outputUnit, 
128         kAudioOutputUnitProperty_EnableIO, 
129         kAudioUnitScope_Output, 
130         0,
131         &flag, 
132         sizeof(flag));
133     ASSERT(!result);
134
135     result = AudioUnitAddPropertyListener(m_outputUnit, kAudioUnitProperty_MaximumFramesPerSlice, frameSizeChangedProc, this);
136     ASSERT(!result);
137
138     result = AudioUnitInitialize(m_outputUnit);
139     ASSERT(!result);
140
141     configure();
142 }
143
144 AudioDestinationIOS::~AudioDestinationIOS()
145 {
146     audioDestinations().remove(this);
147
148     if (m_outputUnit)
149         AudioComponentInstanceDispose(m_outputUnit);
150 }
151
152 void AudioDestinationIOS::configure()
153 {
154     // Set render callback
155     AURenderCallbackStruct input;
156     input.inputProc = inputProc;
157     input.inputProcRefCon = this;
158     OSStatus result = AudioUnitSetProperty(m_outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(input));
159     ASSERT(!result);
160
161     // Set stream format
162     AudioStreamBasicDescription streamFormat;
163
164     UInt32 size = sizeof(AudioStreamBasicDescription);
165     result = AudioUnitGetProperty(m_outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, (void*)&streamFormat, &size);
166     ASSERT(!result);
167
168     const int bytesPerFloat = sizeof(Float32);
169     const int bitsPerByte = 8;
170     streamFormat.mSampleRate = m_sampleRate;
171     streamFormat.mFormatID = kAudioFormatLinearPCM;
172     streamFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
173     streamFormat.mBytesPerPacket = bytesPerFloat;
174     streamFormat.mFramesPerPacket = 1;
175     streamFormat.mBytesPerFrame = bytesPerFloat;
176     streamFormat.mChannelsPerFrame = 2;
177     streamFormat.mBitsPerChannel = bitsPerByte * bytesPerFloat;
178
179     result = AudioUnitSetProperty(m_outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, (void*)&streamFormat, sizeof(AudioStreamBasicDescription));
180     ASSERT(!result);
181
182     AudioSession::sharedSession().setPreferredBufferSize(kPreferredBufferSize);
183 }
184
185 void AudioDestinationIOS::start()
186 {
187     LOG(Media, "AudioDestinationIOS::start");
188
189     OSStatus result = AudioOutputUnitStart(m_outputUnit);
190     if (!result)
191         setIsPlaying(true);
192 }
193
194 void AudioDestinationIOS::stop()
195 {
196     LOG(Media, "AudioDestinationIOS::stop");
197
198     OSStatus result = AudioOutputUnitStop(m_outputUnit);
199     if (!result)
200         setIsPlaying(false);
201 }
202
203 static void assignAudioBuffersToBus(AudioBuffer* buffers, AudioBus& bus, UInt32 numberOfBuffers, UInt32 numberOfFrames, UInt32 frameOffset, UInt32 framesThisTime)
204 {
205     for (UInt32 i = 0; i < numberOfBuffers; ++i) {
206         UInt32 bytesPerFrame = buffers[i].mDataByteSize / numberOfFrames;
207         UInt32 byteOffset = frameOffset * bytesPerFrame;
208         float* memory = reinterpret_cast<float*>(reinterpret_cast<char*>(buffers[i].mData) + byteOffset);
209         bus.setChannelMemory(i, memory, framesThisTime);
210     }
211 }
212
213 // Pulls on our provider to get rendered audio stream.
214 OSStatus AudioDestinationIOS::render(UInt32 numberOfFrames, AudioBufferList* ioData)
215 {
216     AudioBuffer* buffers = ioData->mBuffers;
217     UInt32 numberOfBuffers = ioData->mNumberBuffers;
218     UInt32 framesRemaining = numberOfFrames;
219     UInt32 frameOffset = 0;
220     while (framesRemaining > 0) {
221         if (m_startSpareFrame < m_endSpareFrame) {
222             ASSERT(m_startSpareFrame < m_endSpareFrame);
223             UInt32 framesThisTime = std::min<UInt32>(m_endSpareFrame - m_startSpareFrame, numberOfFrames);
224             assignAudioBuffersToBus(buffers, *m_renderBus, numberOfBuffers, numberOfFrames, frameOffset, framesThisTime);
225             m_renderBus->copyFromRange(*m_spareBus, m_startSpareFrame, m_endSpareFrame);
226             frameOffset += framesThisTime;
227             framesRemaining -= framesThisTime;
228             m_startSpareFrame += framesThisTime;
229         }
230
231         UInt32 framesThisTime = std::min<UInt32>(kRenderBufferSize, framesRemaining);
232         assignAudioBuffersToBus(buffers, *m_renderBus, numberOfBuffers, numberOfFrames, frameOffset, framesThisTime);
233
234         if (!framesThisTime)
235             break;
236         if (framesThisTime < kRenderBufferSize) {
237             m_callback.render(0, m_spareBus.get(), kRenderBufferSize);
238             m_renderBus->copyFromRange(*m_spareBus, 0, framesThisTime);
239             m_startSpareFrame = framesThisTime;
240             m_endSpareFrame = kRenderBufferSize;
241         } else
242             m_callback.render(0, m_renderBus.get(), framesThisTime);
243         frameOffset += framesThisTime;
244         framesRemaining -= framesThisTime;
245     }
246
247     return noErr;
248 }
249
250 void AudioDestinationIOS::setIsPlaying(bool isPlaying)
251 {
252     if (m_isPlaying == isPlaying)
253         return;
254
255     m_isPlaying = isPlaying;
256     m_callback.isPlayingDidChange();
257 }
258
259 // DefaultOutputUnit callback
260 OSStatus AudioDestinationIOS::inputProc(void* userData, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32 /*busNumber*/, UInt32 numberOfFrames, AudioBufferList* ioData)
261 {
262     AudioDestinationIOS* audioOutput = static_cast<AudioDestinationIOS*>(userData);
263     return audioOutput->render(numberOfFrames, ioData);
264 }
265
266 void AudioDestinationIOS::frameSizeChangedProc(void *inRefCon, AudioUnit, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement)
267 {
268     AudioDestinationIOS* audioOutput = static_cast<AudioDestinationIOS*>(inRefCon);
269     UInt32 bufferSize = 0;
270     UInt32 dataSize = sizeof(bufferSize);
271     AudioUnitGetProperty(audioOutput->m_outputUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (void*)&bufferSize, &dataSize);
272     fprintf(stderr, ">>>> frameSizeChanged = %lu\n", static_cast<unsigned long>(bufferSize));
273 }
274
275 } // namespace WebCore
276
277 #endif // ENABLE(WEB_AUDIO) && PLATFORM(IOS_FAMILY)
278