redact_composer_synthesis/
lib.rs1#![deny(missing_docs, missing_debug_implementations)]
2pub mod error;
38#[cfg(test)]
39mod test;
40
41use crate::error::SynthesisError;
42use hound::{SampleFormat, WavSpec, WavWriter};
43use log::{debug, info};
44use midly::Smf;
45use redact_composer_core::Composition;
46use redact_composer_midi::convert::MidiConverter;
47pub use rustysynth::SoundFont;
48use rustysynth::{MidiFile, MidiFileSequencer, Synthesizer, SynthesizerSettings};
49use std::cmp::Ordering::Less;
50use std::fmt::{Debug, Formatter};
51use std::fs;
52use std::fs::File;
53use std::io::{Seek, Write};
54use std::ops::RangeFrom;
55use std::path::Path;
56use std::sync::Arc;
57use std::time::Duration;
58
59pub type Result<T, E = SynthesisError> = std::result::Result<T, E>;
61
62pub struct SF2Synthesizer {
66 pub(crate) sound_font: Arc<SoundFont>,
67 pub(crate) options: SoundFontSynthesizerOptions,
68}
69
70impl Debug for SF2Synthesizer {
71 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
72 f.debug_struct("SF2Synthesizer")
73 .field("sound_font", &self.sound_font.get_info().get_bank_name())
74 .field("options", &self.options)
75 .finish()
76 }
77}
78
79impl SF2Synthesizer {
80 pub fn new<P: AsRef<Path>>(sf2_file: P) -> Result<SF2Synthesizer> {
83 Self::new_with_options(sf2_file, SoundFontSynthesizerOptions::default())
84 }
85
86 pub fn new_with_options<P: AsRef<Path>>(
88 sf2_file: P,
89 options: SoundFontSynthesizerOptions,
90 ) -> Result<SF2Synthesizer> {
91 let mut sound_font_file = File::open(sf2_file)?;
92 let sound_font = SoundFont::new(&mut sound_font_file)?;
93
94 Ok(SF2Synthesizer {
95 sound_font: Arc::new(sound_font),
96 options,
97 })
98 }
99
100 pub fn synthesize<'a, S: MidiBytesProvider>(
104 &'a self,
105 content: &'a S,
106 ) -> SF2SynthesisRequest<'_, S> {
107 content.synthesize_with(self)
108 }
109}
110
111impl MidiBytesProvider for Composition {
112 fn midi_bytes(&self) -> Vec<u8> {
113 let smf = MidiConverter::convert(self);
114 smf.midi_bytes()
115 }
116}
117
118impl MidiBytesProvider for Smf<'_> {
119 fn midi_bytes(&self) -> Vec<u8> {
120 let mut smf_bytes = Vec::new();
121 self.write(&mut smf_bytes).unwrap();
122
123 smf_bytes
124 }
125}
126
127impl<M: MidiBytesProvider> SF2Synthesizable<M> for M {
128 fn synthesize_with<'a>(&'a self, synth: &'a SF2Synthesizer) -> SF2SynthesisRequest<'_, M> {
129 SF2SynthesisRequest {
130 synth,
131 midi_reader: self,
132 }
133 }
134}
135
136pub trait SF2Synthesizable<M: MidiBytesProvider> {
138 fn synthesize_with<'a>(&'a self, synth: &'a SF2Synthesizer) -> SF2SynthesisRequest<'_, M>;
140}
141
142pub trait MidiBytesProvider {
144 fn midi_bytes(&self) -> Vec<u8>;
146}
147
148#[allow(missing_debug_implementations)]
150pub struct SF2SynthesisRequest<'a, M: MidiBytesProvider> {
151 synth: &'a SF2Synthesizer,
152 midi_reader: &'a M,
153}
154
155impl<M: MidiBytesProvider> SF2SynthesisRequest<'_, M> {
156 pub fn write<W: Write + Seek>(&self, writer: W) -> Result<()> {
158 let (mut left, mut right) = self.to_raw_stereo_waveforms()?;
159
160 info!("Writing WAV output.");
161 let wav_spec = WavSpec {
162 channels: 2,
163 sample_rate: self.synth.options.sample_rate,
164 bits_per_sample: self.synth.options.bit_depth as u16,
165 sample_format: SampleFormat::Int,
166 };
167
168 normalize(&mut left, &mut right);
169
170 let bit_depth_max_val = 2_i64.pow((wav_spec.bits_per_sample - 1).into()) - 1;
171 let mut writer = WavWriter::new(writer, wav_spec)?;
172 for (ls, rs) in left.into_iter().zip(right.into_iter()) {
173 writer.write_sample((ls * bit_depth_max_val as f32) as i32)?;
174 writer.write_sample((rs * bit_depth_max_val as f32) as i32)?;
175 }
176
177 Ok(writer.finalize()?)
178 }
179 pub fn to_file<P: AsRef<Path>>(&self, filename: P) -> Result<()> {
181 let path = filename.as_ref();
182 if let Some(dir) = path.parent() {
183 fs::create_dir_all(dir)?
184 }
185 let file = File::create(path)?;
186 let buf_writer = std::io::BufWriter::new(file);
187 self.write(buf_writer)?;
188
189 info!("Output written to '{}'", path.display());
190
191 Ok(())
192 }
193
194 pub fn to_raw_stereo_waveforms(&self) -> Result<(Vec<f32>, Vec<f32>)> {
196 info!("Synthesizing...");
197 debug!("{:?}", self.synth.options);
198 let start_instant = std::time::Instant::now();
199 let midi_bytes = self.midi_reader.midi_bytes();
200 let midi_file = Arc::new(MidiFile::new(&mut &midi_bytes[..])?);
201
202 let settings = SynthesizerSettings::new(self.synth.options.sample_rate as i32);
204 let synthesizer = Synthesizer::new(&self.synth.sound_font, &settings)?;
205 let mut sequencer = MidiFileSequencer::new(synthesizer);
206
207 sequencer.play(&midi_file, false);
209
210 let sample_count = (settings.sample_rate as f64 * (midi_file.get_length() + 10.0)) as usize;
213 let mut left: Vec<f32> = vec![0_f32; sample_count];
214 let mut right: Vec<f32> = vec![0_f32; sample_count];
215
216 sequencer.render(&mut left[..], &mut right[..]);
218
219 let end_trim_range = get_end_trim_range(&left, &right);
221 [&mut left, &mut right].into_iter().for_each(|ch| {
222 ch.drain(end_trim_range.clone());
223 });
224
225 let audio_duration =
226 Duration::from_secs_f32(left.len() as f32 / settings.sample_rate as f32);
227 let duration = std::time::Instant::now().duration_since(start_instant);
228 info!(
229 "Synthesis complete ({:?}). Synthesized {:?} of audio.",
230 duration, audio_duration
231 );
232
233 Ok((left, right))
234 }
235}
236
237fn normalize(left: &mut [f32], right: &mut [f32]) {
239 let abs_max = left
240 .iter()
241 .chain(right.iter())
242 .map(|s| s.abs())
243 .max_by(|a, b| a.partial_cmp(b).unwrap_or(Less));
244
245 if let Some(max) = abs_max {
246 for s in left.iter_mut().chain(right.iter_mut()) {
247 *s /= max;
248 }
249 }
250}
251
252fn get_end_trim_range(left: &[f32], right: &[f32]) -> RangeFrom<usize> {
254 let end = left
255 .iter()
256 .zip(right.iter())
257 .enumerate()
258 .fold(
259 0,
260 |end, (idx, (ls, rs))| {
261 if ls != &0.0 && rs != &0.0 {
262 idx
263 } else {
264 end
265 }
266 },
267 );
268
269 end..
270}
271
272#[derive(Debug, Copy, Clone)]
274pub struct SoundFontSynthesizerOptions {
275 pub sample_rate: u32,
277 pub bit_depth: u8,
279}
280
281impl Default for SoundFontSynthesizerOptions {
282 fn default() -> Self {
283 SoundFontSynthesizerOptions {
284 sample_rate: 44100,
285 bit_depth: 16,
286 }
287 }
288}