AudioFileReaderIOS.cpp [plain text]
#include "config.h"
#include "AudioFileReaderIOS.h"
#if ENABLE(WEB_AUDIO) && PLATFORM(IOS_FAMILY)
#include "AudioBus.h"
#include "AudioFileReader.h"
#include <CoreFoundation/CoreFoundation.h>
#include <wtf/CheckedArithmetic.h>
#include <wtf/RetainPtr.h>
#include <wtf/SoftLinking.h>
SOFT_LINK_FRAMEWORK(AudioToolbox)
SOFT_LINK(AudioToolbox, AudioFileClose, OSStatus, (AudioFileID inAudioFile), (inAudioFile))
SOFT_LINK(AudioToolbox, AudioFileOpenWithCallbacks, OSStatus, (void *inClientData, AudioFile_ReadProc inReadFunc, AudioFile_WriteProc inWriteFunc, AudioFile_GetSizeProc inGetSizeFunc, AudioFile_SetSizeProc inSetSizeFunc, AudioFileTypeID inFileTypeHint, AudioFileID *outAudioFile), (inClientData, inReadFunc, inWriteFunc, inGetSizeFunc, inSetSizeFunc, inFileTypeHint, outAudioFile))
SOFT_LINK(AudioToolbox, ExtAudioFileDispose, OSStatus, (ExtAudioFileRef inExtAudioFile), (inExtAudioFile))
SOFT_LINK(AudioToolbox, ExtAudioFileGetProperty, OSStatus, (ExtAudioFileRef inExtAudioFile, ExtAudioFilePropertyID inPropertyID, UInt32 *ioPropertyDataSize, void *outPropertyData), (inExtAudioFile, inPropertyID, ioPropertyDataSize, outPropertyData))
SOFT_LINK(AudioToolbox, ExtAudioFileRead, OSStatus, (ExtAudioFileRef inExtAudioFile, UInt32 *ioNumberFrames, AudioBufferList *ioData), (inExtAudioFile, ioNumberFrames, ioData))
SOFT_LINK(AudioToolbox, ExtAudioFileSetProperty, OSStatus, (ExtAudioFileRef inExtAudioFile, ExtAudioFilePropertyID inPropertyID, UInt32 inPropertyDataSize, const void *inPropertyData), (inExtAudioFile, inPropertyID, inPropertyDataSize, inPropertyData))
SOFT_LINK(AudioToolbox, ExtAudioFileWrapAudioFileID, OSStatus, (AudioFileID inFileID, Boolean inForWriting, ExtAudioFileRef *outExtAudioFile), (inFileID, inForWriting, outExtAudioFile))
SOFT_LINK(AudioToolbox, ExtAudioFileOpenURL, OSStatus, (CFURLRef inURL, ExtAudioFileRef* outExtAudioFile), (inURL, outExtAudioFile))
namespace WebCore {
static WARN_UNUSED_RETURN AudioBufferList* createAudioBufferList(size_t numberOfBuffers)
{
CheckedSize bufferListSize = sizeof(AudioBufferList) - sizeof(AudioBuffer);
bufferListSize += numberOfBuffers * sizeof(AudioBuffer);
AudioBufferList* bufferList = static_cast<AudioBufferList*>(calloc(1, bufferListSize.unsafeGet()));
if (bufferList)
bufferList->mNumberBuffers = numberOfBuffers;
return bufferList;
}
static inline void destroyAudioBufferList(AudioBufferList* bufferList)
{
free(bufferList);
}
AudioFileReader::AudioFileReader(const char* filePath)
: m_data(0)
, m_dataSize(0)
, m_audioFileID(0)
, m_extAudioFileRef(nullptr)
{
RetainPtr<CFURLRef> urlRef = adoptCF(CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(filePath), strlen(filePath), false));
if (!urlRef)
return;
ExtAudioFileOpenURL(urlRef.get(), &m_extAudioFileRef);
}
AudioFileReader::AudioFileReader(const void* data, size_t dataSize)
: m_data(data)
, m_dataSize(dataSize)
, m_audioFileID(0)
, m_extAudioFileRef(nullptr)
{
if (AudioFileOpenWithCallbacks(this, readProc, 0, getSizeProc, 0, 0, &m_audioFileID) != noErr)
return;
if (ExtAudioFileWrapAudioFileID(m_audioFileID, false, &m_extAudioFileRef) != noErr)
m_extAudioFileRef = nullptr;
}
AudioFileReader::~AudioFileReader()
{
if (m_extAudioFileRef)
ExtAudioFileDispose(m_extAudioFileRef);
m_extAudioFileRef = nullptr;
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);
}
if (actualCount)
*actualCount = bytesToRead;
return noErr;
}
SInt64 AudioFileReader::getSizeProc(void* clientData)
{
return static_cast<AudioFileReader*>(clientData)->dataSize();
}
RefPtr<AudioBus> AudioFileReader::createBus(float sampleRate, bool mixToMono)
{
if (!m_extAudioFileRef)
return nullptr;
UInt32 size = sizeof(m_fileDataFormat);
if (ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileDataFormat, &size, &m_fileDataFormat) != noErr)
return nullptr;
size_t numberOfChannels = m_fileDataFormat.mChannelsPerFrame;
SInt64 numberOfFrames64 = 0;
size = sizeof(numberOfFrames64);
if (ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileLengthFrames, &size, &numberOfFrames64) != noErr)
return nullptr;
double fileSampleRate = m_fileDataFormat.mSampleRate;
m_clientDataFormat = m_fileDataFormat;
const int bytesPerFloat = sizeof(Float32);
const int bitsPerByte = 8;
m_clientDataFormat.mFormatID = kAudioFormatLinearPCM;
m_clientDataFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
m_clientDataFormat.mBytesPerPacket = bytesPerFloat;
m_clientDataFormat.mFramesPerPacket = 1;
m_clientDataFormat.mBytesPerFrame = bytesPerFloat;
m_clientDataFormat.mChannelsPerFrame = numberOfChannels;
m_clientDataFormat.mBitsPerChannel = bitsPerByte * bytesPerFloat;
if (sampleRate)
m_clientDataFormat.mSampleRate = sampleRate;
if (ExtAudioFileSetProperty(m_extAudioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &m_clientDataFormat) != noErr)
return nullptr;
numberOfFrames64 = numberOfFrames64 * (m_clientDataFormat.mSampleRate / fileSampleRate);
size_t numberOfFrames = static_cast<size_t>(numberOfFrames64);
size_t busChannelCount = mixToMono ? 1 : numberOfChannels;
auto audioBus = AudioBus::create(busChannelCount, numberOfFrames);
audioBus->setSampleRate(m_clientDataFormat.mSampleRate);
AudioFloatArray leftChannel;
AudioFloatArray rightChannel;
AudioBufferList* bufferList = createAudioBufferList(numberOfChannels);
if (!bufferList)
return nullptr;
const size_t bufferSize = numberOfFrames * sizeof(float);
if (mixToMono && numberOfChannels == 2) {
leftChannel.allocate(numberOfFrames);
rightChannel.allocate(numberOfFrames);
bufferList->mBuffers[0].mNumberChannels = 1;
bufferList->mBuffers[0].mDataByteSize = bufferSize;
bufferList->mBuffers[0].mData = leftChannel.data();
bufferList->mBuffers[1].mNumberChannels = 1;
bufferList->mBuffers[1].mDataByteSize = bufferSize;
bufferList->mBuffers[1].mData = rightChannel.data();
} else {
ASSERT(!mixToMono || numberOfChannels == 1);
for (size_t i = 0; i < numberOfChannels; ++i) {
audioBus->channel(i)->zero();
bufferList->mBuffers[i].mNumberChannels = 1;
bufferList->mBuffers[i].mDataByteSize = bufferSize;
bufferList->mBuffers[i].mData = audioBus->channel(i)->mutableData();
}
}
UInt32 framesToRead = numberOfFrames;
if (ExtAudioFileRead(m_extAudioFileRef, &framesToRead, bufferList) != noErr) {
destroyAudioBufferList(bufferList);
return nullptr;
}
if (mixToMono && numberOfChannels == 2) {
float* destL = audioBus->channel(0)->mutableData();
for (size_t i = 0; i < numberOfFrames; ++i)
destL[i] = 0.5f * (leftChannel[i] + rightChannel[i]);
}
destroyAudioBufferList(bufferList);
return audioBus;
}
RefPtr<AudioBus> createBusFromAudioFile(const char* filePath, bool mixToMono, float sampleRate)
{
AudioFileReader reader(filePath);
return reader.createBus(sampleRate, mixToMono);
}
RefPtr<AudioBus> createBusFromInMemoryAudioFile(const void* data, size_t dataSize, bool mixToMono, float sampleRate)
{
AudioFileReader reader(data, dataSize);
return reader.createBus(sampleRate, mixToMono);
}
}
#endif // ENABLE(WEB_AUDIO) && PLATFORM(IOS_FAMILY)