Home | History | Annotate | Download | only in mac
      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