2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
35 #include "AudioFileReaderMac.h"
38 #include "AudioFileReader.h"
39 #include "FloatConversion.h"
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <wtf/RetainPtr.h>
45 static AudioBufferList* createAudioBufferList(size_t numberOfBuffers)
47 size_t bufferListSize = sizeof(AudioBufferList) - sizeof(AudioBuffer);
48 bufferListSize += numberOfBuffers * sizeof(AudioBuffer);
50 AudioBufferList* bufferList = static_cast<AudioBufferList*>(calloc(1, bufferListSize));
52 bufferList->mNumberBuffers = numberOfBuffers;
57 static void destroyAudioBufferList(AudioBufferList* bufferList)
62 AudioFileReader::AudioFileReader(const char* filePath)
66 , m_extAudioFileRef(0)
68 RetainPtr<CFStringRef> filePathString = adoptCF(CFStringCreateWithCString(kCFAllocatorDefault, filePath, kCFStringEncodingUTF8));
69 RetainPtr<CFURLRef> url = adoptCF(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePathString.get(), kCFURLPOSIXPathStyle, false));
73 ExtAudioFileOpenURL(url.get(), &m_extAudioFileRef);
76 AudioFileReader::AudioFileReader(const void* data, size_t dataSize)
78 , m_dataSize(dataSize)
80 , m_extAudioFileRef(0)
82 OSStatus result = AudioFileOpenWithCallbacks(this, readProc, 0, getSizeProc, 0, 0, &m_audioFileID);
87 result = ExtAudioFileWrapAudioFileID(m_audioFileID, false, &m_extAudioFileRef);
89 m_extAudioFileRef = 0;
92 AudioFileReader::~AudioFileReader()
94 if (m_extAudioFileRef)
95 ExtAudioFileDispose(m_extAudioFileRef);
97 m_extAudioFileRef = 0;
100 AudioFileClose(m_audioFileID);
105 OSStatus AudioFileReader::readProc(void* clientData, SInt64 position, UInt32 requestCount, void* buffer, UInt32* actualCount)
107 AudioFileReader* audioFileReader = static_cast<AudioFileReader*>(clientData);
109 size_t dataSize = audioFileReader->dataSize();
110 const void* data = audioFileReader->data();
111 size_t bytesToRead = 0;
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);
121 *actualCount = bytesToRead;
126 SInt64 AudioFileReader::getSizeProc(void* clientData)
128 AudioFileReader* audioFileReader = static_cast<AudioFileReader*>(clientData);
129 return audioFileReader->dataSize();
132 PassRefPtr<AudioBus> AudioFileReader::createBus(float sampleRate, bool mixToMono)
134 if (!m_extAudioFileRef)
137 // Get file's data format
138 UInt32 size = sizeof(m_fileDataFormat);
139 OSStatus result = ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileDataFormat, &size, &m_fileDataFormat);
143 // Number of channels
144 size_t numberOfChannels = m_fileDataFormat.mChannelsPerFrame;
147 SInt64 numberOfFrames64 = 0;
148 size = sizeof(numberOfFrames64);
149 result = ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileLengthFrames, &size, &numberOfFrames64);
150 if (result != noErr || numberOfFrames64 <= 0)
154 double fileSampleRate = m_fileDataFormat.mSampleRate;
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;
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;
170 m_clientDataFormat.mSampleRate = sampleRate;
172 result = ExtAudioFileSetProperty(m_extAudioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &m_clientDataFormat);
176 // Change numberOfFrames64 to destination sample-rate
177 numberOfFrames64 = numberOfFrames64 * (m_clientDataFormat.mSampleRate / fileSampleRate);
178 size_t numberOfFrames = static_cast<size_t>(numberOfFrames64);
180 size_t busChannelCount = mixToMono ? 1 : numberOfChannels;
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
186 // Only allocated in the mixToMono case
187 AudioFloatArray bufL;
188 AudioFloatArray bufR;
192 // Setup AudioBufferList in preparation for reading
193 AudioBufferList* bufferList = createAudioBufferList(numberOfChannels);
195 if (mixToMono && numberOfChannels == 2) {
196 bufL.allocate(numberOfFrames);
197 bufR.allocate(numberOfFrames);
198 bufferL = bufL.data();
199 bufferR = bufR.data();
201 bufferList->mBuffers[0].mNumberChannels = 1;
202 bufferList->mBuffers[0].mDataByteSize = numberOfFrames * sizeof(float);
203 bufferList->mBuffers[0].mData = bufferL;
205 bufferList->mBuffers[1].mNumberChannels = 1;
206 bufferList->mBuffers[1].mDataByteSize = numberOfFrames * sizeof(float);
207 bufferList->mBuffers[1].mData = bufferR;
209 ASSERT(!mixToMono || numberOfChannels == 1);
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();
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);
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]);
235 destroyAudioBufferList(bufferList);
240 PassRefPtr<AudioBus> createBusFromAudioFile(const char* filePath, bool mixToMono, float sampleRate)
242 return AudioFileReader(filePath).createBus(sampleRate, mixToMono);
245 PassRefPtr<AudioBus> createBusFromInMemoryAudioFile(const void* data, size_t dataSize, bool mixToMono, float sampleRate)
247 return AudioFileReader(data, dataSize).createBus(sampleRate, mixToMono);
250 } // namespace WebCore
252 #endif // PLATFORM(MAC)
254 #endif // ENABLE(WEB_AUDIO)