#![doc = include_str!("../README.md")]
pub mod cdnz_serde;
pub mod lilypond;
pub mod upgrade;
pub use cdnz_serde::VersionInfo;
use serde_with::{DisplayFromStr, serde_as, skip_serializing_none};
use std::collections::{BTreeMap, HashMap};
use num::Rational32;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Cdnz {
pub cdnz: Metadata,
pub global: GlobalData,
pub parts: HashMap<String, Part>,
pub books: Vec<Book>,
}
impl ToString for Cdnz {
fn to_string(&self) -> String {
format!("{:#?}", self)
}
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Metadata {
pub score_title: String,
pub composer: PersonInfo,
pub arranger: PersonInfo,
pub engraver: PersonInfo,
pub description: String,
pub music_license: String,
pub engraving_license: String,
}
#[derive(Debug, Serialize, Deserialize, Default)]
#[skip_serializing_none]
pub struct PersonInfo {
pub name: String,
pub email: Option<String>,
pub is_person: bool,
}
#[serde_as]
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct GlobalData {
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
pub modifier_events: BTreeMap<Position, Vec<GlobalModEvent>>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum GlobalModEvent {
KeyChange {
note: Pitch,
mode: KeyMode,
},
TimeChange {
count: u16,
unit: u16,
},
TempoChange {
bpm: Bpm,
display_tempo: bool,
label: Option<String>,
},
}
#[derive(Debug, Serialize, Deserialize)]
pub enum KeyMode {
Major,
Minor,
Dorian,
Phrygian,
Lydian,
Mixolydian,
Locrian,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Bpm {
pub unit: Rational32,
}
#[serde_as]
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Part {
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
pub rhythmic_events: BTreeMap<Position, RhythmicEvent>,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
pub modifier_events: BTreeMap<Position, Vec<LocalModEvent>>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum RhythmicEvent {
Note { pitch: Pitch, duration: Duration },
Rest { duration: Duration },
}
#[derive(Debug, Serialize, Deserialize)]
pub enum LocalModEvent {
ClefChange {
sign: ClefSign,
pos: i32,
octave: i32,
},
}
impl LocalModEvent {
pub fn new_treble_clef() -> LocalModEvent {
LocalModEvent::ClefChange {
sign: ClefSign::G,
pos: -2,
octave: 0,
}
}
pub fn new_bass_clef() -> LocalModEvent {
LocalModEvent::ClefChange {
sign: ClefSign::F,
pos: 2,
octave: 0,
}
}
pub fn new_alto_clef() -> LocalModEvent {
LocalModEvent::ClefChange {
sign: ClefSign::C,
pos: 0,
octave: 0,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub enum ClefSign {
G,
F,
C,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Book {
pub label: String,
pub header: Header,
pub layout: Layout,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Header {}
#[derive(Debug, Serialize, Deserialize)]
pub enum Layout {
Staff(Staff),
StaffGroup(StaffGroup),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Staff {
pub parts: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct StaffGroup {
pub children: Vec<Layout>,
}
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Position {
pub measure: u32,
pub pos: Rational32,
pub grace_index: u32,
}
impl std::fmt::Display for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}:{}", self.measure, self.pos, self.grace_index)
}
}
impl std::str::FromStr for Position {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split(':').collect();
if parts.len() != 3 {
return Err("Format must be measure:pos:grace".to_string());
}
let measure = parts[0]
.parse::<u32>()
.map_err(|_| "Invalid measure index")?;
let pos = parts[1]
.parse::<Rational32>()
.map_err(|_| "Invalid rational position")?;
let grace_index = parts[2].parse::<u32>().map_err(|_| "Invalid grace index")?;
Ok(Position {
measure,
pos,
grace_index,
})
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Pitch {
pub step: i32,
pub alteration: Rational32,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Duration {
pub base: i16,
pub dots: u16,
}