From 4d7f7a993a66b38a0692341c3de02b94368b9a39 Mon Sep 17 00:00:00 2001 From: Noel Berry Date: Sun, 20 Nov 2022 19:32:38 -0800 Subject: [PATCH] added audio enabled flag --- include/blah_app.h | 321 ++++++------- src/blah_app.cpp | 888 ++++++++++++++++++----------------- src/blah_audio.cpp | 544 ++++++++++----------- src/internal/blah_internal.h | 2 - 4 files changed, 880 insertions(+), 875 deletions(-) diff --git a/include/blah_app.h b/include/blah_app.h index 1b9b285..efc39c9 100644 --- a/include/blah_app.h +++ b/include/blah_app.h @@ -1,161 +1,162 @@ -#pragma once -#include -#include -#include - -namespace Blah -{ - // Application Event Functions - using AppEventFn = Func; - - // Application Logging Functions - using AppLogFn = Func; - - // Application flags - namespace Flags - { - constexpr u32 FixedTimestep = 1 << 0; - constexpr u32 VSync = 1 << 1; - constexpr u32 Fullscreen = 1 << 2; - constexpr u32 Resizable = 1 << 3; - } - - // Application Configuration - struct Config - { - // Application name. - const char* name = "blah"; - - // Which renderer to use - // Default depends on the Platform - RendererType renderer_type = RendererType::None; - - // Starting width, in pixels. - // Depending on the OS DPI, the true window size may be a multiple of this. - int width = 1280; - - // Starting height, in pixels. - // Depending on the OS DPI, the true window size may be a multiple of this. - int height = 720; - - // maximum updates to run before "giving up" and reducing frame rate. - // this avoids the 'spiral of death'. - // defaults to 5. - int max_updates = 5; - - // target framerate when running with Fixed Timestep - // defaults to 60. - int target_framerate = 60; - - // The expected freqeuncy of audio files and how quickly to play audio. - int audio_frequency_in_Hz = 44100; - - // default starting flags - u32 flags = Flags::VSync | Flags::Resizable | Flags::FixedTimestep; - - // Callback on application startup - AppEventFn on_startup = nullptr; - - // Callback on application shutdown - AppEventFn on_shutdown = nullptr; - - // Callback on application update - AppEventFn on_update = nullptr; - - // Callback on application render - AppEventFn on_render = nullptr; - - // Callback when the user has requested the application close. - // For example, pressing the Close button, ALT+F4, etc. - // By default this calls `App::exit()` - AppEventFn on_exit_request = nullptr; - - // Callback when the application logs info/warning/errors - // Defaults to printf. - AppLogFn on_log = nullptr; - }; - - // Application - namespace App - { - // Runs the application - bool run(const Config* config); - - // Checks if the Application is running yet - bool is_running(); - - // Exits the application. - // This only signals for the application to close, it will not stop - // until the current update and render calls are finished. - void exit(); - - // Gets the config data used to run the application - const Config& config(); - - // Gets the working path - const char* path(); - - // Gets the user path - const char* user_path(); - - // Gets the Window Title - const char* get_title(); - - // Sets the Window Title - void set_title(const char* title); - - // Gets the Window Position in Screen Coordinates - Point get_position(); - - // Sets the Window Position in Screen Coordinates - void set_position(Point point); - - // Gets the size of the Window in Screen Coordinates. - // On High DPI displays this may not be 1:1 with pixels. - // For the size in pixels, use App::get_backbuffer_size() - Point get_size(); - - // Sets the Window Size in Screen Coordinates - void set_size(Point point); - - // Gets the size of the BackBuffer, in pixels - Point get_backbuffer_size(); - - // Gets the content scale based on the platform. - // macOS is usually 2.0, other platforms vary. - float content_scale(); - - // If the window is currently focused or has mouse input - bool focused(); - - // Toggles a specific flag - void set_flag(u32 flag, bool enabled); - - // Gets whether a specific flag is enabled - bool get_flag(u32 flag); - - // Toggles the fullscreen flag - inline void set_fullscreen(bool enabled) { set_flag(Flags::Fullscreen, enabled); } - - // Toggles the V-Sync flag - inline void set_vsync(bool enabled) { set_flag(Flags::VSync, enabled); } - - // Toggles the resizable flag - inline void set_resizable(bool enabled) { set_flag(Flags::Resizable, enabled); } - - // Toggles whether to update with Fixed Timestep - inline void set_fixedtimestep(bool enabled) { set_flag(Flags::FixedTimestep, enabled); } - - // Retrieves the Renderer Information - const RendererInfo& renderer(); - - // Gets the BackBuffer - const TargetRef& backbuffer(); - } - - namespace System - { - // Tries to open the given URL in a web browser - void open_url(const char* url); - } +#pragma once +#include +#include +#include + +namespace Blah +{ + // Application Event Functions + using AppEventFn = Func; + + // Application Logging Functions + using AppLogFn = Func; + + // Application flags + namespace Flags + { + constexpr u32 FixedTimestep = 1 << 0; + constexpr u32 VSync = 1 << 1; + constexpr u32 Fullscreen = 1 << 2; + constexpr u32 Resizable = 1 << 3; + constexpr u32 AudioEnabled = 1 << 4; + } + + // Application Configuration + struct Config + { + // Application name. + const char* name = "blah"; + + // Which renderer to use + // Default depends on the Platform + RendererType renderer_type = RendererType::None; + + // Starting width, in pixels. + // Depending on the OS DPI, the true window size may be a multiple of this. + int width = 1280; + + // Starting height, in pixels. + // Depending on the OS DPI, the true window size may be a multiple of this. + int height = 720; + + // maximum updates to run before "giving up" and reducing frame rate. + // this avoids the 'spiral of death'. + // defaults to 5. + int max_updates = 5; + + // target framerate when running with Fixed Timestep + // defaults to 60. + int target_framerate = 60; + + // The expected freqeuncy of audio files and how quickly to play audio. + int audio_frequency_in_Hz = 44100; + + // default starting flags + u32 flags = Flags::VSync | Flags::Resizable | Flags::FixedTimestep | Flags::AudioEnabled; + + // Callback on application startup + AppEventFn on_startup = nullptr; + + // Callback on application shutdown + AppEventFn on_shutdown = nullptr; + + // Callback on application update + AppEventFn on_update = nullptr; + + // Callback on application render + AppEventFn on_render = nullptr; + + // Callback when the user has requested the application close. + // For example, pressing the Close button, ALT+F4, etc. + // By default this calls `App::exit()` + AppEventFn on_exit_request = nullptr; + + // Callback when the application logs info/warning/errors + // Defaults to printf. + AppLogFn on_log = nullptr; + }; + + // Application + namespace App + { + // Runs the application + bool run(const Config* config); + + // Checks if the Application is running yet + bool is_running(); + + // Exits the application. + // This only signals for the application to close, it will not stop + // until the current update and render calls are finished. + void exit(); + + // Gets the config data used to run the application + const Config& config(); + + // Gets the working path + const char* path(); + + // Gets the user path + const char* user_path(); + + // Gets the Window Title + const char* get_title(); + + // Sets the Window Title + void set_title(const char* title); + + // Gets the Window Position in Screen Coordinates + Point get_position(); + + // Sets the Window Position in Screen Coordinates + void set_position(Point point); + + // Gets the size of the Window in Screen Coordinates. + // On High DPI displays this may not be 1:1 with pixels. + // For the size in pixels, use App::get_backbuffer_size() + Point get_size(); + + // Sets the Window Size in Screen Coordinates + void set_size(Point point); + + // Gets the size of the BackBuffer, in pixels + Point get_backbuffer_size(); + + // Gets the content scale based on the platform. + // macOS is usually 2.0, other platforms vary. + float content_scale(); + + // If the window is currently focused or has mouse input + bool focused(); + + // Toggles a specific flag + void set_flag(u32 flag, bool enabled); + + // Gets whether a specific flag is enabled + bool get_flag(u32 flag); + + // Toggles the fullscreen flag + inline void set_fullscreen(bool enabled) { set_flag(Flags::Fullscreen, enabled); } + + // Toggles the V-Sync flag + inline void set_vsync(bool enabled) { set_flag(Flags::VSync, enabled); } + + // Toggles the resizable flag + inline void set_resizable(bool enabled) { set_flag(Flags::Resizable, enabled); } + + // Toggles whether to update with Fixed Timestep + inline void set_fixedtimestep(bool enabled) { set_flag(Flags::FixedTimestep, enabled); } + + // Retrieves the Renderer Information + const RendererInfo& renderer(); + + // Gets the BackBuffer + const TargetRef& backbuffer(); + } + + namespace System + { + // Tries to open the given URL in a web browser + void open_url(const char* url); + } } \ No newline at end of file diff --git a/src/blah_app.cpp b/src/blah_app.cpp index f886a91..305914e 100644 --- a/src/blah_app.cpp +++ b/src/blah_app.cpp @@ -1,442 +1,448 @@ -#include -#include -#include -#include "internal/blah_internal.h" -#include "internal/blah_platform.h" -#include "internal/blah_renderer.h" - -#ifdef __EMSCRIPTEN__ -#include -#include -#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 +#include +#include +#include "internal/blah_internal.h" +#include "internal/blah_platform.h" +#include "internal/blah_renderer.h" + +#ifdef __EMSCRIPTEN__ +#include +#include +#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); } \ No newline at end of file diff --git a/src/blah_audio.cpp b/src/blah_audio.cpp index 4306c99..ee60ad2 100644 --- a/src/blah_audio.cpp +++ b/src/blah_audio.cpp @@ -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 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 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 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 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" diff --git a/src/internal/blah_internal.h b/src/internal/blah_internal.h index e9a4072..512fd0b 100644 --- a/src/internal/blah_internal.h +++ b/src/internal/blah_internal.h @@ -9,8 +9,6 @@ namespace Blah { namespace Internal { - extern bool audio_is_init; - void app_step(); void app_shutdown(); Renderer* app_renderer();