* Initial positional audio implementation audio

Sat, 21 Mar 2015 14:56:05 +0200

author
cengizkandemir
date
Sat, 21 Mar 2015 14:56:05 +0200
branch
audio
changeset 661
212eab9d2aaf
parent 658
619a1cbe709e
child 665
a8e528f25278

* Initial positional audio implementation
* Fixed typo a in QueryOuputDevices function name
* Added lock guard for callback function

Source/Audio.h file | annotate | diff | comparison | revisions
Source/Audio/Audio.cpp file | annotate | diff | comparison | revisions
Source/Audio/Controller.h file | annotate | diff | comparison | revisions
Testing/Source/Manual/Audio.cpp file | annotate | diff | comparison | revisions
--- a/Source/Audio.h	Wed Mar 18 19:56:38 2015 +0200
+++ b/Source/Audio.h	Sat Mar 21 14:56:05 2015 +0200
@@ -6,6 +6,7 @@
 
 namespace Gorgon { namespace Audio {
 
+
 struct Device {
 	int Index;							// device index
 	std::string Name;
@@ -19,63 +20,103 @@
 	double DefaultSampleRate;
 
 	bool QuerySampleRate(double samplerate);
-	static void QueryOuputDevices();
+	static void QueryOutputDevices();
 	
 };
 
 struct Channel {
-	Geometry::Pointf position;
-	Geometry::Pointf direction;
+	Point3f position;
+	Point3f direction;
 };
 
 class SpeakerLayout {
 public:
 	SpeakerLayout() {}
+
 	SpeakerLayout(const SpeakerLayout &layout) { channels = layout.GetChannels(); }
+
 	SpeakerLayout &operator=(const SpeakerLayout &layout) {
 		channels = layout.GetChannels();
 		return *this;
 	}
 
-	Geometry::Pointf &GetPositionAtIndex(int index) { return channels[index].position; }
-	Geometry::Pointf &GetDirectionAtIndex(int index) { return channels[index].direction; }
+	std::size_t GetSize() { return channels.size(); }
+	void SetSize(int size) { channels.resize(size); }
+
 	const std::vector<Channel> &GetChannels() const { return channels; }
-	std::size_t GetChannelCount() { return channels.size(); }
+	void SetChannels(std::vector<Channel> channels) { std::swap(this->channels, channels); }
+
+	Point3f GetPositionAtIndex(int index) const { return channels[index].position; }
+	void SetPositionAtIndex(Point3f position, int index) { channels[index].position = position; }
 
-	void SetChannelCount(int size) { channels.resize(size); }
-	void SetChannels(std::vector<Channel> channels) { std::swap(this->channels, channels); }
+	Point3f GetDirectionAtIndex(int index) const { return channels[index].direction; }
+	void SetDirectionAtIndex(Point3f direction, int index) { channels[index].direction = direction; }
+	
+	Channel GetChannelAtIndex(int index) const { return channels[index]; }
 	void SetChannelAtIndex(const Channel &channel, int index) { channels[index] = channel; }
-	void SetPositionAtIndex(Geometry::Pointf position, int index) { channels[index].position = position; }
-	void SetDirectionAtIndex(Geometry::Pointf direction, int index) { channels[index].direction = direction; }
-
+	
+	
+	
 private:
 	std::vector<Channel> channels;
 };
 
-class Audio {
+class AudioBase {
 public:
-	Audio(const Resource::Wave &wave, int targetchannel = 0):
-		controller(Controller::NonPositional, wave) {
-		controller.targetchannel = targetchannel;
-	}
+	AudioBase(const Resource::Wave &wave, Controller::Type type): controller(wave, type) {}
+	AudioBase(const Resource::Wave *wave, Controller::Type type): controller(wave, type) {}
+
+	virtual ~AudioBase() {}
+
+	void Play();
+
+	float GetVolume() const { return controller.volume; }
+	void SetVolume(float volume) { controller.volume = volume; }
 
-	Audio(const Resource::Wave *wave, int targetchannel = 0):
-		controller(Controller::NonPositional, wave) {
+	float GetSpeed() const { return controller.speed; }
+	void SetSpeed(float speed) { controller.speed = speed; }
+
+protected:
+	Controller controller;
+};
+
+class Audio: public AudioBase {
+public:
+	Audio(const Resource::Wave &wave, int targetchannel = 0): AudioBase(wave, Controller::NonPositional) {
 		controller.targetchannel = targetchannel;
 	}
 
-	void Play();
+	Audio(const Resource::Wave *wave, int targetchannel = 0): AudioBase(wave, Controller::NonPositional) {
+		controller.targetchannel = targetchannel;
+	}
 
-	void SetVolume(float volume) { controller.volume = volume; }
-	void SetSpeed(float speed) { controller.speed = speed; }
+	int GetTargetChannel() const { return controller.targetchannel; }
 	void SetTargetChannel(int targetch) { controller.targetchannel = targetch; }
+	
+	
+};
 
-	float GetVolume() const { return controller.volume; }
-	float GetSpeed() const { return controller.speed; }
-	int GetTargetChannel() const { return controller.targetchannel; }
+class PositionalAudio: public AudioBase {
+public:
+	PositionalAudio(const Resource::Wave &wave, const Point3f &position): AudioBase(wave, Controller::Positional) {
+		controller.x =  position.x;
+		controller.y =  position.y;
+		controller.z =  position.z;
+	}
 
-private:
-	Controller controller;
+	PositionalAudio(const Resource::Wave *wave, const Point3f &position): AudioBase(wave, Controller::Positional) {
+		controller.x = position.x;
+		controller.y = position.y;
+		controller.z = position.z;
+	}
+
+	Point3f GetPosition() const { return {controller.x, controller.y, controller.z}; }
+	void SetPosition(const Point3f &position) {
+		controller.x = position.x;
+		controller.y = position.y;
+		controller.z = position.z;
+	}
+
 };
 
 
@@ -86,6 +127,6 @@
 void SetSpeakerLayout(const SpeakerLayout &layout);
 Device GetCurrentOutputDevice();
 const std::vector<Device> &GetOutputDevices();
-void SetListenerPosition(int x, int y, int z);
+void SetListenerPosition(const Point3f &position);
 
 } }
\ No newline at end of file
--- a/Source/Audio/Audio.cpp	Wed Mar 18 19:56:38 2015 +0200
+++ b/Source/Audio/Audio.cpp	Sat Mar 21 14:56:05 2015 +0200
@@ -1,8 +1,11 @@
 #include "../Audio.h"
 #include "../External/portaudio/include/portaudio.h"
+#include "../Utils/Assert.h"
 
 #include <sstream>
-#include "../Utils/Assert.h"
+#include <mutex>
+
+
 
 namespace Gorgon { namespace Audio { 
 
@@ -13,7 +16,7 @@
 	StreamSet &operator=(const StreamSet &) = delete;
 
 	SpeakerLayout layout;
-	std::vector<Controller> controllers;
+	std::vector<Controller*> controllers;
 };
 
 // globals
@@ -23,8 +26,13 @@
 	static StreamSet set;
 	static std::vector<Device> devices;
 	static Device outputdevice;
-	static struct {int x; int y; int z;} listenerpos;
+	static Point3f listenerpos;
 	static bool initialized;
+	// 2 * air viscosity [kg/(mĚs)] * frequency^2 [khz] / 3 * air density [kg/m^3] * speed of sound^2 [m/s]
+	static float attenuationcoeff = (2 * 0.0000181f * (10.f * 10.f)) /
+		                            (3 * 1.2f * (340.29f * 340.29f));
+
+	static std::mutex mtx;
 }
 
 /* Global fn implementations */
@@ -44,14 +52,18 @@
 		return status;
 	}
 
-	std::vector<float> values(set.layout.GetChannelCount());
+	// for controllers vector
+	std::lock_guard<std::mutex> lock(internal::mtx);
+
+	std::vector<float> values(set.layout.GetSize());
 
 	for(int i = 0; i < framescount; i++) {
 
 		std::memset(&values[0], 0.f, sizeof(float) * values.size());
 
-		for(auto &controller: set.controllers) {
+		for(auto ctrllr: set.controllers) {
 
+			auto &controller = *ctrllr;
 			auto &resource = *controller.resource;
 			
 			float ratio = internal::outputdevice.DefaultSampleRate / resource.GetSampleRate();
@@ -61,35 +73,69 @@
 				float x = (controller.cursor) / ratio;
 				controller.cursor++;
 
-				float val;
+				float temp;
 				if(int(x + 1) >= controller.size) {
-					val = resource[x];
+					temp = resource[x];
 				}
 				else {
-					val = (1 - x + int(x)) * resource[x] + (x - int(x)) * resource[x + 1.0f];
+					temp = (1 - x + int(x)) * resource[x] + (x - int(x)) * resource[x + 1.0f];
 				}
 
-				for(std::size_t i = 0; i < set.layout.GetChannels().size(); i++) {
-					float temp = val;
+				for(std::size_t i = 0; i < set.layout.GetSize(); i++) {
+
+					auto channel = set.layout.GetChannelAtIndex(i);
+
+					float value = temp;
+					
 					if(controller.type == Controller::NonPositional) {
 
-						float weight = 1.f / set.layout.GetChannels().size();
+						float weight = 1.f / set.layout.GetSize();
 						
 						if(controller.targetchannel != 0) {
 							if(i + 1 != controller.targetchannel) {
 								// silence other channels in the presence of a target channel
-								temp = 0.f;
+								value = 0.f;
 							}
 						}
 						else {
-							temp = val * weight;
+							value = temp * weight;
 						}
 
 					}
 					else {
-						// positional audio calculations
+						float sounddist =
+						std::sqrt((internal::listenerpos.x - controller.x) * (internal::listenerpos.x - controller.x) +
+								  (internal::listenerpos.y - controller.y) * (internal::listenerpos.y - controller.y) +
+								  (internal::listenerpos.z - controller.z) * (internal::listenerpos.z - controller.z));
+
+						float channeldist =
+						std::sqrt((internal::listenerpos.x - channel.position.x) * (internal::listenerpos.x - channel.position.x) +
+								  (internal::listenerpos.y - channel.position.y) * (internal::listenerpos.y - channel.position.y) +
+								  (internal::listenerpos.z - channel.position.z) * (internal::listenerpos.z - channel.position.z));
+
+						// sound to listener unit direction vector
+						Point3f sounddirection;
+						sounddirection.x = (internal::listenerpos.x - controller.x) / sounddist;
+						sounddirection.y = (internal::listenerpos.y - controller.y) / sounddist;
+						sounddirection.z = (internal::listenerpos.z - controller.z) / sounddist;
+
+						// channel to listener unit direction vector
+						Point3f channeldirection;
+						channeldirection.x = (internal::listenerpos.x - channel.position.x) / channeldist;
+						channeldirection.y = (internal::listenerpos.y - channel.position.y) / channeldist;
+						channeldirection.z = (internal::listenerpos.z - channel.position.z) / channeldist;
+
+						float amplitude = value * std::exp(-0.03f/*internal::attenuationcoeff*/ * sounddist);
+
+						// direction coefficient (dot product)
+						float coeff = (channeldirection.x * sounddirection.x +
+							           channeldirection.y * sounddirection.y +
+									   channeldirection.z * sounddirection.z);
+
+						value = amplitude + amplitude * coeff;
+
 					}
-					values[i] += temp;
+					values[i] += value;
 				}
 			}
 			else {
@@ -119,11 +165,13 @@
 	PaError result = Pa_Initialize();
 	handleerror(result);
 
-	//! need to initialize values inside channels
-	internal::set.layout.SetChannelCount(2);
+	internal::set.layout.SetSize(2);
+	// S1 <- 10m -> L <- 10m -> S2
+	internal::set.layout.SetChannelAtIndex({{-10.f, 0.f, 0.f}, {0.f, 0.f, 0.f}}, 0);
+	internal::set.layout.SetChannelAtIndex({{10.f, 0.f, 0.f}, {0.f, 0.f, 0.f}}, 1);
 
 	// update device list
-	Device::QueryOuputDevices();
+	Device::QueryOutputDevices();
 
 	// options for stream
 	internal::parameters.device = Pa_GetDefaultOutputDevice();
@@ -198,15 +246,15 @@
 	return internal::devices;
 }
 
-void SetListenerPosition(int x, int y, int z) {
-	internal::listenerpos.x = x;
-	internal::listenerpos.y = y;
-	internal::listenerpos.z = z;
+void SetListenerPosition(const Point3f &position) {
+	internal::listenerpos.x = position.x;
+	internal::listenerpos.y = position.y;
+	internal::listenerpos.z = position.z;
 }
 
 
 /* Device struct implementations */
-void Device::QueryOuputDevices() {
+void Device::QueryOutputDevices() {
 
 	int count = Pa_GetDeviceCount();
 
@@ -250,6 +298,6 @@
 }
 
 /* Audio class implementations */
-void Audio::Play() { internal::set.controllers.push_back(controller); }
+void AudioBase::Play() { internal::set.controllers.push_back(&controller); }
 
 } }
\ No newline at end of file
--- a/Source/Audio/Controller.h	Wed Mar 18 19:56:38 2015 +0200
+++ b/Source/Audio/Controller.h	Sat Mar 21 14:56:05 2015 +0200
@@ -2,6 +2,14 @@
 #include "../Geometry/Point.h"
 #include "../Resource/Wave.h"
 
+
+struct Point3f {
+	float x;
+	float y;
+	float z;
+};
+
+
 namespace Gorgon { namespace Audio {
 
 struct Controller {
@@ -11,11 +19,11 @@
 		NonPositional,
 	};
 
-	Controller(Type type, const Resource::Wave &resource, float volume = 1.f, float speed = 1.f):
-		type(type), resource(&resource), volume(volume), speed(speed), size(resource.GetSize()) {}
+	Controller(const Resource::Wave &resource, Type type, float volume = 1.f, float speed = 1.f):
+		resource(&resource), type(type), volume(volume), speed(speed), size(resource.GetSize()) {}
 
-	Controller(Type type, const Resource::Wave *resource, float volume = 1.f, float speed = 1.f):
-		type(type), resource(resource), volume(volume), speed(speed), size(resource->GetSize()) {}
+	Controller(const Resource::Wave *resource, Type type, float volume = 1.f, float speed = 1.f):
+		resource(resource), type(type), volume(volume), speed(speed), size(resource->GetSize()) {}
 
 	Type type;
 	const Resource::Wave *resource;
--- a/Testing/Source/Manual/Audio.cpp	Wed Mar 18 19:56:38 2015 +0200
+++ b/Testing/Source/Manual/Audio.cpp	Sat Mar 21 14:56:05 2015 +0200
@@ -125,7 +125,6 @@
 	return 0;
 }
 
-float angularspeed=2;
 
 static int playcb(const void *input, void *output, unsigned long framecnt,
 	              const PaStreamCallbackTimeInfo* time, PaStreamCallbackFlags flags, void *data)
@@ -307,6 +306,8 @@
 
 	try {
 		
+		Gorgon::Audio::SetListenerPosition({0.f, 0.f, 0.f});
+
 		Gorgon::Audio::Initialize();		
 
 		Gorgon::Resource::Wave wave1;
@@ -315,16 +316,30 @@
 		Gorgon::Resource::Wave wave2;
 		//wave2.Import("../Resources/all_u_had_2_say.wav");
 
-		Gorgon::Audio::Audio audio1(wave1, 1);
+		Gorgon::Audio::PositionalAudio audio1(wave1, {-50, -50, 0});
 		audio1.Play();
 
 		Gorgon::Audio::Audio audio2(wave2, 2);
 		//audio2.Play();
 
-		Gorgon::Audio::SetListenerPosition(1, 1, 1);
+		
 
 
-		Pa_Sleep(10000);
+		/*Pa_Sleep(10000);*/
+
+		std::chrono::time_point<std::chrono::system_clock> time = Time::now();
+		float angle = 0.f;
+		float angularspeed=2;
+		while(1) {
+			auto delta = std::chrono::duration_cast<Ms>(time - Time::now());
+		
+			angle += delta.count() * angularspeed / 1000;
+		
+			time = Time::now();
+		
+			audio1.SetPosition({100 * std::sin(angle), 100 * std::cos(angle), 0.f});	
+			
+		}
 		
 
 		Gorgon::Audio::CleanUp();

mercurial