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