use crate::audio::midi::*;
use crate::error::{GpResult, ToPrimitiveGp};
use crate::io::gpif_import::*;
use crate::io::primitive::*;
use crate::model::enums::*;
use crate::model::headers::*;
use crate::model::key_signature::*;
use crate::model::lyric::*;
use crate::model::measure::*;
use crate::model::page::*;
use crate::model::rse::*;
use crate::model::track::*;
#[derive(Debug, Clone)]
pub struct Song {
pub version: Version,
pub clipboard: Option<Clipboard>,
pub name: String,
pub subtitle: String, pub artist: String,
pub album: String,
pub words: String, pub author: String, pub date: String,
pub copyright: String,
pub writer: String,
pub transcriber: String,
pub instructions: String,
pub comments: String,
pub notice: Vec<String>,
pub tracks: Vec<Track>,
pub measure_headers: Vec<MeasureHeader>,
pub channels: Vec<MidiChannel>,
pub lyrics: Lyrics,
pub tempo: i16,
pub hide_tempo: bool,
pub tempo_name: String,
pub key: KeySignature,
pub triplet_feel: TripletFeel,
pub master_effect: RseMasterEffect,
pub page_setup: PageSetup,
pub current_measure_number: Option<usize>,
pub current_track: Option<usize>,
pub current_voice_number: Option<usize>,
pub current_beat_number: Option<usize>,
}
impl Default for Song {
fn default() -> Self {
Song {
version: Version {
data: String::with_capacity(30),
clipboard: false,
number: (5, 1, 0),
},
clipboard: None,
name: String::new(),
subtitle: String::new(),
artist: String::new(),
album: String::new(),
words: String::new(),
author: String::new(),
date: String::new(),
copyright: String::new(),
writer: String::new(),
transcriber: String::new(),
comments: String::new(),
notice: Vec::new(),
instructions: String::new(),
tracks: Vec::new(),
measure_headers: Vec::new(),
channels: Vec::with_capacity(64),
lyrics: Lyrics::default(),
tempo: 120,
hide_tempo: false,
tempo_name: String::from("Moderate"),
key: KeySignature::default(),
triplet_feel: TripletFeel::None,
current_measure_number: None,
current_track: None,
current_voice_number: None,
current_beat_number: None,
page_setup: PageSetup::default(),
master_effect: RseMasterEffect::default(),
}
}
}
impl Song {
pub fn read_gp3(&mut self, data: &[u8]) -> GpResult<()> {
let mut seek: usize = 0;
self.version = read_version_string(data, &mut seek)?;
self.read_info(data, &mut seek)?;
self.triplet_feel = if read_bool(data, &mut seek)? {
TripletFeel::Eighth
} else {
TripletFeel::None
};
self.tempo = read_int(data, &mut seek)?.to_i16_gp("tempo")?;
self.key.key = read_int(data, &mut seek)?.to_i8_gp("key")?;
self.read_midi_channels(data, &mut seek)?;
let measure_count = read_int(data, &mut seek)?.to_usize_gp("measure count")?;
let track_count = read_int(data, &mut seek)?.to_usize_gp("track count")?;
self.read_measure_headers(data, &mut seek, measure_count)?;
self.current_measure_number = Some(0);
self.read_tracks(data, &mut seek, track_count)?;
self.read_measures(data, &mut seek)?;
Ok(())
}
pub fn read_gp4(&mut self, data: &[u8]) -> GpResult<()> {
let mut seek: usize = 0;
self.version = read_version_string(data, &mut seek)?;
self.read_clipboard(data, &mut seek)?;
self.read_info(data, &mut seek)?;
self.triplet_feel = if read_bool(data, &mut seek)? {
TripletFeel::Eighth
} else {
TripletFeel::None
};
self.lyrics = self.read_lyrics(data, &mut seek)?; self.tempo = read_int(data, &mut seek)?.to_i16_gp("tempo")?;
self.key.key = read_int(data, &mut seek)?.to_i8_gp("key")?;
read_signed_byte(data, &mut seek)?; self.read_midi_channels(data, &mut seek)?;
let measure_count = read_int(data, &mut seek)?.to_usize_gp("measure count")?;
let track_count = read_int(data, &mut seek)?.to_usize_gp("track count")?;
self.read_measure_headers(data, &mut seek, measure_count)?;
self.read_tracks(data, &mut seek, track_count)?;
self.read_measures(data, &mut seek)?;
Ok(())
}
pub fn read_gp5(&mut self, data: &[u8]) -> GpResult<()> {
let mut seek: usize = 0;
self.version = read_version_string(data, &mut seek)?;
self.read_clipboard(data, &mut seek)?;
self.read_info(data, &mut seek)?;
self.lyrics = self.read_lyrics(data, &mut seek)?; self.master_effect = self.read_rse_master_effect(data, &mut seek)?;
self.read_page_setup(data, &mut seek)?;
self.tempo_name = read_int_size_string(data, &mut seek)?;
self.tempo = read_int(data, &mut seek)?.to_i16_gp("tempo")?;
self.hide_tempo = if self.version.number > (5, 0, 0) {
read_bool(data, &mut seek)?
} else {
false
};
self.key.key = read_signed_byte(data, &mut seek)?;
read_int(data, &mut seek)?; self.read_midi_channels(data, &mut seek)?;
let directions = self.read_directions(data, &mut seek)?;
self.master_effect.reverb = read_int(data, &mut seek)?.to_f32_gp("reverb")?;
let measure_count = read_int(data, &mut seek)?.to_usize_gp("measure count")?;
let track_count = read_int(data, &mut seek)?.to_usize_gp("track count")?;
self.read_measure_headers_v5(data, &mut seek, measure_count, &directions)?;
self.read_tracks_v5(data, &mut seek, track_count)?;
self.read_measures(data, &mut seek)?;
Ok(())
}
pub fn read_gp(&mut self, data: &[u8]) -> GpResult<()> {
use crate::io::gpx::read_gp;
let gpif = read_gp(data)?;
self.version.number = (7, 0, 0); self.read_gpif(&gpif);
Ok(())
}
pub fn read_gpx(&mut self, data: &[u8]) -> GpResult<()> {
use crate::io::gpx::read_gpx;
let gpif = read_gpx(data)?;
self.version.number = (6, 0, 0);
self.read_gpif(&gpif);
Ok(())
}
fn read_info(&mut self, data: &[u8], seek: &mut usize) -> GpResult<()> {
self.name = read_int_byte_size_string(data, seek)?; self.subtitle = read_int_byte_size_string(data, seek)?;
self.artist = read_int_byte_size_string(data, seek)?;
self.album = read_int_byte_size_string(data, seek)?;
self.words = read_int_byte_size_string(data, seek)?; self.author = if self.version.number.0 < 5 {
self.words.clone()
} else {
read_int_byte_size_string(data, seek)?
};
self.copyright = read_int_byte_size_string(data, seek)?;
self.writer = read_int_byte_size_string(data, seek)?; self.instructions = read_int_byte_size_string(data, seek)?; let nc = read_int(data, seek)?.to_usize_gp("notice count")?;
for _ in 0..nc {
self.notice.push(read_int_byte_size_string(data, seek)?);
}
Ok(())
}
pub fn write(&self, version: (u8, u8, u8), clipboard: Option<bool>) -> GpResult<Vec<u8>> {
let mut data: Vec<u8> = Vec::with_capacity(8388608); write_version(&mut data, version);
if clipboard.is_some_and(|c| c) && version.0 >= 4 {
self.write_clipboard(&mut data, &version)?;
}
self.write_info(&mut data, version)?;
if version.0 < 5 {
write_bool(&mut data, self.triplet_feel != TripletFeel::None);
}
if version.0 >= 4 {
self.write_lyrics(&mut data)?;
}
if version > (5, 0, 0) {
self.write_rse_master_effect(&mut data);
}
if version.0 >= 5 {
self.write_page_setup(&mut data)?;
write_int_size_string(&mut data, &self.tempo_name);
}
write_i32(&mut data, self.tempo.to_i32_gp("tempo")?);
if version > (5, 0, 0) {
write_bool(&mut data, self.hide_tempo);
}
write_i32(&mut data, self.key.key.to_i32_gp("key signature")?);
if version.0 >= 4 {
write_signed_byte(&mut data, 0);
} self.write_midi_channels(&mut data);
if version.0 == 5 {
self.write_directions(&mut data)?;
self.write_master_reverb(&mut data);
}
write_i32(
&mut data,
self.tracks[0].measures.len().to_i32_gp("measures count")?,
);
write_i32(&mut data, self.tracks.len().to_i32_gp("tracks count")?);
self.write_measure_headers(&mut data, &version)?;
self.write_tracks(&mut data, &version)?;
self.write_measures(&mut data, &version)?;
write_i32(&mut data, 0);
Ok(data)
}
fn write_info(&self, data: &mut Vec<u8>, version: (u8, u8, u8)) -> GpResult<()> {
write_int_byte_size_string(data, &self.name);
write_int_byte_size_string(data, &self.subtitle);
write_int_byte_size_string(data, &self.artist);
write_int_byte_size_string(data, &self.album);
if version.0 < 5 {
write_int_byte_size_string(data, &self.pack_author());
} else {
write_int_byte_size_string(data, &self.words);
write_int_byte_size_string(data, &self.author);
}
write_int_byte_size_string(data, &self.copyright);
write_int_byte_size_string(data, &self.writer);
write_int_byte_size_string(data, &self.instructions);
write_i32(data, self.notice.len().to_i32_gp("notice count")?);
for i in 0..self.notice.len() {
write_int_byte_size_string(data, &self.notice[i]);
}
Ok(())
}
fn pack_author(&self) -> String {
if !self.words.is_empty() && !self.author.is_empty() {
if self.words != self.author {
let mut s = self.words.clone();
s.push_str(", ");
s.push_str(&self.author);
s
} else {
self.words.clone()
}
} else {
let mut s = self.words.clone();
s.push_str(&self.author);
s
}
}
}