Pārlūkot izejas kodu

Added WAV, Ogg Vorbis and FLAC file readers

BearishSun 9 gadi atpakaļ
vecāks
revīzija
6ca96399d6

+ 14 - 6
Build/VS2015/BansheeOpenAudio.vcxproj

@@ -124,7 +124,7 @@
     <ClCompile>
     <ClCompile>
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include/vorbis;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ExceptionHandling>false</ExceptionHandling>
       <ExceptionHandling>false</ExceptionHandling>
       <RuntimeTypeInfo>false</RuntimeTypeInfo>
       <RuntimeTypeInfo>false</RuntimeTypeInfo>
@@ -141,7 +141,7 @@
     <ClCompile>
     <ClCompile>
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include/vorbis;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ExceptionHandling>false</ExceptionHandling>
       <ExceptionHandling>false</ExceptionHandling>
       <RuntimeTypeInfo>false</RuntimeTypeInfo>
       <RuntimeTypeInfo>false</RuntimeTypeInfo>
@@ -161,7 +161,7 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <SDLCheck>false</SDLCheck>
       <SDLCheck>false</SDLCheck>
-      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include/vorbis;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <DebugInformationFormat>None</DebugInformationFormat>
       <DebugInformationFormat>None</DebugInformationFormat>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ExceptionHandling>false</ExceptionHandling>
       <ExceptionHandling>false</ExceptionHandling>
@@ -184,7 +184,7 @@
       <Optimization>MaxSpeed</Optimization>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include/vorbis;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ExceptionHandling>false</ExceptionHandling>
       <ExceptionHandling>false</ExceptionHandling>
@@ -208,7 +208,7 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <SDLCheck>false</SDLCheck>
       <SDLCheck>false</SDLCheck>
-      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include/vorbis;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <DebugInformationFormat>None</DebugInformationFormat>
       <DebugInformationFormat>None</DebugInformationFormat>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ExceptionHandling>false</ExceptionHandling>
       <ExceptionHandling>false</ExceptionHandling>
@@ -231,7 +231,7 @@
       <Optimization>MaxSpeed</Optimization>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Source/BansheeCore/Include;../../Source/BansheeUtility/Include;../../Source/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include;../../Dependencies/BansheeOpenAudio/Include/vorbis;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>BS_OA_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ExceptionHandling>false</ExceptionHandling>
       <ExceptionHandling>false</ExceptionHandling>
@@ -249,10 +249,18 @@
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemGroup>
   <ItemGroup>
+    <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAFLACReader.h" />
+    <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAImporter.h" />
+    <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAOggVorbisReader.h" />
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAPrerequisites.h" />
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAPrerequisites.h" />
+    <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAWaveReader.h" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
+    <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAFLACReader.cpp" />
+    <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAImporter.cpp" />
+    <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAOggVorbisReader.cpp" />
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAPlugin.cpp" />
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAPlugin.cpp" />
+    <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAWaveReader.cpp" />
   </ItemGroup>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   <ImportGroup Label="ExtensionTargets">

+ 24 - 0
Build/VS2015/BansheeOpenAudio.vcxproj.filters

@@ -14,10 +14,34 @@
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAPrerequisites.h">
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAPrerequisites.h">
       <Filter>Header Files</Filter>
       <Filter>Header Files</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAImporter.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAWaveReader.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAOggVorbisReader.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAFLACReader.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAPlugin.cpp">
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAPlugin.cpp">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAImporter.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAWaveReader.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAOggVorbisReader.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAFLACReader.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 3 - 3
Source/BansheeCore/Include/BsShaderIncludeImporter.h

@@ -22,13 +22,13 @@ namespace BansheeEngine
 		virtual ~ShaderIncludeImporter();
 		virtual ~ShaderIncludeImporter();
 
 
 		/** @copydoc SpecificImporter::isExtensionSupported */
 		/** @copydoc SpecificImporter::isExtensionSupported */
-		virtual bool isExtensionSupported(const WString& ext) const override;
+		bool isExtensionSupported(const WString& ext) const override;
 
 
 		/** @copydoc SpecificImporter::isMagicNumberSupported */
 		/** @copydoc SpecificImporter::isMagicNumberSupported */
-		virtual bool isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const override;
+		bool isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const override;
 
 
 		/** @copydoc SpecificImporter::import */
 		/** @copydoc SpecificImporter::import */
-		virtual SPtr<Resource> import(const Path& filePath, SPtr<const ImportOptions> importOptions) override;
+		SPtr<Resource> import(const Path& filePath, SPtr<const ImportOptions> importOptions) override;
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 45 - 0
Source/BansheeOpenAudio/Include/BsOAFLACReader.h

@@ -0,0 +1,45 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsOAPrerequisites.h"
+#include "FLAC\stream_decoder.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup OpenAudio
+	 *  @{
+	 */
+
+	struct FLACDecoderData
+	{
+		SPtr<DataStream> stream;
+		AudioFileInfo info;
+		UINT8* output = nullptr;
+		Vector<UINT8> overflow;
+		UINT32 overflowBytesPerSample = 0;
+		UINT32 samplesToRead = 0;
+		bool error = false;
+	};
+
+	/** Used for reading FLAC audio files. */
+	class OAFLACReader
+	{
+	public:
+		OAFLACReader();
+		~OAFLACReader();
+
+		bool open(const SPtr<DataStream>& stream, AudioFileInfo& info);
+		void seek(UINT32 offset); // Offset in number of samples
+		UINT32 read(UINT8* samples, UINT32 numSamples);
+
+		static bool isValid(const SPtr<DataStream>& stream);
+	private:
+		void close();
+
+		FLAC__StreamDecoder* mDecoder;
+		FLACDecoderData mData;
+	};
+
+	/** @} */
+}

+ 32 - 0
Source/BansheeOpenAudio/Include/BsOAImporter.h

@@ -0,0 +1,32 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsOAPrerequisites.h"
+#include "BsSpecificImporter.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup OpenAudio
+	 *  @{
+	 */
+
+	/** Importer using for importing WAV/FLAC/OGGVORBIS audio files. */
+	class BS_OA_EXPORT OAImporter : public SpecificImporter
+	{
+	public:
+		OAImporter();
+		virtual ~OAImporter();
+
+		/** @copydoc SpecificImporter::isExtensionSupported */
+		bool isExtensionSupported(const WString& ext) const override;
+
+		/** @copydoc SpecificImporter::isMagicNumberSupported */
+		bool isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const override;
+
+		/** @copydoc SpecificImporter::import */
+		SPtr<Resource> import(const Path& filePath, SPtr<const ImportOptions> importOptions) override;
+	};
+
+	/** @} */
+}

+ 33 - 0
Source/BansheeOpenAudio/Include/BsOAOggVorbisReader.h

@@ -0,0 +1,33 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsOAPrerequisites.h"
+#include "vorbis\vorbisfile.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup OpenAudio
+	 *  @{
+	 */
+
+	/** Used for reading Ogg Vorbis audio files. */
+	class OAOggVorbisReader
+	{
+	public:
+		OAOggVorbisReader();
+		~OAOggVorbisReader();
+
+		bool open(const SPtr<DataStream>& stream, AudioFileInfo& info);
+		void seek(UINT32 offset); // Offset in number of samples
+		UINT32 read(UINT8* samples, UINT32 numSamples);
+
+		static bool isValid(const SPtr<DataStream>& stream);
+	private:
+		SPtr<DataStream> mStream;
+		OggVorbis_File mOggVorbisFile;
+		UINT32 mChannelCount;
+	};
+
+	/** @} */
+}

+ 15 - 4
Source/BansheeOpenAudio/Include/BsOAPrerequisites.h

@@ -20,12 +20,23 @@
 #    define BS_OA_EXPORT
 #    define BS_OA_EXPORT
 #endif
 #endif
 
 
+namespace BansheeEngine
+{
+	/** Contains data describing an audio file. */
+	struct AudioFileInfo
+	{
+		UINT32 numSamples;
+		UINT32 sampleRate;
+		UINT32 numChannels;
+	};
+}
+
 /** @addtogroup Plugins
 /** @addtogroup Plugins
-*  @{
-*/
+ *  @{
+ */
 
 
 /** @defgroup OpenAudio BansheeOpenAudio
 /** @defgroup OpenAudio BansheeOpenAudio
-*	Open source audio implementation using OpenAL, libFLAC and libvorbis.
-*/
+ *	Open source audio implementation using OpenAL, libFLAC and libvorbis.
+ */
 
 
 /** @} */
 /** @} */

+ 35 - 0
Source/BansheeOpenAudio/Include/BsOAWaveReader.h

@@ -0,0 +1,35 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsOAPrerequisites.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup OpenAudio
+	 *  @{
+	 */
+
+	/** Used for reading .WAV audio files. */
+	class OAWaveReader
+	{
+	public:
+		OAWaveReader();
+
+		bool open(const SPtr<DataStream>& stream, AudioFileInfo& info);
+		void seek(UINT32 offset); // Offset in number of samples
+		UINT32 read(UINT8* samples, UINT32 numSamples);
+
+		static bool isValid(const SPtr<DataStream>& stream);
+	private:
+		bool parseHeader(AudioFileInfo& info);
+
+		SPtr<DataStream> mStream;
+		UINT32 mDataOffset;
+		UINT32 mBytesPerSample;
+
+		static const UINT32 MAIN_CHUNK_SIZE = 12;
+	};
+
+	/** @} */
+}

+ 254 - 0
Source/BansheeOpenAudio/Source/BsOAFLACReader.cpp

@@ -0,0 +1,254 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsOAFLACReader.h"
+#include "BsDataStream.h"
+
+namespace BansheeEngine
+{
+	FLAC__StreamDecoderReadStatus streamRead(const FLAC__StreamDecoder*, FLAC__byte buffer[], size_t* bytes, void* clientData)
+	{
+		FLACDecoderData* data = static_cast<FLACDecoderData*>(clientData);
+
+		INT64 count = (INT64)data->stream->read(buffer, *bytes);
+		if (count > 0)
+		{
+			*bytes = (size_t)count;
+			return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+		}
+		
+		if (count == 0)
+			return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+		
+		return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+	}
+
+	FLAC__StreamDecoderSeekStatus streamSeek(const FLAC__StreamDecoder*, FLAC__uint64 absoluteByteOffset, void* clientData)
+	{
+		FLACDecoderData* data = static_cast<FLACDecoderData*>(clientData);
+
+		data->stream->seek(absoluteByteOffset);
+		INT64 position = (INT64)data->stream->tell();
+		if (position >= 0)
+			return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+		else
+			return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+	}
+
+	FLAC__StreamDecoderTellStatus streamTell(const FLAC__StreamDecoder*, FLAC__uint64* absoluteByteOffset, void* clientData)
+	{
+		FLACDecoderData* data = static_cast<FLACDecoderData*>(clientData);
+
+		INT64 position = (INT64)data->stream->tell();
+		if (position >= 0)
+		{
+			*absoluteByteOffset = position;
+			return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+		}
+		else
+		{
+			return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
+		}
+	}
+
+	FLAC__StreamDecoderLengthStatus streamLength(const FLAC__StreamDecoder*, FLAC__uint64* streamLength, void* clientData)
+	{
+		FLACDecoderData* data = static_cast<FLACDecoderData*>(clientData);
+
+		*streamLength = data->stream->size();
+		return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+	}
+
+	FLAC__bool streamEof(const FLAC__StreamDecoder*, void* clientData)
+	{
+		FLACDecoderData* data = static_cast<FLACDecoderData*>(clientData);
+
+		return data->stream->eof();
+	}
+
+	FLAC__StreamDecoderWriteStatus streamWrite(const FLAC__StreamDecoder*, const FLAC__Frame* frame, const FLAC__int32* const buffer[], void* clientData)
+	{
+		FLACDecoderData* data = static_cast<FLACDecoderData*>(clientData);
+
+		if (!data->output) // Seek
+			return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+
+		UINT32 bytesPerSample = frame->header.bits_per_sample / 8;
+		
+		// If we received more data than we need, store it in the overflow buffer
+		UINT32 frameSamples = frame->header.blocksize * frame->header.channels;
+		if (data->samplesToRead < frameSamples)
+		{
+			UINT32 numExtraSamples = frameSamples - data->samplesToRead;
+			UINT32 extraBytes = numExtraSamples * bytesPerSample;
+			data->overflow.reserve(extraBytes);
+
+			if (data->overflowBytesPerSample != 0)
+			{
+				if (data->overflow.size() > 0) // Overflow already stores data but of a different size
+				{
+					assert(data->overflowBytesPerSample == bytesPerSample);
+				}
+			}
+			else
+				data->overflowBytesPerSample = bytesPerSample;
+		}
+
+		assert(bytesPerSample <= 4);
+		for (UINT32 i = 0; i < frame->header.blocksize; i++)
+		{
+			for (UINT32 j = 0; j < frame->header.channels; j++)
+			{
+				if (data->samplesToRead > 0)
+				{
+					memcpy(data->output, &buffer[j][i], bytesPerSample);
+					data->output += bytesPerSample;
+
+					data->samplesToRead--;
+				}
+				else
+				{
+					UINT8 sample[4];
+					memcpy(sample, &buffer[j][i], bytesPerSample);
+
+					for(UINT32 k = 0; k < bytesPerSample; k++)
+						data->overflow.push_back(sample[k]);
+				}
+			}
+		}
+
+		return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+	}
+
+	void streamMetadata(const FLAC__StreamDecoder*, const FLAC__StreamMetadata* meta, void* clientData)
+	{
+		FLACDecoderData* data = static_cast<FLACDecoderData*>(clientData);
+
+		if (meta->type == FLAC__METADATA_TYPE_STREAMINFO)
+		{
+			data->info.numSamples = meta->data.stream_info.total_samples * meta->data.stream_info.channels;
+			data->info.sampleRate = meta->data.stream_info.sample_rate;
+			data->info.numChannels = meta->data.stream_info.channels;
+		}
+	}
+
+	void streamError(const FLAC__StreamDecoder*, FLAC__StreamDecoderErrorStatus, void* clientData)
+	{
+		FLACDecoderData* data = static_cast<FLACDecoderData*>(clientData);
+		data->error = true;
+	}
+
+	OAFLACReader::OAFLACReader()
+		:mDecoder(nullptr)
+	{ }
+
+	OAFLACReader::~OAFLACReader()
+	{
+		close();
+	}
+
+	bool OAFLACReader::isValid(const SPtr<DataStream>& stream)
+	{
+		stream->seek(0);
+
+		FLAC__StreamDecoder* decoder = FLAC__stream_decoder_new();
+		if (!decoder)
+			return false;
+
+		FLACDecoderData data;
+		data.stream = stream;
+		FLAC__stream_decoder_init_stream(decoder, &streamRead, &streamSeek, &streamTell, &streamLength, &streamEof, 
+			&streamWrite, NULL, &streamError, &data);
+
+		bool valid = FLAC__stream_decoder_process_until_end_of_metadata(decoder) != 0;
+
+		FLAC__stream_decoder_finish(decoder);
+		FLAC__stream_decoder_delete(decoder);
+
+		return valid && !data.error;
+	}
+
+	bool OAFLACReader::open(const SPtr<DataStream>& stream, AudioFileInfo& info)
+	{
+		if (stream == nullptr)
+			return false;
+
+		mDecoder = FLAC__stream_decoder_new();
+		if (mDecoder == nullptr)
+		{
+			LOGERR("Failed to open a FLAC file.");
+			return false;
+		}
+
+		mData.stream = stream;
+		FLAC__stream_decoder_init_stream(mDecoder, &streamRead, &streamSeek, &streamTell, &streamLength, &streamEof,
+			&streamWrite, &streamMetadata, &streamError, &mData);
+
+
+		if (!FLAC__stream_decoder_process_until_end_of_metadata(mDecoder))
+		{
+			close();
+			LOGERR("Failed to open a FLAC file.");
+			return false;
+		}
+
+		info = mData.info;
+
+		return true;
+	}
+
+	void OAFLACReader::seek(UINT32 offset)
+	{
+		mData.output = nullptr;
+		mData.samplesToRead = 0;
+		mData.overflow.clear();
+
+		FLAC__stream_decoder_seek_absolute(mDecoder, offset);
+	}
+
+	UINT32 OAFLACReader::read(UINT8* samples, UINT32 numSamples)
+	{
+		UINT64 overflowSize = mData.overflow.size();
+		UINT64 overflowNumSamples = 0;
+		
+		if (overflowSize > 0)
+		{
+			UINT32 sampleSize = numSamples * mData.overflowBytesPerSample;
+			if (overflowSize > sampleSize)
+			{
+				std::copy(mData.overflow.begin(), mData.overflow.begin() + sampleSize, samples);
+				mData.overflow.erase(mData.overflow.begin(), mData.overflow.begin() + sampleSize);
+
+				return numSamples;
+			}
+			else
+				std::copy(mData.overflow.begin(), mData.overflow.end(), samples);
+
+			overflowNumSamples = overflowSize / mData.overflowBytesPerSample;
+		}
+
+		mData.output = samples + overflowSize;
+		mData.samplesToRead = numSamples - overflowNumSamples;
+		mData.overflow.clear();
+
+		while (mData.samplesToRead > 0)
+		{
+			if (!FLAC__stream_decoder_process_single(mDecoder))
+				break;
+
+			if (FLAC__stream_decoder_get_state(mDecoder) == FLAC__STREAM_DECODER_END_OF_STREAM)
+				break;
+		}
+
+		return numSamples - mData.samplesToRead;
+	}
+
+	void OAFLACReader::close()
+	{
+		if (mDecoder != nullptr)
+		{
+			FLAC__stream_decoder_finish(mDecoder);
+			FLAC__stream_decoder_delete(mDecoder);
+			mDecoder = nullptr;
+		}
+	}
+}

+ 41 - 0
Source/BansheeOpenAudio/Source/BsOAImporter.cpp

@@ -0,0 +1,41 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsOAImporter.h"
+#include "BsDataStream.h"
+#include "BsFileSystem.h"
+
+namespace BansheeEngine
+{
+	OAImporter::OAImporter()
+		:SpecificImporter()
+	{
+
+	}
+
+	OAImporter::~OAImporter()
+	{
+
+	}
+
+	bool OAImporter::isExtensionSupported(const WString& ext) const
+	{
+		WString lowerCaseExt = ext;
+		StringUtil::toLowerCase(lowerCaseExt);
+
+		// TODO
+		return false;
+	}
+
+	bool OAImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const
+	{
+		// TODO
+		return true; // Plain-text so I don't even check for magic number
+	}
+
+	SPtr<Resource> OAImporter::import(const Path& filePath, SPtr<const ImportOptions> importOptions)
+	{
+		SPtr<DataStream> stream = FileSystem::openFile(filePath);
+
+		return nullptr;
+	}
+}

+ 112 - 0
Source/BansheeOpenAudio/Source/BsOAOggVorbisReader.cpp

@@ -0,0 +1,112 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsOAOggVorbisReader.h"
+#include "BsDataStream.h"
+#include <vorbis\codec.h>
+
+namespace BansheeEngine
+{
+	size_t oggRead(void* ptr, size_t size, size_t nmemb, void* data)
+	{
+		DataStream* stream = static_cast<DataStream*>(data);
+		return static_cast<std::size_t>(stream->read(ptr, size * nmemb));
+	}
+
+	int oggSeek(void* data, ogg_int64_t offset, int whence)
+	{
+		DataStream* stream = static_cast<DataStream*>(data);
+		switch (whence)
+		{
+		case SEEK_SET:
+			break;
+		case SEEK_CUR:
+			offset += stream->tell();
+			break;
+		case SEEK_END:
+			offset = stream->size() - offset;
+		}
+
+		stream->seek(offset);
+		return (int)stream->tell();
+	}
+
+	long oggTell(void* data)
+	{
+		DataStream* stream = static_cast<DataStream*>(data);
+		return static_cast<long>(stream->tell());
+	}
+
+	static ov_callbacks callbacks = { &oggRead, &oggSeek, nullptr, &oggTell };
+
+	OAOggVorbisReader::OAOggVorbisReader()
+		:mChannelCount(0)
+	{ }
+
+	OAOggVorbisReader::~OAOggVorbisReader()
+	{
+		if (mOggVorbisFile.datasource != nullptr)
+			ov_clear(&mOggVorbisFile);
+	}
+
+	bool OAOggVorbisReader::isValid(const SPtr<DataStream>& stream)
+	{
+		stream->seek(0);
+
+		OggVorbis_File file;
+		if (ov_test_callbacks(stream.get(), &file, nullptr, 0, callbacks) == 0)
+		{
+			ov_clear(&file);
+			return true;
+		}
+			
+		return false;
+	}
+
+	bool OAOggVorbisReader::open(const SPtr<DataStream>& stream, AudioFileInfo& info)
+	{
+		if (stream == nullptr)
+			return false;
+
+		mStream = stream;
+
+		int status = ov_open_callbacks(stream.get(), &mOggVorbisFile, nullptr, 0, callbacks);
+		if (status < 0)
+		{
+			LOGERR("Failed to open Ogg Vorbis file.");
+			return false;
+		}
+
+		vorbis_info* vorbisInfo = ov_info(&mOggVorbisFile, -1);
+		info.numChannels = vorbisInfo->channels;
+		info.sampleRate = vorbisInfo->rate;
+		info.numSamples = (UINT32)(ov_pcm_total(&mOggVorbisFile, -1) * vorbisInfo->channels);
+
+		mChannelCount = info.numChannels;
+		return true;
+	}
+
+	void OAOggVorbisReader::seek(UINT32 offset)
+	{
+		ov_pcm_seek(&mOggVorbisFile, offset / mChannelCount);
+	}
+
+	UINT32 OAOggVorbisReader::read(UINT8* samples, UINT32 numSamples)
+	{
+		UINT32 numReadSamples = 0;
+		while (numReadSamples < numSamples)
+		{
+			INT32 bytesToRead = (INT32)(numSamples - numReadSamples) * sizeof(INT16);
+			UINT32 bytesRead = ov_read(&mOggVorbisFile, (char*)samples, bytesToRead, 0, 2, 1, nullptr);
+			if (bytesRead > 0)
+			{
+				UINT32 samplesRead = bytesRead / sizeof(INT16);
+				numReadSamples += samplesRead;
+				samples += samplesRead;
+			}
+			else
+				break;
+		}
+
+		return numReadSamples;
+	}
+}

+ 133 - 0
Source/BansheeOpenAudio/Source/BsOAWaveReader.cpp

@@ -0,0 +1,133 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsOAWaveReader.h"
+#include "BsDataStream.h"
+
+namespace BansheeEngine
+{
+	OAWaveReader::OAWaveReader()
+		:mDataOffset(0), mBytesPerSample(0)
+	{ }
+
+	bool OAWaveReader::isValid(const SPtr<DataStream>& stream)
+	{
+		stream->seek(0);
+
+		INT8 header[MAIN_CHUNK_SIZE];
+		if (stream->read(header, sizeof(header)) < (sizeof(header)))
+			return false;
+
+		return (header[0] == 'R') && (header[1] == 'I') && (header[2] == 'F') && (header[3] == 'F')
+			&& (header[8] == 'W') && (header[9] == 'A') && (header[10] == 'V') && (header[11] == 'E');
+	}
+
+	bool OAWaveReader::open(const SPtr<DataStream>& stream, AudioFileInfo& info)
+	{
+		if (stream == nullptr)
+			return false;
+
+		mStream = stream;
+		mStream->seek(MAIN_CHUNK_SIZE);
+
+		if (!parseHeader(info))
+		{
+			LOGERR("Provided file is not a valid WAVE file.");
+			return false;
+		}
+
+		return true;
+	}
+
+	void OAWaveReader::seek(UINT32 offset)
+	{
+		mStream->seek(mDataOffset + offset * mBytesPerSample);
+	}
+
+	UINT32 OAWaveReader::read(UINT8* samples, UINT32 numSamples)
+	{
+		return (UINT32)mStream->read(samples, numSamples * mBytesPerSample);
+	}
+
+	bool OAWaveReader::parseHeader(AudioFileInfo& info)
+	{
+		bool foundData = false;
+		while (!foundData)
+		{
+			// Get sub-chunk ID and size
+			UINT8 subChunkId[4];
+			if (mStream->read(subChunkId, sizeof(subChunkId)) != sizeof(subChunkId))
+				return false;
+
+			UINT32 subChunkSize = 0;
+			if (mStream->read(&subChunkSize, sizeof(subChunkSize) != sizeof(subChunkSize)))
+				return false;
+
+			// FMT chunk
+			if (subChunkId[0] == 'f' && subChunkId[1] == 'm' && subChunkId[2] == 't' && subChunkId[3] == ' ')
+			{
+				UINT16 format = 0;
+				if (mStream->read(&format, sizeof(format) != sizeof(format)))
+					return false;
+
+				if (format != 1) // It's not PCM
+					return false;
+
+				UINT16 numChannels = 0;
+				if (mStream->read(&numChannels, sizeof(numChannels) != sizeof(numChannels)))
+					return false;
+
+				UINT32 sampleRate = 0;
+				if (mStream->read(&sampleRate, sizeof(sampleRate) != sizeof(sampleRate)))
+					return false;
+
+				UINT32 byteRate = 0;
+				if (mStream->read(&byteRate, sizeof(byteRate) != sizeof(byteRate)))
+					return false;
+
+				UINT16 blockAlign = 0;
+				if (mStream->read(&blockAlign, sizeof(blockAlign) != sizeof(blockAlign)))
+					return false;
+
+				UINT16 bitDepth = 0;
+				if (mStream->read(&bitDepth, sizeof(bitDepth) != sizeof(bitDepth)))
+					return false;
+
+				info.numChannels = numChannels;
+				info.sampleRate = sampleRate;
+
+				if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32)
+				{
+					LOGERR("Unsupported number of bits per sample: " + toString(bitDepth));
+					return false;
+				}
+
+				mBytesPerSample = bitDepth / 8;
+
+				// Skip extra data, if any
+				if (subChunkSize > 16)
+				{
+					mStream->skip(subChunkSize - 16);
+					if (mStream->eof())
+						return false;
+				}
+			}
+			// DATA chunk
+			else if (subChunkId[0] == 'd' && subChunkId[1] == 'a' && subChunkId[2] == 't' && subChunkId[3] == 'a')
+			{
+				info.numSamples = subChunkSize / mBytesPerSample;
+				mDataOffset = (UINT32)mStream->tell();
+
+				foundData = true;
+			}
+			// Unsupported chunk type
+			else
+			{
+				mStream->skip(subChunkSize);
+				if (mStream->eof())
+					return false;
+			}
+		}
+
+		return true;
+	}
+}