diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/crepe/facade/Sound.cpp | 64 | ||||
| -rw-r--r-- | src/crepe/facade/Sound.h | 66 | ||||
| -rw-r--r-- | src/crepe/facade/SoundContext.h | 3 | ||||
| -rw-r--r-- | src/crepe/system/AudioSystem.cpp | 29 | ||||
| -rw-r--r-- | src/crepe/system/AudioSystem.h | 26 | ||||
| -rw-r--r-- | src/crepe/util/Private.cpp | 5 | ||||
| -rw-r--r-- | src/crepe/util/Private.h | 6 | ||||
| -rw-r--r-- | src/crepe/util/Private.hpp | 4 | ||||
| -rw-r--r-- | src/test/AudioTest.cpp | 51 | ||||
| -rw-r--r-- | src/test/PrivateTest.cpp | 92 | 
10 files changed, 229 insertions, 117 deletions
diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 52496af..0df1f48 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -13,36 +13,36 @@ Sound::Sound(const Asset & src) : Resource(src) {  }  Sound::~Sound() { dbg_trace(); } -void Sound::play(SoundContext & ctx) { -	if (ctx.engine.getPause(this->handle)) { -		// resume if paused -		ctx.engine.setPause(this->handle, false); -	} else { -		// or start new sound -		this->handle = ctx.engine.play(this->sample, this->volume); -		ctx.engine.setLooping(this->handle, this->looping); -	} -} - -void Sound::pause(SoundContext & ctx) { -	if (ctx.engine.getPause(this->handle)) return; -	ctx.engine.setPause(this->handle, true); -} - -void Sound::rewind(SoundContext & ctx) { -	if (!ctx.engine.isValidVoiceHandle(this->handle)) return; -	ctx.engine.seek(this->handle, 0); -} - -void Sound::set_volume(SoundContext & ctx, float volume) { -	this->volume = volume; -	if (!ctx.engine.isValidVoiceHandle(this->handle)) return; -	ctx.engine.setVolume(this->handle, this->volume); -} - -void Sound::set_looping(SoundContext & ctx, bool looping) { -	this->looping = looping; -	if (!ctx.engine.isValidVoiceHandle(this->handle)) return; -	ctx.engine.setLooping(this->handle, this->looping); -} +// void Sound::play(SoundContext & ctx) { +// 	if (ctx.engine.getPause(this->handle)) { +// 		// resume if paused +// 		ctx.engine.setPause(this->handle, false); +// 	} else { +// 		// or start new sound +// 		this->handle = ctx.engine.play(this->sample, this->volume); +// 		ctx.engine.setLooping(this->handle, this->looping); +// 	} +// } +//  +// void Sound::pause(SoundContext & ctx) { +// 	if (ctx.engine.getPause(this->handle)) return; +// 	ctx.engine.setPause(this->handle, true); +// } +//  +// void Sound::rewind(SoundContext & ctx) { +// 	if (!ctx.engine.isValidVoiceHandle(this->handle)) return; +// 	ctx.engine.seek(this->handle, 0); +// } +//  +// void Sound::set_volume(SoundContext & ctx, float volume) { +// 	this->volume = volume; +// 	if (!ctx.engine.isValidVoiceHandle(this->handle)) return; +// 	ctx.engine.setVolume(this->handle, this->volume); +// } +//  +// void Sound::set_looping(SoundContext & ctx, bool looping) { +// 	this->looping = looping; +// 	if (!ctx.engine.isValidVoiceHandle(this->handle)) return; +// 	ctx.engine.setLooping(this->handle, this->looping); +// } diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h index f33ee58..10d7c3c 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -9,10 +9,6 @@ namespace crepe {  class SoundContext; -struct SoundHandle { -	SoLoud::handle handle; -}; -  /**   * \brief Sound resource facade   * @@ -23,62 +19,20 @@ class Sound : public Resource {  public:  	Sound(const Asset & src);  	~Sound(); // dbg_trace -	/** -	 * \brief Pause this sample -	 * -	 * Pauses this sound if it is playing, or does nothing if it is already paused. The playhead -	 * position is saved, such that calling \c play() after this function makes the sound resume. -	 */ -	void pause(SoundContext & ctx); -	/** -	 * \brief Play this sample -	 * -	 * Resume playback if this sound is paused, or start from the beginning of the sample. -	 * -	 * \note This class only saves a reference to the most recent 'voice' of this sound. Calling -	 * \c play() while the sound is already playing causes multiple instances of the sample to -	 * play simultaniously. The sample started last is the one that is controlled afterwards. -	 */ -	void play(SoundContext & ctx); -	/** -	 * \brief Reset playhead position -	 *  -	 * Resets the playhead position so that calling \c play() after this function makes it play -	 * from the start of the sample. If the sound is not paused before calling this function, -	 * this function will stop playback. -	 */ -	void rewind(SoundContext & ctx); -	/** -	 * \brief Set playback volume / gain -	 * -	 * \param volume  Volume (0 = muted, 1 = full volume) -	 */ -	void set_volume(SoundContext & ctx, float volume); -	/** -	 * \brief Get playback volume / gain -	 * -	 * \return Volume -	 */ -	float get_volume() const { return this->volume; } -	/** -	 * \brief Set looping behavior for this sample -	 * -	 * \param looping  Looping behavior (false = one-shot, true = loop) -	 */ -	void set_looping(SoundContext & ctx, bool looping); -	/** -	 * \brief Get looping behavior -	 * -	 * \return true if looping, false if one-shot -	 */ -	bool get_looping() const { return this->looping; } + +	class Handle { +	private: +		SoLoud::handle handle; +		float volume = 1.0f; +		bool looping = false; + +		friend class SoundContext; +	};  private:  	SoLoud::Wav sample; -	SoLoud::handle handle; -	float volume = 1.0f; -	bool looping = false; +	friend class SoundContext;  };  } // namespace crepe diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h index d22ff7a..286ced8 100644 --- a/src/crepe/facade/SoundContext.h +++ b/src/crepe/facade/SoundContext.h @@ -22,10 +22,9 @@ public:  	SoundContext & operator=(const SoundContext &) = delete;  	SoundContext & operator=(SoundContext &&) = delete; +  private:  	SoLoud::Soloud engine; -	//! Sound directly calls methods on \c engine -	friend class Sound;  };  } // namespace crepe diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index 0f943be..191dbbb 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -15,18 +15,35 @@ void AudioSystem::update() {  	for (AudioSource & component : components) {  		if (!component.active) continue; -		Sound & sound = resource_manager.get<Sound>(component.source); -		if (component.private_data.empty()) -			component.private_data.set<ComponentPrivate>(); +		Sound & resource = resource_manager.get<Sound>(component.source); + +		if (component.private_data.empty()) { +			auto & data = component.private_data.set<ComponentPrivate>(); +			this->update_last(component, data); +		}  		auto & data = component.private_data.get<ComponentPrivate>(); -		// TODO: lots of state diffing +		this->diff_update(component, data, resource); + +		this->update_last(component, data); +	} +} + +void AudioSystem::diff_update(AudioSource & component, const ComponentPrivate & data, Sound & resource) { +	bool update_playing = component.playing != data.last_playing; +	bool update_volume = component.volume != data.last_volume; +	bool update_loop = component.loop != data.last_loop; +	bool update_active = component.active != data.last_active; -		this->update_private(component, data); +	if (update_active) +	if (component.rewind) { +		component.playing = false; +		// this->context.rewind(resource, data.handle);  	} +  } -void AudioSystem::update_private(const AudioSource & component, ComponentPrivate & data) { +void AudioSystem::update_last(const AudioSource & component, ComponentPrivate & data) {  	data.last_active = component.active;  	data.last_loop = component.loop;  	data.last_playing = component.playing; diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index 7f41fda..4650178 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -1,6 +1,7 @@  #pragma once  #include "../facade/SoundContext.h" +#include "../facade/Sound.h"  #include "../api/AudioSource.h"  #include "System.h" @@ -18,19 +19,22 @@ private:  	 */  	struct ComponentPrivate {  		//! This sample's voice handle -		SoLoud::handle handle; - -		//! Value of \c active after last system update -		bool last_active = false; -		//! Value of \c playing after last system update -		bool last_playing = false; -		//! Value of \c volume after last system update -		float last_volume = 1.0; -		//! Value of \c loop after last system update -		bool last_loop = false; +		Sound::Handle handle; + +		/** +		 * \name State diffing variables +		 * \{ +		 */ +		typeof(AudioSource::active) last_active; +		typeof(AudioSource::playing) last_playing; +		typeof(AudioSource::volume) last_volume; +		typeof(AudioSource::loop) last_loop; +		//! \}  	}; -	void update_private(const AudioSource & component, ComponentPrivate & data); +	void update_last(const AudioSource & component, ComponentPrivate & data); + +	void diff_update(AudioSource & component, const ComponentPrivate & data, Sound & resource);  private:  	SoundContext context {}; diff --git a/src/crepe/util/Private.cpp b/src/crepe/util/Private.cpp index c5b5b30..cb4cb5b 100644 --- a/src/crepe/util/Private.cpp +++ b/src/crepe/util/Private.cpp @@ -27,3 +27,8 @@ Private & Private::operator=(Private && other) {  	return *this;  } +Private::Private(const Private & other) { } +Private & Private::operator=(const Private & other) { +	return *this; +} + diff --git a/src/crepe/util/Private.h b/src/crepe/util/Private.h index fc3728f..6dd28bb 100644 --- a/src/crepe/util/Private.h +++ b/src/crepe/util/Private.h @@ -9,16 +9,16 @@ class Private {  public:  	Private() = default;  	~Private(); +	Private(const Private &);  	Private(Private &&); +	Private & operator=(const Private &);  	Private & operator=(Private &&); -	Private(const Private &) = delete; -	Private & operator=(const Private &) = delete;  	template <typename T>  	T & get();  	template <typename T, typename... Args> -	void set(Args &&... args); +	T & set(Args &&... args);  	bool empty() const noexcept; diff --git a/src/crepe/util/Private.hpp b/src/crepe/util/Private.hpp index 30c8146..d6ab23f 100644 --- a/src/crepe/util/Private.hpp +++ b/src/crepe/util/Private.hpp @@ -8,13 +8,15 @@  namespace crepe {  template <typename T, typename... Args> -void Private::set(Args &&... args) { +T & Private::set(Args &&... args) { +	if (!this->empty()) this->destructor(this->instance);  	T * instance = new T(std::forward<Args>(args)...);  	this->instance = static_cast<void*>(instance);  	this->destructor = [](void * instance) {  		delete static_cast<T*>(instance);  	};  	this->type = typeid(T); +	return *instance;  }  template <typename T> diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index 3be5afa..c6f0097 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -1,4 +1,5 @@  #include <gtest/gtest.h> +#include <future>  #include <crepe/manager/ComponentManager.h>  #include <crepe/manager/ResourceManager.h> @@ -7,6 +8,7 @@  #include <crepe/system/AudioSystem.h>  using namespace std; +using namespace std::chrono_literals;  using namespace crepe;  using namespace testing; @@ -17,14 +19,51 @@ public:  	ResourceManager resource_manager{mediator};  	AudioSystem system {mediator}; -	void SetUp() override { -		auto & mgr = this->component_manager; -		GameObject entity = mgr.new_object("name"); -		AudioSource & audio_source = entity.add_component<AudioSource>("mwe/audio/sfx1.wav"); -	} +private: +	GameObject entity = component_manager.new_object("name"); +public: +	AudioSource & bgm = entity.add_component<AudioSource>("mwe/audio/bgm.ogg"); +	AudioSource & sfx1 = entity.add_component<AudioSource>("mwe/audio/sfx1.wav"); +	AudioSource & sfx2 = entity.add_component<AudioSource>("mwe/audio/sfx2.wav"); +	AudioSource & sfx3 = entity.add_component<AudioSource>("mwe/audio/sfx3.wav"); +  };  TEST_F(AudioTest, Default) { -	system.update(); +	bool example_done = false; + +	future example = async([&](){ +		// Start the background track +		bgm.play(); + +		// Play each sample sequentially while pausing and resuming the background track +		this_thread::sleep_for(500ms); +		sfx1.play(); +		this_thread::sleep_for(500ms); +		sfx2.play(); +		bgm.stop(); +		this_thread::sleep_for(500ms); +		sfx3.play(); +		bgm.play(); +		this_thread::sleep_for(500ms); + +		// Play all samples simultaniously +		sfx1.play(); +		sfx2.play(); +		sfx3.play(); +		this_thread::sleep_for(1000ms); +	}); + +	future system_loop = async([&](){ +		while (!example_done) { +			auto next = chrono::steady_clock::now() + 25ms; +			system.update(); +			this_thread::sleep_until(next); +		} +	}); + +	example.wait(); +	example_done = true; +	system_loop.wait();  } diff --git a/src/test/PrivateTest.cpp b/src/test/PrivateTest.cpp index f0d2b1a..0ea67d6 100644 --- a/src/test/PrivateTest.cpp +++ b/src/test/PrivateTest.cpp @@ -64,3 +64,95 @@ TEST_F(PrivateTest, IncorrectTypeException) {  	EXPECT_NO_THROW(foo.get<TestClass>());  } +TEST_F(PrivateTest, MoveConstructor) { +	{ +		Private foo; +		foo.set<TestClass>(); + +		EXPECT_EQ(PrivateTest::constructors, 1); +		EXPECT_EQ(PrivateTest::destructors, 0); + +		Private bar(std::move(foo)); + +		EXPECT_EQ(PrivateTest::constructors, 1); +		EXPECT_EQ(PrivateTest::destructors, 0); +	} + +	EXPECT_EQ(PrivateTest::constructors, 1); +	EXPECT_EQ(PrivateTest::destructors, 1); +} + +TEST_F(PrivateTest, MoveOperator) { +	{ +		Private foo; +		foo.set<TestClass>(); + +		EXPECT_EQ(PrivateTest::constructors, 1); +		EXPECT_EQ(PrivateTest::destructors, 0); + +		Private bar = std::move(foo); + +		EXPECT_EQ(PrivateTest::constructors, 1); +		EXPECT_EQ(PrivateTest::destructors, 0); +	} + +	EXPECT_EQ(PrivateTest::constructors, 1); +	EXPECT_EQ(PrivateTest::destructors, 1); +} + +TEST_F(PrivateTest, CopyConstructor) { +	{ +		Private foo; +		foo.set<TestClass>(); + +		EXPECT_EQ(PrivateTest::constructors, 1); +		EXPECT_EQ(PrivateTest::destructors, 0); + +		Private bar(foo); + +		EXPECT_TRUE(bar.empty()); +		EXPECT_EQ(PrivateTest::constructors, 1); +		EXPECT_EQ(PrivateTest::destructors, 0); +	} + +	EXPECT_EQ(PrivateTest::constructors, 1); +	EXPECT_EQ(PrivateTest::destructors, 1); +} + +TEST_F(PrivateTest, CopyOperator) { +	{ +		Private foo; +		foo.set<TestClass>(); + +		EXPECT_EQ(PrivateTest::constructors, 1); +		EXPECT_EQ(PrivateTest::destructors, 0); + +		Private bar = foo; + +		EXPECT_TRUE(bar.empty()); +		EXPECT_EQ(PrivateTest::constructors, 1); +		EXPECT_EQ(PrivateTest::destructors, 0); +	} + +	EXPECT_EQ(PrivateTest::constructors, 1); +	EXPECT_EQ(PrivateTest::destructors, 1); +} + +TEST_F(PrivateTest, DoubleAssignment) { +	{ +		Private foo; +		foo.set<TestClass>(); + +		EXPECT_EQ(PrivateTest::constructors, 1); +		EXPECT_EQ(PrivateTest::destructors, 0); + +		foo.set<TestClass>(); + +		EXPECT_EQ(PrivateTest::constructors, 2); +		EXPECT_EQ(PrivateTest::destructors, 1); +	} + +	EXPECT_EQ(PrivateTest::constructors, 2); +	EXPECT_EQ(PrivateTest::destructors, 2); +} +  |