mirror of
https://github.com/NoelFB/blah.git
synced 2024-11-25 16:18:57 +08:00
added audio enabled flag
This commit is contained in:
parent
b161457c66
commit
4d7f7a993a
|
@ -1,161 +1,162 @@
|
|||
#pragma once
|
||||
#include <blah_common.h>
|
||||
#include <blah_graphics.h>
|
||||
#include <blah_spatial.h>
|
||||
|
||||
namespace Blah
|
||||
{
|
||||
// Application Event Functions
|
||||
using AppEventFn = Func<void>;
|
||||
|
||||
// Application Logging Functions
|
||||
using AppLogFn = Func<void, const char*, Log::Category>;
|
||||
|
||||
// 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 <blah_common.h>
|
||||
#include <blah_graphics.h>
|
||||
#include <blah_spatial.h>
|
||||
|
||||
namespace Blah
|
||||
{
|
||||
// Application Event Functions
|
||||
using AppEventFn = Func<void>;
|
||||
|
||||
// Application Logging Functions
|
||||
using AppLogFn = Func<void, const char*, Log::Category>;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
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();
|
||||
|
|
Loading…
Reference in New Issue
Block a user