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