use crate::error::{GpError, GpResult, ToPrimitiveGp};
use crate::{
io::primitive::*,
model::{beat::*, effects::*, enums::*, key_signature::*, song::*},
};
#[derive(Debug, Clone, PartialEq)]
pub struct Note {
pub value: i16,
pub velocity: i16,
pub string: i8,
pub effect: NoteEffect,
pub duration_percent: f32,
pub swap_accidentals: bool,
pub kind: NoteType,
pub duration: Option<i8>,
pub tuplet: Option<i8>,
}
impl Default for Note {
fn default() -> Self {
Note {
value: 0,
velocity: DEFAULT_VELOCITY,
string: 0,
effect: NoteEffect::default(),
duration_percent: 1.0,
swap_accidentals: false,
kind: NoteType::Rest,
duration: None,
tuplet: None,
}
}
}
impl Note {
pub(crate) fn real_value(&self, strings: &[(i8, i8)]) -> GpResult<i8> {
if self.string > 0 {
let value = self.value.to_i8_gp("note value")?;
let string_idx = self.string.to_usize_gp("string index")? - 1;
return Ok(value + strings[string_idx].1);
}
Err(GpError::InvalidValue {
context: "note real_value (string must be > 0)",
value: self.string as i64,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NoteEffect {
pub accentuated_note: bool,
pub bend: Option<BendEffect>,
pub ghost_note: bool,
pub grace: Option<GraceEffect>,
pub hammer: bool,
pub harmonic: Option<HarmonicEffect>,
pub heavy_accentuated_note: bool,
pub left_hand_finger: Fingering,
pub let_ring: bool,
pub palm_mute: bool,
pub right_hand_finger: Fingering,
pub slides: Vec<SlideType>,
pub staccato: bool,
pub tremolo_picking: Option<TremoloPickingEffect>,
pub trill: Option<TrillEffect>,
pub vibrato: bool,
pub ornament: Option<String>,
}
impl Default for NoteEffect {
fn default() -> Self {
NoteEffect {
accentuated_note: false,
bend: None,
ghost_note: false,
grace: None,
hammer: false,
harmonic: None,
heavy_accentuated_note: false,
left_hand_finger: Fingering::Open,
let_ring: false,
palm_mute: false,
right_hand_finger: Fingering::Open,
slides: Vec::new(),
staccato: false,
tremolo_picking: None,
trill: None,
vibrato: false,
ornament: None,
}
}
}
impl NoteEffect {
pub(crate) fn is_bend(&self) -> bool {
self.bend.is_some()
}
pub(crate) fn is_harmonic(&self) -> bool {
self.harmonic.is_some()
}
pub(crate) fn is_grace(&self) -> bool {
self.grace.is_some()
}
pub(crate) fn is_trill(&self) -> bool {
self.trill.is_some()
}
pub(crate) fn is_tremollo_picking(&self) -> bool {
self.tremolo_picking.is_some()
}
pub(crate) fn is_default(&self) -> bool {
let d = NoteEffect::default();
self.left_hand_finger == d.left_hand_finger
&& self.right_hand_finger == d.right_hand_finger
&& self.bend == d.bend
&& self.harmonic == d.harmonic
&& self.grace == d.grace
&& self.trill == d.trill
&& self.tremolo_picking == d.tremolo_picking
&& self.vibrato == d.vibrato
&& self.slides == d.slides
&& self.hammer == d.hammer
&& self.palm_mute == d.palm_mute
&& self.staccato == d.staccato
&& self.let_ring == d.let_ring
}
pub(crate) fn is_fingering(&self) -> bool {
self.left_hand_finger != Fingering::Open || self.right_hand_finger != Fingering::Open
}
}
pub trait SongNoteOps {
fn read_notes(
&mut self,
data: &[u8],
seek: &mut usize,
track_index: usize,
beat: &mut Beat,
duration: &Duration,
note_effect: NoteEffect,
) -> GpResult<()>;
fn read_note(
&mut self,
data: &[u8],
seek: &mut usize,
note: &mut Note,
guitar_string: (i8, i8),
track_index: usize,
) -> GpResult<()>;
fn read_note_v5(
&mut self,
data: &[u8],
seek: &mut usize,
note: &mut Note,
guitar_string: (i8, i8),
track_index: usize,
) -> GpResult<()>;
fn read_note_effects_v3(&self, data: &[u8], seek: &mut usize, note: &mut Note) -> GpResult<()>;
fn read_note_effects_v4(
&mut self,
data: &[u8],
seek: &mut usize,
note: &mut Note,
) -> GpResult<()>;
fn get_tied_note_value(&self, string_index: i8, track_index: usize) -> i16;
fn write_notes(
&self,
data: &mut Vec<u8>,
beat: &Beat,
strings: &[(i8, i8)],
version: &(u8, u8, u8),
) -> GpResult<()>;
fn write_note_v3(&self, data: &mut Vec<u8>, note: &Note) -> GpResult<()>;
fn write_note_v4(
&self,
data: &mut Vec<u8>,
note: &Note,
strings: &[(i8, i8)],
version: &(u8, u8, u8),
) -> GpResult<()>;
fn write_note_v5(
&self,
data: &mut Vec<u8>,
note: &Note,
strings: &[(i8, i8)],
version: &(u8, u8, u8),
) -> GpResult<()>;
fn pack_note_flags(&self, note: &Note, version: &(u8, u8, u8)) -> u8;
fn write_note_effects_v3(&self, data: &mut Vec<u8>, note: &Note) -> GpResult<()>;
fn write_note_effects(
&self,
data: &mut Vec<u8>,
note: &Note,
strings: &[(i8, i8)],
version: &(u8, u8, u8),
) -> GpResult<()>;
}
impl SongNoteOps for Song {
fn read_notes(
&mut self,
data: &[u8],
seek: &mut usize,
track_index: usize,
beat: &mut Beat,
duration: &Duration,
note_effect: NoteEffect,
) -> GpResult<()> {
let flags = read_byte(data, seek)?;
for i in 0..self.tracks[track_index].strings.len() {
if (flags & 1 << (7 - self.tracks[track_index].strings[i].0)) > 0 {
let mut note = Note {
effect: note_effect.clone(),
..Default::default()
};
if self.version.number < (5, 0, 0) {
self.read_note(
data,
seek,
&mut note,
self.tracks[track_index].strings[i],
track_index,
)?;
} else {
self.read_note_v5(
data,
seek,
&mut note,
self.tracks[track_index].strings[i],
track_index,
)?;
}
beat.notes.push(note);
}
beat.duration = duration.clone();
}
Ok(())
}
fn read_note(
&mut self,
data: &[u8],
seek: &mut usize,
note: &mut Note,
guitar_string: (i8, i8),
track_index: usize,
) -> GpResult<()> {
let flags = read_byte(data, seek)?;
note.string = guitar_string.0;
note.effect.ghost_note = (flags & 0x04) == 0x04;
if (flags & 0x20) == 0x20 {
note.kind = get_note_type(read_byte(data, seek)?);
}
if (flags & 0x01) == 0x01 {
note.duration = Some(read_signed_byte(data, seek)?);
note.tuplet = Some(read_signed_byte(data, seek)?);
}
if (flags & 0x10) == 0x10 {
let v = read_signed_byte(data, seek)?;
note.velocity = crate::model::effects::unpack_velocity(v.to_i16_gp("velocity")?);
}
if (flags & 0x20) == 0x20 {
let fret = read_signed_byte(data, seek)?;
let value = if note.kind == NoteType::Tie {
self.get_tied_note_value(guitar_string.0, track_index)
} else {
fret.to_i16_gp("fret value")?
};
note.value = value.clamp(0, 99);
}
if (flags & 0x80) == 0x80 {
note.effect.left_hand_finger = get_fingering(read_signed_byte(data, seek)?);
note.effect.right_hand_finger = get_fingering(read_signed_byte(data, seek)?);
}
if (flags & 0x08) == 0x08 {
if self.version.number == (3, 0, 0) {
self.read_note_effects_v3(data, seek, note)?;
} else if self.version.number.0 == 4 {
self.read_note_effects_v4(data, seek, note)?;
}
if let Some(mut h) = note.effect.harmonic.take() {
if h.kind == HarmonicType::Tapped {
h.fret = Some(note.value.to_i8_gp("note value")? + 12);
}
note.effect.harmonic = Some(h);
}
}
Ok(())
}
fn read_note_v5(
&mut self,
data: &[u8],
seek: &mut usize,
note: &mut Note,
guitar_string: (i8, i8),
track_index: usize,
) -> GpResult<()> {
let flags = read_byte(data, seek)?;
note.string = guitar_string.0;
note.effect.heavy_accentuated_note = (flags & 0x02) == 0x02;
note.effect.ghost_note = (flags & 0x04) == 0x04;
note.effect.accentuated_note = (flags & 0x40) == 0x40;
if (flags & 0x20) == 0x20 {
note.kind = get_note_type(read_byte(data, seek)?);
}
if (flags & 0x10) == 0x10 {
let v = read_signed_byte(data, seek)?;
note.velocity = crate::model::effects::unpack_velocity(v.to_i16_gp("velocity")?);
}
if (flags & 0x20) == 0x20 {
let fret = read_signed_byte(data, seek)?;
let value = if note.kind == NoteType::Tie {
self.get_tied_note_value(guitar_string.0, track_index)
} else {
fret.to_i16_gp("fret value")?
};
note.value = value.clamp(0, 99);
}
if (flags & 0x80) == 0x80 {
note.effect.left_hand_finger = get_fingering(read_signed_byte(data, seek)?);
note.effect.right_hand_finger = get_fingering(read_signed_byte(data, seek)?);
}
if (flags & 0x01) == 0x01 {
note.duration_percent = read_double(data, seek)?.to_f32_gp("duration percent")?;
}
note.swap_accidentals = (read_byte(data, seek)? & 0x02) == 0x02;
if (flags & 0x08) == 0x08 {
self.read_note_effects_v4(data, seek, note)?;
}
Ok(())
}
fn read_note_effects_v3(&self, data: &[u8], seek: &mut usize, note: &mut Note) -> GpResult<()> {
let flags = read_byte(data, seek)?;
note.effect.hammer = (flags & 0x02) == 0x02;
note.effect.let_ring = (flags & 0x08) == 0x08;
if (flags & 0x01) == 0x01 {
note.effect.bend = self.read_bend_effect(data, seek)?;
}
if (flags & 0x10) == 0x10 {
note.effect.grace = Some(self.read_grace_effect(data, seek)?);
}
if (flags & 0x04) == 0x04 {
note.effect.slides.push(SlideType::ShiftSlideTo);
}
Ok(())
}
fn read_note_effects_v4(
&mut self,
data: &[u8],
seek: &mut usize,
note: &mut Note,
) -> GpResult<()> {
let flags1 = read_signed_byte(data, seek)?;
let flags2 = read_signed_byte(data, seek)?;
note.effect.hammer = (flags1 & 0x02) == 0x02;
note.effect.let_ring = (flags1 & 0x08) == 0x08;
note.effect.staccato = (flags2 & 0x01) == 0x01;
note.effect.palm_mute = (flags2 & 0x02) == 0x02;
note.effect.vibrato = (flags2 & 0x40) == 0x40 || note.effect.vibrato;
if (flags1 & 0x01) == 0x01 {
note.effect.bend = self.read_bend_effect(data, seek)?;
}
if (flags1 & 0x10) == 0x10 {
if self.version.number >= (5, 0, 0) {
note.effect.grace = Some(self.read_grace_effect_v5(data, seek)?);
} else {
note.effect.grace = Some(self.read_grace_effect(data, seek)?);
}
}
if (flags2 & 0x04) == 0x04 {
note.effect.tremolo_picking = Some(self.read_tremolo_picking(data, seek)?);
}
if (flags2 & 0x08) == 0x08 {
if self.version.number >= (5, 0, 0) {
note.effect.slides.extend(self.read_slides_v5(data, seek)?);
} else {
note.effect
.slides
.push(get_slide_type(read_signed_byte(data, seek)?)?);
}
}
if (flags2 & 0x10) == 0x10 {
if self.version.number >= (5, 0, 0) {
note.effect.harmonic = Some(self.read_harmonic_v5(data, seek)?);
} else {
note.effect.harmonic = Some(self.read_harmonic(data, seek, note)?);
}
}
if (flags2 & 0x20) == 0x20 {
note.effect.trill = Some(self.read_trill(data, seek)?);
}
Ok(())
}
fn get_tied_note_value(&self, string_index: i8, track_index: usize) -> i16 {
for m in (0usize..self.tracks[track_index].measures.len()).rev() {
for v in (0usize..self.tracks[track_index].measures[m].voices.len()).rev() {
for b in 0..self.tracks[track_index].measures[m].voices[v].beats.len() {
if self.tracks[track_index].measures[m].voices[v].beats[b].status
!= BeatStatus::Empty
{
for n in 0..self.tracks[track_index].measures[m].voices[v].beats[b]
.notes
.len()
{
if self.tracks[track_index].measures[m].voices[v].beats[b].notes[n]
.string
== string_index
{
return self.tracks[track_index].measures[m].voices[v].beats[b]
.notes[n]
.value;
}
}
}
}
}
}
-1
}
fn write_notes(
&self,
data: &mut Vec<u8>,
beat: &Beat,
strings: &[(i8, i8)],
version: &(u8, u8, u8),
) -> GpResult<()> {
let mut string_flags: u8 = 0;
for i in 0..beat.notes.len() {
string_flags |= 1 << (7 - beat.notes[i].string);
}
write_byte(data, string_flags);
let mut notes = beat.notes.clone();
notes.sort_by_key(|k| k.string);
for note in ¬es {
if version.0 == 3 {
self.write_note_v3(data, note)?;
} else if version.0 == 4 {
self.write_note_v4(data, note, strings, version)?;
} else if version.0 == 5 {
self.write_note_v5(data, note, strings, version)?;
}
}
Ok(())
}
fn write_note_v3(&self, data: &mut Vec<u8>, note: &Note) -> GpResult<()> {
let flags: u8 = self.pack_note_flags(note, &(3, 0, 0));
write_byte(data, flags);
if (flags & 0x20) == 0x20 {
write_byte(data, from_note_type(¬e.kind));
}
if (flags & 0x01) == 0x01 {
write_signed_byte(
data,
note.duration.ok_or(GpError::MissingState {
field: "note duration",
})?,
);
write_signed_byte(
data,
note.tuplet.ok_or(GpError::MissingState {
field: "note tuplet",
})?,
);
}
if (flags & 0x10) == 0x10 {
write_signed_byte(data, crate::model::effects::pack_velocity(note.velocity)?);
}
if (flags & 0x20) == 0x20 {
if note.kind != NoteType::Rest {
write_signed_byte(data, note.value.to_i8_gp("note value")?);
} else {
write_signed_byte(data, 0);
}
}
if (flags & 0x08) == 0x08 {
self.write_note_effects_v3(data, note)?;
}
Ok(())
}
fn write_note_v4(
&self,
data: &mut Vec<u8>,
note: &Note,
strings: &[(i8, i8)],
version: &(u8, u8, u8),
) -> GpResult<()> {
let flags: u8 = self.pack_note_flags(note, version);
write_byte(data, flags);
if (flags & 0x20) == 0x20 {
write_byte(data, from_note_type(¬e.kind));
}
if (flags & 0x01) == 0x01 {
write_signed_byte(
data,
note.duration.ok_or(GpError::MissingState {
field: "note duration",
})?,
);
write_signed_byte(
data,
note.tuplet.ok_or(GpError::MissingState {
field: "note tuplet",
})?,
);
}
if (flags & 0x10) == 0x10 {
write_signed_byte(data, crate::model::effects::pack_velocity(note.velocity)?);
}
if (flags & 0x20) == 0x20 {
if note.kind != NoteType::Rest {
write_signed_byte(data, note.value.to_i8_gp("note value")?);
} else {
write_signed_byte(data, 0);
}
}
if (flags & 0x80) == 0x80 {
write_signed_byte(data, from_fingering(¬e.effect.left_hand_finger));
write_signed_byte(data, from_fingering(¬e.effect.right_hand_finger));
}
if (flags & 0x08) == 0x08 {
if version.0 == 3 {
self.write_note_effects_v3(data, note)?;
} else {
self.write_note_effects(data, note, strings, version)?;
}
}
Ok(())
}
fn write_note_v5(
&self,
data: &mut Vec<u8>,
note: &Note,
strings: &[(i8, i8)],
version: &(u8, u8, u8),
) -> GpResult<()> {
let flags: u8 = self.pack_note_flags(note, version);
write_byte(data, flags);
if (flags & 0x20) == 0x20 {
write_byte(data, from_note_type(¬e.kind));
}
if (flags & 0x10) == 0x10 {
write_signed_byte(data, crate::model::effects::pack_velocity(note.velocity)?);
}
if (flags & 0x20) == 0x20 {
if note.kind != NoteType::Tie {
write_signed_byte(data, note.value.to_i8_gp("note value")?);
} else {
write_signed_byte(data, 0);
}
}
if (flags & 0x80) == 0x80 {
write_signed_byte(data, from_fingering(¬e.effect.left_hand_finger));
write_signed_byte(data, from_fingering(¬e.effect.right_hand_finger));
}
if (flags & 0x01) == 0x01 {
write_f64(data, note.duration_percent.to_f64_gp("duration percent")?);
}
let mut flags2 = 0u8;
if note.swap_accidentals {
flags2 |= 0x02;
}
write_byte(data, flags2);
if (flags & 0x08) == 0x08 {
self.write_note_effects(data, note, strings, version)?;
}
Ok(())
}
fn pack_note_flags(&self, note: &Note, version: &(u8, u8, u8)) -> u8 {
let mut flags: u8 = 0u8;
if note.duration.is_some() && note.tuplet.is_some() {
flags |= 0x01;
}
if note.effect.heavy_accentuated_note {
flags |= 0x02;
}
if note.effect.ghost_note {
flags |= 0x04;
}
if !note.effect.is_default() {
flags |= 0x08;
}
if note.velocity != DEFAULT_VELOCITY {
flags |= 0x10;
}
flags |= 0x20;
if version.0 > 3 {
if note.effect.accentuated_note {
flags |= 0x40;
}
if note.effect.is_fingering() {
flags |= 0x80;
}
}
if version.0 >= 5 && (note.duration_percent - 1.0).abs() > 1e-3 {
flags |= 0x01;
}
flags
}
fn write_note_effects_v3(&self, data: &mut Vec<u8>, note: &Note) -> GpResult<()> {
let mut flags1 = 0u8;
if note.effect.is_bend() {
flags1 |= 0x01;
}
if note.effect.hammer {
flags1 |= 0x02;
}
if note.effect.slides.contains(&SlideType::ShiftSlideTo)
|| note.effect.slides.contains(&SlideType::LegatoSlideTo)
{
flags1 |= 0x04;
}
if note.effect.let_ring {
flags1 |= 0x08;
}
if note.effect.is_grace() {
flags1 |= 0x10;
}
write_byte(data, flags1);
if (flags1 & 0x01) == 0x01 {
self.write_bend(data, ¬e.effect.bend)?;
}
if (flags1 & 0x10) == 0x10 {
self.write_grace(data, ¬e.effect.grace)?;
}
Ok(())
}
fn write_note_effects(
&self,
data: &mut Vec<u8>,
note: &Note,
strings: &[(i8, i8)],
version: &(u8, u8, u8),
) -> GpResult<()> {
let mut flags1 = 0i8;
if note.effect.is_bend() {
flags1 |= 0x01;
}
if note.effect.hammer {
flags1 |= 0x02;
}
if note.effect.let_ring {
flags1 |= 0x08;
}
if note.effect.is_grace() {
flags1 |= 0x10;
}
write_signed_byte(data, flags1);
let mut flags2 = 0i8;
if note.effect.staccato {
flags2 |= 0x01;
}
if note.effect.palm_mute {
flags2 |= 0x02;
}
if note.effect.is_tremollo_picking() {
flags2 |= 0x04;
}
if !note.effect.slides.is_empty() {
flags2 |= 0x08;
}
if note.effect.is_harmonic() {
flags2 |= 0x10;
}
if note.effect.is_trill() {
flags2 |= 0x20;
}
if note.effect.vibrato {
flags2 |= 0x40;
}
write_signed_byte(data, flags2);
if (flags1 & 0x01) == 0x01 {
self.write_bend(data, ¬e.effect.bend)?;
}
if (flags1 & 0x10) == 0x10 {
if version.0 < 5 {
self.write_grace(data, ¬e.effect.grace)?;
} else {
self.write_grace_v5(data, ¬e.effect.grace)?;
}
}
if (flags2 & 0x04) == 0x04 {
if let Some(tp) = ¬e.effect.tremolo_picking {
let duration_val = tp.duration.value.to_u8_gp("tremolo picking duration")?;
let encoded = match duration_val {
DURATION_EIGHTH => 1,
DURATION_SIXTEENTH => 2,
DURATION_THIRTY_SECOND => 3,
_ => {
return Err(GpError::WriteError(format!(
"Invalid tremolo picking duration: {}",
duration_val
)));
}
};
write_signed_byte(data, encoded);
}
}
if (flags2 & 0x08) == 0x08 {
if version.0 < 5 {
write_signed_byte(data, from_slide_type(¬e.effect.slides[0]));
} else {
self.write_slides_v5(data, ¬e.effect.slides);
}
}
if (flags2 & 0x10) == 0x10 {
if version.0 < 5 {
self.write_harmonic(data, note, strings)?;
} else {
self.write_harmonic_v5(data, note, strings)?;
}
}
if (flags2 & 0x20) == 0x20 {
let t = note.effect.trill.as_ref().ok_or_else(|| {
GpError::WriteError("Trill flag set but no trill data".to_string())
})?;
write_signed_byte(data, t.fret);
let duration_val = t.duration.value.to_u8_gp("trill duration")?;
let encoded = match duration_val {
DURATION_SIXTEENTH => 1,
DURATION_THIRTY_SECOND => 2,
DURATION_SIXTY_FOURTH => 3,
_ => {
return Err(GpError::WriteError(format!(
"Invalid trill duration: {}",
duration_val
)));
}
};
write_signed_byte(data, encoded);
}
Ok(())
}
}