1use primback::prelude::*;
2
3struct AudioApp {
4 sound_angle: f32,
5 pulse_timer: f32,
6}
7
8impl PrimbackApp for AudioApp {
9 fn init(&mut self, update_ctx: &mut UpdateContext) {
10 let camera = Camera::look_at(vec3(0.0, 3.0, 6.0), vec3(0.0, 1.0, 0.0), Vec3::Y);
12 update_ctx.set_camera(camera);
13
14 let light = Light {
15 direction: vec3(0.0, -1.0, -1.0).normalize(),
16 color: Color::new(1.0, 1.0, 1.0),
17 ambient: Color::new(0.3, 0.3, 0.35),
18 };
19 update_ctx.set_light(light);
20 }
21
22 fn update(&mut self, update_ctx: &mut UpdateContext) {
23 if update_ctx.is_key_pressed(KeyCode::Escape) {
24 update_ctx.exit();
25 }
26
27 let dt = update_ctx.delta_time();
28
29 self.sound_angle += dt * 1.5;
31 let sound_pos = vec3(
32 self.sound_angle.cos() * 3.0,
33 1.0,
34 self.sound_angle.sin() * 3.0,
35 );
36
37 self.pulse_timer += dt;
39 if self.pulse_timer >= 0.4 {
40 self.pulse_timer = 0.0;
41 let sound = Synth::sine(Pitch::A(4))
43 .duration(0.08)
44 .volume(0.6)
45 .position(sound_pos);
46 update_ctx.play_synth(sound);
47 }
48
49 if update_ctx.is_key_pressed(KeyCode::Digit1) {
51 update_ctx.play_synth(Synth::coin());
52 }
53 if update_ctx.is_key_pressed(KeyCode::Digit2) {
54 update_ctx.play_synth(Synth::jump());
55 }
56 if update_ctx.is_key_pressed(KeyCode::Digit3) {
57 update_ctx.play_synth(Synth::damage());
58 }
59 if update_ctx.is_key_pressed(KeyCode::Digit4) {
60 update_ctx.play_synth(Synth::explosion());
61 }
62 if update_ctx.is_key_pressed(KeyCode::Digit5) {
63 update_ctx.play_synth(Synth::click());
64 }
65
66 let piano_keys = [
68 (KeyCode::KeyQ, Pitch::C(4)),
69 (KeyCode::KeyW, Pitch::D(4)),
70 (KeyCode::KeyE, Pitch::E(4)),
71 (KeyCode::KeyR, Pitch::F(4)),
72 (KeyCode::KeyT, Pitch::G(4)),
73 (KeyCode::KeyY, Pitch::A(4)),
74 (KeyCode::KeyU, Pitch::B(4)),
75 (KeyCode::KeyI, Pitch::C(5)),
76 ];
77
78 for (key, pitch) in piano_keys {
79 if update_ctx.is_key_pressed(key) {
80 update_ctx.play_synth(Synth::triangle(pitch).duration(0.2).volume(0.5));
82 }
83 }
84 }
85
86 fn draw(&mut self, draw_ctx: &mut DrawContext) {
87 draw_ctx.clear(Color::new(0.08, 0.08, 0.1));
88
89 draw_ctx.draw_shape(
91 Shape::plane(),
92 Transform::new_position(vec3(0.0, 0.0, 0.0)).scale(vec3(8.0, 1.0, 8.0)),
93 Color::new(0.15, 0.15, 0.18),
94 );
95
96 draw_ctx.draw_shape(
98 Shape::cube().size(vec3(0.6, 0.6, 0.6)),
99 Transform::new_position(vec3(0.0, 1.0, 0.0)),
100 Color::new(0.2, 0.6, 0.9),
101 );
102
103 let sound_pos = vec3(
105 self.sound_angle.cos() * 3.0,
106 1.0,
107 self.sound_angle.sin() * 3.0,
108 );
109 draw_ctx.draw_shape(
110 Shape::sphere().radius(0.4),
111 Transform::new_position(sound_pos),
112 Color::new(0.9, 0.3, 0.3),
113 );
114
115 draw_ctx.draw_line_3d(
117 vec3(0.0, 1.0, 0.0),
118 sound_pos,
119 Color::new(0.5, 0.5, 0.5).alpha(0.5),
120 );
121
122 if let Some(screen_pos) = draw_ctx
124 .camera()
125 .world_to_screen(vec3(0.0, 1.5, 0.0), draw_ctx.render_size())
126 {
127 let label = Text::new("Listener (Camera)").scale(1.0);
128 let size = draw_ctx.measure_text(&label);
129 draw_ctx.draw_text(
130 label,
131 RectTransform::new_position(screen_pos - vec2(size.x * 0.5, 0.0)),
132 Color::new(0.2, 0.6, 0.9),
133 );
134 }
135
136 if let Some(screen_pos) = draw_ctx
137 .camera()
138 .world_to_screen(sound_pos + vec3(0.0, 0.6, 0.0), draw_ctx.render_size())
139 {
140 let label = Text::new("3D Sound Source").scale(1.0);
141 let size = draw_ctx.measure_text(&label);
142 draw_ctx.draw_text(
143 label,
144 RectTransform::new_position(screen_pos - vec2(size.x * 0.5, 0.0)),
145 Color::new(0.9, 0.3, 0.3),
146 );
147 }
148
149 draw_ctx.draw_text(
151 Text::new("Audio Synthesizer & Spatial 3D Audio").scale(1.0),
152 RectTransform::new_position(vec2(15.0, 15.0)),
153 Color::WHITE,
154 );
155
156 let sfx_bg = Rect::new(vec2(280.0, 100.0))
158 .color(Color::new(0.12, 0.12, 0.15))
159 .radius(6.0);
160 draw_ctx.draw_rect(sfx_bg, RectTransform::new_position(vec2(15.0, 50.0)));
161
162 draw_ctx.draw_text(
163 Text::new("SFX Presets (Press Keys 1-5):").scale(1.0),
164 RectTransform::new_position(vec2(25.0, 60.0)),
165 Color::new(1.0, 0.8, 0.2),
166 );
167 let sfx_lines = [
168 "1: Coin 2: Jump",
169 "3: Damage 4: Explosion",
170 "5: Click",
171 ];
172 for (idx, line) in sfx_lines.iter().enumerate() {
173 draw_ctx.draw_text(
174 Text::new(*line).scale(1.0),
175 RectTransform::new_position(vec2(25.0, 85.0 + (idx as f32) * 16.0)),
176 Color::WHITE,
177 );
178 }
179
180 let piano_bg = Rect::new(vec2(320.0, 100.0))
182 .color(Color::new(0.12, 0.12, 0.15))
183 .radius(6.0);
184 draw_ctx.draw_rect(piano_bg, RectTransform::new_position(vec2(305.0, 50.0)));
185
186 draw_ctx.draw_text(
187 Text::new("Keyboard Piano (Play Notes):").scale(1.0),
188 RectTransform::new_position(vec2(315.0, 60.0)),
189 Color::new(0.2, 0.8, 0.5),
190 );
191 draw_ctx.draw_text(
192 Text::new("Keys: Q W E R T Y U I").scale(1.0),
193 RectTransform::new_position(vec2(315.0, 85.0)),
194 Color::WHITE,
195 );
196 draw_ctx.draw_text(
197 Text::new("Notes: C4 D4 E4 F4 G4 A4 B4 C5").scale(1.0),
198 RectTransform::new_position(vec2(315.0, 105.0)),
199 Color::new(0.7, 0.7, 0.7),
200 );
201 }
202}
203
204fn main() {
205 let config = PrimbackConfig {
206 window_title: "Primback - Audio Synthesizer Demo".to_string(),
207 render_width: 640,
208 render_height: 480,
209 ..Default::default()
210 };
211
212 Primback::run(
213 config,
214 AudioApp {
215 sound_angle: 0.0,
216 pulse_timer: 0.0,
217 },
218 );
219}