• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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