mirror of
				https://github.com/NoelFB/blah.git
				synced 2025-11-04 01:41:34 +08:00 
			
		
		
		
	added audio enabled flag
This commit is contained in:
		
							
								
								
									
										888
									
								
								src/blah_app.cpp
									
									
									
									
									
								
							
							
						
						
									
										888
									
								
								src/blah_app.cpp
									
									
									
									
									
								
							@ -1,442 +1,448 @@
 | 
			
		||||
#include <blah_app.h>
 | 
			
		||||
#include <blah_common.h>
 | 
			
		||||
#include <blah_time.h>
 | 
			
		||||
#include "internal/blah_internal.h"
 | 
			
		||||
#include "internal/blah_platform.h"
 | 
			
		||||
#include "internal/blah_renderer.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
#include <emscripten.h>
 | 
			
		||||
#include <emscripten/html5.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
// Internal Audio bool
 | 
			
		||||
bool Internal::audio_is_init = false;
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
	// Global App State
 | 
			
		||||
	Config     app_config;
 | 
			
		||||
	bool       app_is_running = false;
 | 
			
		||||
	bool       app_is_exiting = false;
 | 
			
		||||
	u64        app_time_last;
 | 
			
		||||
	u64        app_time_accumulator = 0;
 | 
			
		||||
	u32        app_flags = 0;
 | 
			
		||||
	TargetRef  app_backbuffer;
 | 
			
		||||
	Renderer*  app_renderer_api;
 | 
			
		||||
 | 
			
		||||
	void get_drawable_size(int* w, int* h)
 | 
			
		||||
	{
 | 
			
		||||
		// Some renderer implementations might return their own size
 | 
			
		||||
		if (app_renderer_api->get_draw_size(w, h))
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		// otherwise fallback to the platform size
 | 
			
		||||
		Platform::get_draw_size(w, h);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// A dummy Target that represents the Back Buffer.
 | 
			
		||||
	// It doesn't contain any data, rather it forwards calls along to the actual BackBuffer.
 | 
			
		||||
	class BackBuffer final : public Target
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		Attachments empty_textures;
 | 
			
		||||
		Attachments& textures() override { BLAH_ASSERT(false, "Backbuffer doesn't have any textures"); return empty_textures; }
 | 
			
		||||
		const Attachments& textures() const override { BLAH_ASSERT(false, "Backbuffer doesn't have any textures"); return empty_textures; }
 | 
			
		||||
		int width() const override { int w, h; get_drawable_size(&w, &h); return w; }
 | 
			
		||||
		int height() const override { int w, h; get_drawable_size(&w, &h); return h; }
 | 
			
		||||
		void clear(Color color, float depth, u8 stencil, ClearMask mask) override
 | 
			
		||||
		{
 | 
			
		||||
			BLAH_ASSERT_RENDERER();
 | 
			
		||||
			if (app_renderer_api)
 | 
			
		||||
				app_renderer_api->clear_backbuffer(color, depth, stencil, mask);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::run(const Config* c)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT(!app_is_running, "The Application is already running");
 | 
			
		||||
 | 
			
		||||
	// copy config over
 | 
			
		||||
	app_config = *c;
 | 
			
		||||
 | 
			
		||||
	// exit the application by default
 | 
			
		||||
	if (!app_config.on_exit_request)
 | 
			
		||||
		app_config.on_exit_request = App::exit;
 | 
			
		||||
 | 
			
		||||
	// default renderer type
 | 
			
		||||
	if (app_config.renderer_type == RendererType::None)
 | 
			
		||||
		app_config.renderer_type = Renderer::default_type();
 | 
			
		||||
 | 
			
		||||
	// exit out if setup is wrong
 | 
			
		||||
	BLAH_ASSERT(c != nullptr, "The Application requires a valid Config");
 | 
			
		||||
	BLAH_ASSERT(c->name != nullptr, "The Application Name cannot be null");
 | 
			
		||||
	BLAH_ASSERT(c->width > 0 && c->height > 0, "The Width and Height must be larget than 0");
 | 
			
		||||
	BLAH_ASSERT(c->max_updates > 0, "Max Updates must be >= 1");
 | 
			
		||||
	BLAH_ASSERT(c->target_framerate > 0, "Target Framerate must be >= 1");
 | 
			
		||||
 | 
			
		||||
	if (app_is_running || c == nullptr || c->width <= 0 || c->height <= 0 || c->max_updates <= 0 || c->target_framerate <= 0)
 | 
			
		||||
	{
 | 
			
		||||
		Internal::app_shutdown();
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// default values
 | 
			
		||||
	app_is_running = true;
 | 
			
		||||
	app_is_exiting = false;
 | 
			
		||||
	app_flags = app_config.flags;
 | 
			
		||||
	app_backbuffer = TargetRef(new BackBuffer());
 | 
			
		||||
 | 
			
		||||
	// initialize the system
 | 
			
		||||
	{
 | 
			
		||||
		if (!Platform::init(app_config))
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Failed to initialize Platform module");
 | 
			
		||||
			Internal::app_shutdown();
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// initialize audio
 | 
			
		||||
	{
 | 
			
		||||
		if (!Internal::audio_is_init) {
 | 
			
		||||
			int more_on_emscripten = 1;
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
			more_on_emscripten = 4;
 | 
			
		||||
#endif
 | 
			
		||||
			Internal::audio_is_init = Internal::audio_init(c->audio_frequency_in_Hz, 1024 * more_on_emscripten);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// initialize graphics
 | 
			
		||||
	{
 | 
			
		||||
		app_renderer_api = Renderer::try_make_renderer(app_config.renderer_type);
 | 
			
		||||
		if (app_renderer_api == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Renderer module was not found");
 | 
			
		||||
			Internal::app_shutdown();
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!app_renderer_api->init())
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Failed to initialize Renderer module");
 | 
			
		||||
			Internal::app_shutdown();
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// apply default flags
 | 
			
		||||
	Platform::set_app_flags(app_flags);
 | 
			
		||||
	app_renderer_api->set_app_flags(app_flags);
 | 
			
		||||
 | 
			
		||||
	// input + poll the platform once
 | 
			
		||||
	Internal::input_init();
 | 
			
		||||
	Internal::input_step_state();
 | 
			
		||||
	Platform::update(Input::state);
 | 
			
		||||
 | 
			
		||||
	// startup
 | 
			
		||||
	if (app_config.on_startup != nullptr)
 | 
			
		||||
		app_config.on_startup();
 | 
			
		||||
	app_time_last = Platform::ticks();
 | 
			
		||||
	app_time_accumulator = 0;
 | 
			
		||||
 | 
			
		||||
	// display window
 | 
			
		||||
	Platform::ready();
 | 
			
		||||
 | 
			
		||||
	// Begin main loop
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
	emscripten_set_main_loop(Internal::iterate, 0, 1);
 | 
			
		||||
#else
 | 
			
		||||
	while (!app_is_exiting)
 | 
			
		||||
		Internal::app_step();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// shutdown
 | 
			
		||||
	if (app_config.on_shutdown != nullptr)
 | 
			
		||||
		app_config.on_shutdown();
 | 
			
		||||
	Internal::app_shutdown();
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::is_running()
 | 
			
		||||
{
 | 
			
		||||
	return app_is_running;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Internal::app_step()
 | 
			
		||||
{
 | 
			
		||||
	static const auto step = []()
 | 
			
		||||
	{
 | 
			
		||||
		Internal::input_step_state();
 | 
			
		||||
		Platform::update(Input::state);
 | 
			
		||||
		Internal::input_step_bindings();
 | 
			
		||||
		app_renderer_api->update();
 | 
			
		||||
		if (app_config.on_update != nullptr)
 | 
			
		||||
			app_config.on_update();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool is_fixed_timestep = App::get_flag(Flags::FixedTimestep);
 | 
			
		||||
 | 
			
		||||
	// Update in Fixed Timestep
 | 
			
		||||
	if (is_fixed_timestep)
 | 
			
		||||
	{
 | 
			
		||||
		u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second);
 | 
			
		||||
		u64 ticks_curr = Platform::ticks();
 | 
			
		||||
		u64 ticks_diff = ticks_curr - app_time_last;
 | 
			
		||||
		app_time_last = ticks_curr;
 | 
			
		||||
		app_time_accumulator += ticks_diff;
 | 
			
		||||
 | 
			
		||||
		// do not let us run too fast
 | 
			
		||||
		while (app_time_accumulator < time_target)
 | 
			
		||||
		{
 | 
			
		||||
			int milliseconds = (int)(time_target - app_time_accumulator) / (Time::ticks_per_second / 1000);
 | 
			
		||||
			Platform::sleep(milliseconds);
 | 
			
		||||
 | 
			
		||||
			ticks_curr = Platform::ticks();
 | 
			
		||||
			ticks_diff = ticks_curr - app_time_last;
 | 
			
		||||
			app_time_last = ticks_curr;
 | 
			
		||||
			app_time_accumulator += ticks_diff;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Do not allow us to fall behind too many updates
 | 
			
		||||
		// (otherwise we'll get spiral of death)
 | 
			
		||||
		u64 time_maximum = app_config.max_updates * time_target;
 | 
			
		||||
		if (app_time_accumulator > time_maximum)
 | 
			
		||||
			app_time_accumulator = time_maximum;
 | 
			
		||||
 | 
			
		||||
		// do as many updates as we can
 | 
			
		||||
		while (app_time_accumulator >= time_target)
 | 
			
		||||
		{
 | 
			
		||||
			app_time_accumulator -= time_target;
 | 
			
		||||
 | 
			
		||||
			Time::delta = (1.0f / app_config.target_framerate);
 | 
			
		||||
 | 
			
		||||
			if (Time::pause_timer > 0)
 | 
			
		||||
			{
 | 
			
		||||
				Time::pause_timer -= Time::delta;
 | 
			
		||||
				if (Time::pause_timer <= -0.0001)
 | 
			
		||||
					Time::delta = -Time::pause_timer;
 | 
			
		||||
				else
 | 
			
		||||
					continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Time::previous_ticks = Time::ticks;
 | 
			
		||||
			Time::ticks += time_target;
 | 
			
		||||
			Time::previous_seconds = Time::seconds;
 | 
			
		||||
			Time::seconds += Time::delta;
 | 
			
		||||
 | 
			
		||||
			step();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Update with Variable Timestep
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		u64 ticks_curr = Platform::ticks();
 | 
			
		||||
		u64 ticks_diff = ticks_curr - app_time_last;
 | 
			
		||||
		app_time_last = ticks_curr;
 | 
			
		||||
		app_time_accumulator += ticks_diff;
 | 
			
		||||
 | 
			
		||||
		Time::delta = ticks_diff / (float)Time::ticks_per_second;
 | 
			
		||||
 | 
			
		||||
		if (Time::pause_timer > 0)
 | 
			
		||||
		{
 | 
			
		||||
			Time::pause_timer -= Time::delta;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			Time::previous_ticks = Time::ticks;
 | 
			
		||||
			Time::ticks += ticks_diff;
 | 
			
		||||
			Time::previous_seconds = Time::seconds;
 | 
			
		||||
			Time::seconds += Time::delta;
 | 
			
		||||
 | 
			
		||||
			step();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Draw Frame
 | 
			
		||||
	{
 | 
			
		||||
		app_renderer_api->before_render();
 | 
			
		||||
		if (app_config.on_render != nullptr)
 | 
			
		||||
			app_config.on_render();
 | 
			
		||||
		app_renderer_api->after_render();
 | 
			
		||||
		Platform::present();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update audio
 | 
			
		||||
	if (Internal::audio_is_init)
 | 
			
		||||
		Blah::Internal::audio_update();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Internal::app_shutdown()
 | 
			
		||||
{
 | 
			
		||||
	Internal::input_shutdown();
 | 
			
		||||
 | 
			
		||||
	if (app_renderer_api)
 | 
			
		||||
	{
 | 
			
		||||
		app_renderer_api->shutdown();
 | 
			
		||||
		delete app_renderer_api;
 | 
			
		||||
	}
 | 
			
		||||
	app_renderer_api = nullptr;
 | 
			
		||||
 | 
			
		||||
	if (Internal::audio_is_init)
 | 
			
		||||
	{
 | 
			
		||||
		Internal::audio_shutdown();
 | 
			
		||||
		Internal::audio_is_init = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Platform::shutdown();
 | 
			
		||||
 | 
			
		||||
	// clear static App state
 | 
			
		||||
	app_config = Config();
 | 
			
		||||
	app_is_running = false;
 | 
			
		||||
	app_is_exiting = false;
 | 
			
		||||
	app_time_last = 0;
 | 
			
		||||
	app_time_accumulator = 0;
 | 
			
		||||
	app_backbuffer = TargetRef();
 | 
			
		||||
 | 
			
		||||
	// clear static Time state
 | 
			
		||||
	Time::ticks = 0;
 | 
			
		||||
	Time::seconds = 0;
 | 
			
		||||
	Time::previous_ticks = 0;
 | 
			
		||||
	Time::previous_seconds = 0;
 | 
			
		||||
	Time::delta = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Renderer* Internal::app_renderer()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return app_renderer_api;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::exit()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	if (!app_is_exiting && app_is_running)
 | 
			
		||||
		app_is_exiting = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Config& App::config()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return app_config;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::path()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::app_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::user_path()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::user_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::get_title()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::get_title();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_title(const char* title)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::set_title(title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_position()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Point result;
 | 
			
		||||
	Platform::get_position(&result.x, &result.y);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_position(Point point)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::set_position(point.x, point.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_size()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Point result;
 | 
			
		||||
	Platform::get_size(&result.x, &result.y);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_size(Point point)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::set_size(point.x, point.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_backbuffer_size()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	if (app_renderer_api)
 | 
			
		||||
		return Point(app_backbuffer->width(), app_backbuffer->height());
 | 
			
		||||
	return Point(0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float App::content_scale()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::get_content_scale();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::focused()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::get_focused();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_flag(u32 flag, bool enabled)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
 | 
			
		||||
	u32 was = app_flags;
 | 
			
		||||
 | 
			
		||||
	if (enabled)
 | 
			
		||||
		app_flags |= flag;
 | 
			
		||||
	else
 | 
			
		||||
		app_flags &= ~flag;
 | 
			
		||||
 | 
			
		||||
	if (was != app_flags)
 | 
			
		||||
	{
 | 
			
		||||
		Platform::set_app_flags(app_flags);
 | 
			
		||||
		if (app_renderer_api)
 | 
			
		||||
			app_renderer_api->set_app_flags(app_flags);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::get_flag(u32 flag)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return ((app_flags & flag) == flag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const RendererInfo& App::renderer()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	BLAH_ASSERT_RENDERER();
 | 
			
		||||
	return app_renderer_api->info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const TargetRef& App::backbuffer()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return app_backbuffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::open_url(const char* url)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::open_url(url);
 | 
			
		||||
#include <blah_app.h>
 | 
			
		||||
#include <blah_common.h>
 | 
			
		||||
#include <blah_time.h>
 | 
			
		||||
#include "internal/blah_internal.h"
 | 
			
		||||
#include "internal/blah_platform.h"
 | 
			
		||||
#include "internal/blah_renderer.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
#include <emscripten.h>
 | 
			
		||||
#include <emscripten/html5.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
	// Global App State
 | 
			
		||||
	Config     app_config;
 | 
			
		||||
	bool       app_is_running = false;
 | 
			
		||||
	bool       app_is_exiting = false;
 | 
			
		||||
	bool       app_is_audio_running = false;
 | 
			
		||||
	u64        app_time_last;
 | 
			
		||||
	u64        app_time_accumulator = 0;
 | 
			
		||||
	u32        app_flags = 0;
 | 
			
		||||
	TargetRef  app_backbuffer;
 | 
			
		||||
	Renderer*  app_renderer_api;
 | 
			
		||||
 | 
			
		||||
	void get_drawable_size(int* w, int* h)
 | 
			
		||||
	{
 | 
			
		||||
		// Some renderer implementations might return their own size
 | 
			
		||||
		if (app_renderer_api->get_draw_size(w, h))
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		// otherwise fallback to the platform size
 | 
			
		||||
		Platform::get_draw_size(w, h);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_audio_system(bool on)
 | 
			
		||||
	{
 | 
			
		||||
		if (on && !app_is_audio_running)
 | 
			
		||||
		{
 | 
			
		||||
			int more_on_emscripten = 1;
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
			more_on_emscripten = 4;
 | 
			
		||||
#endif
 | 
			
		||||
			app_is_audio_running = Internal::audio_init(app_config.audio_frequency_in_Hz, 1024 * more_on_emscripten);
 | 
			
		||||
		}
 | 
			
		||||
		else if (!on && app_is_audio_running)
 | 
			
		||||
		{
 | 
			
		||||
			Internal::audio_shutdown();
 | 
			
		||||
			app_is_audio_running = false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// A dummy Target that represents the Back Buffer.
 | 
			
		||||
	// It doesn't contain any data, rather it forwards calls along to the actual BackBuffer.
 | 
			
		||||
	class BackBuffer final : public Target
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		Attachments empty_textures;
 | 
			
		||||
		Attachments& textures() override { BLAH_ASSERT(false, "Backbuffer doesn't have any textures"); return empty_textures; }
 | 
			
		||||
		const Attachments& textures() const override { BLAH_ASSERT(false, "Backbuffer doesn't have any textures"); return empty_textures; }
 | 
			
		||||
		int width() const override { int w, h; get_drawable_size(&w, &h); return w; }
 | 
			
		||||
		int height() const override { int w, h; get_drawable_size(&w, &h); return h; }
 | 
			
		||||
		void clear(Color color, float depth, u8 stencil, ClearMask mask) override
 | 
			
		||||
		{
 | 
			
		||||
			BLAH_ASSERT_RENDERER();
 | 
			
		||||
			if (app_renderer_api)
 | 
			
		||||
				app_renderer_api->clear_backbuffer(color, depth, stencil, mask);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::run(const Config* c)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT(!app_is_running, "The Application is already running");
 | 
			
		||||
 | 
			
		||||
	// copy config over
 | 
			
		||||
	app_config = *c;
 | 
			
		||||
 | 
			
		||||
	// exit the application by default
 | 
			
		||||
	if (!app_config.on_exit_request)
 | 
			
		||||
		app_config.on_exit_request = App::exit;
 | 
			
		||||
 | 
			
		||||
	// default renderer type
 | 
			
		||||
	if (app_config.renderer_type == RendererType::None)
 | 
			
		||||
		app_config.renderer_type = Renderer::default_type();
 | 
			
		||||
 | 
			
		||||
	// exit out if setup is wrong
 | 
			
		||||
	BLAH_ASSERT(c != nullptr, "The Application requires a valid Config");
 | 
			
		||||
	BLAH_ASSERT(c->name != nullptr, "The Application Name cannot be null");
 | 
			
		||||
	BLAH_ASSERT(c->width > 0 && c->height > 0, "The Width and Height must be larget than 0");
 | 
			
		||||
	BLAH_ASSERT(c->max_updates > 0, "Max Updates must be >= 1");
 | 
			
		||||
	BLAH_ASSERT(c->target_framerate > 0, "Target Framerate must be >= 1");
 | 
			
		||||
 | 
			
		||||
	if (app_is_running || c == nullptr || c->width <= 0 || c->height <= 0 || c->max_updates <= 0 || c->target_framerate <= 0)
 | 
			
		||||
	{
 | 
			
		||||
		Internal::app_shutdown();
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// default values
 | 
			
		||||
	app_is_running = true;
 | 
			
		||||
	app_is_exiting = false;
 | 
			
		||||
	app_flags = app_config.flags;
 | 
			
		||||
	app_backbuffer = TargetRef(new BackBuffer());
 | 
			
		||||
 | 
			
		||||
	// initialize the system
 | 
			
		||||
	{
 | 
			
		||||
		if (!Platform::init(app_config))
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Failed to initialize Platform module");
 | 
			
		||||
			Internal::app_shutdown();
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// initialize audio
 | 
			
		||||
	if (get_flag(Flags::AudioEnabled))
 | 
			
		||||
		set_audio_system(true);
 | 
			
		||||
 | 
			
		||||
	// initialize graphics
 | 
			
		||||
	{
 | 
			
		||||
		app_renderer_api = Renderer::try_make_renderer(app_config.renderer_type);
 | 
			
		||||
		if (app_renderer_api == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Renderer module was not found");
 | 
			
		||||
			Internal::app_shutdown();
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!app_renderer_api->init())
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Failed to initialize Renderer module");
 | 
			
		||||
			Internal::app_shutdown();
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// apply default flags
 | 
			
		||||
	Platform::set_app_flags(app_flags);
 | 
			
		||||
	app_renderer_api->set_app_flags(app_flags);
 | 
			
		||||
 | 
			
		||||
	// input + poll the platform once
 | 
			
		||||
	Internal::input_init();
 | 
			
		||||
	Internal::input_step_state();
 | 
			
		||||
	Platform::update(Input::state);
 | 
			
		||||
 | 
			
		||||
	// startup
 | 
			
		||||
	if (app_config.on_startup != nullptr)
 | 
			
		||||
		app_config.on_startup();
 | 
			
		||||
	app_time_last = Platform::ticks();
 | 
			
		||||
	app_time_accumulator = 0;
 | 
			
		||||
 | 
			
		||||
	// display window
 | 
			
		||||
	Platform::ready();
 | 
			
		||||
 | 
			
		||||
	// Begin main loop
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
	emscripten_set_main_loop(Internal::iterate, 0, 1);
 | 
			
		||||
#else
 | 
			
		||||
	while (!app_is_exiting)
 | 
			
		||||
		Internal::app_step();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// shutdown
 | 
			
		||||
	if (app_config.on_shutdown != nullptr)
 | 
			
		||||
		app_config.on_shutdown();
 | 
			
		||||
	Internal::app_shutdown();
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::is_running()
 | 
			
		||||
{
 | 
			
		||||
	return app_is_running;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Internal::app_step()
 | 
			
		||||
{
 | 
			
		||||
	static const auto step = []()
 | 
			
		||||
	{
 | 
			
		||||
		Internal::input_step_state();
 | 
			
		||||
		Platform::update(Input::state);
 | 
			
		||||
		Internal::input_step_bindings();
 | 
			
		||||
		app_renderer_api->update();
 | 
			
		||||
		if (app_config.on_update != nullptr)
 | 
			
		||||
			app_config.on_update();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool is_fixed_timestep = App::get_flag(Flags::FixedTimestep);
 | 
			
		||||
 | 
			
		||||
	// Update in Fixed Timestep
 | 
			
		||||
	if (is_fixed_timestep)
 | 
			
		||||
	{
 | 
			
		||||
		u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second);
 | 
			
		||||
		u64 ticks_curr = Platform::ticks();
 | 
			
		||||
		u64 ticks_diff = ticks_curr - app_time_last;
 | 
			
		||||
		app_time_last = ticks_curr;
 | 
			
		||||
		app_time_accumulator += ticks_diff;
 | 
			
		||||
 | 
			
		||||
		// do not let us run too fast
 | 
			
		||||
		while (app_time_accumulator < time_target)
 | 
			
		||||
		{
 | 
			
		||||
			int milliseconds = (int)(time_target - app_time_accumulator) / (Time::ticks_per_second / 1000);
 | 
			
		||||
			Platform::sleep(milliseconds);
 | 
			
		||||
 | 
			
		||||
			ticks_curr = Platform::ticks();
 | 
			
		||||
			ticks_diff = ticks_curr - app_time_last;
 | 
			
		||||
			app_time_last = ticks_curr;
 | 
			
		||||
			app_time_accumulator += ticks_diff;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Do not allow us to fall behind too many updates
 | 
			
		||||
		// (otherwise we'll get spiral of death)
 | 
			
		||||
		u64 time_maximum = app_config.max_updates * time_target;
 | 
			
		||||
		if (app_time_accumulator > time_maximum)
 | 
			
		||||
			app_time_accumulator = time_maximum;
 | 
			
		||||
 | 
			
		||||
		// do as many updates as we can
 | 
			
		||||
		while (app_time_accumulator >= time_target)
 | 
			
		||||
		{
 | 
			
		||||
			app_time_accumulator -= time_target;
 | 
			
		||||
 | 
			
		||||
			Time::delta = (1.0f / app_config.target_framerate);
 | 
			
		||||
 | 
			
		||||
			if (Time::pause_timer > 0)
 | 
			
		||||
			{
 | 
			
		||||
				Time::pause_timer -= Time::delta;
 | 
			
		||||
				if (Time::pause_timer <= -0.0001)
 | 
			
		||||
					Time::delta = -Time::pause_timer;
 | 
			
		||||
				else
 | 
			
		||||
					continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Time::previous_ticks = Time::ticks;
 | 
			
		||||
			Time::ticks += time_target;
 | 
			
		||||
			Time::previous_seconds = Time::seconds;
 | 
			
		||||
			Time::seconds += Time::delta;
 | 
			
		||||
 | 
			
		||||
			step();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Update with Variable Timestep
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		u64 ticks_curr = Platform::ticks();
 | 
			
		||||
		u64 ticks_diff = ticks_curr - app_time_last;
 | 
			
		||||
		app_time_last = ticks_curr;
 | 
			
		||||
		app_time_accumulator += ticks_diff;
 | 
			
		||||
 | 
			
		||||
		Time::delta = ticks_diff / (float)Time::ticks_per_second;
 | 
			
		||||
 | 
			
		||||
		if (Time::pause_timer > 0)
 | 
			
		||||
		{
 | 
			
		||||
			Time::pause_timer -= Time::delta;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			Time::previous_ticks = Time::ticks;
 | 
			
		||||
			Time::ticks += ticks_diff;
 | 
			
		||||
			Time::previous_seconds = Time::seconds;
 | 
			
		||||
			Time::seconds += Time::delta;
 | 
			
		||||
 | 
			
		||||
			step();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Draw Frame
 | 
			
		||||
	{
 | 
			
		||||
		app_renderer_api->before_render();
 | 
			
		||||
		if (app_config.on_render != nullptr)
 | 
			
		||||
			app_config.on_render();
 | 
			
		||||
		app_renderer_api->after_render();
 | 
			
		||||
		Platform::present();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update audio
 | 
			
		||||
	if (app_is_audio_running)
 | 
			
		||||
		Blah::Internal::audio_update();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Internal::app_shutdown()
 | 
			
		||||
{
 | 
			
		||||
	// shutdown systems
 | 
			
		||||
	Internal::input_shutdown();
 | 
			
		||||
	if (app_renderer_api)
 | 
			
		||||
	{
 | 
			
		||||
		app_renderer_api->shutdown();
 | 
			
		||||
		delete app_renderer_api;
 | 
			
		||||
		app_renderer_api = nullptr;
 | 
			
		||||
	}
 | 
			
		||||
	set_audio_system(false);
 | 
			
		||||
	Platform::shutdown();
 | 
			
		||||
 | 
			
		||||
	// clear static App state
 | 
			
		||||
	app_config = Config();
 | 
			
		||||
	app_is_running = false;
 | 
			
		||||
	app_is_exiting = false;
 | 
			
		||||
	app_time_last = 0;
 | 
			
		||||
	app_time_accumulator = 0;
 | 
			
		||||
	app_backbuffer = TargetRef();
 | 
			
		||||
 | 
			
		||||
	// clear static Time state
 | 
			
		||||
	Time::ticks = 0;
 | 
			
		||||
	Time::seconds = 0;
 | 
			
		||||
	Time::previous_ticks = 0;
 | 
			
		||||
	Time::previous_seconds = 0;
 | 
			
		||||
	Time::delta = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Renderer* Internal::app_renderer()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return app_renderer_api;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::exit()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	if (!app_is_exiting && app_is_running)
 | 
			
		||||
		app_is_exiting = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Config& App::config()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return app_config;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::path()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::app_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::user_path()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::user_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::get_title()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::get_title();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_title(const char* title)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::set_title(title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_position()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Point result;
 | 
			
		||||
	Platform::get_position(&result.x, &result.y);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_position(Point point)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::set_position(point.x, point.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_size()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Point result;
 | 
			
		||||
	Platform::get_size(&result.x, &result.y);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_size(Point point)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::set_size(point.x, point.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_backbuffer_size()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	if (app_renderer_api)
 | 
			
		||||
		return Point(app_backbuffer->width(), app_backbuffer->height());
 | 
			
		||||
	return Point(0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float App::content_scale()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::get_content_scale();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::focused()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::get_focused();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_flag(u32 flag, bool enabled)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
 | 
			
		||||
	u32 was = app_flags;
 | 
			
		||||
 | 
			
		||||
	if (enabled)
 | 
			
		||||
		app_flags |= flag;
 | 
			
		||||
	else
 | 
			
		||||
		app_flags &= ~flag;
 | 
			
		||||
 | 
			
		||||
	if (was != app_flags)
 | 
			
		||||
	{
 | 
			
		||||
		// tell platform & renderer
 | 
			
		||||
		Platform::set_app_flags(app_flags);
 | 
			
		||||
		if (app_renderer_api)
 | 
			
		||||
			app_renderer_api->set_app_flags(app_flags);
 | 
			
		||||
 | 
			
		||||
		// potentially toggle audio system
 | 
			
		||||
		set_audio_system(get_flag(Flags::AudioEnabled));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::get_flag(u32 flag)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return ((app_flags & flag) == flag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const RendererInfo& App::renderer()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	BLAH_ASSERT_RENDERER();
 | 
			
		||||
	return app_renderer_api->info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const TargetRef& App::backbuffer()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return app_backbuffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::open_url(const char* url)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::open_url(url);
 | 
			
		||||
}
 | 
			
		||||
@ -1,272 +1,272 @@
 | 
			
		||||
#include "blah_audio.h"
 | 
			
		||||
#include "blah_time.h"
 | 
			
		||||
#include "internal/blah_internal.h"
 | 
			
		||||
 | 
			
		||||
#define STB_VORBIS_HEADER_ONLY
 | 
			
		||||
#include "third_party/stb_vorbis.c"
 | 
			
		||||
 | 
			
		||||
#ifdef BLAH_PLATFORM_SDL2
 | 
			
		||||
#define CUTE_SOUND_FORCE_SDL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define CUTE_SOUND_IMPLEMENTATION
 | 
			
		||||
#include "third_party/cute_sound.h"
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	namespace Internal
 | 
			
		||||
	{
 | 
			
		||||
		bool audio_init(unsigned play_frequency_in_Hz, int buffered_samples)
 | 
			
		||||
		{
 | 
			
		||||
			cs_error_t err = cs_init(Platform::d3d11_get_hwnd(), play_frequency_in_Hz, buffered_samples, NULL);
 | 
			
		||||
			if (err != CUTE_SOUND_ERROR_NONE) {
 | 
			
		||||
				Log::error(cs_error_as_string(err));
 | 
			
		||||
				return false;
 | 
			
		||||
			} else {
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
#ifndef BLAH_NO_THREADING
 | 
			
		||||
			cs_spawn_mix_thread();
 | 
			
		||||
#endif
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void audio_shutdown()
 | 
			
		||||
		{
 | 
			
		||||
			cs_shutdown();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void audio_update()
 | 
			
		||||
		{
 | 
			
		||||
			cs_update(Time::delta);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Audio::Audio(void* audio)
 | 
			
		||||
	{
 | 
			
		||||
		m_ptr = audio;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Audio::~Audio()
 | 
			
		||||
	{
 | 
			
		||||
		cs_free_audio_source((cs_audio_source_t*)m_ptr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u64 Audio::get_sample_count()
 | 
			
		||||
	{
 | 
			
		||||
		return m_ptr ? ((cs_audio_source_t*)m_ptr)->sample_count : 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	AudioRef Audio::create(const FilePath& path)
 | 
			
		||||
	{
 | 
			
		||||
		FileStream fs(path, FileMode::OpenRead);
 | 
			
		||||
		if (!fs.is_readable())
 | 
			
		||||
			return AudioRef();
 | 
			
		||||
 | 
			
		||||
		if (path.ends_with(".ogg")) {
 | 
			
		||||
			return create_from_ogg(fs);
 | 
			
		||||
		} else if (path.ends_with(".wav")) {
 | 
			
		||||
			return create_from_wav(fs);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Log::error("Unable to load unrecognized audio file type.");
 | 
			
		||||
		return AudioRef();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	AudioRef Audio::create_from_wav(Stream& stream)
 | 
			
		||||
	{
 | 
			
		||||
		if (!stream.is_readable())
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Unable to load audio as the Stream was not readable");
 | 
			
		||||
			return AudioRef();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// read into buffer
 | 
			
		||||
		Vector<u8> buffer;
 | 
			
		||||
		buffer.reserve((int)stream.length());
 | 
			
		||||
		stream.read(buffer.data(), stream.length());
 | 
			
		||||
 | 
			
		||||
		// load wav file from memory using cute_sound.h
 | 
			
		||||
		cs_error_t err;
 | 
			
		||||
		void* audio = cs_read_mem_wav((void*)buffer.data(), stream.length(), &err);
 | 
			
		||||
		if (!audio) {
 | 
			
		||||
			Log::error(cs_error_as_string(err));
 | 
			
		||||
			return AudioRef();
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			return AudioRef(new Audio(audio));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	AudioRef Audio::create_from_ogg(Stream& stream)
 | 
			
		||||
	{
 | 
			
		||||
		if (!stream.is_readable())
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Unable to load audio as the Stream was not readable");
 | 
			
		||||
			return AudioRef();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// read into buffer
 | 
			
		||||
		Vector<u8> buffer;
 | 
			
		||||
		buffer.reserve((int)stream.length());
 | 
			
		||||
		stream.read(buffer.data(), stream.length());
 | 
			
		||||
 | 
			
		||||
		// load ogg file from memory using cute_sound.h
 | 
			
		||||
		cs_error_t err;
 | 
			
		||||
		void* audio = cs_read_mem_ogg((void*)buffer.data(), stream.length(), &err);
 | 
			
		||||
		if (!audio) {
 | 
			
		||||
			Log::error(cs_error_as_string(err));
 | 
			
		||||
			return AudioRef();
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			return AudioRef(new Audio(audio));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_global_pan(float pan)
 | 
			
		||||
	{
 | 
			
		||||
		cs_set_global_pan(pan);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_global_volume(float volume)
 | 
			
		||||
	{
 | 
			
		||||
		cs_set_global_volume(volume);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_global_pause(bool true_for_paused)
 | 
			
		||||
	{
 | 
			
		||||
		cs_set_global_pause(true_for_paused);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_sound_volume(float volume)
 | 
			
		||||
	{
 | 
			
		||||
		cs_set_playing_sounds_volume(volume);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_music_volume(float volume)
 | 
			
		||||
	{
 | 
			
		||||
		cs_music_set_volume(volume);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namespace Music
 | 
			
		||||
	{
 | 
			
		||||
		void play(AudioRef audio, float fade_in_time)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_play((cs_audio_source_t*)audio->get_backend_handle(), fade_in_time);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void stop(float fade_out_time)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_stop(fade_out_time);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void set_volume(float volume)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_set_volume(volume);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void set_loop(bool true_to_loop)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_set_loop(true_to_loop);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void pause()
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_pause();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void resume()
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_resume();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void switch_to(AudioRef audio, float fade_out_time, float fade_in_time)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_switch_to((cs_audio_source_t*)audio->get_backend_handle(), fade_out_time, fade_in_time);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void crossfade(AudioRef audio, float cross_fade_time)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_crossfade((cs_audio_source_t*)audio->get_backend_handle(), cross_fade_time);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		u64 get_sample_index()
 | 
			
		||||
		{
 | 
			
		||||
			return cs_music_get_sample_index();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void set_sample_index(u64 sample_index)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_set_sample_index(sample_index);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	Sound Sound::play(AudioRef audio, SoundParams params)
 | 
			
		||||
	{
 | 
			
		||||
		cs_sound_params_t csparams;
 | 
			
		||||
		csparams.paused = params.paused;
 | 
			
		||||
		csparams.looped = params.looped;
 | 
			
		||||
		csparams.volume = params.volume;
 | 
			
		||||
		csparams.pan = params.pan;
 | 
			
		||||
		csparams.delay = params.delay;
 | 
			
		||||
		cs_playing_sound_t cssound = cs_play_sound((cs_audio_source_t*)audio->get_backend_handle(), csparams);
 | 
			
		||||
		Sound sound;
 | 
			
		||||
		sound.id = cssound.id;
 | 
			
		||||
		return sound;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool Sound::is_active()
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		return cs_sound_is_active(cssound);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool Sound::is_paused()
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		return cs_sound_get_is_paused(cssound);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool Sound::is_looped()
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		return cs_sound_get_is_looped(cssound);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	float Sound::get_volume()
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		return cs_sound_get_volume(cssound);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u64 Sound::get_sample_index()
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		return cs_sound_get_sample_index(cssound);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Sound::set_is_paused(bool true_for_paused)
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		cs_sound_set_is_paused(cssound, true_for_paused);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Sound::set_is_looped(bool true_for_looped)
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		cs_sound_set_is_looped(cssound, true_for_looped);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Sound::set_volume(float volume)
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		cs_sound_set_volume(cssound, volume);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Sound::set_sample_index(u64 sample_index)
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		cs_sound_set_sample_index(cssound, sample_index);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef STB_VORBIS_HEADER_ONLY
 | 
			
		||||
#include "third_party/stb_vorbis.c"
 | 
			
		||||
#include "blah_audio.h"
 | 
			
		||||
#include "blah_time.h"
 | 
			
		||||
#include "internal/blah_internal.h"
 | 
			
		||||
 | 
			
		||||
#define STB_VORBIS_HEADER_ONLY
 | 
			
		||||
#include "third_party/stb_vorbis.c"
 | 
			
		||||
 | 
			
		||||
#ifdef BLAH_PLATFORM_SDL2
 | 
			
		||||
#define CUTE_SOUND_FORCE_SDL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define CUTE_SOUND_IMPLEMENTATION
 | 
			
		||||
#include "third_party/cute_sound.h"
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	namespace Internal
 | 
			
		||||
	{
 | 
			
		||||
		bool audio_init(unsigned play_frequency_in_Hz, int buffered_samples)
 | 
			
		||||
		{
 | 
			
		||||
			cs_error_t err = cs_init(Platform::d3d11_get_hwnd(), play_frequency_in_Hz, buffered_samples, NULL);
 | 
			
		||||
			if (err != CUTE_SOUND_ERROR_NONE)
 | 
			
		||||
			{
 | 
			
		||||
				Log::error(cs_error_as_string(err));
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
#ifndef BLAH_NO_THREADING
 | 
			
		||||
			cs_spawn_mix_thread();
 | 
			
		||||
#endif
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void audio_shutdown()
 | 
			
		||||
		{
 | 
			
		||||
			cs_shutdown();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void audio_update()
 | 
			
		||||
		{
 | 
			
		||||
			cs_update(Time::delta);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Audio::Audio(void* audio)
 | 
			
		||||
	{
 | 
			
		||||
		m_ptr = audio;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Audio::~Audio()
 | 
			
		||||
	{
 | 
			
		||||
		cs_free_audio_source((cs_audio_source_t*)m_ptr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u64 Audio::get_sample_count()
 | 
			
		||||
	{
 | 
			
		||||
		return m_ptr ? ((cs_audio_source_t*)m_ptr)->sample_count : 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	AudioRef Audio::create(const FilePath& path)
 | 
			
		||||
	{
 | 
			
		||||
		FileStream fs(path, FileMode::OpenRead);
 | 
			
		||||
		if (!fs.is_readable())
 | 
			
		||||
			return AudioRef();
 | 
			
		||||
 | 
			
		||||
		if (path.ends_with(".ogg")) {
 | 
			
		||||
			return create_from_ogg(fs);
 | 
			
		||||
		} else if (path.ends_with(".wav")) {
 | 
			
		||||
			return create_from_wav(fs);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Log::error("Unable to load unrecognized audio file type.");
 | 
			
		||||
		return AudioRef();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	AudioRef Audio::create_from_wav(Stream& stream)
 | 
			
		||||
	{
 | 
			
		||||
		if (!stream.is_readable())
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Unable to load audio as the Stream was not readable");
 | 
			
		||||
			return AudioRef();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// read into buffer
 | 
			
		||||
		Vector<u8> buffer;
 | 
			
		||||
		buffer.reserve((int)stream.length());
 | 
			
		||||
		stream.read(buffer.data(), stream.length());
 | 
			
		||||
 | 
			
		||||
		// load wav file from memory using cute_sound.h
 | 
			
		||||
		cs_error_t err;
 | 
			
		||||
		void* audio = cs_read_mem_wav((void*)buffer.data(), stream.length(), &err);
 | 
			
		||||
		if (!audio) {
 | 
			
		||||
			Log::error(cs_error_as_string(err));
 | 
			
		||||
			return AudioRef();
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			return AudioRef(new Audio(audio));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	AudioRef Audio::create_from_ogg(Stream& stream)
 | 
			
		||||
	{
 | 
			
		||||
		if (!stream.is_readable())
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Unable to load audio as the Stream was not readable");
 | 
			
		||||
			return AudioRef();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// read into buffer
 | 
			
		||||
		Vector<u8> buffer;
 | 
			
		||||
		buffer.reserve((int)stream.length());
 | 
			
		||||
		stream.read(buffer.data(), stream.length());
 | 
			
		||||
 | 
			
		||||
		// load ogg file from memory using cute_sound.h
 | 
			
		||||
		cs_error_t err;
 | 
			
		||||
		void* audio = cs_read_mem_ogg((void*)buffer.data(), stream.length(), &err);
 | 
			
		||||
		if (!audio) {
 | 
			
		||||
			Log::error(cs_error_as_string(err));
 | 
			
		||||
			return AudioRef();
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			return AudioRef(new Audio(audio));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_global_pan(float pan)
 | 
			
		||||
	{
 | 
			
		||||
		cs_set_global_pan(pan);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_global_volume(float volume)
 | 
			
		||||
	{
 | 
			
		||||
		cs_set_global_volume(volume);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_global_pause(bool true_for_paused)
 | 
			
		||||
	{
 | 
			
		||||
		cs_set_global_pause(true_for_paused);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_sound_volume(float volume)
 | 
			
		||||
	{
 | 
			
		||||
		cs_set_playing_sounds_volume(volume);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_music_volume(float volume)
 | 
			
		||||
	{
 | 
			
		||||
		cs_music_set_volume(volume);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namespace Music
 | 
			
		||||
	{
 | 
			
		||||
		void play(AudioRef audio, float fade_in_time)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_play((cs_audio_source_t*)audio->get_backend_handle(), fade_in_time);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void stop(float fade_out_time)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_stop(fade_out_time);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void set_volume(float volume)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_set_volume(volume);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void set_loop(bool true_to_loop)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_set_loop(true_to_loop);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void pause()
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_pause();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void resume()
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_resume();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void switch_to(AudioRef audio, float fade_out_time, float fade_in_time)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_switch_to((cs_audio_source_t*)audio->get_backend_handle(), fade_out_time, fade_in_time);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void crossfade(AudioRef audio, float cross_fade_time)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_crossfade((cs_audio_source_t*)audio->get_backend_handle(), cross_fade_time);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		u64 get_sample_index()
 | 
			
		||||
		{
 | 
			
		||||
			return cs_music_get_sample_index();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void set_sample_index(u64 sample_index)
 | 
			
		||||
		{
 | 
			
		||||
			cs_music_set_sample_index(sample_index);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	Sound Sound::play(AudioRef audio, SoundParams params)
 | 
			
		||||
	{
 | 
			
		||||
		cs_sound_params_t csparams;
 | 
			
		||||
		csparams.paused = params.paused;
 | 
			
		||||
		csparams.looped = params.looped;
 | 
			
		||||
		csparams.volume = params.volume;
 | 
			
		||||
		csparams.pan = params.pan;
 | 
			
		||||
		csparams.delay = params.delay;
 | 
			
		||||
		cs_playing_sound_t cssound = cs_play_sound((cs_audio_source_t*)audio->get_backend_handle(), csparams);
 | 
			
		||||
		Sound sound;
 | 
			
		||||
		sound.id = cssound.id;
 | 
			
		||||
		return sound;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool Sound::is_active()
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		return cs_sound_is_active(cssound);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool Sound::is_paused()
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		return cs_sound_get_is_paused(cssound);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool Sound::is_looped()
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		return cs_sound_get_is_looped(cssound);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	float Sound::get_volume()
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		return cs_sound_get_volume(cssound);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u64 Sound::get_sample_index()
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		return cs_sound_get_sample_index(cssound);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Sound::set_is_paused(bool true_for_paused)
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		cs_sound_set_is_paused(cssound, true_for_paused);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Sound::set_is_looped(bool true_for_looped)
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		cs_sound_set_is_looped(cssound, true_for_looped);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Sound::set_volume(float volume)
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		cs_sound_set_volume(cssound, volume);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Sound::set_sample_index(u64 sample_index)
 | 
			
		||||
	{
 | 
			
		||||
		cs_playing_sound_t cssound = { id };
 | 
			
		||||
		cs_sound_set_sample_index(cssound, sample_index);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef STB_VORBIS_HEADER_ONLY
 | 
			
		||||
#include "third_party/stb_vorbis.c"
 | 
			
		||||
 | 
			
		||||
@ -9,8 +9,6 @@ namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	namespace Internal
 | 
			
		||||
	{
 | 
			
		||||
		extern bool audio_is_init;
 | 
			
		||||
 | 
			
		||||
		void app_step();
 | 
			
		||||
		void app_shutdown();
 | 
			
		||||
		Renderer* app_renderer();
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user