Repository navigation
Expand file tree
/
Copy pathmain.cpp
More file actions
133 lines (112 loc) · 4.27 KB
/
Copy pathmain.cpp
File metadata and controls
133 lines (112 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <iostream>
#include <imgui/imgui_impl_wgpu.h>
#include "logstorm/logstorm.h"
#include "gui/gui_renderer.h"
#include "render/webgpu_renderer.h"
#include "emscripten_audio.h"
class game_manager {
struct audio_generator {
bool started{false};
float sample_rate{0.0f}; // set by set_sample_rate
float target_tone_frequency{440.0f};
float target_volume{0.3f};
float phase{0.0f};
float phase_increment{0}; // set by set_sample_rate
float current_volume{0.0};
void set_sample_rate(unsigned int sample_rate);
void output(std::span<AudioSampleFrame> outputs);
};
logstorm::manager logger{logstorm::manager::build_with_sink<logstorm::sink::emscripten_out>()}; // logging system
render::webgpu_renderer renderer{logger}; // WebGPU rendering system
gui::gui_renderer gui{logger}; // GUI top level
emscripten_audio audio{{
.callbacks{
.playback_started{[&]{
on_playback_started();
}},
},
}};
audio_generator tone_generator;
public:
game_manager();
~game_manager();
void loop_main();
private:
game_manager(game_manager const&) = delete;
void operator=(game_manager const&) = delete;
void on_playback_started();
};
game_manager::game_manager() {
/// Run the game
renderer.init(
[&](render::webgpu_renderer::webgpu_data const& webgpu){
ImGui_ImplWGPU_InitInfo imgui_wgpu_info;
imgui_wgpu_info.Device = webgpu.device.Get();
imgui_wgpu_info.RenderTargetFormat = static_cast<WGPUTextureFormat>(webgpu.surface_preferred_format);
imgui_wgpu_info.DepthStencilFormat = static_cast<WGPUTextureFormat>(webgpu.depth_texture_format);
gui.init(imgui_wgpu_info);
},
[&]{
loop_main();
}
);
}
game_manager::~game_manager() {
/// Destructor for debugging purposes only
assert(false && "Game manager destructing, this should never happen!");
}
void game_manager::loop_main() {
/// Main pseudo-loop
gui.draw(
tone_generator.started,
tone_generator.sample_rate,
tone_generator.target_tone_frequency,
tone_generator.target_volume,
tone_generator.phase,
tone_generator.phase_increment,
tone_generator.current_volume
);
renderer.draw();
}
void game_manager::on_playback_started() {
/// Playback started callback
logger << "Audio: Starting playback after first user interaction";
tone_generator.set_sample_rate(audio.get_sample_rate());
audio.callbacks.processing = [&](std::span<AudioSampleFrame const> /*inputs*/,
std::span<AudioSampleFrame> outputs,
std::span<AudioParamFrame const > /*params*/){
tone_generator.output(outputs);
};
tone_generator.started = true;
}
void game_manager::audio_generator::set_sample_rate(unsigned int new_sample_rate) {
sample_rate = static_cast<float>(new_sample_rate);
phase_increment = target_tone_frequency * 2.0f * boost::math::constants::pi<float>() / sample_rate;
}
void game_manager::audio_generator::output(std::span<AudioSampleFrame> outputs) {
// interpolate towards the target frequency and volume values
float const target_phase_increment{target_tone_frequency * 2.0f * boost::math::constants::pi<float>() / sample_rate};
phase_increment = phase_increment * 0.95f + 0.05f * target_phase_increment;
current_volume = current_volume * 0.95f + 0.05f * target_volume;
// produce a sine wave tone of desired frequency to all output channels
for(auto const &output : outputs) {
for(int i{0}; i != output.samplesPerChannel; ++i) {
float const sample{static_cast<float>(std::sin(phase)) * current_volume};
phase += phase_increment;
for(int channel{0}; channel != output.numberOfChannels; ++channel) {
output.data[channel * output.samplesPerChannel + i] = sample;
}
}
}
// range reduce to keep precision around zero
phase = std::fmod(phase, 2.0f * boost::math::constants::pi<float>());
}
auto main()->int {
try {
new game_manager;
} catch (std::exception const &e) {
std::cerr << "Exception: " << e.what() << std::endl;
EM_ASM(alert("Error: Press F12 to see console for details."));
}
return EXIT_SUCCESS;
}