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 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#if PLATFORM(MAC) 34 35#include "AudioFileReaderMac.h" 36 37#include "AudioBus.h" 38#include "AudioFileReader.h" 39#include "FloatConversion.h" 40#include <CoreFoundation/CoreFoundation.h> 41#include <wtf/RetainPtr.h> 42 43namespace WebCore { 44 45static AudioBufferList* createAudioBufferList(size_t numberOfBuffers) 46{ 47 size_t bufferListSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); 48 bufferListSize += numberOfBuffers * sizeof(AudioBuffer); 49 50 AudioBufferList* bufferList = static_cast<AudioBufferList*>(calloc(1, bufferListSize)); 51 if (bufferList) 52 bufferList->mNumberBuffers = numberOfBuffers; 53 54 return bufferList; 55} 56 57static void destroyAudioBufferList(AudioBufferList* bufferList) 58{ 59 free(bufferList); 60} 61 62AudioFileReader::AudioFileReader(const char* filePath) 63 : m_data(nullptr) 64 , m_dataSize(0) 65 , m_audioFileID(0) 66 , m_extAudioFileRef(0) 67{ 68 RetainPtr<CFStringRef> filePathString = adoptCF(CFStringCreateWithCString(kCFAllocatorDefault, filePath, kCFStringEncodingUTF8)); 69 RetainPtr<CFURLRef> url = adoptCF(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePathString.get(), kCFURLPOSIXPathStyle, false)); 70 if (!url) 71 return; 72 73 ExtAudioFileOpenURL(url.get(), &m_extAudioFileRef); 74} 75 76AudioFileReader::AudioFileReader(const void* data, size_t dataSize) 77 : m_data(data) 78 , m_dataSize(dataSize) 79 , m_audioFileID(0) 80 , m_extAudioFileRef(0) 81{ 82 OSStatus result = AudioFileOpenWithCallbacks(this, readProc, 0, getSizeProc, 0, 0, &m_audioFileID); 83 84 if (result != noErr) 85 return; 86 87 result = ExtAudioFileWrapAudioFileID(m_audioFileID, false, &m_extAudioFileRef); 88 if (result != noErr) 89 m_extAudioFileRef = 0; 90} 91 92AudioFileReader::~AudioFileReader() 93{ 94 if (m_extAudioFileRef) 95 ExtAudioFileDispose(m_extAudioFileRef); 96 97 m_extAudioFileRef = 0; 98 99 if (m_audioFileID) 100 AudioFileClose(m_audioFileID); 101 102 m_audioFileID = 0; 103} 104 105OSStatus AudioFileReader::readProc(void* clientData, SInt64 position, UInt32 requestCount, void* buffer, UInt32* actualCount) 106{ 107 AudioFileReader* audioFileReader = static_cast<AudioFileReader*>(clientData); 108 109 size_t dataSize = audioFileReader->dataSize(); 110 const void* data = audioFileReader->data(); 111 size_t bytesToRead = 0; 112 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); 117 } else 118 bytesToRead = 0; 119 120 if (actualCount) 121 *actualCount = bytesToRead; 122 123 return noErr; 124} 125 126SInt64 AudioFileReader::getSizeProc(void* clientData) 127{ 128 AudioFileReader* audioFileReader = static_cast<AudioFileReader*>(clientData); 129 return audioFileReader->dataSize(); 130} 131 132PassRefPtr<AudioBus> AudioFileReader::createBus(float sampleRate, bool mixToMono) 133{ 134 if (!m_extAudioFileRef) 135 return 0; 136 137 // Get file's data format 138 UInt32 size = sizeof(m_fileDataFormat); 139 OSStatus result = ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileDataFormat, &size, &m_fileDataFormat); 140 if (result != noErr) 141 return 0; 142 143 // Number of channels 144 size_t numberOfChannels = m_fileDataFormat.mChannelsPerFrame; 145 146 // Number of frames 147 SInt64 numberOfFrames64 = 0; 148 size = sizeof(numberOfFrames64); 149 result = ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileLengthFrames, &size, &numberOfFrames64); 150 if (result != noErr) 151 return 0; 152 153 // Sample-rate 154 double fileSampleRate = m_fileDataFormat.mSampleRate; 155 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; 159 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; 168 169 if (sampleRate) 170 m_clientDataFormat.mSampleRate = sampleRate; 171 172 result = ExtAudioFileSetProperty(m_extAudioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &m_clientDataFormat); 173 if (result != noErr) 174 return 0; 175 176 // Change numberOfFrames64 to destination sample-rate 177 numberOfFrames64 = numberOfFrames64 * (m_clientDataFormat.mSampleRate / fileSampleRate); 178 size_t numberOfFrames = static_cast<size_t>(numberOfFrames64); 179 180 size_t busChannelCount = mixToMono ? 1 : numberOfChannels; 181 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 185 186 // Only allocated in the mixToMono case 187 AudioFloatArray bufL; 188 AudioFloatArray bufR; 189 float* bufferL = 0; 190 float* bufferR = 0; 191 192 // Setup AudioBufferList in preparation for reading 193 AudioBufferList* bufferList = createAudioBufferList(numberOfChannels); 194 195 if (mixToMono && numberOfChannels == 2) { 196 bufL.allocate(numberOfFrames); 197 bufR.allocate(numberOfFrames); 198 bufferL = bufL.data(); 199 bufferR = bufR.data(); 200 201 bufferList->mBuffers[0].mNumberChannels = 1; 202 bufferList->mBuffers[0].mDataByteSize = numberOfFrames * sizeof(float); 203 bufferList->mBuffers[0].mData = bufferL; 204 205 bufferList->mBuffers[1].mNumberChannels = 1; 206 bufferList->mBuffers[1].mDataByteSize = numberOfFrames * sizeof(float); 207 bufferList->mBuffers[1].mData = bufferR; 208 } else { 209 ASSERT(!mixToMono || numberOfChannels == 1); 210 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(); 216 } 217 } 218 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); 224 return 0; 225 } 226 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]); 232 } 233 234 // Cleanup 235 destroyAudioBufferList(bufferList); 236 237 return audioBus; 238} 239 240PassRefPtr<AudioBus> createBusFromAudioFile(const char* filePath, bool mixToMono, float sampleRate) 241{ 242 return AudioFileReader(filePath).createBus(sampleRate, mixToMono); 243} 244 245PassRefPtr<AudioBus> createBusFromInMemoryAudioFile(const void* data, size_t dataSize, bool mixToMono, float sampleRate) 246{ 247 return AudioFileReader(data, dataSize).createBus(sampleRate, mixToMono); 248} 249 250} // namespace WebCore 251 252#endif // PLATFORM(MAC) 253 254#endif // ENABLE(WEB_AUDIO) 255