docs/bevy/examples/audio/decodable.rs 2.9 K · 101 lines · raw · history

1 //! Shows how to create a custom [`Decodable`] type by implementing a Sine wave.
2
3 use bevy::{
4 audio::{AddAudioSource, AudioPlugin, ChannelCount, SampleRate, Source, Volume},
5 math::ops,
6 prelude::*,
7 reflect::TypePath,
8 };
9 use core::time::Duration;
10
11 // This struct usually contains the data for the audio being played.
12 // This is where data read from an audio file would be stored, for example.
13 // This allows the type to be registered as an asset.
14 #[derive(Asset, TypePath)]
15 struct SineAudio {
16 frequency: f32,
17 }
18 // This decoder is responsible for playing the audio,
19 // and so stores data about the audio being played.
20 struct SineDecoder {
21 // how far along one period the wave is (between 0 and 1)
22 current_progress: f32,
23 // how much we move along the period every frame
24 progress_per_frame: f32,
25 // how long a period is
26 period: f32,
27 sample_rate: SampleRate,
28 }
29
30 impl SineDecoder {
31 fn new(frequency: f32) -> Self {
32 // standard sample rate for most recordings
33 let sample_rate = 44_100;
34 SineDecoder {
35 current_progress: 0.,
36 progress_per_frame: frequency / sample_rate as f32,
37 period: std::f32::consts::PI * 2.,
38 sample_rate: SampleRate::new(sample_rate).unwrap(),
39 }
40 }
41 }
42
43 // The decoder must implement iterator so that it can implement `Decodable`.
44 impl Iterator for SineDecoder {
45 type Item = f32;
46
47 fn next(&mut self) -> Option<Self::Item> {
48 self.current_progress += self.progress_per_frame;
49 // we loop back round to 0 to avoid floating point inaccuracies
50 self.current_progress %= 1.;
51 Some(ops::sin(self.period * self.current_progress))
52 }
53 }
54 // `Source` is what allows the audio source to be played by bevy.
55 // This trait provides information on the audio.
56 impl Source for SineDecoder {
57 fn current_span_len(&self) -> Option<usize> {
58 None
59 }
60
61 fn channels(&self) -> ChannelCount {
62 ChannelCount::new(1).unwrap()
63 }
64
65 fn sample_rate(&self) -> SampleRate {
66 self.sample_rate
67 }
68
69 fn total_duration(&self) -> Option<Duration> {
70 None
71 }
72 }
73
74 // Finally `Decodable` can be implemented for our `SineAudio`.
75 impl Decodable for SineAudio {
76 type Decoder = SineDecoder;
77
78 fn decoder(&self) -> Self::Decoder {
79 SineDecoder::new(self.frequency)
80 }
81 }
82
83 fn main() {
84 let mut app = App::new();
85 // register the audio source so that it can be used
86 app.add_plugins(DefaultPlugins.set(AudioPlugin {
87 global_volume: Volume::Linear(0.2).into(),
88 ..default()
89 }))
90 .add_audio_source::<SineAudio>()
91 .add_systems(Startup, setup)
92 .run();
93 }
94
95 fn setup(mut assets: ResMut<Assets<SineAudio>>, mut commands: Commands) {
96 // add a `SineAudio` to the asset server so that it can be played
97 let audio_handle = assets.add(SineAudio {
98 frequency: 440., // this is the frequency of A4
99 });
100 commands.spawn(AudioPlayer(audio_handle));
101 }