diff --git a/platform/macosx/love-framework.xcodeproj/project.pbxproj b/platform/macosx/love-framework.xcodeproj/project.pbxproj index ee16186cf..46d51da64 100644 --- a/platform/macosx/love-framework.xcodeproj/project.pbxproj +++ b/platform/macosx/love-framework.xcodeproj/project.pbxproj @@ -321,6 +321,8 @@ FAD5970C196E174000E8EA36 /* wrap_Text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAD5970B196E174000E8EA36 /* wrap_Text.cpp */; }; FAD8E84F1A7C949B0084E24D /* Vera.ttf.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD8E84E1A7C949B0084E24D /* Vera.ttf.h */; }; FADD58DD18C30367005FC3BF /* FormatHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADD58DC18C30367005FC3BF /* FormatHandler.cpp */; }; + FADDCDAD1A91817800B32118 /* CoreAudioDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADDCDAB1A91817800B32118 /* CoreAudioDecoder.cpp */; }; + FADDCDAE1A91817800B32118 /* CoreAudioDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = FADDCDAC1A91817800B32118 /* CoreAudioDecoder.h */; }; FADE620919368D5C00C25B97 /* STBHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADE620719368D5C00C25B97 /* STBHandler.cpp */; }; FADE620A19368D5C00C25B97 /* STBHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FADE620819368D5C00C25B97 /* STBHandler.h */; }; FAE010DB170DDE99006F29D0 /* ddsinfo.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE010D8170DDE99006F29D0 /* ddsinfo.h */; }; @@ -913,6 +915,8 @@ FAD5970D196E17C100E8EA36 /* wrap_Text.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wrap_Text.h; sourceTree = ""; }; FAD8E84E1A7C949B0084E24D /* Vera.ttf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Vera.ttf.h; sourceTree = ""; }; FADD58DC18C30367005FC3BF /* FormatHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FormatHandler.cpp; sourceTree = ""; }; + FADDCDAB1A91817800B32118 /* CoreAudioDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioDecoder.cpp; sourceTree = ""; }; + FADDCDAC1A91817800B32118 /* CoreAudioDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioDecoder.h; sourceTree = ""; }; FADE620719368D5C00C25B97 /* STBHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = STBHandler.cpp; sourceTree = ""; }; FADE620819368D5C00C25B97 /* STBHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STBHandler.h; sourceTree = ""; }; FAE010D8170DDE99006F29D0 /* ddsinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ddsinfo.h; sourceTree = ""; }; @@ -1444,6 +1448,8 @@ 47217E6729AC72E74FF651E3 /* lullaby */ = { isa = PBXGroup; children = ( + FADDCDAB1A91817800B32118 /* CoreAudioDecoder.cpp */, + FADDCDAC1A91817800B32118 /* CoreAudioDecoder.h */, 4E15567759041CC379292BE6 /* Decoder.cpp */, 733758E8028B20BB799A7BE6 /* Decoder.h */, 4A774BC26B7235E410D40C8E /* FLACDecoder.cpp */, @@ -2105,6 +2111,7 @@ FA0A4BF9182E260200E1E4D2 /* wrap_MotorJoint.h in Headers */, FA6F557D1A6E28A200062204 /* CompressedFormatHandler.h in Headers */, FAF272AD16E3D44400CC193A /* wrap_LuaThread.h in Headers */, + FADDCDAE1A91817800B32118 /* CoreAudioDecoder.h in Headers */, FAF272AF16E3D44400CC193A /* wrap_ThreadModule.h in Headers */, FAF272B616E3D46400CC193A /* Thread.h in Headers */, FAF272B816E3D46400CC193A /* threads.h in Headers */, @@ -2336,6 +2343,7 @@ FA08F60B16C753DB00F007B5 /* wrap_FileData.cpp in Sources */, FA08F60C16C753DB00F007B5 /* wrap_Filesystem.cpp in Sources */, FA08F60D16C753E700F007B5 /* Font.cpp in Sources */, + FADDCDAD1A91817800B32118 /* CoreAudioDecoder.cpp in Sources */, FA08F60E16C753E700F007B5 /* TrueTypeRasterizer.cpp in Sources */, FA08F60F16C753E700F007B5 /* wrap_Font.cpp in Sources */, FA08F61016C753E700F007B5 /* GlyphData.cpp in Sources */, diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.cpp b/src/modules/sound/lullaby/CoreAudioDecoder.cpp new file mode 100644 index 000000000..5b71d44b7 --- /dev/null +++ b/src/modules/sound/lullaby/CoreAudioDecoder.cpp @@ -0,0 +1,274 @@ +/** + * Copyright (c) 2006-2015 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifdef LOVE_SUPPORT_CORE_AUDIO + +// LOVE +#include "CoreAudioDecoder.h" + +// C++ +#include + +namespace love +{ +namespace sound +{ +namespace lullaby +{ + +// Callbacks +namespace +{ +OSStatus readFunc(void *inClientData, SInt64 inPosition, UInt32 requestCount, void *buffer, UInt32 *actualCount) +{ + Data *data = (Data *) inClientData; + SInt64 bytesLeft = data->getSize() - inPosition; + + if (bytesLeft > 0) + { + UInt32 actualSize = bytesLeft >= requestCount ? requestCount : (UInt32) bytesLeft; + memcpy(buffer, (char *) data->getData() + inPosition, actualSize); + *actualCount = actualSize; + } + else + { + *actualCount = 0; + return kAudioFileUnspecifiedError; + } + + return noErr; +} + +SInt64 getSizeFunc(void *inClientData) +{ + Data *data = (Data *) inClientData; + return data->getSize(); +} +} // anonymous namespace + +CoreAudioDecoder::CoreAudioDecoder(Data *data, const std::string &ext, int bufferSize) + : Decoder(data, ext, bufferSize) + , audioFile(nullptr) + , extAudioFile(nullptr) + , inputInfo() + , outputInfo() +{ + try + { + OSStatus err = noErr; + + // Open the file represented by the Data. + err = AudioFileOpenWithCallbacks(data, readFunc, nullptr, getSizeFunc, nullptr, kAudioFileMP3Type, &audioFile); + if (err != noErr) + throw love::Exception("Could open audio file for decoding."); + + // We want to use the Extended AudioFile API. + err = ExtAudioFileWrapAudioFileID(audioFile, false, &extAudioFile); + + if (err != noErr) + throw love::Exception("Could open audio file for decoding."); + + // Get the format of the audio data. + UInt32 propertySize = sizeof(inputInfo); + err = ExtAudioFileGetProperty(extAudioFile, kExtAudioFileProperty_FileDataFormat, &propertySize, &inputInfo); + + if (err != noErr) + throw love::Exception("Could not determine file format."); + + // Set the output format to 16 bit signed integer (native-endian) data. + // Keep the channel count and sample rate of the source format. + outputInfo.mSampleRate = inputInfo.mSampleRate; + outputInfo.mChannelsPerFrame = inputInfo.mChannelsPerFrame; + + int bytes = (inputInfo.mBitsPerChannel == 8) ? 1 : 2; + + outputInfo.mFormatID = kAudioFormatLinearPCM; + outputInfo.mBitsPerChannel = bytes * 8; + outputInfo.mBytesPerFrame = bytes * outputInfo.mChannelsPerFrame; + outputInfo.mFramesPerPacket = 1; + outputInfo.mBytesPerPacket = bytes * outputInfo.mChannelsPerFrame; + outputInfo.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; + + // unsigned 8-bit or signed 16-bit integer PCM data. + if (outputInfo.mBitsPerChannel == 16) + outputInfo.mFormatFlags |= kAudioFormatFlagIsSignedInteger; + + // Set the desired output format. + propertySize = sizeof(outputInfo); + err = ExtAudioFileSetProperty(extAudioFile, kExtAudioFileProperty_ClientDataFormat, propertySize, &outputInfo); + + if (err != noErr) + throw love::Exception("Could not set decoder properties."); + } + catch (love::Exception &) + { + closeAudioFile(); + throw; + } + + sampleRate = (int) outputInfo.mSampleRate; +} + +CoreAudioDecoder::~CoreAudioDecoder() +{ + closeAudioFile(); +} + +void CoreAudioDecoder::closeAudioFile() +{ + if (extAudioFile != nullptr) + ExtAudioFileDispose(extAudioFile); + else if (audioFile != nullptr) + AudioFileClose(audioFile); + + extAudioFile = nullptr; + audioFile = nullptr; +} + +bool CoreAudioDecoder::accepts(const std::string &ext) +{ + UInt32 size = 0; + std::vector types; + + // Get the size in bytes of the type array we're about to get. + OSStatus err = AudioFileGetGlobalInfoSize(kAudioFileGlobalInfo_ReadableTypes, sizeof(UInt32), nullptr, &size); + if (err != noErr) + return false; + + types.resize(size / sizeof(UInt32)); + + // Get an array of supported types. + err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ReadableTypes, 0, nullptr, &size, &types[0]); + if (err != noErr) + return false; + + // Turn the extension string into a CFStringRef. + CFStringRef extstr = CFStringCreateWithCString(nullptr, ext.c_str(), kCFStringEncodingUTF8); + + CFArrayRef exts = nullptr; + size = sizeof(CFArrayRef); + + for (UInt32 type : types) + { + // Get the extension strings for the type. + err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ExtensionsForType, sizeof(UInt32), &type, &size, &exts); + if (err != noErr) + continue; + + // A type can have more than one extension string. + for (CFIndex i = 0; i < CFArrayGetCount(exts); i++) + { + CFStringRef value = (CFStringRef) CFArrayGetValueAtIndex(exts, i); + + if (CFStringCompare(extstr, value, 0) == kCFCompareEqualTo) + { + CFRelease(extstr); + CFRelease(exts); + return true; + } + } + + CFRelease(exts); + } + + CFRelease(extstr); + return false; +} + +love::sound::Decoder *CoreAudioDecoder::clone() +{ + return new CoreAudioDecoder(data.get(), ext, bufferSize); +} + +int CoreAudioDecoder::decode() +{ + int size = 0; + + while (size < bufferSize) + { + AudioBufferList dataBuffer; + dataBuffer.mNumberBuffers = 1; + dataBuffer.mBuffers[0].mDataByteSize = bufferSize - size; + dataBuffer.mBuffers[0].mData = (char *) buffer + size; + dataBuffer.mBuffers[0].mNumberChannels = outputInfo.mChannelsPerFrame; + + UInt32 frames = (bufferSize - size) / outputInfo.mBytesPerFrame; + + if (ExtAudioFileRead(extAudioFile, &frames, &dataBuffer) != noErr) + return size; + + if (frames == 0) + { + eof = true; + break; + } + + size += frames * outputInfo.mBytesPerFrame; + } + + return size; +} + +bool CoreAudioDecoder::seek(float s) +{ + OSStatus err = ExtAudioFileSeek(extAudioFile, (SInt64) (s * inputInfo.mSampleRate)); + + if (err == noErr) + { + eof = false; + return true; + } + + return false; +} + +bool CoreAudioDecoder::rewind() +{ + OSStatus err = ExtAudioFileSeek(extAudioFile, 0); + + if (err == noErr) + { + eof = false; + return true; + } + + return false; +} + +bool CoreAudioDecoder::isSeekable() +{ + return true; +} + +int CoreAudioDecoder::getChannels() const +{ + return outputInfo.mChannelsPerFrame; +} + +int CoreAudioDecoder::getBitDepth() const +{ + return outputInfo.mBitsPerChannel; +} + +} // lullaby +} // sound +} // love + +#endif // LOVE_SUPPORT_CORE_AUDIO diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.h b/src/modules/sound/lullaby/CoreAudioDecoder.h new file mode 100644 index 000000000..58de380bd --- /dev/null +++ b/src/modules/sound/lullaby/CoreAudioDecoder.h @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2006-2015 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_SOUND_LULLABY_CORE_AUDIO_DECODER_H +#define LOVE_SOUND_LULLABY_CORE_AUDIO_DECODER_H + +#ifdef LOVE_SUPPORT_CORE_AUDIO + +// LOVE +#include "common/Data.h" +#include "Decoder.h" + +// Core Audio +#include +#include + +namespace love +{ +namespace sound +{ +namespace lullaby +{ + +/** + * Decoder which supports all formats handled by Apple's Core Audio framework. + **/ +class CoreAudioDecoder : public Decoder +{ +public: + + CoreAudioDecoder(Data *data, const std::string &ext, int bufferSize); + virtual ~CoreAudioDecoder(); + + static bool accepts(const std::string &ext); + + love::sound::Decoder *clone(); + int decode(); + bool seek(float s); + bool rewind(); + bool isSeekable(); + int getChannels() const; + int getBitDepth() const; + +private: + + void closeAudioFile(); + + AudioFileID audioFile; + ExtAudioFileRef extAudioFile; + + AudioStreamBasicDescription inputInfo; + AudioStreamBasicDescription outputInfo; + +}; // CoreAudioDecoder + +} // lullaby +} // sound +} // love + +#endif // LOVE_SUPPORT_CORE_AUDIO + +#endif // LOVE_SOUND_LULLABY_CORE_AUDIO_DECODER_H diff --git a/src/modules/sound/lullaby/Sound.cpp b/src/modules/sound/lullaby/Sound.cpp index 54a0a61f6..0bb749477 100644 --- a/src/modules/sound/lullaby/Sound.cpp +++ b/src/modules/sound/lullaby/Sound.cpp @@ -34,6 +34,10 @@ # include "Mpg123Decoder.h" #endif // LOVE_NOMPG123 +#ifdef LOVE_SUPPORT_CORE_AUDIO +# include "CoreAudioDecoder.h" +#endif + namespace love { namespace sound @@ -77,6 +81,10 @@ sound::Decoder *Sound::newDecoder(love::filesystem::FileData *data, int bufferSi else if (GmeDecoder::accepts(ext)) decoder = new GmeDecoder(data, ext, bufferSize); #endif // LOVE_SUPPORT_GME +#ifdef LOVE_SUPPORT_CORE_AUDIO + else if (CoreAudioDecoder::accepts(ext)) + decoder = new CoreAudioDecoder(data, ext, bufferSize); +#endif else if (WaveDecoder::accepts(ext)) decoder = new WaveDecoder(data, ext, bufferSize); /*else if (FLACDecoder::accepts(ext))