Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 64 additions & 4 deletions src/contract/notes.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ use cairo_wave::note::Note;
trait INotesContract<TContractState> {
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;
Expand All @@ -20,7 +21,11 @@ mod NotesContract {
#[abi(embed_v0)]
impl NotesContractImpl of INotesContract<ContractState> {
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()
}
Expand All @@ -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,
Expand Down Expand Up @@ -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);
}
}
55 changes: 43 additions & 12 deletions src/note.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::traits::TryInto;
use core::array::SpanTrait;
use cairo_wave::utils;
use cairo_wave::wave::WavFile;
Expand All @@ -17,21 +18,26 @@ struct Note {
note_type: NoteType,
}

#[derive(Drop, Copy, Serde)]
struct Channel {
notes: Span<Note>,
}

#[derive(Drop, Copy)]
struct Music {
notes: Span<Note>,
channels: Span<Channel>,
sample_rate: u32,
bit_depth: u16,
}

// TODO generics
trait NoteToSamples {
fn to_mono(self: Note, sample_rate_hz: u32, bit_depth: u16) -> Array<u32>;
fn append_to_mono(self: Note, ref data: Array<u32>, sample_rate_hz: u32, bit_depth: u16);
fn to_samples(self: Note, sample_rate_hz: u32, bit_depth: u16) -> Array<u32>;
fn append_to_samples(self: Note, ref data: Array<u32>, sample_rate_hz: u32, bit_depth: u16);
}

impl NoteToSamplesImpl of NoteToSamples {
fn to_mono(self: Note, sample_rate_hz: u32, bit_depth: u16) -> Array<u32> {
fn to_samples(self: Note, sample_rate_hz: u32, bit_depth: u16) -> Array<u32> {
match self.note_type {
NoteType::Sine => utils::generate_sine_wave(
self.frequency_hz, self.duration_ms, sample_rate_hz, bit_depth
Expand All @@ -48,8 +54,8 @@ impl NoteToSamplesImpl of NoteToSamples {
}
}

fn append_to_mono(self: Note, ref data: Array<u32>, 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<u32>, 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);
}
Expand All @@ -71,14 +77,39 @@ impl MusicToWavFile of Into<Music, WavFile> {
fn into(self: Music) -> WavFile {
// TODO: loop over notes
let mut data: Array<u32> = 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<u32> = 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<u32> = 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,
Expand Down