use rand::Rng;
use redact_composer::{
elements::Part,
midi::convert::MidiConverter,
musical::{
elements::Chord,
rhythm::Rhythm,
ChordShape::maj,
Note, NoteIterator,
NoteName::{C, F, G},
},
render::context::{CompositionContext, TimingRelation::Within},
render::{RenderEngine, Result},
synthesis::SF2Synthesizer,
util::IntoSegment,
Composer, Renderer, Segment, SegmentRef,
};
use serde::{Deserialize, Serialize};
use std::fs;
fn main() {
let composer = Composer::from(RenderEngine::new() + CompositionRenderer + PlayChordsRenderer);
let composition_length = composer.options.ticks_per_beat * 16;
let composition = composer.compose(CompositionRoot.over(0..composition_length));
MidiConverter::convert(&composition)
.save("./composition.mid")
.unwrap();
let synth = SF2Synthesizer::new("./sounds/sound_font.sf2").unwrap();
synth
.synthesize(&composition)
.to_file("./composition.wav")
.unwrap();
fs::write(
"./composition.json",
serde_json::to_string_pretty(&composition).unwrap(),
)
.unwrap();
}
#[derive(redact_composer::Element, Serialize, Deserialize, Debug)]
pub struct CompositionRoot;
#[derive(redact_composer::Element, Serialize, Deserialize, Debug)]
struct PlayChords;
struct CompositionRenderer;
impl Renderer for CompositionRenderer {
type Element = CompositionRoot;
fn render(
&self,
composition: SegmentRef<CompositionRoot>,
context: CompositionContext,
) -> Result<Vec<Segment>> {
let chords: [Chord; 4] = [
(C, maj).into(),
(F, maj).into(),
(G, maj).into(),
(C, maj).into(),
];
Ok(
Rhythm::from([2 * context.beat_length()])
.iter_over(composition)
.zip(chords.into_iter().cycle())
.map(|(subdivision, chord)| chord.over(subdivision))
.chain([
Part::instrument(PlayChords).over(composition),
])
.collect(),
)
}
}
struct PlayChordsRenderer;
impl Renderer for PlayChordsRenderer {
type Element = PlayChords;
fn render(
&self,
play_chords: SegmentRef<PlayChords>,
context: CompositionContext,
) -> Result<Vec<Segment>> {
let chord_segments = context
.find::<Chord>()
.with_timing(Within, play_chords)
.require_all()?;
let mut rng = context.rng();
let notes = chord_segments
.iter()
.flat_map(|chord| {
chord
.element
.iter_notes_in_range(Note::from((C, 4))..Note::from((C, 5)))
.map(|note|
note.play(rng.gen_range(80..110) )
.over(chord))
.collect::<Vec<_>>()
})
.collect();
Ok(notes)
}
}