diff options
Diffstat (limited to 'Source/WebCore/platform/audio/mac')
| -rw-r--r-- | Source/WebCore/platform/audio/mac/AudioBusMac.mm | 66 | ||||
| -rw-r--r-- | Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp | 171 | ||||
| -rw-r--r-- | Source/WebCore/platform/audio/mac/AudioDestinationMac.h | 69 | ||||
| -rw-r--r-- | Source/WebCore/platform/audio/mac/AudioFileReaderMac.cpp | 258 | ||||
| -rw-r--r-- | Source/WebCore/platform/audio/mac/AudioFileReaderMac.h | 71 | ||||
| -rw-r--r-- | Source/WebCore/platform/audio/mac/FFTFrameMac.cpp | 191 |
6 files changed, 826 insertions, 0 deletions
diff --git a/Source/WebCore/platform/audio/mac/AudioBusMac.mm b/Source/WebCore/platform/audio/mac/AudioBusMac.mm new file mode 100644 index 0000000..3d454a9 --- /dev/null +++ b/Source/WebCore/platform/audio/mac/AudioBusMac.mm @@ -0,0 +1,66 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#import "config.h" + +#if ENABLE(WEB_AUDIO) + +#import "AudioBus.h" + +#import "AudioFileReader.h" +#import <wtf/OwnPtr.h> +#import <wtf/PassOwnPtr.h> +#import <Foundation/Foundation.h> + +@interface WebCoreAudioBundleClass : NSObject +@end + +@implementation WebCoreAudioBundleClass +@end + +namespace WebCore { + +PassOwnPtr<AudioBus> AudioBus::loadPlatformResource(const char* name, double sampleRate) +{ + // This method can be called from other than the main thread, so we need an auto-release pool. + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSBundle *bundle = [NSBundle bundleForClass:[WebCoreAudioBundleClass class]]; + NSString *audioFilePath = [bundle pathForResource:[NSString stringWithUTF8String:name] ofType:@"wav" inDirectory:@"audio"]; + NSData *audioData = [NSData dataWithContentsOfFile:audioFilePath]; + + if (audioData) { + OwnPtr<AudioBus> bus(createBusFromInMemoryAudioFile([audioData bytes], [audioData length], false, sampleRate)); + [pool release]; + return bus.release(); + } + + ASSERT_NOT_REACHED(); + [pool release]; + return 0; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp b/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp new file mode 100644 index 0000000..523729f --- /dev/null +++ b/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp @@ -0,0 +1,171 @@ +/* + * 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 "AudioDestinationMac.h" + +#include "AudioSourceProvider.h" +#include <CoreAudio/AudioHardware.h> + +namespace WebCore { + +const int kBufferSize = 128; + +// Factory method: Mac-implementation +PassOwnPtr<AudioDestination> AudioDestination::create(AudioSourceProvider& provider, double sampleRate) +{ + return adoptPtr(new AudioDestinationMac(provider, sampleRate)); +} + +double AudioDestination::hardwareSampleRate() +{ + // Determine the default output device's sample-rate. + AudioDeviceID deviceID = kAudioDeviceUnknown; + UInt32 infoSize = sizeof(deviceID); + + AudioObjectPropertyAddress defaultInputDeviceAddress = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultInputDeviceAddress, 0, 0, &infoSize, (void*)&deviceID); + if (result) + return 0.0; // error + + Float64 nominalSampleRate; + infoSize = sizeof(Float64); + + AudioObjectPropertyAddress nominalSampleRateAddress = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + result = AudioObjectGetPropertyData(deviceID, &nominalSampleRateAddress, 0, 0, &infoSize, (void*)&nominalSampleRate); + if (result) + return 0.0; // error + + return nominalSampleRate; +} + +AudioDestinationMac::AudioDestinationMac(AudioSourceProvider& provider, double sampleRate) + : m_outputUnit(0) + , m_provider(provider) + , m_renderBus(2, kBufferSize, false) + , m_sampleRate(sampleRate) + , m_isPlaying(false) +{ + // Open and initialize DefaultOutputUnit + Component comp; + ComponentDescription desc; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + comp = FindNextComponent(0, &desc); + + ASSERT(comp); + + OSStatus result = OpenAComponent(comp, &m_outputUnit); + ASSERT(!result); + + result = AudioUnitInitialize(m_outputUnit); + ASSERT(!result); + + configure(); +} + +AudioDestinationMac::~AudioDestinationMac() +{ + if (m_outputUnit) + CloseComponent(m_outputUnit); +} + +void AudioDestinationMac::configure() +{ + // Set render callback + AURenderCallbackStruct input; + input.inputProc = inputProc; + input.inputProcRefCon = this; + OSStatus result = AudioUnitSetProperty(m_outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &input, sizeof(input)); + ASSERT(!result); + + // Set stream format + AudioStreamBasicDescription streamFormat; + streamFormat.mSampleRate = m_sampleRate; + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags = kAudioFormatFlagsCanonical | kAudioFormatFlagIsNonInterleaved; + streamFormat.mBitsPerChannel = 8 * sizeof(AudioSampleType); + streamFormat.mChannelsPerFrame = 2; + streamFormat.mFramesPerPacket = 1; + streamFormat.mBytesPerPacket = sizeof(AudioSampleType); + streamFormat.mBytesPerFrame = sizeof(AudioSampleType); + + result = AudioUnitSetProperty(m_outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, (void*)&streamFormat, sizeof(AudioStreamBasicDescription)); + ASSERT(!result); + + // Set the buffer frame size. + UInt32 bufferSize = kBufferSize; + result = AudioUnitSetProperty(m_outputUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Output, 0, (void*)&bufferSize, sizeof(bufferSize)); + ASSERT(!result); +} + +void AudioDestinationMac::start() +{ + OSStatus result = AudioOutputUnitStart(m_outputUnit); + + if (!result) + m_isPlaying = true; +} + +void AudioDestinationMac::stop() +{ + OSStatus result = AudioOutputUnitStop(m_outputUnit); + + if (!result) + m_isPlaying = false; +} + +// Pulls on our provider to get rendered audio stream. +OSStatus AudioDestinationMac::render(UInt32 numberOfFrames, AudioBufferList* ioData) +{ + AudioBuffer* buffers = ioData->mBuffers; + m_renderBus.setChannelMemory(0, (float*)buffers[0].mData, numberOfFrames); + m_renderBus.setChannelMemory(1, (float*)buffers[1].mData, numberOfFrames); + + m_provider.provideInput(&m_renderBus, numberOfFrames); + + return noErr; +} + +// DefaultOutputUnit callback +OSStatus AudioDestinationMac::inputProc(void* userData, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32 /*busNumber*/, UInt32 numberOfFrames, AudioBufferList* ioData) +{ + AudioDestinationMac* audioOutput = static_cast<AudioDestinationMac*>(userData); + return audioOutput->render(numberOfFrames, ioData); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/mac/AudioDestinationMac.h b/Source/WebCore/platform/audio/mac/AudioDestinationMac.h new file mode 100644 index 0000000..197440c --- /dev/null +++ b/Source/WebCore/platform/audio/mac/AudioDestinationMac.h @@ -0,0 +1,69 @@ +/* + * 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 AudioDestinationMac_h +#define AudioDestinationMac_h + +#include "AudioBus.h" +#include "AudioDestination.h" +#include <AudioUnit/AudioUnit.h> + +namespace WebCore { + +// An AudioDestination using CoreAudio's default output AudioUnit + +class AudioDestinationMac : public AudioDestination { +public: + AudioDestinationMac(AudioSourceProvider&, double sampleRate); + virtual ~AudioDestinationMac(); + + virtual void start(); + virtual void stop(); + bool isPlaying() { return m_isPlaying; } + + double sampleRate() const { return m_sampleRate; } + +private: + void configure(); + + // DefaultOutputUnit callback + static OSStatus inputProc(void* userData, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32 busNumber, UInt32 numberOfFrames, AudioBufferList* ioData); + + OSStatus render(UInt32 numberOfFrames, AudioBufferList* ioData); + + AudioUnit m_outputUnit; + AudioSourceProvider& m_provider; + AudioBus m_renderBus; + + double m_sampleRate; + bool m_isPlaying; +}; + +} // namespace WebCore + +#endif // AudioDestinationMac_h diff --git a/Source/WebCore/platform/audio/mac/AudioFileReaderMac.cpp b/Source/WebCore/platform/audio/mac/AudioFileReaderMac.cpp new file mode 100644 index 0000000..6d17152 --- /dev/null +++ b/Source/WebCore/platform/audio/mac/AudioFileReaderMac.cpp @@ -0,0 +1,258 @@ +/* + * 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> +#include <CoreServices/CoreServices.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/Source/WebCore/platform/audio/mac/AudioFileReaderMac.h b/Source/WebCore/platform/audio/mac/AudioFileReaderMac.h new file mode 100644 index 0000000..d531266 --- /dev/null +++ b/Source/WebCore/platform/audio/mac/AudioFileReaderMac.h @@ -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 diff --git a/Source/WebCore/platform/audio/mac/FFTFrameMac.cpp b/Source/WebCore/platform/audio/mac/FFTFrameMac.cpp new file mode 100644 index 0000000..0f7efb7 --- /dev/null +++ b/Source/WebCore/platform/audio/mac/FFTFrameMac.cpp @@ -0,0 +1,191 @@ +/* + * 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. + */ + +// Mac OS X - specific FFTFrame implementation + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "FFTFrame.h" + +namespace WebCore { + +const int kMaxFFTPow2Size = 24; + +FFTSetup* FFTFrame::fftSetups = 0; + +// Normal constructor: allocates for a given fftSize +FFTFrame::FFTFrame(unsigned fftSize) + : m_realData(fftSize) + , m_imagData(fftSize) +{ + m_FFTSize = fftSize; + m_log2FFTSize = static_cast<unsigned>(log2(fftSize)); + + // We only allow power of two + ASSERT(1UL << m_log2FFTSize == m_FFTSize); + + // Lazily create and share fftSetup with other frames + m_FFTSetup = fftSetupForSize(fftSize); + + // Setup frame data + m_frame.realp = m_realData.data(); + m_frame.imagp = m_imagData.data(); +} + +// Creates a blank/empty frame (interpolate() must later be called) +FFTFrame::FFTFrame() + : m_realData(0) + , m_imagData(0) +{ + // Later will be set to correct values when interpolate() is called + m_frame.realp = 0; + m_frame.imagp = 0; + + m_FFTSize = 0; + m_log2FFTSize = 0; +} + +// Copy constructor +FFTFrame::FFTFrame(const FFTFrame& frame) + : m_FFTSize(frame.m_FFTSize) + , m_log2FFTSize(frame.m_log2FFTSize) + , m_FFTSetup(frame.m_FFTSetup) + , m_realData(frame.m_FFTSize) + , m_imagData(frame.m_FFTSize) +{ + // Setup frame data + m_frame.realp = m_realData.data(); + m_frame.imagp = m_imagData.data(); + + // Copy/setup frame data + unsigned nbytes = sizeof(float) * m_FFTSize; + memcpy(realData(), frame.m_frame.realp, nbytes); + memcpy(imagData(), frame.m_frame.imagp, nbytes); +} + +FFTFrame::~FFTFrame() +{ +} + +void FFTFrame::multiply(const FFTFrame& frame) +{ + FFTFrame& frame1 = *this; + const FFTFrame& frame2 = frame; + + float* realP1 = frame1.realData(); + float* imagP1 = frame1.imagData(); + const float* realP2 = frame2.realData(); + const float* imagP2 = frame2.imagData(); + + // Scale accounts for vecLib's peculiar scaling + // This ensures the right scaling all the way back to inverse FFT + float scale = 0.5f; + + // Multiply packed DC/nyquist component + realP1[0] *= scale * realP2[0]; + imagP1[0] *= scale * imagP2[0]; + + // Multiply the rest, skipping packed DC/Nyquist components + DSPSplitComplex sc1 = frame1.dspSplitComplex(); + sc1.realp++; + sc1.imagp++; + + DSPSplitComplex sc2 = frame2.dspSplitComplex(); + sc2.realp++; + sc2.imagp++; + + unsigned halfSize = m_FFTSize / 2; + + // Complex multiply + vDSP_zvmul(&sc1, 1, &sc2, 1, &sc1, 1, halfSize - 1, 1 /* normal multiplication */); + + // We've previously scaled the packed part, now scale the rest..... + vDSP_vsmul(sc1.realp, 1, &scale, sc1.realp, 1, halfSize - 1); + vDSP_vsmul(sc1.imagp, 1, &scale, sc1.imagp, 1, halfSize - 1); +} + +void FFTFrame::doFFT(float* data) +{ + vDSP_ctoz((DSPComplex*)data, 2, &m_frame, 1, m_FFTSize / 2); + vDSP_fft_zrip(m_FFTSetup, &m_frame, 1, m_log2FFTSize, FFT_FORWARD); +} + +void FFTFrame::doInverseFFT(float* data) +{ + vDSP_fft_zrip(m_FFTSetup, &m_frame, 1, m_log2FFTSize, FFT_INVERSE); + vDSP_ztoc(&m_frame, 1, (DSPComplex*)data, 2, m_FFTSize / 2); + + // Do final scaling so that x == IFFT(FFT(x)) + float scale = 0.5f / m_FFTSize; + vDSP_vsmul(data, 1, &scale, data, 1, m_FFTSize); +} + +FFTSetup FFTFrame::fftSetupForSize(unsigned fftSize) +{ + if (!fftSetups) { + fftSetups = (FFTSetup*)malloc(sizeof(FFTSetup) * kMaxFFTPow2Size); + memset(fftSetups, 0, sizeof(FFTSetup) * kMaxFFTPow2Size); + } + + int pow2size = static_cast<int>(log2(fftSize)); + ASSERT(pow2size < kMaxFFTPow2Size); + if (!fftSetups[pow2size]) + fftSetups[pow2size] = vDSP_create_fftsetup(pow2size, FFT_RADIX2); + + return fftSetups[pow2size]; +} + +void FFTFrame::cleanup() +{ + if (!fftSetups) + return; + + for (int i = 0; i < kMaxFFTPow2Size; ++i) { + if (fftSetups[i]) + vDSP_destroy_fftsetup(fftSetups[i]); + } + + free(fftSetups); + fftSetups = 0; +} + +float* FFTFrame::realData() const +{ + return m_frame.realp; +} + +float* FFTFrame::imagData() const +{ + return m_frame.imagp; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) |
