/ src / assets.rs
assets.rs
  1  use bevy::asset::{io::Reader, AssetLoader, LoadContext};
  2  #[cfg(feature = "bevy_audio")]
  3  use bevy::prelude::*;
  4  use itertools::Itertools;
  5  use rustysynth::{MidiFile, MidiFileSequencer, SoundFont, Synthesizer, SynthesizerSettings};
  6  #[cfg(feature = "kira")]
  7  use std::future::Future;
  8  use std::{
  9      io::{self, Cursor},
 10      sync::Arc,
 11      time::Duration,
 12  };
 13  
 14  use crate::SOUNDFONT;
 15  
 16  /// Represents a single MIDI note in a sequence
 17  #[derive(Clone, Debug)]
 18  pub struct MidiNote {
 19      /// Channel to play the note on
 20      pub channel: i32,
 21      /// Preset (instrument) to play the note with (see GM spec.)
 22      pub preset: i32,
 23      /// Bank to play note with
 24      pub bank: i32,
 25      /// Key to play (60 is middle C)
 26      pub key: i32,
 27      /// Velocity to play note at
 28      pub velocity: i32,
 29      /// Duration to play note for
 30      pub duration: Duration,
 31  }
 32  
 33  impl Default for MidiNote {
 34      fn default() -> Self {
 35          Self {
 36              channel: 0,
 37              preset: 0,
 38              bank: 0,
 39              key: 60,
 40              velocity: 100,
 41              duration: Duration::from_secs(1),
 42          }
 43      }
 44  }
 45  
 46  /// AssetLoader for MIDI files (.mid/.midi)
 47  #[derive(Default, Debug)]
 48  pub struct MidiAssetLoader;
 49  
 50  /// Decoder for MIDI file playback
 51  pub struct MidiFileDecoder {
 52      sample_rate: usize,
 53      data: Vec<f32>,
 54      #[cfg(feature = "bevy_audio")]
 55      index: usize,
 56  }
 57  
 58  impl MidiFileDecoder {
 59      /// Construct and render a MIDI sequence with the given MIDI data and soundfont.
 60      pub fn new(midi_data: Vec<u8>, soundfont: Arc<SoundFont>) -> Self {
 61          let sample_rate = 44100_usize;
 62          let settings = SynthesizerSettings::new(sample_rate as i32);
 63          let synthesizer =
 64              Synthesizer::new(&soundfont, &settings).expect("Failed to create synthesizer.");
 65  
 66          let mut data = Vec::new();
 67          let mut sequencer = MidiFileSequencer::new(synthesizer);
 68          let mut midi_data = Cursor::new(midi_data);
 69          let midi = Arc::new(MidiFile::new(&mut midi_data).expect("Failed to read midi file."));
 70          sequencer.play(&midi, false);
 71          let mut left: Vec<f32> = vec![0_f32; sample_rate];
 72          let mut right: Vec<f32> = vec![0_f32; sample_rate];
 73          while !sequencer.end_of_sequence() {
 74              sequencer.render(&mut left, &mut right);
 75              for value in left.iter().interleave(right.iter()) {
 76                  data.push(*value);
 77              }
 78          }
 79          Self {
 80              sample_rate,
 81              data,
 82              #[cfg(feature = "bevy_audio")]
 83              index: 0,
 84          }
 85      }
 86  
 87      /// Render a MIDI sequence with the given soundfont.
 88      pub fn new_sequence(midi_sequence: Vec<MidiNote>, soundfont: Arc<SoundFont>) -> Self {
 89          let sample_rate = 44100_usize;
 90          let settings = SynthesizerSettings::new(sample_rate as i32);
 91          let mut synthesizer =
 92              Synthesizer::new(&soundfont, &settings).expect("Failed to create synthesizer.");
 93  
 94          let mut data = Vec::new();
 95  
 96          for MidiNote {
 97              channel,
 98              preset,
 99              bank,
100              key,
101              velocity,
102              duration,
103          } in midi_sequence.iter()
104          {
105              synthesizer.process_midi_message(*channel, 0xB0, 0x00, *bank);
106              synthesizer.process_midi_message(*channel, 0xC0, *preset, 0);
107              synthesizer.note_on(*channel, *key, *velocity);
108              let note_length = (sample_rate as f32 * duration.as_secs_f32()) as usize;
109              let mut left: Vec<f32> = vec![0_f32; note_length];
110              let mut right: Vec<f32> = vec![0_f32; note_length];
111              for (left, right) in left
112                  .chunks_mut(sample_rate)
113                  .zip(right.chunks_mut(sample_rate))
114              {
115                  synthesizer.render(left, right);
116                  for value in left.iter().interleave(right.iter()) {
117                      data.push(*value);
118                  }
119              }
120              synthesizer.note_off(*channel, *key);
121          }
122          Self {
123              sample_rate,
124              data,
125              #[cfg(feature = "bevy_audio")]
126              index: 0,
127          }
128      }
129  }
130  
131  #[cfg(all(feature = "bevy_audio", not(feature = "kira")))]
132  /// Asset containing MIDI file data to be used as a `Decodable` audio source
133  #[derive(Asset, TypePath, Debug)]
134  pub struct MidiAudio(Vec<u8>);
135  
136  #[cfg(all(feature = "bevy_audio", not(feature = "kira")))]
137  mod bevy_audio {
138      use super::*;
139      use bevy::audio::{Decodable, Source};
140  
141      impl Source for MidiFileDecoder {
142          fn current_frame_len(&self) -> Option<usize> {
143              None
144          }
145  
146          fn channels(&self) -> u16 {
147              2
148          }
149  
150          fn sample_rate(&self) -> u32 {
151              self.sample_rate as u32
152          }
153  
154          fn total_duration(&self) -> Option<std::time::Duration> {
155              None
156          }
157      }
158  
159      impl Decodable for MidiAudio {
160          type Decoder = MidiFileDecoder;
161  
162          type DecoderItem = <MidiFileDecoder as Iterator>::Item;
163  
164          fn decoder(&self) -> Self::Decoder {
165              MidiFileDecoder::new(
166                  self.0.clone(),
167                  SOUNDFONT.lock().unwrap().as_ref().unwrap().clone(),
168              )
169          }
170      }
171  
172      impl AssetLoader for MidiAssetLoader {
173          type Asset = MidiAudio;
174  
175          type Settings = ();
176  
177          type Error = io::Error;
178  
179          async fn load(
180              &self,
181              reader: &mut dyn Reader,
182              _settings: &Self::Settings,
183              _load_context: &mut LoadContext<'_>,
184          ) -> Result<Self::Asset, Self::Error> {
185              let mut bytes = vec![];
186              reader.read_to_end(&mut bytes).await?;
187              Ok(MidiAudio(bytes))
188          }
189  
190          fn extensions(&self) -> &[&str] {
191              &["mid", "midi"]
192          }
193      }
194  
195      impl Iterator for MidiFileDecoder {
196          type Item = f32;
197  
198          fn next(&mut self) -> Option<Self::Item> {
199              let result = self.data.get(self.index).copied();
200              self.index += 1;
201              result
202          }
203      }
204  }
205  
206  #[cfg(all(feature = "kira", not(feature = "bevy_audio")))]
207  /// Extensions For Rendering MIDI Audio
208  pub trait MidiAudioExtensions {
209      /// Renders MIDI audio from orovided data
210      fn from_midi_file(data: Vec<u8>) -> impl Future<Output = Self> + Send;
211      /// Renders MIDI audio from provided note sequence
212      fn from_midi_sequence(sequence: Vec<MidiNote>) -> impl Future<Output = Self> + Send;
213  }
214  
215  #[cfg(all(feature = "kira", not(feature = "bevy_audio")))]
216  mod kira {
217      use super::*;
218      use bevy_kira_audio::{
219          prelude::{Frame, StaticSoundData, StaticSoundSettings},
220          AudioSource,
221      };
222  
223      impl AssetLoader for MidiAssetLoader {
224          type Asset = AudioSource;
225  
226          type Settings = ();
227  
228          type Error = io::Error;
229  
230          async fn load(
231              &self,
232              reader: &mut dyn Reader,
233              _settings: &Self::Settings,
234              _load_context: &mut LoadContext<'_>,
235          ) -> Result<Self::Asset, Self::Error> {
236              let mut bytes = vec![];
237              reader.read_to_end(&mut bytes).await?;
238              Ok(AudioSource::from_midi_file(bytes).await)
239          }
240  
241          fn extensions(&self) -> &[&str] {
242              &["mid", "midi"]
243          }
244      }
245  
246      impl MidiAudioExtensions for AudioSource {
247          async fn from_midi_file(data: Vec<u8>) -> Self {
248              let decoder =
249                  MidiFileDecoder::new(data, SOUNDFONT.lock().unwrap().as_ref().unwrap().clone());
250              let frames = decoder
251                  .data
252                  .chunks(2)
253                  .map(|sample| Frame::new(sample[0], sample[1]))
254                  .collect::<Arc<[_]>>();
255              let sample_rate = decoder.sample_rate as u32;
256              let settings = StaticSoundSettings {
257                  ..Default::default()
258              };
259              AudioSource {
260                  sound: StaticSoundData {
261                      sample_rate,
262                      frames,
263                      settings,
264                      slice: None,
265                  },
266              }
267          }
268  
269          async fn from_midi_sequence(sequence: Vec<MidiNote>) -> Self {
270              let decoder = MidiFileDecoder::new_sequence(
271                  sequence,
272                  SOUNDFONT.lock().unwrap().as_ref().unwrap().clone(),
273              );
274              let frames = decoder
275                  .data
276                  .chunks(2)
277                  .map(|sample| Frame::new(sample[0], sample[1]))
278                  .collect::<Arc<[_]>>();
279              let sample_rate = decoder.sample_rate as u32;
280              let settings = StaticSoundSettings {
281                  ..Default::default()
282              };
283              AudioSource {
284                  sound: StaticSoundData {
285                      sample_rate,
286                      frames,
287                      settings,
288                      slice: None,
289                  },
290              }
291          }
292      }
293  }