Browse Source

Added Ogg Vorbis encoder

BearishSun 9 years ago
parent
commit
d162629377

+ 8 - 6
Build/VS2015/BansheeOpenAudio.vcxproj

@@ -134,7 +134,7 @@
       <ImportLibrary>..\..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <AdditionalLibraryDirectories>../../lib/x86/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x86/Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>../../lib/x86/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x86/Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;libogg.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -151,7 +151,7 @@
       <ImportLibrary>..\..\lib\$(Platform)\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\..\lib\$(Platform)\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <AdditionalLibraryDirectories>../../lib/$(Platform)/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x64/Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>../../lib/$(Platform)/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x64/Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;libogg.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -175,7 +175,7 @@
       <ImportLibrary>..\..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <AdditionalLibraryDirectories>../../lib/x86/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x86/Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>../../lib/x86/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x86/Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <GenerateDebugInformation>No</GenerateDebugInformation>
       <GenerateDebugInformation>No</GenerateDebugInformation>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;libogg.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='OptimizedDebug|Win32'">
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='OptimizedDebug|Win32'">
@@ -198,7 +198,7 @@
       <ImportLibrary>..\..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <AdditionalLibraryDirectories>../../lib/x86/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x86/OptimizedDebug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>../../lib/x86/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x86/OptimizedDebug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;libogg.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -222,7 +222,7 @@
       <ImportLibrary>..\..\lib\$(Platform)\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\..\lib\$(Platform)\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <AdditionalLibraryDirectories>../../lib/$(Platform)/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x64/Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>../../lib/$(Platform)/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x64/Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <GenerateDebugInformation>No</GenerateDebugInformation>
       <GenerateDebugInformation>No</GenerateDebugInformation>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;libogg.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='OptimizedDebug|x64'">
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='OptimizedDebug|x64'">
@@ -245,7 +245,7 @@
       <ImportLibrary>..\..\lib\$(Platform)\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\..\lib\$(Platform)\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <AdditionalLibraryDirectories>../../lib/$(Platform)/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x64/OptimizedDebug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>../../lib/$(Platform)/$(Configuration);../../Dependencies/BansheeOpenAudio/lib/x64/OptimizedDebug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;libFLAC.lib;libvorbis.lib;libvorbisfile.lib;libogg.lib;OpenAL32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemGroup>
   <ItemGroup>
@@ -253,6 +253,7 @@
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAFLACReader.h" />
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAFLACReader.h" />
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAImporter.h" />
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAImporter.h" />
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAOggVorbisReader.h" />
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAOggVorbisReader.h" />
+    <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAOggVorbisWriter.h" />
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAPrerequisites.h" />
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAPrerequisites.h" />
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAWaveReader.h" />
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAWaveReader.h" />
   </ItemGroup>
   </ItemGroup>
@@ -260,6 +261,7 @@
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAFLACReader.cpp" />
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAFLACReader.cpp" />
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAImporter.cpp" />
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAImporter.cpp" />
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAOggVorbisReader.cpp" />
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAOggVorbisReader.cpp" />
+    <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAOggVorbisWriter.cpp" />
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAPlugin.cpp" />
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAPlugin.cpp" />
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAWaveReader.cpp" />
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAWaveReader.cpp" />
   </ItemGroup>
   </ItemGroup>

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

@@ -29,6 +29,9 @@
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAFileReader.h">
     <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAFileReader.h">
       <Filter>Header Files</Filter>
       <Filter>Header Files</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="..\..\Source\BansheeOpenAudio\Include\BsOAOggVorbisWriter.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAPlugin.cpp">
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAPlugin.cpp">
@@ -46,5 +49,8 @@
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAFLACReader.cpp">
     <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAFLACReader.cpp">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="..\..\Source\BansheeOpenAudio\Source\BsOAOggVorbisWriter.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 1 - 1
Source/BansheeOpenAudio/Include/BsOAFLACReader.h

@@ -23,7 +23,7 @@ namespace BansheeEngine
 		bool error = false;
 		bool error = false;
 	};
 	};
 
 
-	/** Used for reading FLAC audio files. */
+	/** Used for reading FLAC audio data. */
 	class OAFLACReader : public OAFileReader
 	class OAFLACReader : public OAFileReader
 	{
 	{
 	public:
 	public:

+ 1 - 1
Source/BansheeOpenAudio/Include/BsOAOggVorbisReader.h

@@ -12,7 +12,7 @@ namespace BansheeEngine
 	 *  @{
 	 *  @{
 	 */
 	 */
 
 
-	/** Used for reading Ogg Vorbis audio files. */
+	/** Used for reading Ogg Vorbis audio data. */
 	class OAOggVorbisReader : public OAFileReader
 	class OAOggVorbisReader : public OAFileReader
 	{
 	{
 	public:
 	public:

+ 42 - 0
Source/BansheeOpenAudio/Include/BsOAOggVorbisWriter.h

@@ -0,0 +1,42 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsOAPrerequisites.h"
+#include "vorbis\vorbisenc.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup OpenAudio
+	 *  @{
+	 */
+
+	/** Used for writing Ogg Vorbis audio data. */
+	class OAOggVorbisWriter
+	{
+	public:
+		OAOggVorbisWriter();
+		~OAOggVorbisWriter();
+
+		bool open(std::function<void(UINT8*, UINT32)> writeCallback, UINT32 sampleRate, UINT32 numChannels);
+
+		void write(UINT8* samples, UINT32 numSamples); // Assumes 16-bit samples
+		void flush();
+		void close();
+	private:
+		void writeBlocks();
+
+		static const UINT32 BUFFER_SIZE = 4096;
+
+		std::function<void(UINT8*, UINT32)> mWriteCallback;
+		UINT8 mBuffer[BUFFER_SIZE];
+		UINT32 mBufferOffset;
+		UINT32 mNumChannels;
+
+		ogg_stream_state mOggState;
+		vorbis_info mVorbisInfo;
+		vorbis_dsp_state mVorbisState;
+	};
+
+	/** @} */
+}

+ 3 - 3
Source/BansheeOpenAudio/Source/BsOAFLACReader.cpp

@@ -115,7 +115,7 @@ namespace BansheeEngine
 
 
 		if (meta->type == FLAC__METADATA_TYPE_STREAMINFO)
 		if (meta->type == FLAC__METADATA_TYPE_STREAMINFO)
 		{
 		{
-			data->info.numSamples = meta->data.stream_info.total_samples * meta->data.stream_info.channels;
+			data->info.numSamples = (UINT32)meta->data.stream_info.total_samples * meta->data.stream_info.channels;
 			data->info.sampleRate = meta->data.stream_info.sample_rate;
 			data->info.sampleRate = meta->data.stream_info.sample_rate;
 			data->info.numChannels = meta->data.stream_info.channels;
 			data->info.numChannels = meta->data.stream_info.channels;
 			data->info.bitDepth = meta->data.stream_info.bits_per_sample;
 			data->info.bitDepth = meta->data.stream_info.bits_per_sample;
@@ -197,8 +197,8 @@ namespace BansheeEngine
 
 
 	UINT32 OAFLACReader::read(UINT8* samples, UINT32 numSamples)
 	UINT32 OAFLACReader::read(UINT8* samples, UINT32 numSamples)
 	{
 	{
-		UINT64 overflowSize = mData.overflow.size();
-		UINT64 overflowNumSamples = 0;
+		UINT32 overflowSize = (UINT32)mData.overflow.size();
+		UINT32 overflowNumSamples = 0;
 		
 		
 		UINT32 bytesPerSample = mData.info.bitDepth / 8;
 		UINT32 bytesPerSample = mData.info.bitDepth / 8;
 		if (overflowSize > 0)
 		if (overflowSize > 0)

+ 150 - 0
Source/BansheeOpenAudio/Source/BsOAOggVorbisWriter.cpp

@@ -0,0 +1,150 @@
+#include "BsOAOggVorbisWriter.h"
+
+namespace BansheeEngine
+{
+// Writes to the internal cached buffer and flushes it if needed
+#define WRITE_TO_BUFFER(data, length)					\
+	if ((mBufferOffset + length) > BUFFER_SIZE)			\
+		flush();										\
+														\
+	if(length > BUFFER_SIZE)							\
+		mWriteCallback(data, length);					\
+	else												\
+	{													\
+		memcpy(mBuffer + mBufferOffset, data, length);	\
+		mBufferOffset += length;						\
+	}
+
+	OAOggVorbisWriter::OAOggVorbisWriter()
+		:mBufferOffset(0), mNumChannels(0)
+	{ }
+
+	OAOggVorbisWriter::~OAOggVorbisWriter()
+	{
+		close();
+	}
+
+	bool OAOggVorbisWriter::open(std::function<void(UINT8*, UINT32)> writeCallback, UINT32 sampleRate, UINT32 numChannels)
+	{
+		mNumChannels = numChannels;
+		mWriteCallback = writeCallback;
+
+		ogg_stream_init(&mOggState, std::rand());
+		vorbis_info_init(&mVorbisInfo);
+
+		// Automatic bitrate management with quality 0.4 (~128 kbps for 44 KHz stereo sound)
+		INT32 status = vorbis_encode_init_vbr(&mVorbisInfo, numChannels, sampleRate, 0.4f);
+		if (status < 0)
+		{
+			LOGERR("Failed to write Ogg Vorbis file.");
+			close();
+			return false;
+		}
+
+		vorbis_analysis_init(&mVorbisState, &mVorbisInfo);
+
+		// Generate header
+		vorbis_comment comment;
+		vorbis_comment_init(&comment);
+
+		ogg_packet headerPacket, commentPacket, codePacket;
+		status = vorbis_analysis_headerout(&mVorbisState, &comment, &headerPacket, &commentPacket, &codePacket);
+		vorbis_comment_clear(&comment);
+
+		if (status < 0)
+		{
+			LOGERR("Failed to write Ogg Vorbis file.");
+			close();
+			return false;
+		}
+
+		// Write header
+		ogg_stream_packetin(&mOggState, &headerPacket);
+		ogg_stream_packetin(&mOggState, &commentPacket);
+		ogg_stream_packetin(&mOggState, &codePacket);
+
+		ogg_page page;
+		while (ogg_stream_flush(&mOggState, &page) > 0)
+		{
+			WRITE_TO_BUFFER(page.header, page.header_len);
+			WRITE_TO_BUFFER(page.body, page.body_len);
+		}
+
+		return true;
+	}
+
+	void OAOggVorbisWriter::write(UINT8* samples, UINT32 numSamples)
+	{
+		// Allocate new buffer and write PCM data to it
+		UINT32 numFrames = numSamples / mNumChannels;
+		float** buffer = vorbis_analysis_buffer(&mVorbisState, numFrames);
+
+		for (UINT32 i = 0; i < numFrames; i++)
+		{
+			for (UINT32 j = 0; j < mNumChannels; j++)
+			{
+				float encodedSample = *samples / 32767.0f;
+				buffer[j][i] = encodedSample;
+
+				samples++;
+			}
+		}
+
+		// Signal how many frames were written
+		vorbis_analysis_wrote(&mVorbisState, numFrames);
+		writeBlocks();
+	}
+
+	void OAOggVorbisWriter::writeBlocks()
+	{
+		// Create a new block
+		vorbis_block block;
+		vorbis_block_init(&mVorbisState, &block);
+		while (vorbis_analysis_blockout(&mVorbisState, &block) == 1)
+		{
+			// Analyze and determine optimal bitrate
+			vorbis_analysis(&block, nullptr);
+			vorbis_bitrate_addblock(&block);
+
+			// Write block into ogg packets
+			ogg_packet packet;
+			while (vorbis_bitrate_flushpacket(&mVorbisState, &packet))
+			{
+				ogg_stream_packetin(&mOggState, &packet);
+
+				// If new page, write it to the internal buffer
+				ogg_page page;
+				while (ogg_stream_flush(&mOggState, &page) > 0)
+				{
+					WRITE_TO_BUFFER(page.header, page.header_len);
+					WRITE_TO_BUFFER(page.body, page.body_len);
+				}
+			}
+		}
+
+		// Clear the allocated block
+		vorbis_block_clear(&block);
+	}
+
+	void OAOggVorbisWriter::flush()
+	{
+		if (mBufferOffset > 0 && mWriteCallback != nullptr)
+			mWriteCallback(mBuffer, mBufferOffset);
+
+		mBufferOffset = 0;
+	}
+
+	void OAOggVorbisWriter::close()
+	{
+		// Mark end of data and flush any remaining data in the buffers
+		vorbis_analysis_wrote(&mVorbisState, 0);
+		writeBlocks();
+		flush();
+
+		ogg_stream_clear(&mOggState);
+		vorbis_dsp_clear(&mVorbisState);
+		vorbis_info_clear(&mVorbisInfo);
+	}
+
+#undef WRITE_TO_BUFFER
+}