[Web Audio] Decoding specific .m4a file crashes tab
[WebKit-https.git] / Source / WebCore / platform / audio / mac / AudioFileReaderMac.cpp
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 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 #if PLATFORM(MAC)
34
35 #include "AudioFileReaderMac.h"
36
37 #include "AudioBus.h"
38 #include "AudioFileReader.h"
39 #include "FloatConversion.h"
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <wtf/RetainPtr.h>
42
43 namespace WebCore {
44
45 static AudioBufferList* createAudioBufferList(size_t numberOfBuffers)
46 {
47     size_t bufferListSize = sizeof(AudioBufferList) - sizeof(AudioBuffer);
48     bufferListSize += numberOfBuffers * sizeof(AudioBuffer);
49
50     AudioBufferList* bufferList = static_cast<AudioBufferList*>(calloc(1, bufferListSize));
51     if (bufferList)
52         bufferList->mNumberBuffers = numberOfBuffers;
53
54     return bufferList;
55 }
56
57 static void destroyAudioBufferList(AudioBufferList* bufferList)
58 {
59     free(bufferList);
60 }
61
62 AudioFileReader::AudioFileReader(const char* filePath)
63     : m_data(nullptr)
64     , m_dataSize(0)
65     , m_audioFileID(0)
66     , m_extAudioFileRef(0)
67 {
68     RetainPtr<CFStringRef> filePathString = adoptCF(CFStringCreateWithCString(kCFAllocatorDefault, filePath, kCFStringEncodingUTF8));
69     RetainPtr<CFURLRef> url = adoptCF(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePathString.get(), kCFURLPOSIXPathStyle, false));
70     if (!url)
71         return;
72
73     ExtAudioFileOpenURL(url.get(), &m_extAudioFileRef);
74 }
75
76 AudioFileReader::AudioFileReader(const void* data, size_t dataSize)
77     : m_data(data)
78     , m_dataSize(dataSize)
79     , m_audioFileID(0)
80     , m_extAudioFileRef(0)
81 {
82     OSStatus result = AudioFileOpenWithCallbacks(this, readProc, 0, getSizeProc, 0, 0, &m_audioFileID);
83
84     if (result != noErr)
85         return;
86
87     result = ExtAudioFileWrapAudioFileID(m_audioFileID, false, &m_extAudioFileRef);
88     if (result != noErr)
89         m_extAudioFileRef = 0;
90 }
91
92 AudioFileReader::~AudioFileReader()
93 {
94     if (m_extAudioFileRef)
95         ExtAudioFileDispose(m_extAudioFileRef);
96
97     m_extAudioFileRef = 0;
98
99     if (m_audioFileID)
100         AudioFileClose(m_audioFileID);
101         
102     m_audioFileID = 0;
103 }
104
105 OSStatus AudioFileReader::readProc(void* clientData, SInt64 position, UInt32 requestCount, void* buffer, UInt32* actualCount)
106 {
107     AudioFileReader* audioFileReader = static_cast<AudioFileReader*>(clientData);
108
109     size_t dataSize = audioFileReader->dataSize();
110     const void* data = audioFileReader->data();
111     size_t bytesToRead = 0;
112
113     if (static_cast<UInt64>(position) < dataSize) {
114         size_t bytesAvailable = dataSize - static_cast<size_t>(position);
115         bytesToRead = requestCount <= bytesAvailable ? requestCount : bytesAvailable;
116         memcpy(buffer, static_cast<const char*>(data) + position, bytesToRead);
117     } else
118         bytesToRead = 0;
119
120     if (actualCount)
121         *actualCount = bytesToRead;
122
123     return noErr;
124 }
125
126 SInt64 AudioFileReader::getSizeProc(void* clientData)
127 {
128     AudioFileReader* audioFileReader = static_cast<AudioFileReader*>(clientData);
129     return audioFileReader->dataSize();
130 }
131
132 PassRefPtr<AudioBus> AudioFileReader::createBus(float sampleRate, bool mixToMono)
133 {
134     if (!m_extAudioFileRef)
135         return 0;
136
137     // Get file's data format
138     UInt32 size = sizeof(m_fileDataFormat);
139     OSStatus result = ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileDataFormat, &size, &m_fileDataFormat);
140     if (result != noErr)
141         return 0;
142
143     // Number of channels
144     size_t numberOfChannels = m_fileDataFormat.mChannelsPerFrame;
145
146     // Number of frames
147     SInt64 numberOfFrames64 = 0;
148     size = sizeof(numberOfFrames64);
149     result = ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileLengthFrames, &size, &numberOfFrames64);
150     if (result != noErr || numberOfFrames64 <= 0)
151         return 0;
152
153     // Sample-rate
154     double fileSampleRate = m_fileDataFormat.mSampleRate;
155
156     // Make client format same number of channels as file format, but tweak a few things.
157     // Client format will be linear PCM (canonical), and potentially change sample-rate.
158     m_clientDataFormat = m_fileDataFormat;
159
160     m_clientDataFormat.mFormatID = kAudioFormatLinearPCM;
161     m_clientDataFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
162     m_clientDataFormat.mBitsPerChannel = 8 * sizeof(Float32);
163     m_clientDataFormat.mChannelsPerFrame = numberOfChannels;
164     m_clientDataFormat.mFramesPerPacket = 1;
165     m_clientDataFormat.mBytesPerPacket = sizeof(Float32);
166     m_clientDataFormat.mBytesPerFrame = sizeof(Float32);
167     m_clientDataFormat.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
168
169     if (sampleRate)
170         m_clientDataFormat.mSampleRate = sampleRate;
171
172     result = ExtAudioFileSetProperty(m_extAudioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &m_clientDataFormat);
173     if (result != noErr)
174         return 0;
175
176     // Change numberOfFrames64 to destination sample-rate
177     numberOfFrames64 = numberOfFrames64 * (m_clientDataFormat.mSampleRate / fileSampleRate);
178     size_t numberOfFrames = static_cast<size_t>(numberOfFrames64);
179
180     size_t busChannelCount = mixToMono ? 1 : numberOfChannels;
181
182     // Create AudioBus where we'll put the PCM audio data
183     RefPtr<AudioBus> audioBus = AudioBus::create(busChannelCount, numberOfFrames);
184     audioBus->setSampleRate(narrowPrecisionToFloat(m_clientDataFormat.mSampleRate)); // save for later
185
186     // Only allocated in the mixToMono case
187     AudioFloatArray bufL;
188     AudioFloatArray bufR;
189     float* bufferL = 0;
190     float* bufferR = 0;
191     
192     // Setup AudioBufferList in preparation for reading
193     AudioBufferList* bufferList = createAudioBufferList(numberOfChannels);
194
195     if (mixToMono && numberOfChannels == 2) {
196         bufL.allocate(numberOfFrames);
197         bufR.allocate(numberOfFrames);
198         bufferL = bufL.data();
199         bufferR = bufR.data();
200
201         bufferList->mBuffers[0].mNumberChannels = 1;
202         bufferList->mBuffers[0].mDataByteSize = numberOfFrames * sizeof(float);
203         bufferList->mBuffers[0].mData = bufferL;
204
205         bufferList->mBuffers[1].mNumberChannels = 1;
206         bufferList->mBuffers[1].mDataByteSize = numberOfFrames * sizeof(float);
207         bufferList->mBuffers[1].mData = bufferR;
208     } else {
209         ASSERT(!mixToMono || numberOfChannels == 1);
210
211         // for True-stereo (numberOfChannels == 4)
212         for (size_t i = 0; i < numberOfChannels; ++i) {
213             bufferList->mBuffers[i].mNumberChannels = 1;
214             bufferList->mBuffers[i].mDataByteSize = numberOfFrames * sizeof(float);
215             bufferList->mBuffers[i].mData = audioBus->channel(i)->mutableData();
216         }
217     }
218
219     // Read from the file (or in-memory version)
220     UInt32 framesToRead = numberOfFrames;
221     result = ExtAudioFileRead(m_extAudioFileRef, &framesToRead, bufferList);
222     if (result != noErr) {
223         destroyAudioBufferList(bufferList);
224         return 0;
225     }
226
227     if (mixToMono && numberOfChannels == 2) {
228         // Mix stereo down to mono
229         float* destL = audioBus->channel(0)->mutableData();
230         for (size_t i = 0; i < numberOfFrames; i++)
231             destL[i] = 0.5f * (bufferL[i] + bufferR[i]);
232     }
233
234     // Cleanup
235     destroyAudioBufferList(bufferList);
236
237     return audioBus;
238 }
239
240 PassRefPtr<AudioBus> createBusFromAudioFile(const char* filePath, bool mixToMono, float sampleRate)
241 {
242     return AudioFileReader(filePath).createBus(sampleRate, mixToMono);
243 }
244
245 PassRefPtr<AudioBus> createBusFromInMemoryAudioFile(const void* data, size_t dataSize, bool mixToMono, float sampleRate)
246 {
247     return AudioFileReader(data, dataSize).createBus(sampleRate, mixToMono);
248 }
249
250 } // namespace WebCore
251
252 #endif // PLATFORM(MAC)
253
254 #endif // ENABLE(WEB_AUDIO)