Jelajahi Sumber

Creating an OpenAL sound buffer in AudioClip
Refactored AudioClip to avoid duplicated audio data memory

BearishSun 9 tahun lalu
induk
melakukan
5634cd5b7b

+ 14 - 5
Source/BansheeCore/Include/BsAudioClip.h

@@ -72,12 +72,14 @@ namespace BansheeEngine
 		UINT32 getNumChannels() const { return mDesc.numChannels; }
 		AudioFormat getFormat() const { return mDesc.format; }
 		AudioReadMode getReadMode() const { return mDesc.readMode; }
-		UINT32 getLength() const { return mNumSamples / mDesc.frequency; }
+		float getLength() const { return mLength; }
 		UINT32 getNumSamples() const { return mNumSamples; }
+		bool is3D() const { return mDesc.is3D; }
 
 		/** 
 		 * Returns audio samples in PCM format, channel data interleaved. Sample read pointer is advanced by the read 
-		 * amount.
+		 * amount. Only available if the audio data has been created with AudioReadMode::Stream, 
+		 * AudioReadMode::LoadCompressed (and the format is compressed), or if @p keepSourceData was enabled on creation.
 		 *
 		 * @param[in]	samples		Previously allocated buffer to contain the samples.
 		 * @param[in]	count		Number of samples to read (should be a multiple of number of channels).
@@ -87,7 +89,9 @@ namespace BansheeEngine
 		virtual void getSamples(UINT8* samples, UINT32 count) const = 0;
 
 		/**
-		 * Moves the read location from which the getSamples method retrieves samples.
+		 * Moves the read location from which the getSamples method retrieves samples. Only available if the audio data
+		 * has been created with AudioReadMode::Stream, AudioReadMode::LoadCompressed (and the format is compressed), 
+		 * or if @p keepSourceData was enabled on creation.
 		 *
 		 * @param[in]	offset	Offset in number of samples at which to start reading (should be a multiple of number
 		 *						of channels).
@@ -112,14 +116,19 @@ namespace BansheeEngine
 	protected:
 		AudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc);
 
-		/** Returns audio data in the original source format. */
-		virtual SPtr<DataStream> getSourceFormatData(UINT32& size) = 0;
+		/** @copydoc Resource::initialize */
+		void initialize() override;
+
+		/** Returns original audio data. Only available if @p keepSourceData has been provided on creation. */
+		virtual SPtr<DataStream> getSourceStream(UINT32& size) = 0;
 
 	protected:
 		AUDIO_CLIP_DESC mDesc;
 		UINT32 mNumSamples;
 		UINT32 mStreamSize;
 		UINT32 mStreamOffset;
+		float mLength;
+		bool mKeepSourceData;
 		SPtr<DataStream> mStreamData;
 
 		/************************************************************************/

+ 3 - 1
Source/BansheeCore/Include/BsAudioClipRTTI.h

@@ -26,11 +26,13 @@ namespace BansheeEngine
 			BS_RTTI_MEMBER_PLAIN(mNumSamples, 5)
 			BS_RTTI_MEMBER_PLAIN(mStreamSize, 7)
 			BS_RTTI_MEMBER_PLAIN(mStreamOffset, 8)
+			BS_RTTI_MEMBER_PLAIN_NAMED(is3D, mDesc.is3D, 9)
+			BS_RTTI_MEMBER_PLAIN(mLength, 10)
 		BS_END_RTTI_MEMBERS
 
 		SPtr<DataStream> getData(AudioClip* obj, UINT32& size)
 		{
-			SPtr<DataStream> stream = obj->getSourceFormatData(size);
+			SPtr<DataStream> stream = obj->getSourceStream(size);
 			if (stream != nullptr && stream->isFile())
 				LOGWRN("Saving an AudioClip which uses streaming data. Streaming data might not be available if saving to the same file.");
 

+ 23 - 2
Source/BansheeCore/Include/BsAudioUtility.h

@@ -26,7 +26,7 @@ namespace BansheeEngine
 		 * @param[in]	numSamples	Number of samples per a single channel.
 		 * @param[in]	numChannels	Number of channels in the input data.
 		 */
-		static void convertToMono(UINT8* input, UINT8* output, UINT32 bitDepth, UINT32 numSamples, UINT32 numChannels);
+		static void convertToMono(const UINT8* input, UINT8* output, UINT32 bitDepth, UINT32 numSamples, UINT32 numChannels);
 
 		/**
 		 * Converts a set of audio samples of a certain bit depth to a new bit depth.
@@ -39,7 +39,28 @@ namespace BansheeEngine
 		 * @param[in]	inBitDepth	Size of a single sample in the @p output array, in bits.
 		 * @param[in]	numSamples	Total number of samples to process.
 		 */
-		static void convertBitDepth(UINT8* input, UINT32 inBitDepth, UINT8* output, UINT32 outBitDepth, UINT32 numSamples);
+		static void convertBitDepth(const UINT8* input, UINT32 inBitDepth, UINT8* output, UINT32 outBitDepth, UINT32 numSamples);
+
+		/**
+		 * Converts a set of audio samples of a certain bit depth to a set of floating point samples in range [-1, 1].
+		 *
+		 * @param[in]	input		A set of input samples. Total size of the buffer should be @p numSamples *
+		 *							@p inBitDepth / 8. 8-bit samples should be unsigned, while signed samples should be
+		 *							provided for higher bit depths.
+		 * @param[in]	inBitDepth	Size of a single sample in the @p input array, in bits.
+		 * @param[out]	output		Pre-allocated buffer to store the output samples in. Total size of the buffer should be
+		 *							@p numSamples * sizeof(float).
+		 * @param[in]	numSamples	Total number of samples to process.
+		 */
+		static void convertToFloat(const UINT8* input, UINT32 inBitDepth, float* output, UINT32 numSamples);
+
+		/** 
+		 * Converts a 24-bit signed integer into a 32-bit signed integer. 
+		 *
+		 * @param[in]	input	24-bit signed integer as an array of 3 bytes.
+		 * @return				32-bit signed integer.
+		 */
+		static INT32 convert24To32Bits(const UINT8* input);
 	};
 
 	/** @} */

+ 9 - 1
Source/BansheeCore/Source/BsAudioClip.cpp

@@ -9,12 +9,20 @@
 namespace BansheeEngine
 {
 	AudioClip::AudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc)
-		:Resource(false), mDesc(desc), mNumSamples(numSamples), mStreamData(samples), mStreamSize(streamSize), mStreamOffset(0)
+		: Resource(false), mDesc(desc), mNumSamples(numSamples), mStreamData(samples), mStreamSize(streamSize)
+		, mStreamOffset(0), mLength(0.0f), mKeepSourceData(true)
 	{
 		if (samples != nullptr)
 			mStreamOffset = (UINT32)samples->tell();
 	}
 
+	void AudioClip::initialize()
+	{
+		mLength = mNumSamples / mDesc.numChannels / (float)mDesc.frequency;
+
+		Resource::initialize();
+	}
+
 	HAudioClip AudioClip::create(UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc)
 	{
 		return static_resource_cast<AudioClip>(gResources()._createResourceHandle(_createPtr(nullptr, streamSize, numSamples, desc)));

+ 70 - 24
Source/BansheeCore/Source/BsAudioUtility.cpp

@@ -4,7 +4,7 @@
 
 namespace BansheeEngine
 {
-	void convertToMono8(UINT8* input, UINT8* output, UINT32 numSamples, UINT32 numChannels)
+	void convertToMono8(const UINT8* input, UINT8* output, UINT32 numSamples, UINT32 numChannels)
 	{
 		for (UINT32 i = 0; i < numSamples; i++)
 		{
@@ -20,7 +20,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void convertToMono16(INT16* input, INT16* output, UINT32 numSamples, UINT32 numChannels)
+	void convertToMono16(const INT16* input, INT16* output, UINT32 numSamples, UINT32 numChannels)
 	{
 		for (UINT32 i = 0; i < numSamples; i++)
 		{
@@ -36,16 +36,7 @@ namespace BansheeEngine
 		}
 	}
 
-	INT32 convert24To32Bits(const UINT8* input)
-	{
-		bool isNegative = (input[2] & 0x80) != 0;
-		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)
+	void convert32To24Bits(const INT32 input, UINT8* output)
 	{
 		UINT32 valToEncode = *(UINT32*)&input;
 		output[0] = valToEncode & 0x000000FF;
@@ -53,14 +44,14 @@ namespace BansheeEngine
 		output[2] = (valToEncode >> 16) & 0x000000FF;
 	}
 
-	void convertToMono24(UINT8* input, UINT8* output, UINT32 numSamples, UINT32 numChannels)
+	void convertToMono24(const UINT8* input, UINT8* output, UINT32 numSamples, UINT32 numChannels)
 	{
 		for (UINT32 i = 0; i < numSamples; i++)
 		{
 			INT32 sum = 0;
 			for (UINT32 j = 0; j < numChannels; j++)
 			{
-				sum += convert24To32Bits(input);
+				sum += AudioUtility::convert24To32Bits(input);
 				input += 3;
 			}
 
@@ -70,7 +61,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void convertToMono32(INT32* input, INT32* output, UINT32 numSamples, UINT32 numChannels)
+	void convertToMono32(const INT32* input, INT32* output, UINT32 numSamples, UINT32 numChannels)
 	{
 		for (UINT32 i = 0; i < numSamples; i++)
 		{
@@ -86,7 +77,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void convert8To32Bits(UINT8* input, INT32* output, UINT32 numSamples)
+	void convert8To32Bits(const UINT8* input, INT32* output, UINT32 numSamples)
 	{
 		for (UINT32 i = 0; i < numSamples; i++)
 		{
@@ -95,22 +86,22 @@ namespace BansheeEngine
 		}
 	}
 
-	void convert16To32Bits(INT16* input, INT32* output, UINT32 numSamples)
+	void convert16To32Bits(const 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)
+	void convert24To32Bits(const UINT8* input, INT32* output, UINT32 numSamples)
 	{
 		for (UINT32 i = 0; i < numSamples; i++)
 		{
-			output[i] = convert24To32Bits(input);
+			output[i] = AudioUtility::convert24To32Bits(input);
 			input += 3;
 		}
 	}
 
-	void convert32To8Bits(INT32* input, UINT8* output, UINT32 numSamples)
+	void convert32To8Bits(const INT32* input, UINT8* output, UINT32 numSamples)
 	{
 		for (UINT32 i = 0; i < numSamples; i++)
 		{
@@ -119,13 +110,13 @@ namespace BansheeEngine
 		}
 	}
 
-	void convert32To16Bits(INT32* input, INT16* output, UINT32 numSamples)
+	void convert32To16Bits(const 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)
+	void convert32To24Bits(const INT32* input, UINT8* output, UINT32 numSamples)
 	{
 		for (UINT32 i = 0; i < numSamples; i++)
 		{
@@ -134,7 +125,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void AudioUtility::convertToMono(UINT8* input, UINT8* output, UINT32 bitDepth, UINT32 numSamples, UINT32 numChannels)
+	void AudioUtility::convertToMono(const UINT8* input, UINT8* output, UINT32 bitDepth, UINT32 numSamples, UINT32 numChannels)
 	{
 		switch (bitDepth)
 		{
@@ -156,7 +147,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void AudioUtility::convertBitDepth(UINT8* input, UINT32 inBitDepth, UINT8* output, UINT32 outBitDepth, UINT32 numSamples)
+	void AudioUtility::convertBitDepth(const UINT8* input, UINT32 inBitDepth, UINT8* output, UINT32 outBitDepth, UINT32 numSamples)
 	{
 		INT32* srcBuffer = nullptr;
 
@@ -213,4 +204,59 @@ namespace BansheeEngine
 			srcBuffer = nullptr;
 		}
 	}
+
+	void AudioUtility::convertToFloat(const UINT8* input, UINT32 inBitDepth, float* output, UINT32 numSamples)
+	{
+		if (inBitDepth == 8)
+		{
+			for (UINT32 i = 0; i < numSamples; i++)
+			{
+				UINT8 sample = *input;
+				output[i] = sample / 255.0f;
+
+				input++;
+			}
+		}
+		else if (inBitDepth == 16)
+		{
+			for (UINT32 i = 0; i < numSamples; i++)
+			{
+				INT16 sample = *(INT16*)input;
+				output[i] = sample / 32767.0f;
+
+				input += 2;
+			}
+		}
+		else if (inBitDepth == 24)
+		{
+			for (UINT32 i = 0; i < numSamples; i++)
+			{
+				INT32 sample = AudioUtility::convert24To32Bits(input);
+				output[i] = sample / 8388607.0f;
+
+				input += 3;
+			}
+		}
+		else if (inBitDepth == 32)
+		{
+			for (UINT32 i = 0; i < numSamples; i++)
+			{
+				INT32 sample = *(INT32*)input;
+				output[i] = sample / 2147483647.0f;
+
+				input += 4;
+			}
+		}
+		else
+			assert(false);
+	}
+
+	INT32 AudioUtility::convert24To32Bits(const UINT8* input)
+	{
+		bool isNegative = (input[2] & 0x80) != 0;
+		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];
+	}
 }

+ 15 - 0
Source/BansheeOpenAudio/Include/BsOAAudio.h

@@ -4,6 +4,7 @@
 
 #include "BsOAPrerequisites.h"
 #include "BsAudio.h"
+#include "AL/alc.h"
 
 namespace BansheeEngine
 {
@@ -43,7 +44,21 @@ namespace BansheeEngine
 
 		/** @copydoc Audio::getAllDevices */
 		Vector<AudioDevice> getAllDevices() const override;
+
+		/** @name Internal 
+		 *  @{
+		 */
+
+		bool isExtensionSupported(const String& extension) const;
+
+		/** @} */
+
+	private:
+		ALCdevice* mDevice;
 	};
 
+	/** Provides easier access to OAAudio. */
+	OAAudio& gOAAudio();
+
 	/** @} */
 }

+ 17 - 1
Source/BansheeOpenAudio/Include/BsOAAudioClip.h

@@ -15,24 +15,40 @@ namespace BansheeEngine
 	class BS_OA_EXPORT OAAudioClip : public AudioClip
 	{
 	public:
+		virtual ~OAAudioClip();
+
 		/** @copydoc AudioClip::getSamples */
 		void getSamples(UINT8* samples, UINT32 count) const override;
 
 		/** @copydoc AudioClip::seekSamples */
 		void seekSamples(UINT32 offset) override;
 
+		/** @name Internal
+		 *  @{
+		 */
+
+		/** Returns the internal OpenAL buffer. Only valid if the audio clip was created without AudioReadMode::Stream. */
+		UINT32 _getOpenALBuffer() const { return mBufferId; }
+
+		/** @} */
 	protected:
 		/** @copydoc Resource::initialize */
 		void initialize() override;
 
 		/** @copydoc AudioClip::getSourceFormatData */
-		SPtr<DataStream> getSourceFormatData(UINT32& size) override;
+		SPtr<DataStream> getSourceStream(UINT32& size) override;
 	private:
 		OAAudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc);
 
 		mutable Mutex mMutex;
 		mutable OAOggVorbisReader mVorbisReader;
 		bool mNeedsDecompression;
+		UINT32 mBufferId;
+
+		// These streams exist to save original audio data in case it's needed later (usually for saving with the editor, or
+		// manual data manipulation). In normal usage (in-game) these will be null so no memory is wasted.
+		SPtr<DataStream> mSourceStreamData;
+		UINT32 mSourceStreamSize;
 	};
 
 	/** @} */

+ 14 - 0
Source/BansheeOpenAudio/Source/BsOAAudio.cpp

@@ -1,6 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsOAAudio.h"
+#include "AL\al.h"
 
 namespace BansheeEngine
 {
@@ -63,4 +64,17 @@ namespace BansheeEngine
 		// TODO
 		return Vector<AudioDevice>();
 	}
+
+	bool OAAudio::isExtensionSupported(const String& extension) const
+	{
+		if ((extension.length() > 2) && (extension.substr(0, 3) == "ALC"))
+			return alcIsExtensionPresent(mDevice, extension.c_str()) != AL_FALSE;
+		else
+			return alIsExtensionPresent(extension.c_str()) != AL_FALSE;
+	}
+
+	OAAudio& gOAAudio()
+	{
+		return static_cast<OAAudio&>(OAAudio::instance());
+	}
 }

+ 258 - 74
Source/BansheeOpenAudio/Source/BsOAAudioClip.cpp

@@ -4,13 +4,148 @@
 #include "BsOAOggVorbisWriter.h"
 #include "BsOAOggVorbisReader.h"
 #include "BsDataStream.h"
+#include "BsAudioUtility.h"
+#include "BsOAAudio.h"
+#include "AL/al.h"
 
 namespace BansheeEngine
 {
+	/** 
+	 * Returns optimal format for the provided number of channels and bit depth. It is assumed the user has checked if
+	 * extensions providing these formats are actually available.
+	 */
+	ALenum getSoundBufferFormat(UINT32 numChannels, UINT32 bitDepth)
+	{
+		switch (bitDepth)
+		{
+		case 8:
+		{
+			switch (numChannels)
+			{
+			case 1:  return AL_FORMAT_MONO8;
+			case 2:  return AL_FORMAT_STEREO8;
+			case 4:  return alGetEnumValue("AL_FORMAT_QUAD8");
+			case 6:  return alGetEnumValue("AL_FORMAT_51CHN8");
+			case 7:  return alGetEnumValue("AL_FORMAT_61CHN8");
+			case 8:  return alGetEnumValue("AL_FORMAT_71CHN8");
+			default:
+				assert(false);
+				return 0;
+			}
+		}
+		case 16:
+		{
+			switch (numChannels)
+			{
+			case 1:  return AL_FORMAT_MONO16;
+			case 2:  return AL_FORMAT_STEREO16;
+			case 4:  return alGetEnumValue("AL_FORMAT_QUAD16");
+			case 6:  return alGetEnumValue("AL_FORMAT_51CHN16");
+			case 7:  return alGetEnumValue("AL_FORMAT_61CHN16");
+			case 8:  return alGetEnumValue("AL_FORMAT_71CHN16");
+			default:
+				assert(false);
+				return 0;
+			}
+		}
+		case 32:
+		{
+			switch (numChannels)
+			{
+			case 1:  return alGetEnumValue("AL_FORMAT_MONO_FLOAT32");
+			case 2:  return alGetEnumValue("AL_FORMAT_STEREO_FLOAT32");
+			case 4:  return alGetEnumValue("AL_FORMAT_QUAD32");
+			case 6:  return alGetEnumValue("AL_FORMAT_51CHN32");
+			case 7:  return alGetEnumValue("AL_FORMAT_61CHN32");
+			case 8:  return alGetEnumValue("AL_FORMAT_71CHN32");
+			default:
+				assert(false);
+				return 0;
+			}
+		}
+		default:
+			assert(false);
+			return 0;
+		}
+	}
+
+	/** 
+	 * Writes provided samples into the OpenAL buffer with the provided ID. If the supported format is not supported the
+	 * samples will first be converted into a valid format.
+	 */
+	void writeToSoundBuffer(UINT32 bufferId, UINT8* samples, const AudioFileInfo& info)
+	{
+		if (info.numChannels <= 2) // Mono or stereo
+		{
+			if (info.bitDepth > 16)
+			{
+				if (gOAAudio().isExtensionSupported("AL_EXT_float32"))
+				{
+					UINT32 bufferSize = info.numSamples * sizeof(float);
+					float* sampleBufferFloat = (float*)bs_stack_alloc(bufferSize);
+
+					AudioUtility::convertToFloat(samples, info.bitDepth, sampleBufferFloat, info.numSamples);
+
+					ALenum format = getSoundBufferFormat(info.numChannels, info.bitDepth);
+					alBufferData(bufferId, format, sampleBufferFloat, bufferSize, info.sampleRate);
+
+					bs_stack_free(sampleBufferFloat);
+				}
+				else
+				{
+					LOGWRN("OpenAL doesn't support bit depth larger than 16. Your audio data will be truncated.");
+
+					UINT32 bufferSize = info.numSamples * 2;
+					UINT8* sampleBuffer16 = (UINT8*)bs_stack_alloc(bufferSize);
+
+					AudioUtility::convertBitDepth(samples, info.bitDepth, sampleBuffer16, 16, info.numSamples);
+
+					ALenum format = getSoundBufferFormat(info.numChannels, 16);
+					alBufferData(bufferId, format, sampleBuffer16, bufferSize, info.sampleRate);
+
+					bs_stack_free(sampleBuffer16);
+				}
+			}
+			else
+			{
+				ALenum format = getSoundBufferFormat(info.numChannels, 16);
+				alBufferData(bufferId, format, samples, info.numSamples * (info.bitDepth / 8), info.sampleRate);
+			}
+		}
+		else // Multichannel
+		{
+			// Note: Assuming AL_EXT_MCFORMATS is supported. If it's not, channels should be reduced to mono or stereo.
+
+			if (info.bitDepth == 24) // 24-bit not supported, convert to 32-bit
+			{
+				UINT32 bufferSize = info.numSamples * sizeof(INT32);
+				UINT8* sampleBuffer32 = (UINT8*)bs_stack_alloc(bufferSize);
+
+				AudioUtility::convertBitDepth(samples, info.bitDepth, sampleBuffer32, 32, info.numSamples);
+
+				ALenum format = getSoundBufferFormat(info.numChannels, 32);
+				alBufferData(bufferId, format, sampleBuffer32, bufferSize, info.sampleRate);
+
+				bs_stack_free(sampleBuffer32);
+			}
+			else
+			{
+				ALenum format = getSoundBufferFormat(info.numChannels, info.bitDepth);
+				alBufferData(bufferId, format, samples, info.numSamples * (info.bitDepth / 8), info.sampleRate);
+			}
+		}
+	}
+
 	OAAudioClip::OAAudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc)
-		:AudioClip(samples, streamSize, numSamples, desc), mNeedsDecompression(false)
+		:AudioClip(samples, streamSize, numSamples, desc), mNeedsDecompression(false), mBufferId((UINT32)-1), mSourceStreamSize(0)
 	{ }
 
+	OAAudioClip::~OAAudioClip()
+	{
+		if (mBufferId != (UINT32)-1)
+			alDeleteBuffers(1, &mBufferId);
+	}
+
 	void OAAudioClip::initialize()
 	{
 		{
@@ -23,44 +158,94 @@ namespace BansheeEngine
 			info.numSamples = mNumSamples;
 			info.sampleRate = mDesc.frequency;
 
-			if (mDesc.readMode != AudioReadMode::Stream)
+			// If we need to keep source data, read everything into memory and keep a copy
+			if (mKeepSourceData)
 			{
-				// If not streaming, but input stream is a file, read all of it into an internal memory stream
-				if (mStreamData != nullptr)
+				mStreamData->seek(mStreamOffset);
+
+				UINT8* sampleBuffer = (UINT8*)bs_alloc(mStreamSize);
+				mStreamData->read(sampleBuffer, mStreamSize);
+
+				mSourceStreamData = bs_shared_ptr_new<MemoryDataStream>(sampleBuffer, mStreamSize);
+				mSourceStreamSize = mStreamSize;
+			}
+
+			if(mDesc.readMode == AudioReadMode::LoadDecompressed && mDesc.format == AudioFormat::VORBIS)
+			{
+				// Read all uncompressed data into memory
+				SPtr<DataStream> stream;
+				UINT32 offset = 0;
+				if (mSourceStreamData != nullptr) // If it's already loaded in memory, use it directly
+					stream = mSourceStreamData;
+				else
 				{
-					// Decompress if needed
-					if (mDesc.format == AudioFormat::VORBIS && mDesc.readMode == AudioReadMode::LoadDecompressed)
+					stream = mStreamData;
+					offset = mStreamOffset;
+				}
+
+				stream->seek(offset);
+
+				// Decompress from Ogg
+				OAOggVorbisReader reader;
+				if (reader.open(stream, info))
+				{
+					bs_frame_mark();
+
+					UINT32 bufferSize = info.numSamples * info.bitDepth;
+					UINT8* sampleBuffer = (UINT8*)bs_frame_alloc(bufferSize);
+
+					reader.read(sampleBuffer, info.numSamples);
+
+					alGenBuffers(1, &mBufferId);
+
+
+
+					if(info.bitDepth > 16)
 					{
-						mStreamData->seek(mStreamOffset);
+						LOGWRN("OpenAL doesn't support bit depth larger than 16. Your audio data will be truncated. "
+							"Consider re-importing the audio clip as 16-bit. Audio clip: " + toString(getName()) + ".");
 
-						OAOggVorbisReader reader;
-						if (reader.open(mStreamData, info))
-						{
-							UINT32 bufferSize = info.numSamples * info.bitDepth;
-							UINT8* sampleBuffer = (UINT8*)bs_alloc(bufferSize);
+						bufferSize = info.numSamples * 16;
+						UINT8* sampleBuffer16 = (UINT8*)bs_frame_alloc(bufferSize);
 
-							reader.read(sampleBuffer, info.numSamples);
+						AudioUtility::convertBitDepth(sampleBuffer, info.bitDepth, sampleBuffer16, 16, info.numSamples);
 
-							mStreamData = bs_shared_ptr_new<MemoryDataStream>(sampleBuffer, bufferSize);
-							mStreamOffset = 0;
-							mStreamSize = bufferSize;
-						}
-						else
-							LOGERR("Failed decompressing AudioClip stream.");
+						info.bitDepth = 16;
+
+						bs_frame_free(sampleBuffer);
+						sampleBuffer = sampleBuffer16;
 					}
+
+					writeToSoundBuffer(mBufferId, sampleBuffer, info);
+
+					mStreamData = nullptr;
+					mStreamOffset = 0;
+					mStreamSize = 0;
+
+					bs_frame_free(sampleBuffer);
+					bs_frame_clear();
+				}
+				else
+					LOGERR("Failed decompressing AudioClip stream.");
+			}
+			else if(mDesc.readMode == AudioReadMode::LoadCompressed)
+			{
+				// If reading from file, make a copy of data in memory, otherwise just take ownership of the existing buffer
+				if (mStreamData->isFile()) 
+				{
+					if (mSourceStreamData != nullptr) // If it's already loaded in memory, use it directly
+						mStreamData = mSourceStreamData;
 					else
 					{
-						if (mStreamData->isFile()) // If reading from file, make a copy of data in memory
-						{
-							UINT8* data = (UINT8*)bs_alloc(mStreamSize);
+						UINT8* data = (UINT8*)bs_alloc(mStreamSize);
 
-							mStreamData->seek(mStreamOffset);
-							mStreamData->read(data, mStreamSize);
+						mStreamData->seek(mStreamOffset);
+						mStreamData->read(data, mStreamSize);
 
-							mStreamData = bs_shared_ptr_new<MemoryDataStream>(data, mStreamSize);
-							mStreamOffset = 0;
-						}
+						mStreamData = bs_shared_ptr_new<MemoryDataStream>(data, mStreamSize);
 					}
+
+					mStreamOffset = 0;
 				}
 			}
 
@@ -86,78 +271,77 @@ namespace BansheeEngine
 	{
 		Lock lock(mMutex);
 
+		// Try to read from normal stream, and if that fails read from in-memory stream if it exists
 		if (mStreamData == nullptr)
+		{
+			if (mNeedsDecompression)
+				mVorbisReader.read(samples, count);
+			else
+			{
+				UINT32 bytesPerSample = mDesc.bitDepth / 8;
+				UINT32 size = count * bytesPerSample;
+
+				mStreamData->read(samples, size);
+			}
+
 			return;
+		}
 
-		if (mNeedsDecompression)
-			mVorbisReader.read(samples, count);
-		else
+		if (mSourceStreamData != nullptr)
 		{
+			assert(!mNeedsDecompression); // Normal stream must exist if decompressing
+
 			UINT32 bytesPerSample = mDesc.bitDepth / 8;
 			UINT32 size = count * bytesPerSample;
 
-			mStreamData->read(samples, size);
+			mSourceStreamData->read(samples, size);
+			return;
 		}
+
+		LOGWRN("Attempting to read samples while sample data is not available.");
 	}
 
 	void OAAudioClip::seekSamples(UINT32 offset)
 	{
 		Lock lock(mMutex);
 
-		if (mStreamData == nullptr)
-			return;
-
-		if (mNeedsDecompression)
-			mVorbisReader.seek(offset);
-		else
+		// Try to seek normal stream, and if that fails seek in-memory stream if it exists
+		if (mStreamData != nullptr)
 		{
-			UINT32 bytesPerSample = mDesc.bitDepth / 8;
-			UINT32 streamOffset = mStreamOffset + offset * bytesPerSample;
-
-			mStreamData->seek(streamOffset);
-		}
-	}
+			if (mNeedsDecompression)
+				mVorbisReader.seek(offset);
+			else
+			{
+				UINT32 bytesPerSample = mDesc.bitDepth / 8;
+				UINT32 streamOffset = mStreamOffset + offset * bytesPerSample;
 
-	SPtr<DataStream> OAAudioClip::getSourceFormatData(UINT32& size)
-	{
-		Lock lock(mMutex);
+				mStreamData->seek(streamOffset);
+			}
 
-		if (mStreamData == nullptr)
-		{
-			size = 0;
-			return nullptr;
+			return;
 		}
 
-		if(mDesc.format == AudioFormat::PCM || mDesc.readMode == AudioReadMode::LoadCompressed || 
-			mDesc.readMode == AudioReadMode::Stream)
+		if(mSourceStreamData != nullptr)
 		{
-			SPtr<DataStream> outStream = mStreamData->clone(false);
-			outStream->seek(mStreamOffset);
+			assert(!mNeedsDecompression); // Normal stream must exist if decompressing
 
-			size = mStreamSize;
-			return outStream;
-		}
-		else // Data has been decompressed, we need to re-compress it to get the original stream
-		{
-			assert(!mStreamData->isFile());
-			SPtr<MemoryDataStream> memStream = std::static_pointer_cast<MemoryDataStream>(mStreamData);
+			UINT32 bytesPerSample = mDesc.bitDepth / 8;
+			UINT32 streamOffset = offset * bytesPerSample;
 
-			AudioFileInfo info;
-			info.bitDepth = mDesc.bitDepth;
-			info.numChannels = mDesc.numChannels;
-			info.numSamples = mNumSamples;
-			info.sampleRate = mDesc.frequency;
+			mSourceStreamData->seek(streamOffset);
+			return;
+		}
 
-			UINT32 orgOffset = (UINT32)memStream->tell();
-			memStream->seek(mStreamOffset);
+		LOGWRN("Seeking samples while sample data is not available.");
+	}
 
-			UINT32 bufferSize = 0;
-			UINT8* encodedSamples = OAOggVorbisWriter::PCMToOggVorbis(memStream->getCurrentPtr(), info, bufferSize);
+	SPtr<DataStream> OAAudioClip::getSourceStream(UINT32& size)
+	{
+		Lock lock(mMutex);
 
-			memStream->seek(orgOffset);
+		size = mSourceStreamSize;
+		mSourceStreamData->seek(0);
 
-			size = bufferSize;
-			return bs_shared_ptr_new<MemoryDataStream>(encodedSamples, bufferSize);
-		}
+		return mSourceStreamData;
 	}
 }

+ 2 - 1
Source/BansheeOpenAudio/Source/BsOAOggVorbisWriter.cpp

@@ -1,5 +1,6 @@
 #include "BsOAOggVorbisWriter.h"
 #include "BsDataStream.h"
+#include "BsAudioUtility.h"
 
 namespace BansheeEngine
 {
@@ -116,7 +117,7 @@ namespace BansheeEngine
 			{
 				for (UINT32 j = 0; j < mNumChannels; j++)
 				{
-					INT32 sample = (INT32)(samples[2] << 16 | samples[1] << 8 | samples[0]);
+					INT32 sample = AudioUtility::convert24To32Bits(samples);
 					float encodedSample = sample / 8388607.0f;
 					buffer[j][i] = encodedSample;