use fraction::ToPrimitive;
use crate::{
error::{GpError, GpResult, ToPrimitiveGp},
io::primitive::*,
model::{chord::*, enums::*, key_signature::*, song::*},
};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct BendPoint {
pub position: u8,
pub value: i8,
pub vibrato: bool,
}
impl BendPoint {
fn _get_time(&self, duration: u8) -> GpResult<u16> {
let time =
f32::from(duration) * f32::from(self.position) / f32::from(BEND_EFFECT_MAX_POSITION);
Ok(time.to_i16_gp("bend point time")? as u16)
}
}
pub const BEND_EFFECT_MAX_POSITION: u8 = 12;
pub const GP_BEND_SEMITONE: f32 = 25.0;
pub const GP_BEND_POSITION: f32 = 60.0;
pub const GP_BEND_SEMITONE_LENGTH: f32 = 1.0;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BendEffect {
pub kind: BendType,
pub value: i16,
pub points: Vec<BendPoint>,
pub semitone_length: u8,
pub max_position: u8,
pub max_value: u8,
}
impl Default for BendEffect {
fn default() -> Self {
BendEffect {
kind: BendType::None,
value: 0,
points: Vec::with_capacity(12),
semitone_length: 1,
max_position: BEND_EFFECT_MAX_POSITION,
max_value: 12,
}
}
}
pub const MIN_VELOCITY: i16 = 15;
pub const VELOCITY_INCREMENT: i16 = 16;
pub const FORTE: i16 = MIN_VELOCITY + VELOCITY_INCREMENT * 5;
pub const DEFAULT_VELOCITY: i16 = FORTE;
pub(crate) fn unpack_velocity(v: i16) -> i16 {
MIN_VELOCITY + VELOCITY_INCREMENT * v - VELOCITY_INCREMENT
}
pub(crate) fn pack_velocity(velocity: i16) -> GpResult<i8> {
((velocity + VELOCITY_INCREMENT - MIN_VELOCITY) as f32 / VELOCITY_INCREMENT as f32)
.ceil()
.to_i8_gp("pack_velocity")
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GraceEffect {
pub duration: u8,
pub fret: i8,
pub is_dead: bool,
pub is_on_beat: bool,
pub transition: GraceEffectTransition,
pub velocity: i16,
}
impl Default for GraceEffect {
fn default() -> Self {
GraceEffect {
duration: 1,
fret: 0,
is_dead: false,
is_on_beat: false,
transition: GraceEffectTransition::None,
velocity: DEFAULT_VELOCITY,
}
}
}
impl GraceEffect {
pub(crate) fn _duration_time(self) -> GpResult<i16> {
let quarter_time =
crate::model::key_signature::DURATION_QUARTER_TIME.to_i16_gp("quarter time")?;
let time = f32::from(quarter_time) / 16f32 * f32::from(self.duration);
time.to_i16_gp("grace duration time")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HarmonicEffect {
pub kind: HarmonicType,
pub pitch: Option<PitchClass>,
pub octave: Option<Octave>,
pub fret: Option<i8>,
}
impl Default for HarmonicEffect {
fn default() -> Self {
HarmonicEffect {
kind: HarmonicType::Natural,
pitch: None,
octave: None,
fret: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct TremoloPickingEffect {
pub duration: Duration,
}
fn from_tremolo_value(value: i8) -> GpResult<u8> {
match value {
1 => Ok(DURATION_EIGHTH),
2 => Ok(DURATION_SIXTEENTH),
3 => Ok(DURATION_THIRTY_SECOND),
_ => Ok(DURATION_SIXTEENTH),
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct TrillEffect {
pub fret: i8,
pub duration: Duration,
}
pub trait SongEffectOps {
fn read_bend_effect(&self, data: &[u8], seek: &mut usize) -> GpResult<Option<BendEffect>>;
fn read_grace_effect(&self, data: &[u8], seek: &mut usize) -> GpResult<GraceEffect>;
fn read_grace_effect_v5(&self, data: &[u8], seek: &mut usize) -> GpResult<GraceEffect>;
fn read_tremolo_picking(&self, data: &[u8], seek: &mut usize)
-> GpResult<TremoloPickingEffect>;
fn read_slides_v5(&self, data: &[u8], seek: &mut usize) -> GpResult<Vec<SlideType>>;
fn read_harmonic(
&self,
data: &[u8],
seek: &mut usize,
note: &crate::model::note::Note,
) -> GpResult<HarmonicEffect>;
fn read_harmonic_v5(&mut self, data: &[u8], seek: &mut usize) -> GpResult<HarmonicEffect>;
fn read_trill(&self, data: &[u8], seek: &mut usize) -> GpResult<TrillEffect>;
fn write_bend(&self, data: &mut Vec<u8>, bend: &Option<BendEffect>) -> GpResult<()>;
fn write_grace(&self, data: &mut Vec<u8>, grace: &Option<GraceEffect>) -> GpResult<()>;
fn write_grace_v5(&self, data: &mut Vec<u8>, grace: &Option<GraceEffect>) -> GpResult<()>;
fn write_harmonic(
&self,
data: &mut Vec<u8>,
note: &crate::model::note::Note,
strings: &[(i8, i8)],
) -> GpResult<()>;
fn write_harmonic_v5(
&self,
data: &mut Vec<u8>,
note: &crate::model::note::Note,
strings: &[(i8, i8)],
) -> GpResult<()>;
fn write_slides_v5(&self, data: &mut Vec<u8>, slides: &[SlideType]);
}
fn from_trill_period(period: i8) -> GpResult<u16> {
match period {
1 => Ok(u16::from(DURATION_SIXTEENTH)),
2 => Ok(u16::from(DURATION_THIRTY_SECOND)),
3 => Ok(u16::from(DURATION_SIXTY_FOURTH)),
_ => Ok(u16::from(DURATION_SIXTEENTH)),
}
}
impl SongEffectOps for Song {
fn read_bend_effect(&self, data: &[u8], seek: &mut usize) -> GpResult<Option<BendEffect>> {
let mut be = BendEffect {
kind: get_bend_type(read_signed_byte(data, seek)?)?,
..Default::default()
};
be.value = read_int(data, seek)?.to_i16().unwrap_or(0);
let count: u8 = read_int(data, seek)?.to_u8().unwrap_or(0);
for _ in 0..count {
let mut bp = BendPoint {
position: (f32::from(read_int(data, seek)?.to_i16().unwrap_or(0))
* f32::from(BEND_EFFECT_MAX_POSITION)
/ GP_BEND_POSITION)
.round()
.to_u8()
.unwrap_or(0),
..Default::default()
};
bp.value = (f32::from(read_int(data, seek)?.to_i16().unwrap_or(0))
* f32::from(be.semitone_length)
/ GP_BEND_SEMITONE)
.round()
.to_i8()
.unwrap_or(0);
bp.vibrato = read_bool(data, seek)?;
be.points.push(bp);
}
if count > 0 { Ok(Some(be)) } else { Ok(None) }
}
fn read_grace_effect(&self, data: &[u8], seek: &mut usize) -> GpResult<GraceEffect> {
let mut g = GraceEffect {
fret: read_signed_byte(data, seek)?,
..Default::default()
};
g.velocity = unpack_velocity(read_byte(data, seek)? as i16);
g.duration = 1 << (7 - read_byte(data, seek)?);
g.is_dead = g.fret == -1;
g.transition = get_grace_effect_transition(read_signed_byte(data, seek)?)?;
Ok(g)
}
fn read_grace_effect_v5(&self, data: &[u8], seek: &mut usize) -> GpResult<GraceEffect> {
let mut g = GraceEffect {
fret: read_byte(data, seek)? as i8,
..Default::default()
};
g.velocity = unpack_velocity(read_byte(data, seek)? as i16);
g.transition = get_grace_effect_transition(read_byte(data, seek)? as i8)?;
let dur_byte = read_byte(data, seek)?;
g.duration = if dur_byte <= 7 {
1 << (7 - dur_byte)
} else {
1
};
let flags = read_byte(data, seek)?;
g.is_dead = (flags & 0x01) == 0x01;
g.is_on_beat = (flags & 0x02) == 0x02;
Ok(g)
}
fn read_tremolo_picking(
&self,
data: &[u8],
seek: &mut usize,
) -> GpResult<TremoloPickingEffect> {
let mut tp = TremoloPickingEffect::default();
tp.duration.value = u16::from(from_tremolo_value(read_signed_byte(data, seek)?)?);
Ok(tp)
}
fn read_slides_v5(&self, data: &[u8], seek: &mut usize) -> GpResult<Vec<SlideType>> {
let t = read_byte(data, seek)?;
let mut v: Vec<SlideType> = Vec::with_capacity(6);
if (t & 0x01) == 0x01 {
v.push(SlideType::ShiftSlideTo);
}
if (t & 0x02) == 0x02 {
v.push(SlideType::LegatoSlideTo);
}
if (t & 0x04) == 0x04 {
v.push(SlideType::OutDownwards);
}
if (t & 0x08) == 0x08 {
v.push(SlideType::OutUpWards);
}
if (t & 0x10) == 0x10 {
v.push(SlideType::IntoFromBelow);
}
if (t & 0x20) == 0x20 {
v.push(SlideType::IntoFromAbove);
}
Ok(v)
}
fn read_harmonic(
&self,
data: &[u8],
seek: &mut usize,
note: &crate::model::note::Note,
) -> GpResult<HarmonicEffect> {
let mut he = HarmonicEffect::default();
match read_signed_byte(data, seek)? {
1 => he.kind = HarmonicType::Natural,
3 => he.kind = HarmonicType::Tapped,
4 => he.kind = HarmonicType::Pinch,
5 => he.kind = HarmonicType::Semi,
15 => {
he.pitch = Some(PitchClass::from(((note.value + 7) % 12) as i8, None, None));
he.octave = Some(Octave::Ottava);
he.kind = HarmonicType::Artificial;
}
17 => {
let track_idx = self.current_track.ok_or(GpError::MissingState {
field: "current_track",
})?;
he.pitch = Some(PitchClass::from(
note.real_value(&self.tracks[track_idx].strings)?,
None,
None,
));
he.octave = Some(Octave::Quindicesima);
he.kind = HarmonicType::Artificial;
}
22 => {
let track_idx = self.current_track.ok_or(GpError::MissingState {
field: "current_track",
})?;
he.pitch = Some(PitchClass::from(
note.real_value(&self.tracks[track_idx].strings)?,
None,
None,
));
he.octave = Some(Octave::Ottava);
he.kind = HarmonicType::Artificial;
}
_ => he.kind = HarmonicType::Natural,
};
Ok(he)
}
fn read_harmonic_v5(&mut self, data: &[u8], seek: &mut usize) -> GpResult<HarmonicEffect> {
let mut he = HarmonicEffect::default();
match read_signed_byte(data, seek)? {
1 => he.kind = HarmonicType::Natural,
2 => {
he.kind = HarmonicType::Artificial;
let semitone = read_byte(data, seek)? as i8;
let accidental = read_signed_byte(data, seek)?;
he.pitch = Some(PitchClass::from(semitone, Some(accidental), None));
he.octave = Some(get_octave(read_byte(data, seek)?)?);
}
3 => {
he.kind = HarmonicType::Tapped;
he.fret = Some(read_byte(data, seek)? as i8);
}
4 => he.kind = HarmonicType::Pinch,
5 => he.kind = HarmonicType::Semi,
_ => he.kind = HarmonicType::Natural,
};
Ok(he)
}
fn read_trill(&self, data: &[u8], seek: &mut usize) -> GpResult<TrillEffect> {
let mut t = TrillEffect {
fret: read_signed_byte(data, seek)?,
..Default::default()
};
t.duration.value = from_trill_period(read_signed_byte(data, seek)?)?;
Ok(t)
}
fn write_bend(&self, data: &mut Vec<u8>, bend: &Option<BendEffect>) -> GpResult<()> {
if let Some(b) = bend {
write_signed_byte(data, from_bend_type(&b.kind));
write_i32(data, i32::from(b.value));
write_i32(data, b.points.len().to_i32_gp("bend points count")?);
for i in 0..b.points.len() {
write_i32(
data,
(f32::from(b.points[i].position) * GP_BEND_POSITION
/ f32::from(BEND_EFFECT_MAX_POSITION))
.round()
.to_i32_gp("bend point position")?,
);
write_i32(
data,
(f32::from(b.points[i].value) * GP_BEND_SEMITONE / GP_BEND_SEMITONE_LENGTH)
.round()
.to_i32_gp("bend point value")?,
);
write_bool(data, b.points[i].vibrato);
}
}
Ok(())
}
fn write_grace(&self, data: &mut Vec<u8>, grace: &Option<GraceEffect>) -> GpResult<()> {
let g = grace.as_ref().ok_or(GpError::MissingState {
field: "grace effect",
})?;
write_signed_byte(data, g.fret);
write_byte(data, pack_velocity(g.velocity)?.to_u8_gp("grace velocity")?);
write_byte(data, g.duration.leading_zeros() as u8); write_signed_byte(data, from_grace_effect_transition(&g.transition));
Ok(())
}
fn write_grace_v5(&self, data: &mut Vec<u8>, grace: &Option<GraceEffect>) -> GpResult<()> {
let g = grace.as_ref().ok_or(GpError::MissingState {
field: "grace effect",
})?;
write_byte(data, g.fret.to_u8_gp("grace fret")?);
write_byte(data, pack_velocity(g.velocity)?.to_u8_gp("grace velocity")?);
write_byte(
data,
from_grace_effect_transition(&g.transition).to_u8_gp("grace transition")?,
);
write_byte(data, g.duration.leading_zeros() as u8); let mut flags = 0u8;
if g.is_dead {
flags |= 0x01;
}
if g.is_on_beat {
flags |= 0x02;
}
write_byte(data, flags);
Ok(())
}
fn write_harmonic(
&self,
data: &mut Vec<u8>,
note: &crate::model::note::Note,
strings: &[(i8, i8)],
) -> GpResult<()> {
if let Some(h) = ¬e.effect.harmonic {
let mut byte = from_harmonic_type(&h.kind);
if h.kind == HarmonicType::Artificial {
byte = 22;
if let (Some(p), Some(o)) = (&h.pitch, &h.octave) {
let real_val = note.real_value(strings)?;
if p.value == ((real_val + 7) % 12) && *o == Octave::Ottava {
byte = 15;
} else if p.value == (real_val % 12) && *o == Octave::Quindicesima {
byte = 17;
}
} else {
byte = 22;
}
}
write_signed_byte(data, byte);
}
Ok(())
}
fn write_harmonic_v5(
&self,
data: &mut Vec<u8>,
note: &crate::model::note::Note,
strings: &[(i8, i8)],
) -> GpResult<()> {
if let Some(h) = ¬e.effect.harmonic {
write_signed_byte(data, from_harmonic_type(&h.kind));
if h.kind == HarmonicType::Artificial {
if let (Some(p), Some(o)) = (&h.pitch, &h.octave) {
write_byte(data, p.just.to_u8_gp("pitch class just")?);
write_signed_byte(data, p.accidental);
write_byte(data, from_octave(o));
} else {
let p = PitchClass::from(note.real_value(strings)? % 12, None, None);
let o = Octave::Ottava;
write_byte(data, p.just.to_u8_gp("pitch class just")?);
write_signed_byte(data, p.accidental);
write_byte(data, from_octave(&o));
}
} else if h.kind == HarmonicType::Tapped {
let fret = h.fret.ok_or(GpError::MissingState {
field: "harmonic fret",
})?;
write_byte(data, fret.to_u8_gp("harmonic fret")?);
}
}
Ok(())
}
fn write_slides_v5(&self, data: &mut Vec<u8>, slides: &[SlideType]) {
let mut st = 0u8; for s in slides {
if s == &SlideType::ShiftSlideTo {
st |= 0x01;
} else if s == &SlideType::LegatoSlideTo {
st |= 0x02;
} else if s == &SlideType::OutDownwards {
st |= 0x04;
} else if s == &SlideType::OutUpWards {
st |= 0x08;
} else if s == &SlideType::IntoFromBelow {
st |= 0x10;
} else if s == &SlideType::IntoFromAbove {
st |= 0x20;
}
}
write_byte(data, st);
}
}