/* * Copyright (C) 2010 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #if ENABLE(WEB_AUDIO) #include "AudioFileReaderMac.h" #include "AudioBus.h" #include "AudioFileReader.h" #include #include namespace WebCore { static AudioBufferList* createAudioBufferList(size_t numberOfBuffers) { size_t bufferListSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); bufferListSize += numberOfBuffers * sizeof(AudioBuffer); AudioBufferList* bufferList = static_cast(calloc(1, bufferListSize)); if (bufferList) bufferList->mNumberBuffers = numberOfBuffers; return bufferList; } static void destroyAudioBufferList(AudioBufferList* bufferList) { free(bufferList); } AudioFileReader::AudioFileReader(const char* filePath) : m_data(0) , m_dataSize(0) , m_filePath(filePath) , m_audioFileID(0) , m_extAudioFileRef(0) { FSRef fsref; OSStatus result = FSPathMakeRef((UInt8*)filePath, &fsref, 0); if (result != noErr) return; CFURLRef urlRef = CFURLCreateFromFSRef(0, &fsref); if (!urlRef) return; ExtAudioFileOpenURL(urlRef, &m_extAudioFileRef); if (urlRef) CFRelease(urlRef); } AudioFileReader::AudioFileReader(const void* data, size_t dataSize) : m_data(data) , m_dataSize(dataSize) , m_filePath(0) , m_audioFileID(0) , m_extAudioFileRef(0) { OSStatus result = AudioFileOpenWithCallbacks(this, readProc, 0, getSizeProc, 0, 0, &m_audioFileID); if (result != noErr) return; result = ExtAudioFileWrapAudioFileID(m_audioFileID, false, &m_extAudioFileRef); if (result != noErr) m_extAudioFileRef = 0; } AudioFileReader::~AudioFileReader() { if (m_extAudioFileRef) ExtAudioFileDispose(m_extAudioFileRef); m_extAudioFileRef = 0; if (m_audioFileID) AudioFileClose(m_audioFileID); m_audioFileID = 0; } OSStatus AudioFileReader::readProc(void* clientData, SInt64 position, UInt32 requestCount, void* buffer, UInt32* actualCount) { AudioFileReader* audioFileReader = static_cast(clientData); size_t dataSize = audioFileReader->dataSize(); const void* data = audioFileReader->data(); size_t bytesToRead = 0; if (static_cast(position) < dataSize) { size_t bytesAvailable = dataSize - static_cast(position); bytesToRead = requestCount <= bytesAvailable ? requestCount : bytesAvailable; memcpy(buffer, static_cast(data) + position, bytesToRead); } else bytesToRead = 0; if (actualCount) *actualCount = bytesToRead; return noErr; } SInt64 AudioFileReader::getSizeProc(void* clientData) { AudioFileReader* audioFileReader = static_cast(clientData); return audioFileReader->dataSize(); } PassOwnPtr AudioFileReader::createBus(double sampleRate, bool mixToMono) { if (!m_extAudioFileRef) return 0; // Get file's data format UInt32 size = sizeof(m_fileDataFormat); OSStatus result = ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileDataFormat, &size, &m_fileDataFormat); if (result != noErr) return 0; // Number of channels size_t numberOfChannels = m_fileDataFormat.mChannelsPerFrame; // Number of frames SInt64 numberOfFrames64 = 0; size = sizeof(numberOfFrames64); result = ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileLengthFrames, &size, &numberOfFrames64); if (result != noErr) return 0; // Sample-rate double fileSampleRate = m_fileDataFormat.mSampleRate; // Make client format same number of channels as file format, but tweak a few things. // Client format will be linear PCM (canonical), and potentially change sample-rate. m_clientDataFormat = m_fileDataFormat; m_clientDataFormat.mFormatID = kAudioFormatLinearPCM; m_clientDataFormat.mFormatFlags = kAudioFormatFlagsCanonical; m_clientDataFormat.mBitsPerChannel = 8 * sizeof(AudioSampleType); m_clientDataFormat.mChannelsPerFrame = numberOfChannels; m_clientDataFormat.mFramesPerPacket = 1; m_clientDataFormat.mBytesPerPacket = sizeof(AudioSampleType); m_clientDataFormat.mBytesPerFrame = sizeof(AudioSampleType); m_clientDataFormat.mFormatFlags |= kAudioFormatFlagIsNonInterleaved; if (sampleRate) m_clientDataFormat.mSampleRate = sampleRate; result = ExtAudioFileSetProperty(m_extAudioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &m_clientDataFormat); if (result != noErr) return 0; // Change numberOfFrames64 to destination sample-rate numberOfFrames64 = numberOfFrames64 * (m_clientDataFormat.mSampleRate / fileSampleRate); size_t numberOfFrames = static_cast(numberOfFrames64); size_t busChannelCount = mixToMono ? 1 : numberOfChannels; // Create AudioBus where we'll put the PCM audio data OwnPtr audioBus = adoptPtr(new AudioBus(busChannelCount, numberOfFrames)); audioBus->setSampleRate(m_clientDataFormat.mSampleRate); // save for later // Only allocated in the mixToMono case AudioFloatArray bufL; AudioFloatArray bufR; float* bufferL = 0; float* bufferR = 0; // Setup AudioBufferList in preparation for reading AudioBufferList* bufferList = createAudioBufferList(numberOfChannels); if (mixToMono && numberOfChannels == 2) { bufL.resize(numberOfFrames); bufR.resize(numberOfFrames); bufferL = bufL.data(); bufferR = bufR.data(); bufferList->mBuffers[0].mNumberChannels = 1; bufferList->mBuffers[0].mDataByteSize = numberOfFrames * sizeof(float); bufferList->mBuffers[0].mData = bufferL; bufferList->mBuffers[1].mNumberChannels = 1; bufferList->mBuffers[1].mDataByteSize = numberOfFrames * sizeof(float); bufferList->mBuffers[1].mData = bufferR; } else { ASSERT(!mixToMono || numberOfChannels == 1); // for True-stereo (numberOfChannels == 4) for (size_t i = 0; i < numberOfChannels; ++i) { bufferList->mBuffers[i].mNumberChannels = 1; bufferList->mBuffers[i].mDataByteSize = numberOfFrames * sizeof(float); bufferList->mBuffers[i].mData = audioBus->channel(i)->data(); } } // Read from the file (or in-memory version) UInt32 framesToRead = numberOfFrames; result = ExtAudioFileRead(m_extAudioFileRef, &framesToRead, bufferList); if (result != noErr) return 0; if (mixToMono && numberOfChannels == 2) { // Mix stereo down to mono float* destL = audioBus->channel(0)->data(); for (size_t i = 0; i < numberOfFrames; i++) destL[i] = 0.5f * (bufferL[i] + bufferR[i]); } // Cleanup destroyAudioBufferList(bufferList); return audioBus.release(); } PassOwnPtr createBusFromAudioFile(const char* filePath, bool mixToMono, double sampleRate) { AudioFileReader reader(filePath); return reader.createBus(sampleRate, mixToMono); } PassOwnPtr createBusFromInMemoryAudioFile(const void* data, size_t dataSize, bool mixToMono, double sampleRate) { AudioFileReader reader(data, dataSize); return reader.createBus(sampleRate, mixToMono); } } // WebCore #endif // ENABLE(WEB_AUDIO)