From 2ff933c227ccb6d2827748511b4b98340dc615b7 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 11 Apr 2024 01:51:04 +0200 Subject: [PATCH] add get_notes_stereo --- src/contract/notes.cairo | 68 +++++++++++++++++++++++++++++++++++++--- src/note.cairo | 55 +++++++++++++++++++++++++------- 2 files changed, 107 insertions(+), 16 deletions(-) diff --git a/src/contract/notes.cairo b/src/contract/notes.cairo index 96d79b2..485085b 100644 --- a/src/contract/notes.cairo +++ b/src/contract/notes.cairo @@ -4,12 +4,13 @@ use cairo_wave::note::Note; trait INotesContract { fn get_note(self: @TContractState, note: Note) -> ByteArray; fn get_notes(self: @TContractState, note_dur_ms: u32) -> ByteArray; + fn get_notes_stereo(self: @TContractState, note_dur_ms: u32) -> ByteArray; } #[starknet::contract] mod NotesContract { use core::traits::Into; - use cairo_wave::note::{Note, NoteType, Music, MusicToWavFile}; + use cairo_wave::note::{Note, NoteType, Channel, Music, MusicToWavFile}; use cairo_wave::wave::WavFile; use super::INotesContract; @@ -20,7 +21,11 @@ mod NotesContract { #[abi(embed_v0)] impl NotesContractImpl of INotesContract { fn get_note(self: @ContractState, note: Note) -> ByteArray { - let music = Music { notes: array![note].span(), sample_rate: 8000, bit_depth: 16 }; + let music = Music { + channels: array![Channel { notes: array![note].span() }].span(), + sample_rate: 8000, + bit_depth: 16 + }; let wav: WavFile = music.into(); wav.into() } @@ -37,8 +42,51 @@ mod NotesContract { let empty: Note = Note { frequency_hz: 16000, duration_ms: 10, note_type }; let music = Music { - notes: array![ - sol, la, si, sol, la2, empty, la, si, do2, empty, do2, si2, empty, si2 + channels: array![ + Channel { + notes: array![ + sol, la, si, sol, la2, empty, la, si, do2, empty, do2, si2, empty, si2, + ] + .span() + }, + ] + .span(), + sample_rate: 8000, + bit_depth: 16, + }; + let wav: WavFile = music.into(); + wav.into() + } + + fn get_notes_stereo(self: @ContractState, note_dur_ms: u32) -> ByteArray { + let note_type = NoteType::Square; + let duration_ms = note_dur_ms; + let sol: Note = Note { frequency_hz: 392, duration_ms, note_type }; + let la: Note = Note { frequency_hz: 440, duration_ms, note_type }; + let la2: Note = Note { frequency_hz: 440, duration_ms: 2 * duration_ms, note_type }; + let si: Note = Note { frequency_hz: 494, duration_ms, note_type }; + let do2: Note = Note { frequency_hz: 523, duration_ms: duration_ms * 2, note_type }; + let si2: Note = Note { frequency_hz: 494, duration_ms: duration_ms * 2, note_type }; + let empty: Note = Note { frequency_hz: 16000, duration_ms: 10, note_type }; + let mute: Note = Note { frequency_hz: 16000, duration_ms, note_type }; + let mute2: Note = Note { frequency_hz: 16000, duration_ms: duration_ms * 2, note_type }; + + let music = Music { + channels: array![ + Channel { + notes: array![ + sol, la, si, sol, la2, empty, la, si, do2, empty, do2, si2, empty, si2, + mute, mute, mute, mute, mute2, empty, mute, mute, mute2, empty, mute2, mute2, empty, mute2, + ] + .span() + }, + Channel { + notes: array![ + mute, mute, mute, mute, mute2, empty, mute, mute, mute2, empty, mute2, mute2, empty, mute2, + sol, la, si, sol, la2, empty, la, si, do2, empty, do2, si2, empty, si2 + ] + .span() + }, ] .span(), sample_rate: 8000, @@ -91,4 +139,16 @@ mod tests { assert!(res[3] == 'F'); println!("{:}", res); } + + #[test] + fn test_get_notes_stereo() { + let contract = deploy(); + + let res: ByteArray = contract.get_notes_stereo(100_u32); + assert!(res[0] == 'R'); + assert!(res[1] == 'I'); + assert!(res[2] == 'F'); + assert!(res[3] == 'F'); + println!("{:}", res); + } } diff --git a/src/note.cairo b/src/note.cairo index e4577a1..2b59507 100644 --- a/src/note.cairo +++ b/src/note.cairo @@ -1,3 +1,4 @@ +use core::traits::TryInto; use core::array::SpanTrait; use cairo_wave::utils; use cairo_wave::wave::WavFile; @@ -17,21 +18,26 @@ struct Note { note_type: NoteType, } +#[derive(Drop, Copy, Serde)] +struct Channel { + notes: Span, +} + #[derive(Drop, Copy)] struct Music { - notes: Span, + channels: Span, sample_rate: u32, bit_depth: u16, } // TODO generics trait NoteToSamples { - fn to_mono(self: Note, sample_rate_hz: u32, bit_depth: u16) -> Array; - fn append_to_mono(self: Note, ref data: Array, sample_rate_hz: u32, bit_depth: u16); + fn to_samples(self: Note, sample_rate_hz: u32, bit_depth: u16) -> Array; + fn append_to_samples(self: Note, ref data: Array, sample_rate_hz: u32, bit_depth: u16); } impl NoteToSamplesImpl of NoteToSamples { - fn to_mono(self: Note, sample_rate_hz: u32, bit_depth: u16) -> Array { + fn to_samples(self: Note, sample_rate_hz: u32, bit_depth: u16) -> Array { match self.note_type { NoteType::Sine => utils::generate_sine_wave( self.frequency_hz, self.duration_ms, sample_rate_hz, bit_depth @@ -48,8 +54,8 @@ impl NoteToSamplesImpl of NoteToSamples { } } - fn append_to_mono(self: Note, ref data: Array, sample_rate_hz: u32, bit_depth: u16) { - let mut new_data = self.to_mono(sample_rate_hz, bit_depth); + fn append_to_samples(self: Note, ref data: Array, sample_rate_hz: u32, bit_depth: u16) { + let mut new_data = self.to_samples(sample_rate_hz, bit_depth); while let Option::Some(value) = new_data.pop_front() { data.append(value); } @@ -71,14 +77,39 @@ impl MusicToWavFile of Into { fn into(self: Music) -> WavFile { // TODO: loop over notes let mut data: Array = array![]; - let mut notes = self.notes; - while let Option::Some(note) = notes - .pop_front() { - (*note).append_to_mono(ref data, self.sample_rate, self.bit_depth); - }; + let num_channels: u16 = self.channels.len().try_into().unwrap(); + if num_channels == 1 { + let channel: Channel = *self.channels[0]; + let mut notes = channel.notes; + while let Option::Some(note) = notes + .pop_front() { + (*note).append_to_samples(ref data, self.sample_rate, self.bit_depth); + }; + } else if num_channels == 2 { + let left_channel: Channel = *self.channels[0]; + let right_channel: Channel = *self.channels[1]; + let mut left_notes = left_channel.notes; + let mut right_notes = right_channel.notes; + let mut left_samples: Array = array![]; + while let Option::Some(note) = left_notes + .pop_front() { + (*note).append_to_samples(ref left_samples, self.sample_rate, self.bit_depth); + }; + let mut right_samples: Array = array![]; + while let Option::Some(note) = right_notes + .pop_front() { + (*note).append_to_samples(ref right_samples, self.sample_rate, self.bit_depth); + }; + let mut count = 0; + while count < left_samples.len() { + data.append(*left_samples[count]); + data.append(*right_samples[count]); + count += 1; + } + } WavFile { chunk_size: (36 + data.len()), - num_channels: 1_u16, + num_channels, sample_rate: self.sample_rate, bits_per_sample: self.bit_depth, subchunk2_size: data.len() * 8,