Browse Source

Added a Sound Decoder using Apple's CoreAudio framework.

--HG--
branch : minor
Alex Szpakowski 10 years ago
parent
commit
976deb0b74

+ 8 - 0
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 = "<group>"; };
 		FAD8E84E1A7C949B0084E24D /* Vera.ttf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Vera.ttf.h; sourceTree = "<group>"; };
 		FADD58DC18C30367005FC3BF /* FormatHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FormatHandler.cpp; sourceTree = "<group>"; };
+		FADDCDAB1A91817800B32118 /* CoreAudioDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioDecoder.cpp; sourceTree = "<group>"; };
+		FADDCDAC1A91817800B32118 /* CoreAudioDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioDecoder.h; sourceTree = "<group>"; };
 		FADE620719368D5C00C25B97 /* STBHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = STBHandler.cpp; sourceTree = "<group>"; };
 		FADE620819368D5C00C25B97 /* STBHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STBHandler.h; sourceTree = "<group>"; };
 		FAE010D8170DDE99006F29D0 /* ddsinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ddsinfo.h; sourceTree = "<group>"; };
@@ -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 */,

+ 274 - 0
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 <vector>
+
+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<UInt32> 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

+ 79 - 0
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 <AudioToolbox/AudioFormat.h>
+#include <AudioToolbox/ExtendedAudioFile.h>
+
+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

+ 8 - 0
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))