Explorar el Código

Added a way to convert audio bit depth on import (OpenAL only supports 8 and 16 bits)

BearishSun hace 9 años
padre
commit
4286c7a151

+ 4 - 0
Source/BansheeCore/Include/BsAudioClipImportOptions.h

@@ -25,12 +25,16 @@ namespace BansheeEngine
 		bool getIs3D() const { return mIs3D; }
 		bool getIs3D() const { return mIs3D; }
 		void setIs3D(bool is3d) { mIs3D = is3d; }
 		void setIs3D(bool is3d) { mIs3D = is3d; }
 
 
+		UINT32 getBitDepth() const { return mBitDepth; }
+		void setBitDepth(UINT32 bitDepth) { mBitDepth = bitDepth; }
+
 		// Note: Add options to resample to a different frequency, reduce/increase bit depth
 		// Note: Add options to resample to a different frequency, reduce/increase bit depth
 
 
 	private:
 	private:
 		AudioFormat mFormat = AudioFormat::PCM;
 		AudioFormat mFormat = AudioFormat::PCM;
 		AudioReadMode mReadMode = AudioReadMode::LoadDecompressed;
 		AudioReadMode mReadMode = AudioReadMode::LoadDecompressed;
 		bool mIs3D = true;
 		bool mIs3D = true;
+		UINT32 mBitDepth = 16;
 
 
 		/************************************************************************/
 		/************************************************************************/
 		/* 								SERIALIZATION                      		*/
 		/* 								SERIALIZATION                      		*/

+ 1 - 0
Source/BansheeCore/Include/BsAudioClipImportOptionsRTTI.h

@@ -20,6 +20,7 @@ namespace BansheeEngine
 			BS_RTTI_MEMBER_PLAIN(mFormat, 0)
 			BS_RTTI_MEMBER_PLAIN(mFormat, 0)
 			BS_RTTI_MEMBER_PLAIN(mReadMode, 1)
 			BS_RTTI_MEMBER_PLAIN(mReadMode, 1)
 			BS_RTTI_MEMBER_PLAIN(mIs3D, 2)
 			BS_RTTI_MEMBER_PLAIN(mIs3D, 2)
+			BS_RTTI_MEMBER_PLAIN(mBitDepth, 3)
 		BS_END_RTTI_MEMBERS
 		BS_END_RTTI_MEMBERS
 	public:
 	public:
 		AudioClipImportOptionsRTTI()
 		AudioClipImportOptionsRTTI()

+ 141 - 11
Source/BansheeOpenAudio/Source/BsOAImporter.cpp

@@ -43,6 +43,23 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	INT32 convert24To32Bits(const UINT8* input)
+	{
+		bool isNegative = input[2] & 0x80;
+		if (isNegative) // Sign extend if negative
+			return (0xFF << 24) | (input[2] << 16) | (input[1] << 8) | input[0];
+		else
+			return (input[2] << 16) | (input[1] << 8) | input[0];
+	}
+
+	void convert32To24Bits(INT32 input, UINT8* output)
+	{
+		UINT32 valToEncode = *(UINT32*)&input;
+		output[0] = valToEncode & 0x000000FF;
+		output[1] = (valToEncode >> 8) & 0x000000FF;
+		output[2] = (valToEncode >> 16) & 0x000000FF;
+	}
+
 	void convertToMono24(UINT8* input, UINT8* output, UINT32 numSamples, UINT32 numChannels)
 	void convertToMono24(UINT8* input, UINT8* output, UINT32 numSamples, UINT32 numChannels)
 	{
 	{
 		for (UINT32 i = 0; i < numSamples; i++)
 		for (UINT32 i = 0; i < numSamples; i++)
@@ -50,21 +67,12 @@ namespace BansheeEngine
 			INT32 sum = 0;
 			INT32 sum = 0;
 			for (UINT32 j = 0; j < numChannels; j++)
 			for (UINT32 j = 0; j < numChannels; j++)
 			{
 			{
-				INT32 value = 0;
-				bool isNegative = input[2] & 0x80;
-				if (isNegative) // Sign extend if negative
-					value = (0xFF << 24) | (input[2] << 16) | (input[1] << 8) | input[0];
-				else
-					value = (input[2] << 16) | (input[1] << 8) | input[0];
-
-				sum += value;
+				sum += convert24To32Bits(input);
 				input += 3;
 				input += 3;
 			}
 			}
 
 
 			INT32 avg = sum / numChannels;
 			INT32 avg = sum / numChannels;
-			output[0] = avg & 0x000000FF;
-			output[1] = (avg >> 8) & 0x000000FF;
-			output[2] = (avg >> 16) & 0x000000FF;
+			convert32To24Bits(avg, output);
 			output += 3;
 			output += 3;
 		}
 		}
 	}
 	}
@@ -85,6 +93,112 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	void convert8To32Bits(UINT8* input, INT32* output, UINT32 numSamples)
+	{
+		for(UINT32 i = 0; i < numSamples; i++)
+		{
+			INT8 val = (INT8)(input[i] - 128);
+			output[i] = val << 24;
+		}
+	}
+
+	void convert16To32Bits(INT16* input, INT32* output, UINT32 numSamples)
+	{
+		for (UINT32 i = 0; i < numSamples; i++)
+			output[i] = input[i] << 16;
+	}
+
+	void convert24To32Bits(UINT8* input, INT32* output, UINT32 numSamples)
+	{
+		for (UINT32 i = 0; i < numSamples; i++)
+		{
+			output[i] = convert24To32Bits(input);
+			input += 3;
+		}
+	}
+
+	void convert32To8Bits(INT32* input, UINT8* output, UINT32 numSamples)
+	{
+		for (UINT32 i = 0; i < numSamples; i++)
+		{
+			INT8 val = (INT8)(input[i] >> 24);
+			output[i] = (UINT8)(val + 128);
+		}
+	}
+
+	void convert32To16Bits(INT32* input, INT16* output, UINT32 numSamples)
+	{
+		for (UINT32 i = 0; i < numSamples; i++)
+			output[i] = (INT16)(input[i] >> 16);
+	}
+
+	void convert32To24Bits(INT32* input, UINT8* output, UINT32 numSamples)
+	{
+		for (UINT32 i = 0; i < numSamples; i++)
+		{
+			convert32To24Bits(input[i], output);
+			output += 3;
+		}
+	}
+
+	void convertBitDepth(UINT8* input, UINT32 inBitDepth, UINT8* output, UINT32 outBitDepth, UINT32 numSamples)
+	{
+		INT32* srcBuffer = nullptr;
+
+		bool needTempBuffer = inBitDepth != 32;
+		if (needTempBuffer)
+			srcBuffer = (INT32*)bs_stack_alloc(numSamples * sizeof(INT32));
+		else
+			srcBuffer = (INT32*)input;
+
+		// Note: I convert to a temporary 32-bit buffer and then use that to convert to actual requested bit depth. 
+		//       It would be more efficient to convert directly from source to requested depth without a temporary buffer,
+		//       at the cost of additional complexity. If this method ever becomes a performance issue consider that.
+		switch(inBitDepth)
+		{
+		case 8:
+			convert8To32Bits(input, srcBuffer, numSamples);
+			break;
+		case 16:
+			convert16To32Bits((INT16*)input, srcBuffer, numSamples);
+			break;
+		case 24:
+			convert8To32Bits(input, srcBuffer, numSamples);
+			break;
+		case 32:
+			// Do nothing
+			break;
+		default:
+			assert(false);
+			break;
+		}
+
+		switch(outBitDepth)
+		{
+		case 8:
+			convert32To8Bits(srcBuffer, output, numSamples);
+			break;
+		case 16:
+			convert32To16Bits(srcBuffer, (INT16*)output, numSamples);
+			break;
+		case 24:
+			convert32To24Bits(srcBuffer, output, numSamples);
+			break;
+		case 32:
+			memcpy(output, srcBuffer, numSamples * sizeof(INT32));
+			break;
+		default:
+			assert(false);
+			break;
+		}
+
+		if (needTempBuffer)
+		{
+			bs_stack_free(srcBuffer);
+			srcBuffer = nullptr;
+		}
+	}
+
 	OAImporter::OAImporter()
 	OAImporter::OAImporter()
 		:SpecificImporter()
 		:SpecificImporter()
 	{
 	{
@@ -181,6 +295,22 @@ namespace BansheeEngine
 			bufferSize = monoBufferSize;
 			bufferSize = monoBufferSize;
 		}
 		}
 
 
+		// Convert bit depth if needed
+		if(clipIO->getBitDepth() != info.bitDepth)
+		{
+			UINT32 outBufferSize = info.numSamples * (clipIO->getBitDepth() / 8);
+			UINT8* outBuffer = (UINT8*)bs_alloc(outBufferSize);
+
+			convertBitDepth(sampleBuffer, info.bitDepth, outBuffer, clipIO->getBitDepth(), info.numSamples);
+
+			info.bitDepth = clipIO->getBitDepth();
+
+			bs_free(sampleBuffer);
+
+			sampleBuffer = outBuffer;
+			bufferSize = outBufferSize;
+		}
+
 		// Encode to Ogg Vorbis if needed
 		// Encode to Ogg Vorbis if needed
 		if(clipIO->getFormat() == AudioFormat::VORBIS)
 		if(clipIO->getFormat() == AudioFormat::VORBIS)
 		{
 		{