2010-09-14 Chris Rogers <crogers@google.com>
authorcrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Sep 2010 03:19:02 +0000 (03:19 +0000)
committercrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Sep 2010 03:19:02 +0000 (03:19 +0000)
        Reviewed by Kenneth Russell.

        audio engine: add AudioFileReader files (Mac implementation)
        https://bugs.webkit.org/show_bug.cgi?id=36475

        No new tests since audio API is not yet implemented.

        * platform/audio/AudioFileReader.h: Added.
        * platform/audio/mac/AudioFileReaderMac.cpp: Added.
        (WebCore::createAudioBufferList):
        (WebCore::destroyAudioBufferList):
        (WebCore::AudioFileReader::AudioFileReader):
        (WebCore::AudioFileReader::~AudioFileReader):
        (WebCore::AudioFileReader::readProc):
        (WebCore::AudioFileReader::getSizeProc):
        (WebCore::AudioFileReader::createBus):
        (WebCore::createBusFromAudioFile):
        (WebCore::createBusFromInMemoryAudioFile):
        * platform/audio/mac/AudioFileReaderMac.h: Added.
        (WebCore::AudioFileReader::data):
        (WebCore::AudioFileReader::dataSize):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@67530 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/platform/audio/AudioFileReader.h [new file with mode: 0644]
WebCore/platform/audio/mac/AudioFileReaderMac.cpp [new file with mode: 0644]
WebCore/platform/audio/mac/AudioFileReaderMac.h [new file with mode: 0644]

index e2fa2aae263d30074d2246751a405e2d51378c77..eb6804095ae2a35a45d1dcbab4bafaebc631e3b4 100644 (file)
@@ -1,3 +1,27 @@
+2010-09-14  Chris Rogers  <crogers@google.com>
+
+        Reviewed by Kenneth Russell.
+
+        audio engine: add AudioFileReader files (Mac implementation)
+        https://bugs.webkit.org/show_bug.cgi?id=36475
+
+        No new tests since audio API is not yet implemented.
+
+        * platform/audio/AudioFileReader.h: Added.
+        * platform/audio/mac/AudioFileReaderMac.cpp: Added.
+        (WebCore::createAudioBufferList):
+        (WebCore::destroyAudioBufferList):
+        (WebCore::AudioFileReader::AudioFileReader):
+        (WebCore::AudioFileReader::~AudioFileReader):
+        (WebCore::AudioFileReader::readProc):
+        (WebCore::AudioFileReader::getSizeProc):
+        (WebCore::AudioFileReader::createBus):
+        (WebCore::createBusFromAudioFile):
+        (WebCore::createBusFromInMemoryAudioFile):
+        * platform/audio/mac/AudioFileReaderMac.h: Added.
+        (WebCore::AudioFileReader::data):
+        (WebCore::AudioFileReader::dataSize):
+
 2010-09-14  Simon Fraser  <simon.fraser@apple.com>
 
         Reviewed by Oliver Hunt.
diff --git a/WebCore/platform/audio/AudioFileReader.h b/WebCore/platform/audio/AudioFileReader.h
new file mode 100644 (file)
index 0000000..3c02490
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioFileReader_h
+#define AudioFileReader_h
+
+#include <stdlib.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class AudioBus;
+
+// For both create functions:
+// Pass in 0.0 for sampleRate to use the file's sample-rate, otherwise a sample-rate conversion to the requested
+// sampleRate will be made (if it doesn't already match the file's sample-rate).
+// The created buffer will have its sample-rate set correctly to the result.
+
+PassOwnPtr<AudioBus> createBusFromInMemoryAudioFile(const void* data, size_t dataSize, bool mixToMono, double sampleRate);
+
+PassOwnPtr<AudioBus> createBusFromAudioFile(const char* filePath, bool mixToMono, double sampleRate);
+                                
+// May pass in 0.0 for sampleRate in which case it will use the AudioBus's sampleRate                               
+void writeBusToAudioFile(AudioBus* bus, const char* filePath, double fileSampleRate);
+
+} // namespace WebCore
+
+#endif // AudioFileReader_h
diff --git a/WebCore/platform/audio/mac/AudioFileReaderMac.cpp b/WebCore/platform/audio/mac/AudioFileReaderMac.cpp
new file mode 100644 (file)
index 0000000..9dad611
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * 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 <CoreFoundation/CoreFoundation.h>
+
+namespace WebCore {
+
+static AudioBufferList* createAudioBufferList(size_t numberOfBuffers)
+{
+    size_t bufferListSize = sizeof(AudioBufferList) - sizeof(AudioBuffer);
+    bufferListSize += numberOfBuffers * sizeof(AudioBuffer);
+
+    AudioBufferList* bufferList = static_cast<AudioBufferList*>(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<AudioFileReader*>(clientData);
+
+    size_t dataSize = audioFileReader->dataSize();
+    const void* data = audioFileReader->data();
+    size_t bytesToRead = 0;
+
+    if (static_cast<UInt64>(position) < dataSize) {
+        size_t bytesAvailable = dataSize - static_cast<size_t>(position);
+        bytesToRead = requestCount <= bytesAvailable ? requestCount : bytesAvailable;
+        memcpy(buffer, static_cast<const char*>(data) + position, bytesToRead);
+    } else
+        bytesToRead = 0;
+
+    if (actualCount)
+        *actualCount = bytesToRead;
+
+    return noErr;
+}
+
+SInt64 AudioFileReader::getSizeProc(void* clientData)
+{
+    AudioFileReader* audioFileReader = static_cast<AudioFileReader*>(clientData);
+    return audioFileReader->dataSize();
+}
+
+PassOwnPtr<AudioBus> 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<size_t>(numberOfFrames64);
+
+    size_t busChannelCount = mixToMono ? 1 : numberOfChannels;
+
+    // Create AudioBus where we'll put the PCM audio data
+    OwnPtr<AudioBus> 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<AudioBus> createBusFromAudioFile(const char* filePath, bool mixToMono, double sampleRate)
+{
+    AudioFileReader reader(filePath);
+    return reader.createBus(sampleRate, mixToMono);
+}
+
+PassOwnPtr<AudioBus> 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)
diff --git a/WebCore/platform/audio/mac/AudioFileReaderMac.h b/WebCore/platform/audio/mac/AudioFileReaderMac.h
new file mode 100644 (file)
index 0000000..d531266
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef AudioFileReaderMac_h
+#define AudioFileReaderMac_h
+
+#include <AudioToolbox/AudioFile.h>
+#include <AudioToolbox/ExtendedAudioFile.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class AudioBus;
+    
+// Wrapper class for AudioFile and ExtAudioFile CoreAudio APIs for reading files and in-memory versions of them...
+
+class AudioFileReader {
+public:
+    AudioFileReader(const char* filePath);
+    AudioFileReader(const void* data, size_t dataSize);
+    ~AudioFileReader();
+
+    // Returns 0 if error
+    PassOwnPtr<AudioBus> createBus(double sampleRate, bool mixToMono);
+
+    const void* data() const { return m_data; }
+    size_t dataSize() const { return m_dataSize; }
+
+private:
+    static OSStatus readProc(void* clientData, SInt64 position, UInt32 requestCount, void* buffer, UInt32* actualCount);
+    static SInt64 getSizeProc(void* clientData);
+
+    const void* m_data;
+    size_t m_dataSize;
+    const char* m_filePath;
+
+    AudioFileID m_audioFileID;
+    ExtAudioFileRef m_extAudioFileRef;
+
+    AudioStreamBasicDescription m_fileDataFormat;
+    AudioStreamBasicDescription m_clientDataFormat;
+};
+
+} // namespace WebCore
+#endif // AudioFileReaderMac_h