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 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 54 static void destroyAudioBufferList(AudioBufferList* bufferList) 55 { 56 free(bufferList); 57 } 58 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 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 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 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 132 SInt64 AudioFileReader::getSizeProc(void* clientData) 133 { 134 AudioFileReader* audioFileReader = static_cast<AudioFileReader*>(clientData); 135 return audioFileReader->dataSize(); 136 } 137 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 244 PassOwnPtr<AudioBus> createBusFromAudioFile(const char* filePath, bool mixToMono, double sampleRate) 245 { 246 AudioFileReader reader(filePath); 247 return reader.createBus(sampleRate, mixToMono); 248 } 249 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