├── web_server ├── src │ └── main.rs └── Cargo.toml ├── test ├── Key.gp4 ├── Key.gp5 ├── RSE.gp5 ├── Wah.gp5 ├── barre.gp ├── bend.gp ├── bend.gp3 ├── bend.gp4 ├── bend.gp5 ├── bend.gpx ├── brush.gp ├── clefs.gp ├── grace.gp ├── rasg.gp ├── rasg.gpx ├── slur.gp ├── slur.gp4 ├── slur.gpx ├── tempo.gp ├── test.gp ├── test.gp5 ├── text.gp ├── text.gpx ├── timer.gp ├── trill.gp ├── turn.gp ├── turn.gpx ├── volta.gp ├── wah.gp ├── wah.gpx ├── Chords.gp3 ├── Chords.gp4 ├── Chords.gp5 ├── No Wah.gp5 ├── Repeat.gp4 ├── Repeat.gp5 ├── Slides.gp4 ├── Slides.gp5 ├── Voices.gp5 ├── Wah-m.gp5 ├── accent.gp ├── accent.gpx ├── barre.gpx ├── brush.gp4 ├── brush.gp5 ├── brush.gpx ├── clefs.gpx ├── dynamic.gp ├── fade-in.gp ├── fermata.gp ├── grace.gp5 ├── grace.gpx ├── keysig.gp ├── keysig.gp4 ├── keysig.gp5 ├── keysig.gpx ├── ottava1.gp ├── ottava2.gp ├── ottava3.gp ├── ottava4.gp ├── ottava5.gp ├── repeats.gp ├── tempo.gp3 ├── tempo.gp4 ├── tempo.gp5 ├── tempo.gpx ├── timer.gpx ├── trill.gp4 ├── trill.gpx ├── vibrato.gp ├── volta.gp3 ├── volta.gp4 ├── volta.gp5 ├── volta.gpx ├── Demo v5.gp5 ├── Duration.gp3 ├── Effects.gp3 ├── Effects.gp4 ├── Effects.gp5 ├── Harmonics.gp3 ├── Harmonics.gp4 ├── Harmonics.gp5 ├── Strokes.gp4 ├── Strokes.gp5 ├── Unknown-m.gp5 ├── Unknown.gp5 ├── Vibrato.gp4 ├── arpeggio.gp ├── arpeggio.gpx ├── basic-bend.gp ├── capo-fret.gp3 ├── capo-fret.gp4 ├── capo-fret.gp5 ├── copyright.gp ├── copyright.gp3 ├── copyright.gp4 ├── copyright.gp5 ├── copyright.gpx ├── dead-note.gp ├── dead-note.gpx ├── directions.gp ├── double-bar.gp ├── dynamic.gp5 ├── dynamic.gpx ├── fade-in.gp4 ├── fade-in.gp5 ├── fade-in.gpx ├── fermata.gpx ├── fingering.gp ├── fingering.gp4 ├── fingering.gp5 ├── fingering.gpx ├── free-time.gp ├── free-time.gpx ├── ghost-note.gp ├── high-pitch.gp ├── let-ring.gp ├── let-ring.gp4 ├── let-ring.gp5 ├── let-ring.gpx ├── mordents.gp ├── mordents.gpx ├── ottava1.gpx ├── ottava2.gpx ├── ottava3.gpx ├── ottava4.gpx ├── ottava5.gpx ├── palm-mute.gp ├── palm-mute.gp4 ├── palm-mute.gp5 ├── palm-mute.gpx ├── repeats.gpx ├── sforzato.gp ├── sforzato.gp4 ├── sforzato.gpx ├── tremolos.gp ├── tremolos.gp5 ├── tremolos.gpx ├── tuplets.gpx ├── tuplets2.gpx ├── vibrato.gp5 ├── vibrato.gpx ├── Directions.gp5 ├── basic-bend.gp5 ├── basic-bend.gpx ├── directions.gpx ├── dotted-gliss.gp ├── double-bar.gpx ├── fret-diagram.gp ├── ghost-note.gpx ├── ghost_note.gp3 ├── heavy-accent.gp ├── high-pitch.gp3 ├── high-pitch.gpx ├── legato-slide.gp ├── multivoices.gp ├── multivoices.gpx ├── pick-up-down.gp ├── shift-slide.gp ├── shift-slide.gp4 ├── shift-slide.gp5 ├── shift-slide.gpx ├── slide-out-up.gp ├── slur_voices.gp ├── slur_voices.gpx ├── tap-slap-pop.gp ├── tremolo-bar.gp ├── volume-swell.gp ├── 001_Funky_Guy.gp5 ├── 2 whole bars.tmp ├── all-percussion.gp ├── all-percussion.gp5 ├── all-percussion.gpx ├── dotted-gliss.gp3 ├── dotted-gliss.gpx ├── dotted-tuplets.gp ├── dotted-tuplets.gp5 ├── dotted-tuplets.gpx ├── fret-diagram.gp4 ├── fret-diagram.gp5 ├── fret-diagram.gpx ├── grace-on-beat.gp ├── grace-on-beat.gpx ├── heavy-accent.gp5 ├── heavy-accent.gpx ├── legato-slide.gp4 ├── legato-slide.gp5 ├── legato-slide.gpx ├── pick-up-down.gp4 ├── pick-up-down.gp5 ├── pick-up-down.gpx ├── repeated-bars.gp ├── repeated-bars.gpx ├── rest-centered.gp ├── rest-centered.gp4 ├── rest-centered.gp5 ├── rest-centered.gpx ├── slide-in-above.gp ├── slide-in-above.gp4 ├── slide-in-above.gp5 ├── slide-in-above.gpx ├── slide-in-below.gp ├── slide-in-below.gp4 ├── slide-in-below.gp5 ├── slide-in-below.gpx ├── slide-out-down.gp ├── slide-out-down.gp4 ├── slide-out-down.gp5 ├── slide-out-down.gpx ├── slide-out-up.gp4 ├── slide-out-up.gp5 ├── slide-out-up.gpx ├── tap-slap-pop.gp5 ├── testIrrTuplet.gp ├── testIrrTuplet.gp4 ├── testIrrTuplet.gpx ├── volume-swell.gpx ├── grace-before-beat.gp ├── slur_hammer_slur.gp ├── slur_hammer_slur.gpx ├── slur_slur_hammer.gp ├── slur_slur_hammer.gpx ├── tuplet-with-slur.gp ├── tuplet-with-slur.gp4 ├── tuplet-with-slur.gpx ├── artificial-harmonic.gp ├── artificial-harmonic.gpx ├── chord_without_notes.gp5 ├── chordnames_keyboard.gp ├── chordnames_keyboard.gpx ├── crescendo-diminuendo.gp ├── grace-before-beat.gpx ├── slur_over_3_measures.gp ├── crescendo-diminuendo.gpx ├── slur-notes-effect-mask.gp ├── slur_over_3_measures.gpx ├── Unknown Chord Extension.gp5 ├── beams-stems-ledger-lines.gp ├── beams-stems-ledger-lines.gp5 ├── beams-stems-ledger-lines.gpx ├── fret-diagram_2instruments.gp ├── slur-notes-effect-mask.gp5 ├── slur-notes-effect-mask.gpx ├── fret-diagram_2instruments.gpx └── gamma_ray-heading_for_tomorrow.gp3 ├── Cargo.toml ├── .github ├── workflows │ ├── rust.yml │ └── rust-clippy.yml └── dependabot.yml ├── .gitignore ├── cli ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── lib ├── Cargo.toml ├── README.md ├── src │ ├── lyric.rs │ ├── page.rs │ ├── measure.rs │ ├── midi.rs │ ├── key_signature.rs │ ├── rse.rs │ ├── io.rs │ ├── song.rs │ ├── mix_table.rs │ ├── track.rs │ └── enums.rs ├── FILE-STRUCTURE-CHORD-DIAGRAMS.md ├── FILE-STRUCTURE.md ├── FILE-STRUCTURE-NOTE.md └── FILE-STRUCTURE-BODY.md ├── README.md ├── LICENSE └── CODE_OF_CONDUCT.md /web_server/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /test/Key.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Key.gp4 -------------------------------------------------------------------------------- /test/Key.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Key.gp5 -------------------------------------------------------------------------------- /test/RSE.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/RSE.gp5 -------------------------------------------------------------------------------- /test/Wah.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Wah.gp5 -------------------------------------------------------------------------------- /test/barre.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/barre.gp -------------------------------------------------------------------------------- /test/bend.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/bend.gp -------------------------------------------------------------------------------- /test/bend.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/bend.gp3 -------------------------------------------------------------------------------- /test/bend.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/bend.gp4 -------------------------------------------------------------------------------- /test/bend.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/bend.gp5 -------------------------------------------------------------------------------- /test/bend.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/bend.gpx -------------------------------------------------------------------------------- /test/brush.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/brush.gp -------------------------------------------------------------------------------- /test/clefs.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/clefs.gp -------------------------------------------------------------------------------- /test/grace.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/grace.gp -------------------------------------------------------------------------------- /test/rasg.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/rasg.gp -------------------------------------------------------------------------------- /test/rasg.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/rasg.gpx -------------------------------------------------------------------------------- /test/slur.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur.gp -------------------------------------------------------------------------------- /test/slur.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur.gp4 -------------------------------------------------------------------------------- /test/slur.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur.gpx -------------------------------------------------------------------------------- /test/tempo.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tempo.gp -------------------------------------------------------------------------------- /test/test.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/test.gp -------------------------------------------------------------------------------- /test/test.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/test.gp5 -------------------------------------------------------------------------------- /test/text.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/text.gp -------------------------------------------------------------------------------- /test/text.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/text.gpx -------------------------------------------------------------------------------- /test/timer.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/timer.gp -------------------------------------------------------------------------------- /test/trill.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/trill.gp -------------------------------------------------------------------------------- /test/turn.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/turn.gp -------------------------------------------------------------------------------- /test/turn.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/turn.gpx -------------------------------------------------------------------------------- /test/volta.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/volta.gp -------------------------------------------------------------------------------- /test/wah.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/wah.gp -------------------------------------------------------------------------------- /test/wah.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/wah.gpx -------------------------------------------------------------------------------- /test/Chords.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Chords.gp3 -------------------------------------------------------------------------------- /test/Chords.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Chords.gp4 -------------------------------------------------------------------------------- /test/Chords.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Chords.gp5 -------------------------------------------------------------------------------- /test/No Wah.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/No Wah.gp5 -------------------------------------------------------------------------------- /test/Repeat.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Repeat.gp4 -------------------------------------------------------------------------------- /test/Repeat.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Repeat.gp5 -------------------------------------------------------------------------------- /test/Slides.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Slides.gp4 -------------------------------------------------------------------------------- /test/Slides.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Slides.gp5 -------------------------------------------------------------------------------- /test/Voices.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Voices.gp5 -------------------------------------------------------------------------------- /test/Wah-m.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Wah-m.gp5 -------------------------------------------------------------------------------- /test/accent.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/accent.gp -------------------------------------------------------------------------------- /test/accent.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/accent.gpx -------------------------------------------------------------------------------- /test/barre.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/barre.gpx -------------------------------------------------------------------------------- /test/brush.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/brush.gp4 -------------------------------------------------------------------------------- /test/brush.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/brush.gp5 -------------------------------------------------------------------------------- /test/brush.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/brush.gpx -------------------------------------------------------------------------------- /test/clefs.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/clefs.gpx -------------------------------------------------------------------------------- /test/dynamic.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/dynamic.gp -------------------------------------------------------------------------------- /test/fade-in.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fade-in.gp -------------------------------------------------------------------------------- /test/fermata.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fermata.gp -------------------------------------------------------------------------------- /test/grace.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/grace.gp5 -------------------------------------------------------------------------------- /test/grace.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/grace.gpx -------------------------------------------------------------------------------- /test/keysig.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/keysig.gp -------------------------------------------------------------------------------- /test/keysig.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/keysig.gp4 -------------------------------------------------------------------------------- /test/keysig.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/keysig.gp5 -------------------------------------------------------------------------------- /test/keysig.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/keysig.gpx -------------------------------------------------------------------------------- /test/ottava1.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ottava1.gp -------------------------------------------------------------------------------- /test/ottava2.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ottava2.gp -------------------------------------------------------------------------------- /test/ottava3.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ottava3.gp -------------------------------------------------------------------------------- /test/ottava4.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ottava4.gp -------------------------------------------------------------------------------- /test/ottava5.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ottava5.gp -------------------------------------------------------------------------------- /test/repeats.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/repeats.gp -------------------------------------------------------------------------------- /test/tempo.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tempo.gp3 -------------------------------------------------------------------------------- /test/tempo.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tempo.gp4 -------------------------------------------------------------------------------- /test/tempo.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tempo.gp5 -------------------------------------------------------------------------------- /test/tempo.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tempo.gpx -------------------------------------------------------------------------------- /test/timer.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/timer.gpx -------------------------------------------------------------------------------- /test/trill.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/trill.gp4 -------------------------------------------------------------------------------- /test/trill.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/trill.gpx -------------------------------------------------------------------------------- /test/vibrato.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/vibrato.gp -------------------------------------------------------------------------------- /test/volta.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/volta.gp3 -------------------------------------------------------------------------------- /test/volta.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/volta.gp4 -------------------------------------------------------------------------------- /test/volta.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/volta.gp5 -------------------------------------------------------------------------------- /test/volta.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/volta.gpx -------------------------------------------------------------------------------- /test/Demo v5.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Demo v5.gp5 -------------------------------------------------------------------------------- /test/Duration.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Duration.gp3 -------------------------------------------------------------------------------- /test/Effects.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Effects.gp3 -------------------------------------------------------------------------------- /test/Effects.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Effects.gp4 -------------------------------------------------------------------------------- /test/Effects.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Effects.gp5 -------------------------------------------------------------------------------- /test/Harmonics.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Harmonics.gp3 -------------------------------------------------------------------------------- /test/Harmonics.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Harmonics.gp4 -------------------------------------------------------------------------------- /test/Harmonics.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Harmonics.gp5 -------------------------------------------------------------------------------- /test/Strokes.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Strokes.gp4 -------------------------------------------------------------------------------- /test/Strokes.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Strokes.gp5 -------------------------------------------------------------------------------- /test/Unknown-m.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Unknown-m.gp5 -------------------------------------------------------------------------------- /test/Unknown.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Unknown.gp5 -------------------------------------------------------------------------------- /test/Vibrato.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Vibrato.gp4 -------------------------------------------------------------------------------- /test/arpeggio.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/arpeggio.gp -------------------------------------------------------------------------------- /test/arpeggio.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/arpeggio.gpx -------------------------------------------------------------------------------- /test/basic-bend.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/basic-bend.gp -------------------------------------------------------------------------------- /test/capo-fret.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/capo-fret.gp3 -------------------------------------------------------------------------------- /test/capo-fret.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/capo-fret.gp4 -------------------------------------------------------------------------------- /test/capo-fret.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/capo-fret.gp5 -------------------------------------------------------------------------------- /test/copyright.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/copyright.gp -------------------------------------------------------------------------------- /test/copyright.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/copyright.gp3 -------------------------------------------------------------------------------- /test/copyright.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/copyright.gp4 -------------------------------------------------------------------------------- /test/copyright.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/copyright.gp5 -------------------------------------------------------------------------------- /test/copyright.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/copyright.gpx -------------------------------------------------------------------------------- /test/dead-note.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/dead-note.gp -------------------------------------------------------------------------------- /test/dead-note.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/dead-note.gpx -------------------------------------------------------------------------------- /test/directions.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/directions.gp -------------------------------------------------------------------------------- /test/double-bar.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/double-bar.gp -------------------------------------------------------------------------------- /test/dynamic.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/dynamic.gp5 -------------------------------------------------------------------------------- /test/dynamic.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/dynamic.gpx -------------------------------------------------------------------------------- /test/fade-in.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fade-in.gp4 -------------------------------------------------------------------------------- /test/fade-in.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fade-in.gp5 -------------------------------------------------------------------------------- /test/fade-in.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fade-in.gpx -------------------------------------------------------------------------------- /test/fermata.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fermata.gpx -------------------------------------------------------------------------------- /test/fingering.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fingering.gp -------------------------------------------------------------------------------- /test/fingering.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fingering.gp4 -------------------------------------------------------------------------------- /test/fingering.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fingering.gp5 -------------------------------------------------------------------------------- /test/fingering.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fingering.gpx -------------------------------------------------------------------------------- /test/free-time.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/free-time.gp -------------------------------------------------------------------------------- /test/free-time.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/free-time.gpx -------------------------------------------------------------------------------- /test/ghost-note.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ghost-note.gp -------------------------------------------------------------------------------- /test/high-pitch.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/high-pitch.gp -------------------------------------------------------------------------------- /test/let-ring.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/let-ring.gp -------------------------------------------------------------------------------- /test/let-ring.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/let-ring.gp4 -------------------------------------------------------------------------------- /test/let-ring.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/let-ring.gp5 -------------------------------------------------------------------------------- /test/let-ring.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/let-ring.gpx -------------------------------------------------------------------------------- /test/mordents.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/mordents.gp -------------------------------------------------------------------------------- /test/mordents.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/mordents.gpx -------------------------------------------------------------------------------- /test/ottava1.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ottava1.gpx -------------------------------------------------------------------------------- /test/ottava2.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ottava2.gpx -------------------------------------------------------------------------------- /test/ottava3.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ottava3.gpx -------------------------------------------------------------------------------- /test/ottava4.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ottava4.gpx -------------------------------------------------------------------------------- /test/ottava5.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ottava5.gpx -------------------------------------------------------------------------------- /test/palm-mute.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/palm-mute.gp -------------------------------------------------------------------------------- /test/palm-mute.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/palm-mute.gp4 -------------------------------------------------------------------------------- /test/palm-mute.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/palm-mute.gp5 -------------------------------------------------------------------------------- /test/palm-mute.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/palm-mute.gpx -------------------------------------------------------------------------------- /test/repeats.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/repeats.gpx -------------------------------------------------------------------------------- /test/sforzato.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/sforzato.gp -------------------------------------------------------------------------------- /test/sforzato.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/sforzato.gp4 -------------------------------------------------------------------------------- /test/sforzato.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/sforzato.gpx -------------------------------------------------------------------------------- /test/tremolos.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tremolos.gp -------------------------------------------------------------------------------- /test/tremolos.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tremolos.gp5 -------------------------------------------------------------------------------- /test/tremolos.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tremolos.gpx -------------------------------------------------------------------------------- /test/tuplets.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tuplets.gpx -------------------------------------------------------------------------------- /test/tuplets2.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tuplets2.gpx -------------------------------------------------------------------------------- /test/vibrato.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/vibrato.gp5 -------------------------------------------------------------------------------- /test/vibrato.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/vibrato.gpx -------------------------------------------------------------------------------- /test/Directions.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Directions.gp5 -------------------------------------------------------------------------------- /test/basic-bend.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/basic-bend.gp5 -------------------------------------------------------------------------------- /test/basic-bend.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/basic-bend.gpx -------------------------------------------------------------------------------- /test/directions.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/directions.gpx -------------------------------------------------------------------------------- /test/dotted-gliss.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/dotted-gliss.gp -------------------------------------------------------------------------------- /test/double-bar.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/double-bar.gpx -------------------------------------------------------------------------------- /test/fret-diagram.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fret-diagram.gp -------------------------------------------------------------------------------- /test/ghost-note.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ghost-note.gpx -------------------------------------------------------------------------------- /test/ghost_note.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/ghost_note.gp3 -------------------------------------------------------------------------------- /test/heavy-accent.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/heavy-accent.gp -------------------------------------------------------------------------------- /test/high-pitch.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/high-pitch.gp3 -------------------------------------------------------------------------------- /test/high-pitch.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/high-pitch.gpx -------------------------------------------------------------------------------- /test/legato-slide.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/legato-slide.gp -------------------------------------------------------------------------------- /test/multivoices.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/multivoices.gp -------------------------------------------------------------------------------- /test/multivoices.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/multivoices.gpx -------------------------------------------------------------------------------- /test/pick-up-down.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/pick-up-down.gp -------------------------------------------------------------------------------- /test/shift-slide.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/shift-slide.gp -------------------------------------------------------------------------------- /test/shift-slide.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/shift-slide.gp4 -------------------------------------------------------------------------------- /test/shift-slide.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/shift-slide.gp5 -------------------------------------------------------------------------------- /test/shift-slide.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/shift-slide.gpx -------------------------------------------------------------------------------- /test/slide-out-up.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-out-up.gp -------------------------------------------------------------------------------- /test/slur_voices.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur_voices.gp -------------------------------------------------------------------------------- /test/slur_voices.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur_voices.gpx -------------------------------------------------------------------------------- /test/tap-slap-pop.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tap-slap-pop.gp -------------------------------------------------------------------------------- /test/tremolo-bar.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tremolo-bar.gp -------------------------------------------------------------------------------- /test/volume-swell.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/volume-swell.gp -------------------------------------------------------------------------------- /test/001_Funky_Guy.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/001_Funky_Guy.gp5 -------------------------------------------------------------------------------- /test/2 whole bars.tmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/2 whole bars.tmp -------------------------------------------------------------------------------- /test/all-percussion.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/all-percussion.gp -------------------------------------------------------------------------------- /test/all-percussion.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/all-percussion.gp5 -------------------------------------------------------------------------------- /test/all-percussion.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/all-percussion.gpx -------------------------------------------------------------------------------- /test/dotted-gliss.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/dotted-gliss.gp3 -------------------------------------------------------------------------------- /test/dotted-gliss.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/dotted-gliss.gpx -------------------------------------------------------------------------------- /test/dotted-tuplets.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/dotted-tuplets.gp -------------------------------------------------------------------------------- /test/dotted-tuplets.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/dotted-tuplets.gp5 -------------------------------------------------------------------------------- /test/dotted-tuplets.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/dotted-tuplets.gpx -------------------------------------------------------------------------------- /test/fret-diagram.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fret-diagram.gp4 -------------------------------------------------------------------------------- /test/fret-diagram.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fret-diagram.gp5 -------------------------------------------------------------------------------- /test/fret-diagram.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fret-diagram.gpx -------------------------------------------------------------------------------- /test/grace-on-beat.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/grace-on-beat.gp -------------------------------------------------------------------------------- /test/grace-on-beat.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/grace-on-beat.gpx -------------------------------------------------------------------------------- /test/heavy-accent.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/heavy-accent.gp5 -------------------------------------------------------------------------------- /test/heavy-accent.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/heavy-accent.gpx -------------------------------------------------------------------------------- /test/legato-slide.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/legato-slide.gp4 -------------------------------------------------------------------------------- /test/legato-slide.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/legato-slide.gp5 -------------------------------------------------------------------------------- /test/legato-slide.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/legato-slide.gpx -------------------------------------------------------------------------------- /test/pick-up-down.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/pick-up-down.gp4 -------------------------------------------------------------------------------- /test/pick-up-down.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/pick-up-down.gp5 -------------------------------------------------------------------------------- /test/pick-up-down.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/pick-up-down.gpx -------------------------------------------------------------------------------- /test/repeated-bars.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/repeated-bars.gp -------------------------------------------------------------------------------- /test/repeated-bars.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/repeated-bars.gpx -------------------------------------------------------------------------------- /test/rest-centered.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/rest-centered.gp -------------------------------------------------------------------------------- /test/rest-centered.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/rest-centered.gp4 -------------------------------------------------------------------------------- /test/rest-centered.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/rest-centered.gp5 -------------------------------------------------------------------------------- /test/rest-centered.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/rest-centered.gpx -------------------------------------------------------------------------------- /test/slide-in-above.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-in-above.gp -------------------------------------------------------------------------------- /test/slide-in-above.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-in-above.gp4 -------------------------------------------------------------------------------- /test/slide-in-above.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-in-above.gp5 -------------------------------------------------------------------------------- /test/slide-in-above.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-in-above.gpx -------------------------------------------------------------------------------- /test/slide-in-below.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-in-below.gp -------------------------------------------------------------------------------- /test/slide-in-below.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-in-below.gp4 -------------------------------------------------------------------------------- /test/slide-in-below.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-in-below.gp5 -------------------------------------------------------------------------------- /test/slide-in-below.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-in-below.gpx -------------------------------------------------------------------------------- /test/slide-out-down.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-out-down.gp -------------------------------------------------------------------------------- /test/slide-out-down.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-out-down.gp4 -------------------------------------------------------------------------------- /test/slide-out-down.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-out-down.gp5 -------------------------------------------------------------------------------- /test/slide-out-down.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-out-down.gpx -------------------------------------------------------------------------------- /test/slide-out-up.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-out-up.gp4 -------------------------------------------------------------------------------- /test/slide-out-up.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-out-up.gp5 -------------------------------------------------------------------------------- /test/slide-out-up.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slide-out-up.gpx -------------------------------------------------------------------------------- /test/tap-slap-pop.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tap-slap-pop.gp5 -------------------------------------------------------------------------------- /test/testIrrTuplet.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/testIrrTuplet.gp -------------------------------------------------------------------------------- /test/testIrrTuplet.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/testIrrTuplet.gp4 -------------------------------------------------------------------------------- /test/testIrrTuplet.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/testIrrTuplet.gpx -------------------------------------------------------------------------------- /test/volume-swell.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/volume-swell.gpx -------------------------------------------------------------------------------- /test/grace-before-beat.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/grace-before-beat.gp -------------------------------------------------------------------------------- /test/slur_hammer_slur.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur_hammer_slur.gp -------------------------------------------------------------------------------- /test/slur_hammer_slur.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur_hammer_slur.gpx -------------------------------------------------------------------------------- /test/slur_slur_hammer.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur_slur_hammer.gp -------------------------------------------------------------------------------- /test/slur_slur_hammer.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur_slur_hammer.gpx -------------------------------------------------------------------------------- /test/tuplet-with-slur.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tuplet-with-slur.gp -------------------------------------------------------------------------------- /test/tuplet-with-slur.gp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tuplet-with-slur.gp4 -------------------------------------------------------------------------------- /test/tuplet-with-slur.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/tuplet-with-slur.gpx -------------------------------------------------------------------------------- /test/artificial-harmonic.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/artificial-harmonic.gp -------------------------------------------------------------------------------- /test/artificial-harmonic.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/artificial-harmonic.gpx -------------------------------------------------------------------------------- /test/chord_without_notes.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/chord_without_notes.gp5 -------------------------------------------------------------------------------- /test/chordnames_keyboard.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/chordnames_keyboard.gp -------------------------------------------------------------------------------- /test/chordnames_keyboard.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/chordnames_keyboard.gpx -------------------------------------------------------------------------------- /test/crescendo-diminuendo.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/crescendo-diminuendo.gp -------------------------------------------------------------------------------- /test/grace-before-beat.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/grace-before-beat.gpx -------------------------------------------------------------------------------- /test/slur_over_3_measures.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur_over_3_measures.gp -------------------------------------------------------------------------------- /test/crescendo-diminuendo.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/crescendo-diminuendo.gpx -------------------------------------------------------------------------------- /test/slur-notes-effect-mask.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur-notes-effect-mask.gp -------------------------------------------------------------------------------- /test/slur_over_3_measures.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur_over_3_measures.gpx -------------------------------------------------------------------------------- /test/Unknown Chord Extension.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/Unknown Chord Extension.gp5 -------------------------------------------------------------------------------- /test/beams-stems-ledger-lines.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/beams-stems-ledger-lines.gp -------------------------------------------------------------------------------- /test/beams-stems-ledger-lines.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/beams-stems-ledger-lines.gp5 -------------------------------------------------------------------------------- /test/beams-stems-ledger-lines.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/beams-stems-ledger-lines.gpx -------------------------------------------------------------------------------- /test/fret-diagram_2instruments.gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fret-diagram_2instruments.gp -------------------------------------------------------------------------------- /test/slur-notes-effect-mask.gp5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur-notes-effect-mask.gp5 -------------------------------------------------------------------------------- /test/slur-notes-effect-mask.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/slur-notes-effect-mask.gpx -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace.package] 2 | authors = ["slundi"] 3 | 4 | [workspace] 5 | members = ["lib", "cli", "web_server"] 6 | -------------------------------------------------------------------------------- /test/fret-diagram_2instruments.gpx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/fret-diagram_2instruments.gpx -------------------------------------------------------------------------------- /test/gamma_ray-heading_for_tomorrow.gp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slundi/guitarpro/HEAD/test/gamma_ray-heading_for_tomorrow.gp3 -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb -------------------------------------------------------------------------------- /web_server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "web_server" 3 | version = "0.2.2" 4 | authors = ["slundi"] 5 | edition = "2021" 6 | description = "Web server to perform queries and operations to music files" 7 | repository = "https://gitlab.com/slundi/guitar-io" 8 | license = "MIT" 9 | readme = "README.md" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [[bin]] 14 | name = "score_server" 15 | path = "src/main.rs" 16 | 17 | [dependencies] 18 | 19 | [dev-dependencies] 20 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | -------------------------------------------------------------------------------- /cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cli" 3 | version = "0.0.1" 4 | authors = ["slundi"] 5 | edition = "2021" 6 | description = "Rust command line interface (CLI) for guitar tab files." 7 | repository = "https://gitlab.com/slundi/guitar-io" 8 | license = "MIT" 9 | readme = "README.md" 10 | 11 | [[bin]] 12 | name = "score_tool" 13 | path = "src/main.rs" 14 | required-features = ["clap"] 15 | 16 | [dependencies] 17 | lib = { path = "../lib" } 18 | clap = { version = "4", features = ["derive"], optional = true } 19 | fraction = "0.13" 20 | encoding_rs = "0.8" 21 | 22 | [dev-dependencies] 23 | -------------------------------------------------------------------------------- /lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lib" 3 | version = "0.2.2" 4 | authors = ["slundi"] 5 | edition = "2021" 6 | description = "Rust library and command line interface (CLI) for guitar tab files." 7 | repository = "https://gitlab.com/slundi/guitar-io" 8 | license = "MIT" 9 | readme = "README.md" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [lib] 14 | name = "scorelib" 15 | path = "src/lib.rs" 16 | 17 | [dependencies] 18 | clap = { version = "4", features = ["derive"], optional = true } 19 | fraction = "0.13" 20 | encoding_rs = "0.8" 21 | 22 | [features] 23 | build-binary = ["clap"] 24 | 25 | [dev-dependencies] 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Guitar tools 2 | 3 | ## Introduction 4 | 5 | In the beginning, this was just a library to read gGuitar Pro files, but I had lot of ideas so I split this sproject: 6 | 7 | - the [library](lib/README.md) to read **Guitar Pro** files (GP3, GP4, GP5, GPX, GP) and **MuseScore** files (MSCZ) 8 | - the [CLI tool](cli/README.md) to perform batch operations 9 | - a [web server](web_server/README.md) to search music scores and tabs using a database through a future documented API 10 | 11 | ## About me 12 | 13 | I started to play guitar in 2019 and I have a pack of Guitar Pro files and sometimes I get a new files from [Songsterr](https://www.songsterr.com/). I want to write a better documentation but I don't know all the stuffs that I can see on a score ;) 14 | 15 | In order to make another software (with advanced search, chord detection, ...), I need a library that is able to parse and write Guitar Pro files. 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ================================== 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /.github/workflows/rust-clippy.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # rust-clippy is a tool that runs a bunch of lints to catch common 6 | # mistakes in your Rust code and help improve your Rust code. 7 | # More details at https://github.com/rust-lang/rust-clippy 8 | # and https://rust-lang.github.io/rust-clippy/ 9 | 10 | name: rust-clippy analyze 11 | 12 | on: 13 | push: 14 | branches: [ master ] 15 | pull_request: 16 | # The branches below must be a subset of the branches above 17 | branches: [ master ] 18 | schedule: 19 | - cron: '5 4 * * 1' 20 | 21 | jobs: 22 | rust-clippy-analyze: 23 | name: Run rust-clippy analyzing 24 | runs-on: ubuntu-latest 25 | permissions: 26 | contents: read 27 | security-events: write 28 | steps: 29 | - name: Checkout code 30 | uses: actions/checkout@v2 31 | 32 | - name: Install Rust toolchain 33 | uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1 34 | with: 35 | profile: minimal 36 | toolchain: stable 37 | components: clippy 38 | override: true 39 | 40 | - name: Install required cargo 41 | run: cargo install clippy-sarif sarif-fmt 42 | 43 | - name: Run rust-clippy 44 | run: 45 | cargo clippy 46 | --all-features 47 | --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt 48 | continue-on-error: true 49 | 50 | - name: Upload analysis results to GitHub 51 | uses: github/codeql-action/upload-sarif@v1 52 | with: 53 | sarif_file: rust-clippy-results.sarif 54 | wait-for-processing: true 55 | -------------------------------------------------------------------------------- /cli/README.md: -------------------------------------------------------------------------------- 1 | # CLI 2 | 3 | Ideas: 4 | 5 | * [ ] `-l` Load bellow parameters from a file (YAML?, JSON?, other?) 6 | * [ ] `-f` Find in files 7 | * [ ] song information (artist, title, album, ...) with wildcard and regexes 8 | * [ ] instrument 9 | * [ ] `-ft ` [tuning](https://en.wikipedia.org/wiki/List_of_guitar_tunings) (value example: `E-A-D-g-b-e`, `EADgbe`, ` E2–A2–D3–G3–B3–E4`, ) 10 | * [ ] range (string count, piano keys count, drum elements, ...) 11 | * [ ] `-f? ` tempo: is constant, range if variable, min, max, ... (example: `80`, `60-90`, `60>`, `<120` or `0-120`, `60,80,90` or `80,70-85` for a list) 12 | * [ ] `-fb `beats 13 | * [ ] `-fn ` notes (example: `DADDC` anywhere in the track, `|DADDC|` in a mesure, `|A|E|CC|` measures with those notes) 14 | * [ ] `-fr` Repetitions: 15 | * [ ] `-frm <1|2|4>` [same mesures](https://musescore.org/en/handbook/4/measure-and-multi-measure-repeats) 16 | * [ ] `-frs` [repeat signs](https://musescore.org/en/handbook/4/repeat-signs) 17 | * [ ] `-frv` [voltas](https://musescore.org/en/handbook/4/voltas) 18 | * [ ] `-fv 0.8` detect verse with a similarity percentage 19 | * [ ] `-x` Extract: 20 | * [ ] `-xi ` above information in various format (CSV, JSON) 21 | * [ ] `-xt ` tracks 22 | * [ ] `-xl ` lyrics 23 | * [ ] `-c format` Conversion between formats with alerts when information are lost (like GP5 -> GP3) 24 | * [ ] `-r` Replace repetitions 25 | * [ ] `m` [same mesures](https://musescore.org/en/handbook/4/measure-and-multi-measure-repeats) 26 | * [ ] `s` repeat signs and `v` voltas when mesures are the same for all tracks 27 | * [ ] `-t -0.5` Change tuning if possible 28 | * [ ] `-ts x2` Divide/multiply time signatures (I had once an guitar tab that needed to be rewritten by changing the time signature and the beams) 29 | * [ ] `-p` Apply page format parametters (margin, spacing, ...) 30 | -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | # Guitarpro 2 | 3 | A Rust safe library to parse and write guitar pro files. 4 | 5 | [![Tests](https://github.com/slundi/guitarpro/actions/workflows/rust.yml/badge.svg)](https://github.com/slundi/guitarpro/actions/workflows/rust.yml) [![rust-clippy](https://github.com/slundi/guitarpro/actions/workflows/rust-clippy.yml/badge.svg)](https://github.com/slundi/guitarpro/actions/workflows/rust-clippy.yml) 6 | 7 | It is based on [Perlence/PyGuitarPro](https://github.com/Perlence/PyGuitarPro), [TuxGuitar](http://tuxguitar.com.ar/) and [MuseScore](https://musescore.org) sources. 8 | 9 | ## usage 10 | 11 | ```rust 12 | use guitarpro; 13 | 14 | fn main() { 15 | let f = fs::OpenOptions::new().read(true).open("my_awesome_song.gp5").unwrap_or_else(|_error| { 16 | panic!("Unknown error while opening my_awesome_song.gp5"); 17 | }); 18 | let mut data: Vec = Vec::with_capacity(size); 19 | f.take(u64::from_ne_bytes(size.to_ne_bytes())).read_to_end(&mut data).unwrap_or_else(|_error|{panic!("Unable to read file contents");}); 20 | let mut song: guitarpro::Song = gp::Song::default(); 21 | match ext.as_str() { 22 | "GP3" => song.read_gp3(&data), 23 | "GP4" => song.read_gp4(&data), 24 | "GP5" => song.read_gp5(&data), 25 | "GPX" => println!("Guitar pro file (new version) is not supported yet"), //new Guitar Pro files 26 | _ => panic!("Unable to process a {} file (GP1 and GP2 files are not supported)", ext), 27 | } 28 | } 29 | ``` 30 | 31 | ## Roadmap 32 | 33 | ### Library 34 | 35 | * [ ] Documentation 36 | * [x] Read GP3 files 37 | * [x] Read GP4 files 38 | * [ ] Read GP5 files: almost working, Coda and similar directions are not working, use `test/Demo v5.gp5` to test/fix/pull request/... 39 | * [ ] Read GPX files (version 6) 40 | * [ ] Read GPX files (version 7) 41 | * [ ] Read MuseScore files (ZIP + XML) 42 | * [ ] Write GP3 files 43 | * [ ] Write GP4 files 44 | * [ ] Write GP5 files 45 | * [ ] Write GPX files (version 6) 46 | * [ ] Write GPX files (version 7) 47 | * [ ] Write MuseScore files 48 | -------------------------------------------------------------------------------- /lib/src/lyric.rs: -------------------------------------------------------------------------------- 1 | use fraction::ToPrimitive; 2 | 3 | use crate::io::*; 4 | 5 | pub const _MAX_LYRICS_LINE_COUNT: u8 = 5; 6 | 7 | /// Struct to keep lyrics 8 | /// On guitar pro files (gp4 or later), you can have 5 lines of lyrics. 9 | /// It is store on a BTreeMap: 10 | /// * the key is the mesure number. The start mesure is 1 11 | /// * the value is the text. Syntax: 12 | /// * " " (spaces or carry returns): separates the syllables of a word 13 | /// * "+": merge two syllables for the same beat 14 | /// * "\[lorem ipsum...\]": hidden text 15 | #[derive(Debug,Clone,Default)] 16 | pub struct Lyrics { 17 | pub track_choice: u8, 18 | pub lines: Vec<(u8, u16, String)>, 19 | } 20 | //impl Default for Lyrics { fn default() -> Self { Lyrics { track_choice: 0, line1: BTreeMap::new(), line2: BTreeMap::new(), line3: BTreeMap::new(), line4: BTreeMap::new(), line5: BTreeMap::new(), }}} 21 | impl std::fmt::Display for Lyrics { 22 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 23 | let mut s = String::new(); 24 | for l in &self.lines { s.push_str(&l.2); s.push('\n'); } 25 | write!(f, "{}", s.trim().replace(['\n', '\r'], " ")) 26 | } 27 | } 28 | 29 | impl crate::gp::Song { 30 | /// Read lyrics. 31 | /// 32 | /// First, read an `i32` that points to the track lyrics are bound to. Then it is followed by 5 lyric lines. Each one consists of 33 | /// number of starting measure encoded in`i32` and`int-size-string` holding text of the lyric line. 34 | pub(crate) fn read_lyrics(&self, data: &[u8], seek: &mut usize) -> Lyrics { 35 | let mut lyrics = Lyrics{track_choice: read_int(data, seek).to_u8().unwrap(), ..Default::default()}; 36 | for i in 0..5u8 { 37 | let starting_measure = read_int(data, seek).to_u16().unwrap(); 38 | lyrics.lines.push((i, starting_measure, read_int_size_string(data, seek))); 39 | } 40 | lyrics 41 | } 42 | pub(crate) fn write_lyrics(&self, data: &mut Vec) { 43 | write_i32(data, self.lyrics.track_choice.to_i32().unwrap()); 44 | for i in 0..5 { 45 | write_i32(data, self.lyrics.lines[i].1.to_i32().unwrap()); 46 | write_int_size_string(data, &self.lyrics.lines[i].2); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use fraction::ToPrimitive; 3 | use scorelib::gp; 4 | use std::path::Path; 5 | use std::ffi::OsStr; 6 | use std::fs; 7 | use std::io::Read; 8 | 9 | const GUITAR_FILE_MAX_SIZE:usize = 16777216; //16 MB, it should be enough 10 | 11 | #[derive(Parser, Debug)] 12 | #[clap(author="slundi", version, about="Perform operation on music files", long_about = None)] 13 | struct Args { 14 | /// Action 15 | #[clap()] action: String, 16 | 17 | /// Input file 18 | #[clap(short='i', long, help="Input file path")] input: String, 19 | 20 | } 21 | 22 | fn main() { 23 | let args: Args = Args::parse(); 24 | let f = Path::new(&args.input); 25 | //check if path OK, file exists and is file 26 | if !f.exists() || !f.is_file() {panic!("Unable to access file: {}", &args.input);} 27 | //check file format 28 | let ext = f.extension().and_then(OsStr::to_str).unwrap_or_else(||{panic!("Cannont get input file extension");}).to_uppercase(); 29 | let size: usize = fs::metadata(&args.input).unwrap_or_else(|_e|{panic!("Unable to get file size")}).len().to_usize().unwrap(); 30 | if size > GUITAR_FILE_MAX_SIZE {panic!("File is too big (bigger than 16 MB)");} 31 | let f = fs::OpenOptions::new().read(true).open(&args.input).unwrap_or_else(|_error| { 32 | /*if error.kind() == fs::ErrorKind::NotFound {panic!("File {} was not found", &file);} 33 | else if error.kind() == fs::ErrorKind::PermissionDenieds {panic!("File {} is unreadable, check permissions", &file);} 34 | else {panic!("Unknown error while opening {}", &file);}*/ 35 | panic!("Unknown error while opening {}", args.input); 36 | }); 37 | let mut data: Vec = Vec::with_capacity(size); 38 | f.take(u64::from_ne_bytes(size.to_ne_bytes())).read_to_end(&mut data).unwrap_or_else(|_error|{panic!("Unable to read file contents");}); 39 | let mut song: gp::Song = gp::Song::default(); 40 | match ext.as_str() { 41 | "GP3" => song.read_gp3(&data), 42 | "GP4" => song.read_gp4(&data), 43 | "GP5" => song.read_gp5(&data), 44 | "GPX" => println!("Guitar pro file (new version) is not supported yet"), //new Guitar Pro files 45 | _ => panic!("Unable to process a {} file (GP1 and GP2 files are not supported)", ext), 46 | } 47 | println!("Artist: \"{}\"", song.artist); 48 | println!("Title: \"{}\"", song.name); 49 | println!("Album: \"{}\"", song.album); 50 | println!("Author: \"{}\"", song.author); 51 | println!("Date: \"{}\"", song.date); 52 | println!("Copyright: \"{}\"", song.copyright); 53 | println!("Writer: \"{}\"", song.writer); 54 | println!("Transcriber: \"{}\"", song.transcriber); 55 | println!("Comments: \"{}\"", song.comments); 56 | let _out = song.write((3,0,0), Some(false)); 57 | } 58 | -------------------------------------------------------------------------------- /lib/FILE-STRUCTURE-CHORD-DIAGRAMS.md: -------------------------------------------------------------------------------- 1 | # Chord diagrams 2 | 3 | * **Header**: `byte`. This value is 0x01, indicating a Guitar Pro 4 format chord. 4 | * **Sharp**: `byte`. Determines if the chord is displayed sharp or flat. 5 | * Blank1 6 | * Blank2 7 | * Blank3: Bytes. Blank bytes needed for ascendant compatibility with versions 3 of the software. 8 | * **Root**: `byte`: `-1 for the customized chords`, `0: C`, `1: C#`, ... 9 | * **Major/minor**: `byte`, Determines the chord type as followed: 10 | * 0: M 11 | * 1: 7 12 | * 2: 7M 13 | * 3: 6 14 | * 4: m 15 | * 5: m7 16 | * 6: m7M 17 | * 7: m6 18 | * 8: sus2 19 | * 9: sus4 20 | * 10: 7sus2 21 | * 11: 7sus4 22 | * 12: dim 23 | * 13: aug 24 | * 14: 5 25 | * **Nine, Eleven of Thirteen**: `byte`. Determines if the chord goes until the ninth, the eleventh, or the thirteenth. 26 | * **Bass**: `integer`. Lowest note of the chord. It gives the chord inversions. 27 | * **Diminished/Augmented**: `integer`. Tonality linked with 9/11/13: `0: perfect ("juste")`, `1: augmented`, `2: diminished` 28 | * **add**: `byte`. Allows to determine if a 'add' (added note) is present in the chord. 29 | * **Name**: `string`. 20 characters long string containing the chord name. 30 | * Blank4, 31 | * Blank5: Bytes. Blank bytes needed for ascendant compatibility with versions 3 of the software. 32 | * **Fifth**: `byte`. Tonality of the fifth: `0: perfect ("juste")`, `1: augmented`, `2: diminished` 33 | * **Ninth**: `byte`. Tonality of the ninth: `0: perfect ("juste")`, `1: augmented`, `2: diminished`. This tonality is valid only if the value "Nine, Eleven or Thirteen" is 11 or 13. 34 | * **Eleventh**: `byte`. Tonality of the eleventh: `0: perfect ("juste")`, `1: augmented`, `2: diminished`. This tonality is valid only if the value "Nine, Eleven or Thirteen" is 13. 35 | * **Base fret**: `integer`. Shows the fret from which the chord is displayed. 36 | * **Frets**: List of 7 integers. Corresponds to the fret number played on each string, from the highest to the lowest: -1 means a string unplayed, 0 means a string played "blank" (ie no fret). 37 | * **Number of barres**: `byte`. Indicates the number of barres there are in the chord. A chord can contain up to 5 barres. 38 | * **Fret of the barre**: List of 5 Bytes. Indicates the fret number of each possible barre. 39 | * **Barre start**: List of 5 Bytes. Indicates the first string of the barre, 1 being the highest.The list order matches the chord different barres frets list order. 40 | * **Barre end**: List of 5 Bytes. Indicates the first string of the barre, 1 being the lowest. The list order matches the chord different barres frets list order. 41 | * Omission1, 42 | * Omission3, 43 | * Omission5, 44 | * Omission7, 45 | * Omission9, 46 | * Omission11, 47 | * Omission13: Bytes. Gives the notes there are in the chord. If the value is `0x00`, the note is not in the chord, and if the value is `0x01`, the note is in the chord. 9, 11 or 13 can only be present for a "Nine, Eleven or Thirteen" big enough. 48 | * Blank6: `byte`. Blank byte needed for ascendant compatibility with versions 3 of the software. 49 | * **Fingering**: List of 7 Bytes. Describes the fingering used to play the chord. Below is given the fingering from the highest string to the lowest: 50 | * -2: unknown; 51 | * -1: X or 0 (no finger); 52 | * 0: thumb; 53 | * 1: index; 54 | * 2: middle finger; 55 | * 3: annular; 56 | * 4: little finger. 57 | * **ShowDiagFingering**: `byte`. If it is `0x01`, the chord fingering is displayed, if it is `0x00`, the chord fingering is masked. 58 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /lib/FILE-STRUCTURE.md: -------------------------------------------------------------------------------- 1 | # Guitar Pro 4 file format 2 | 3 | I have found years ago a file description for Guitar Pro version 4.06 files only. 4 | 5 | ## General 6 | 7 | Values of type "integer" and "byte" presented in this document are written from left to right of the the LSB (least significant byte) to the MSB (most significant byte). 8 | 9 | | Type | Number of bytes | 10 | |----------------|:---------------:| 11 | | **integer** | 4 | 12 | | **short** | 4 | 13 | | **short int** | 2 | 14 | | **byte** | 1 | 15 | 16 | The **colors** are stored in 4 bytes in the following way, the data is written from left to right of the LSB (least significant byte) to the MSB (most significant byte): 17 | 18 | | Bit 3 | Bit 2 | Bit 1 | Bit 0 | 19 | |-----------------------------------------|-------|-------|-------| 20 | | White byte, it always evaluates to 0x00 | Blue | Green | Red | 21 | 22 | When this document uses the expression "character string," it means a couple of a first byte encoding the number 'n' of characters in the string (consequently limited to 255 characters), followed by the 'n' characters themselves. 23 | 24 | The musical notes are stored as integers. The value of the C(0) is 0, and each incrementation of the variable represents a half tone: 25 | 26 | | Note | Value | 27 | |--------|------:| 28 | | C#(0) | 1 | 29 | | C (1) | 12 | 30 | | E (2) | 28 | 31 | | ... | ... | 32 | 33 | ## File structure 34 | 35 | * Headers 36 | * Version 37 | * Tablature 38 | * Lyrics 39 | * Other Tablature Information 40 | * [Body](FILE-STRUCTURE-BODY.md) 41 | * Measures 42 | * Tracks 43 | * Measure-track pairs 44 | * Beat 1 : note 1..i 45 | * Beat 2 : note 1..i 46 | * Beat i : note 1..i 47 | * [Chord diagrams](FILE-STRUCTURE-CHORD-DIAGRAMS.md) 48 | 49 | ## Headers 50 | 51 | ### Version 52 | 53 | 30 character string (not counting the byte announcing the real length of the string as explained in GENERAL) representing the version number. Only the first n characters (n being given by the value described previously) characters are considered. 54 | 55 | Possible values are: 56 | 57 | | **Extension** | **Header values** | 58 | |:-------------:|-------------------| 59 | | **GP1** | FICHIER GUITARE PRO v1
FICHIER GUITARE PRO v1.01
FICHIER GUITARE PRO v1.02
FICHIER GUITARE PRO v1.03
FICHIER GUITARE PRO v1.04 | 60 | | **GP2** | FICHIER GUITAR PRO v2.20
FICHIER GUITAR PRO v2.21 | 61 | | **GP3** | FICHIER GUITAR PRO v3.00 | 62 | | **GP4** | FICHIER GUITAR PRO v4.00
FICHIER GUITAR PRO v4.06
FICHIER GUITAR PRO L4.06 | 63 | | **GP5** | FICHIER GUITAR PRO v5.00
FICHIER GUITAR PRO v5.10 | 64 | 65 | *__Translator's Note__: "Fichier" is French for "File". It is left untranslated above, as the program expects the string in French.* 66 | 67 | Only the specifications relating to version "FICHIER GUITAR PRO v4.06" are described here. 68 | 69 | After this point (0x0000001F), the data is written sequentially without a precise address. 70 | 71 | ### Information about the piece 72 | 73 | The follollowing information is presented in the form of a block of data containing: 74 | 75 | * an `integer` representing the size of the stored information + 1; 76 | * the `string` of characters representing the data 77 | 78 | This structure requires that a null `string` be written `01 00 00 00 00` in the file. 79 | 80 | The information concerned is, in order of reading within the file: 81 | 82 | | Field name | Guitar Pro versions | 83 | |--------------------------------------------|---------------------| 84 | | **title** | 3, 4 | 85 | | **subtitle** | 3, 4 | 86 | | **interpret** | 4 | 87 | | **album** from which the piece was taken | 3, 4 | 88 | | **author** | 4 | 89 | | **copyright** | 3, 4 | 90 | | **tablature author** | 3, 4 | 91 | | **instructional** line about the tablature | 3, 4 | 92 | 93 | The following section contains information regarding the 'notice' (note) field in the piece's properties. 94 | It begins with an integer containing the number of lines of Nb_Notice notes, then Nb_Notice instances of the structure previously described, each containing lines of notes. 95 | 96 | The file then contains a byte determining the '**TripletFeel**' attribute of the piece. 97 | If TripletFeel is activated, the value is 1. If not, the integer value is 0. 98 | 99 | ### Lyrics 100 | 101 | The next section of the file contains the **lyrics** associated with the piece. 102 | 103 | *__WARNING__: The strings presented in this section and in this section only do not conform to the format presented in "General", but are written directly.* 104 | 105 | The lyrics section begins with an integer giving the number of the track with which the lyrics are associated. 106 | 107 | Then, for each of the 5 lines of lyrics generated by Guitar Pro 4, the file contains an integer representing the number of characters in the string followed by the character string itself. 108 | 109 | ### Other information about the piece 110 | 111 | The next data are: 112 | 113 | * **tempo**: `integer` 114 | * **key**: `byte`. This value encodes the key (signature) at the beginning of the piece. It is encoded as: `0: C`, `1: G(#)`, `2: D (##)`, `-1: F (b)` ... 115 | * **octave**: `byte`. The standard value is 0. It becomes 8 if the sheet is to be played one octave higher (8va). This functionality is not yet used, but is presented as a preview of a revision of the GP4 format. 116 | * **MIDI channels**: Table of midi channels. There are 4 ports and 16 channels, the channels are stored in this order: `port1/channel1`, `port1/channel2`, ..., `port1/channel16`, `port2/channel1`, ... Each element of the table has the following form: 117 | 118 | | Element | Data type | 119 | |----------------|----------------------------------------------------| 120 | | **instrument** | `integer` | 121 | | **Volume** | `byte` | 122 | | **Balance** | `byte` | 123 | | **Chorus** | `byte` | 124 | | **Reverb** | `byte` | 125 | | **Phaser** | `byte` | 126 | | **Tremolo** | `byte` | 127 | | **blank1** | `byte` => Backward compatibility with version 3.0 | 128 | | **blank2** | `byte` => Backward compatibility with version 3.0 | 129 | 130 | * **number of measures**: `integer` 131 | * **number of tracks**: `integer` 132 | -------------------------------------------------------------------------------- /lib/FILE-STRUCTURE-NOTE.md: -------------------------------------------------------------------------------- 1 | # Note 2 | 3 | The first byte is the note header. It lists the information about the different parameters linked to the note: 4 | 5 | | **Bit 7** | **Bit 6** | **Bit 5** | **Bit 4** | **Bit 3** | **Bit 2** | **Bit 1** | **Bit 0** | 6 | |-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------| 7 | | Right hand or left hand fingering | Accentuated note | Note type (rest, empty note, normal note) | Note dynamic | Presence of effects linked to the note | Ghost note | Dotted note | Time-independent duration | 8 | 9 | * **Note type**: `short int`. If the bit 5 is true, we start by writing the note type. The value is: 10 | * `0x0100` if the note is normal; 11 | * `0x0200` if the note is a ghost note; 12 | * `0x0300` if the note is a tie note. 13 | * **Note duration**: If the bit 0 is true, we write the 2 following information linked to the note duration. 14 | * **Time duration**: `byte`. The basic time duration is the quarter note, which is equivalent to a time duration of 1. Therefore, we obtain the relations: 15 | * -2:Whole Note 16 | * -1:Half note 17 | * 0:Quarter note 18 | * 1:Eighth note 19 | * 2:Sixteenth note 20 | * 3:Thirty-second note 21 | * ... 22 | * **N-tuplet**: `byte`. This int matches the N in "N-tuplet" (ie 3, 5, 6, 7, 9, 10, 11, 12, 13). 23 | * **Note dynamic**: `byte`. If the bit 4 is false, we then consider that the note is forte (value 6). If the bit 4 of the header byte is true, we write here the note strength with the following rule: 24 | * 1: ppp 25 | * 2: pp 26 | * 3: p 27 | * 4: mp 28 | * 5: mf 29 | * 7: f 30 | * 8: ff 31 | * 9: fff 32 | * **Fret number**: `byte`. If the bit 5 is true, we write here the number of the fret on which the note is applied. 33 | * **Fingering**: List of 2 bytes. If the bit 7 is true, we write here successively the fingerings left hand, then right hand, each of them being written on one byte. The fingering is coded this way: `-1: nothing`, `0: thumb`, `1: index`, `2: middle finger`, `3: annular`, `4: little finger`. 34 | * **Effects on the note**: If the presence of an effect is indicated by the bit 3 of the header, it is saved at this place. The details of this operation is specified in the next paragraph. Those include for example vibratos, palm muting, slides, harmonices... 35 | 36 | ## Effects on the notes 37 | 38 | a. Effects on the notes 39 | 40 | The effects presence for the current note is set by the 2 header bytes. First byte: 41 | 42 | | **Bit 7** | **Bit 6** | **Bit 5** | **Bit 4** | **Bit 3** | **Bit 2** | **Bit 1** | **Bit 0** | 43 | |-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------| 44 | | Blank bit | Blank bit | Blank bit | Grace note presence | Let ring | Presence of a slide from the current note (version 3) | Presence of a hammer-on or a pull-off from the current note | Presence of a bend | 45 | 46 | Second byte: 47 | 48 | | **Bit 7** | **Bit 6** | **Bit 5** | **Bit 4** | **Bit 3** | **Bit 2** | **Bit 1** | **Bit 0** | 49 | |-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------| 50 | | Blank bit | Left hand vibrato | Trill | Harmonic note | Presence of a slide from the current note | Tremolo picking | Palm Mute | Note played staccato | 51 | 52 | * **Bend**: If the bit 0 of the first header byte is true, we write here the bend as described in section [Bends](#Bends). 53 | * **Grace Note**: If the bit 4 of the first header byte is true, we write here the data relative to the Grace Note, as described in section [Grace Notes](#Grace-notes). 54 | * **Tremolo Picking**: `byte`. If the bit 2 of the second header byte is true, the information linked to tremolo picking are saved here. It is encoded like this: `1: eighth note`, `2: sixteenth note`, `3: thirty-second note`. 55 | * **Slide**: `byte`. If the bit 3 of the second header byte is true, the information linked to the slide is stored here and is coded like this: 56 | * -2: slide into from above 57 | * -1: slide into from below 58 | * 0: no slide 59 | * 1: shift slide 60 | * 2: legato slide 61 | * 3: slide out of downwards 62 | * 4: slide out of upwards 63 | * **Harmonics**: `byte`. If the bit 4 of the second header byte is true, the information linked to the harmonics is stored here and is coded like this: 64 | * 0: None 65 | * 1: Natural 66 | * 15: Artificial+5 67 | * 17: Artificial+7 68 | * 22: Artificial+12 69 | * 3: Tapped 70 | * 4: Pitch 71 | * 5: Semi 72 | * **Trill**: If the bit 5 of the second header byte is true, the information linked to the Trill effect is stored here. It is written in two steps: 73 | * **Fret**: byte`. The fret the trill is made with. 74 | * **Period**: `byte`. The period between each note. The value is encoded as: `0: Quarter note`, `1: Eighth note`, `2: Sixteenth note`. 75 | 76 | **The following effects are present if the bit of the header is true**: 77 | 78 | * Let ring 79 | * Presence of a hammer-on or a pull-off from the current note 80 | * Left hand vibrato 81 | * Palm Mute 82 | * Note played staccato. 83 | 84 | ## Grace notes 85 | 86 | The grace notes are stored in the file with 4 variables, written in the following order. 87 | 88 | * **Fret**: `byte`. The fret number the grace note is made from. 89 | * **Dynamic**: `byte`. The grace note dynamic is coded like this (default value is 6): 90 | * 1: ppp 91 | * 2: pp 92 | * 3: p 93 | * 4: mp 94 | * 5: mf 95 | * 6: f 96 | * 7: ff 97 | * 8: fff 98 | * **Transition**: `byte`. This variable determines the transition type used to make the grace note: `0: None`, `1: Slide`, `2: Bend`, `3: Hammer`. 99 | * **Duration**: `byte`. Determines the grace note duration, coded this way: `3: Sixteenth note`, `2: Twenty-fourth note`, `1: Thirty-second note`. 100 | 101 | ## Bends 102 | 103 | * Type: `byte`. Gives the bend type. Different types are allowed and are context-dependent (tremolo bar or bend). 104 | The model list is: 105 | * **Common**: 0: None 106 | * **Bend specific**: 107 | * 1: Bend 108 | * 2: Bend and Release 109 | * 3: Bend and Release and Bend 110 | * 4: Prebend 111 | * 5: Prebend and Release 112 | * **Tremolo bar specific**: 113 | * 6: Dip 114 | * 7: Dive 115 | * 8: Release (up) 116 | * 9: Inverted dip 117 | * 10: Return 118 | * 11: Release (down) 119 | * **Value**: `integer`. Bend height. It is 100 per tone and goes by quarter tone. 120 | * Normal position:0 121 | * Quarter tone: 25 122 | * Half tone:50 123 | * Three-quarters tone:75 124 | * Whole tone:100 125 | * ... until 126 | * Three tones:300 127 | * Number of points: `integer`. Number of points used to display the bend. Is followed by the list of points. 128 | * **List of points**: Each point is written like this: 129 | * **Absolute time position**: `integer`. Gives the point position from the previous point. This value goes between 0 and 60 and represents sixties of the note duration. 130 | * **Vertical position**: `integer`. It is 100 per tone and goes by quarter tone. 131 | * Normal position:0 132 | * Quarter tone: 25 133 | * Half tone:50 134 | * Three-quarters tone:75 135 | * Whole tone:100 136 | * ... until 137 | * Three tones:300 138 | * **Vibrato**: `byte`. Determines how to play the section, with different vibrato types: `0: none`, `1: fast`, `2: average`, `3: slow`. 139 | -------------------------------------------------------------------------------- /lib/src/page.rs: -------------------------------------------------------------------------------- 1 | use fraction::ToPrimitive; 2 | 3 | use crate::{gp::*, io::*}; 4 | 5 | ///A padding construct 6 | #[derive(Debug,Clone)] 7 | pub struct Padding { 8 | pub right: u16, 9 | pub top: u16, 10 | pub left: u16, 11 | pub bottom: u16, 12 | } 13 | 14 | /// A point construct using integer coordinates 15 | #[derive(Debug,Clone)] 16 | pub struct Point { pub x: u16, pub y: u16, } 17 | 18 | // An enumeration of the elements which can be shown in the header and footer of a rendered song sheet. 19 | // All values can be combined using bit-operators as they are flags 20 | pub const HEADER_FOOTER_NONE: u16 = 0x000; 21 | pub const HEADER_FOOTER_TITLE: u16 = 0x001; 22 | pub const HEADER_FOOTER_SUBTITLE: u16 = 0x002; 23 | pub const HEADER_FOOTER_ARTIST: u16 = 0x004; 24 | pub const HEADER_FOOTER_ALBUM: u16 = 0x008; 25 | pub const HEADER_FOOTER_WORDS: u16 = 0x010; 26 | pub const HEADER_FOOTER_MUSIC: u16 = 0x020; 27 | pub const HEADER_FOOTER_WORD_AND_MUSIC: u16 = 0x040; 28 | pub const HEADER_FOOTER_COPYRIGHT: u16 = 0x080; 29 | pub const HEADER_FOOTER_PAGE_NUMBER: u16 = 0x100; 30 | pub const HEADER_FOOTER_ALL: u16 = 0x1ff; 31 | 32 | /// The page setup describes how the document is rendered. 33 | /// 34 | /// Page setup contains page size, margins, paddings, and how the title elements are rendered. 35 | /// 36 | /// Following template vars are available for defining the page texts: 37 | /// * ``%title%``: will be replaced with Song.title 38 | /// - ``%subtitle%``: will be replaced with Song.subtitle 39 | /// - ``%artist%``: will be replaced with Song.artist 40 | /// - ``%album%``: will be replaced with Song.album 41 | /// - ``%words%``: will be replaced with Song.words 42 | /// - ``%music%``: will be replaced with Song.music 43 | /// - ``%WORDSANDMUSIC%``: will be replaced with the according word and music values 44 | /// - ``%copyright%``: will be replaced with Song.copyright 45 | /// - ``%N%``: will be replaced with the current page number (if supported by layout) 46 | /// - ``%P%``: will be replaced with the number of pages (if supported by layout) 47 | #[derive(Debug,Clone)] 48 | pub struct PageSetup { 49 | pub page_size: Point, 50 | pub page_margin: Padding, 51 | pub score_size_proportion: f32, 52 | pub header_and_footer: u16, 53 | pub title: String, 54 | pub subtitle: String, //Guitar Pro 55 | pub artist: String, 56 | pub album: String, 57 | pub words: String, //GP 58 | pub music: String, 59 | pub word_and_music: String, 60 | pub copyright: String, 61 | pub page_number: String, 62 | } 63 | impl Default for PageSetup {fn default() -> Self { PageSetup { page_size:Point{x:210,y:297}, page_margin:Padding{right:10,top:15,left:10,bottom:10}, score_size_proportion:1.0, header_and_footer:HEADER_FOOTER_ALL, 64 | title:String::from("%title%"), subtitle:String::from("%subtitle%"), artist:String::from("%artist%"), album:String::from("%album%"), 65 | words:String::from("Words by %words%"), music:String::from("Music by %music%"), word_and_music:String::from("Words & Music by %WORDSMUSIC%"), 66 | copyright:String::from("Copyright %copyright%\nAll Rights Reserved - International Copyright Secured"), 67 | page_number:String::from("Page %N%/%P%"), 68 | }}} 69 | 70 | impl Song { 71 | /// Read page setup. Page setup is read as follows: 72 | /// - Page size: 2 `Ints `. Width and height of the page. 73 | /// - Page padding: 4 `Ints `. Left, right, top, bottom padding of the page. 74 | /// - Score size proportion: `int`. 75 | /// - Header and footer elements: `short`. See `HeaderFooterElements` for value mapping. 76 | /// - List of placeholders: 77 | /// * title 78 | /// * subtitle 79 | /// * artist 80 | /// * album 81 | /// * words 82 | /// * music 83 | /// * wordsAndMusic 84 | /// * copyright1, e.g. *"Copyright %copyright%"* 85 | /// * copyright2, e.g. *"All Rights Reserved - International Copyright Secured"* 86 | /// * pageNumber 87 | pub(crate) fn read_page_setup(&mut self, data: &[u8], seek: &mut usize) { 88 | self.page_setup.page_size.x = read_int(data, seek).to_u16().unwrap(); 89 | self.page_setup.page_size.y = read_int(data, seek).to_u16().unwrap(); 90 | self.page_setup.page_margin.left = read_int(data, seek).to_u16().unwrap(); 91 | self.page_setup.page_margin.right = read_int(data, seek).to_u16().unwrap(); 92 | self.page_setup.page_margin.top = read_int(data, seek).to_u16().unwrap(); 93 | self.page_setup.page_margin.bottom = read_int(data, seek).to_u16().unwrap(); 94 | self.page_setup.score_size_proportion = read_int(data, seek).to_f32().unwrap() / 100.0; 95 | self.page_setup.header_and_footer = read_short(data, seek).to_u16().unwrap(); 96 | self.page_setup.title = read_int_size_string(data, seek); 97 | self.page_setup.subtitle = read_int_size_string(data, seek); 98 | self.page_setup.artist = read_int_size_string(data, seek); 99 | self.page_setup.album = read_int_size_string(data, seek); 100 | self.page_setup.words = read_int_size_string(data, seek); 101 | self.page_setup.music = read_int_size_string(data, seek); 102 | self.page_setup.word_and_music = read_int_size_string(data, seek); 103 | let mut c = read_int_size_string(data, seek); 104 | c.push('\n'); 105 | c.push_str(&read_int_size_string(data, seek)); 106 | self.page_setup.copyright = c; 107 | self.page_setup.page_number = read_int_size_string(data, seek); 108 | } 109 | 110 | pub(crate) fn write_page_setup(&self, data: &mut Vec) { 111 | write_i32(data, self.page_setup.page_size.x.to_i32().unwrap()); 112 | write_i32(data, self.page_setup.page_size.y.to_i32().unwrap()); 113 | 114 | write_i32(data, self.page_setup.page_margin.left.to_i32().unwrap()); 115 | write_i32(data, self.page_setup.page_margin.right.to_i32().unwrap()); 116 | write_i32(data, self.page_setup.page_margin.top.to_i32().unwrap()); 117 | write_i32(data, self.page_setup.page_margin.bottom.to_i32().unwrap()); 118 | write_i32(data, (self.page_setup.score_size_proportion * 100f32).ceil().to_i32().unwrap()); 119 | 120 | write_byte(data, (self.page_setup.header_and_footer & 0xff).to_u8().unwrap()); 121 | 122 | let mut flags2 = 0u8; 123 | if self.page_setup.header_and_footer != 0 && (self.page_setup.header_and_footer & HEADER_FOOTER_PAGE_NUMBER) != 0 {flags2 |= 0x01;} //TODO: check 124 | write_byte(data, flags2); 125 | write_int_byte_size_string(data, &self.page_setup.title); 126 | write_int_byte_size_string(data, &self.page_setup.subtitle); 127 | write_int_byte_size_string(data, &self.page_setup.artist); 128 | write_int_byte_size_string(data, &self.page_setup.album); 129 | write_int_byte_size_string(data, &self.page_setup.word_and_music); 130 | write_int_byte_size_string(data, &self.page_setup.music); 131 | write_int_byte_size_string(data, &self.page_setup.word_and_music); 132 | let c: Vec<&str> = self.page_setup.copyright.split('\n').collect(); 133 | write_int_byte_size_string(data, c[0]); 134 | write_int_byte_size_string(data, c[1]); 135 | write_int_byte_size_string(data, &self.page_setup.page_number); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /lib/src/measure.rs: -------------------------------------------------------------------------------- 1 | use fraction::ToPrimitive; 2 | 3 | use crate::{beat::*, gp::*, key_signature::*, io::*, enums::*}; 4 | 5 | const MAX_VOICES: usize = 2; 6 | 7 | /// A measure header contains metadata for measures over multiple tracks. 8 | #[derive(Debug,Clone)] 9 | pub struct Measure { 10 | pub number: usize, 11 | pub start: i64, 12 | pub has_double_bar: bool, 13 | pub key_signature: KeySignature, 14 | pub time_signature: TimeSignature, 15 | pub track_index: usize, 16 | pub header_index: usize, 17 | pub clef: MeasureClef, 18 | /// Max voice count is 2 19 | pub voices: Vec, 20 | pub line_break: LineBreak, 21 | 22 | /*marker: Optional['Marker'] = None 23 | isRepeatOpen: bool = False 24 | repeatAlternative: int = 0 25 | repeatClose: int = -1 26 | tripletFeel: TripletFeel = TripletFeel.none 27 | direction: Optional[DirectionSign] = None 28 | fromDirection: Optional[DirectionSign] = None*/ 29 | } 30 | impl Default for Measure {fn default() -> Self { Measure { 31 | number: 1, 32 | start: DURATION_QUARTER_TIME, 33 | has_double_bar: false, 34 | key_signature: KeySignature::default(), //Cmajor 35 | time_signature: TimeSignature::default(), 36 | track_index: 0, 37 | header_index: 0, 38 | clef: MeasureClef::Treble, 39 | voices: Vec::with_capacity(2), 40 | line_break: LineBreak::None 41 | }}} 42 | 43 | impl Song { 44 | /// Read measures. Measures are written in the following order: 45 | /// - measure 1/track 1 46 | /// - measure 1/track 2 47 | /// - ... 48 | /// - measure 1/track m 49 | /// - measure 2/track 1 50 | /// - measure 2/track 2 51 | /// - ... 52 | /// - measure 2/track m 53 | /// - ... 54 | /// - measure n/track 1 55 | /// - measure n/track 2 56 | /// - ... 57 | /// - measure n/track m 58 | pub(crate) fn read_measures(&mut self, data: &[u8], seek: &mut usize) { 59 | let mut start = DURATION_QUARTER_TIME; 60 | for h in 0..self.measure_headers.len() { 61 | self.measure_headers[h].start = start; 62 | for t in 0..self.tracks.len() { 63 | self.current_track = Some(t); 64 | let mut m = Measure{track_index:t, header_index:h, ..Default::default()}; 65 | self.current_measure_number = Some(m.number); 66 | if self.version.number < (5,0,0) {self.read_measure(data, seek, &mut m, t);}else {self.read_measure_v5(data, seek, &mut m, t);} 67 | self.tracks[t].measures.push(m); 68 | } 69 | //println!("read_measures(), start: {} \t numerator: {} \t denominator: {} \t length: {}", start, self.measure_headers[h].time_signature.numerator, self.measure_headers[h].time_signature.denominator.value, self.measure_headers[h].length()); 70 | start += self.measure_headers[h].length(); 71 | } 72 | self.current_track = None; 73 | self.current_measure_number = None; 74 | } 75 | 76 | /// Read measure. The measure is written as number of beats followed by sequence of beats. 77 | fn read_measure(&mut self, data: &[u8], seek: &mut usize, measure: &mut Measure, track_index: usize) { 78 | //println!("read_measure()"); 79 | let mut voice = Voice::default(); 80 | self.current_voice_number = Some(1); 81 | self.read_voice(data, seek, &mut voice, &mut measure.start, track_index); 82 | self.current_voice_number = None; 83 | measure.voices.push(voice); 84 | /* 85 | //read a voice 86 | let beats = read_int(data, seek).to_usize().unwrap(); 87 | 88 | //println!("read_measure() read_voice(), beat count: {}", beats); 89 | for i in 0..beats { 90 | self.current_beat_number = Some(i + 1); 91 | //println!("read_measure() read_voice(), start: {}", measure.start); 92 | measure.start += self.read_beat(data, seek, &mut measure.voices[0], measure.start, track_index); 93 | //println!("read_measure() read_voice(), start: {}", measure.start); 94 | } 95 | self.current_beat_number = None; 96 | //end read a voice 97 | self.current_voice_number = None;*/ 98 | } 99 | /// Read measure. Guitar Pro 5 stores twice more measures compared to Guitar Pro 3. One measure consists of two sub-measures for each of two voices. 100 | /// 101 | /// Sub-measures are followed by a `LineBreak` stored in `byte`. 102 | fn read_measure_v5(&mut self, data: &[u8], seek: &mut usize, measure: &mut Measure, track_index: usize) { 103 | //println!("read_measure_v5()"); 104 | let mut start = measure.start; 105 | for number in 0..MAX_VOICES { 106 | self.current_voice_number = Some(number + 1); 107 | //println!("read_measure_v5() {:?}",self.current_voice_number); 108 | let mut voice = Voice::default(); 109 | self.read_voice(data, seek, &mut voice, &mut start, track_index); 110 | measure.voices.push(voice); 111 | } 112 | self.current_voice_number = None; 113 | if *seek < data.len() {measure.line_break = get_line_break(read_byte(data, seek));} else {measure.line_break = get_line_break(0);} 114 | } 115 | 116 | fn read_voice(&mut self, data: &[u8], seek: &mut usize, voice: &mut Voice, start: &mut i64, track_index: usize) { 117 | let beats = read_int(data, seek).to_usize().unwrap(); 118 | for i in 0..beats { 119 | self.current_beat_number = Some(i + 1); 120 | //println!("read_measure() read_voice(), start: {}", measure.start); 121 | *start += if self.version.number < (5,0,0) {self.read_beat(data, seek, voice, *start, track_index)} else {self.read_beat_v5(data, seek, voice, &mut *start, track_index)}; 122 | //println!("read_measure() read_voice(), start: {}", measure.start); 123 | } 124 | self.current_beat_number = None; 125 | } 126 | 127 | pub(crate) fn write_measures(&self, data: &mut Vec, version: &(u8,u8,u8)) { 128 | for i in 0..self.tracks.len() { 129 | //self.current_track = Some(i); 130 | for m in 0..self.tracks[i].measures.len() { 131 | //self.current_measure_number = Some(self.tracks[i].measure.number); 132 | self.write_measure(data, i, m, version); 133 | } 134 | } 135 | //self.current_track = None; 136 | //self.current_measure_number = None; 137 | } 138 | fn write_measure(&self, data: &mut Vec, track: usize, measure: usize, version: &(u8,u8,u8)) { 139 | //self.current_voice_number = Some(1); 140 | if version.0 < 5 {self.write_voice(data, track, measure,0, version);} 141 | else { 142 | for v in 0..self.tracks[track].measures[measure].voices.len() {self.write_voice(data, track, measure,v, version);} //self.current_voice_number = Some(v+1); 143 | if version.0 == 5 {write_byte(data, from_line_break(&self.tracks[track].measures[measure].line_break));} 144 | } 145 | //self.current_voice_number = None; 146 | } 147 | fn write_voice(&self, data: &mut Vec, track: usize, measure: usize, voice: usize, version: &(u8,u8,u8)) { 148 | write_i32(data, self.tracks[track].measures[measure].voices[voice].beats.len().to_i32().unwrap()); 149 | for b in 0..self.tracks[track].measures[measure].voices[voice].beats.len() { 150 | //self.current_beat_number = Some(b+1); 151 | if version.0 ==3 {self.write_beat_v3(data, &self.tracks[track].measures[measure].voices[voice].beats[b]);} 152 | else {self.write_beat(data, &self.tracks[track].measures[measure].voices[voice].beats[b], &self.tracks[track].strings, version);} 153 | //self.current_beat_number = None; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /lib/FILE-STRUCTURE-BODY.md: -------------------------------------------------------------------------------- 1 | # Body 2 | 3 | Contains: 4 | 5 | * Measures 6 | * Tracks 7 | * Measure-track pairs 8 | * Beat 1 : note 1..i 9 | * Beat 2 : note 1..i 10 | * Beat i : note 1..i 11 | 12 | ## Measures 13 | 14 | The measures are written one after another, their number having been specified previously. The first byte is the measure's header. It lists the data given in the current measure. 15 | 16 | | **Bit 7** | **Bit 6** | **Bit 5** | **Bit 4** | **Bit 3** | **Bit 2** | **Bit 1** | **Bit 0** | 17 | |-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------| 18 | | Presence of a double bar | Tonality of the measure | Presence of a marker | Number of alternate ending | End of repeat | Beginning of repeat | Denominator of the (key) signature | Numerator of the (key) signature. | 19 | 20 | Each of these elements is present only if the corresponding bit is a 1. The different elements are written (if they are present) from lowest to highest bit. 21 | Exceptions are made for the double bar and the beginning of repeat whose sole presence is enough, complementary data is not necessary. 22 | 23 | * **Numerator of the (key) signature**: `byte`. Numerator of the (key) signature of the piece 24 | * **Denominator of the (key) signature**: `byte`. Denominator of the (key) signature of the piece 25 | * **End of repeat**: `byte`. Number of repeats until the previous Beginning of repeat. Nombre de renvoi jusqu'au début de renvoi précédent. 26 | * **Number of alternate ending**: `byte`. The number of alternate ending. 27 | * **Marker**: The markers are written in two steps: 28 | 1) First is written an `integer` equal to the marker's name length + 1 29 | 2) a string containing the marker's name. Finally the marker's color is written. 30 | * **Tonality of the measure**: `byte`. This value encodes a key (signature) change on the current piece. It is encoded as: `0: C`, `1: G (#)`, `2: D (##)`, `-1: F (b)`, ... 31 | 32 | ## Tracks 33 | 34 | The tracks are written one after another, their number having been specified previously. The first byte is the track's header. It precises the track's attributes: 35 | 36 | | **bit 7 to 3** | **bit 2** | **bit 1** | **bit 0** | 37 | |----------------|-------------|--------------------------|-------------| 38 | | Blank bits | Banjo track | 12 stringed guitar track | Drums track | 39 | 40 | * **Name**: `string`. A 40 characters long string containing the track's name. 41 | * **Number of strings**: `integer`. An integer equal to the number of strings of the track. 42 | * **Tuning of the strings**: Table of integers. The tuning of the strings is stored as a 7-integers table, the "Number of strings" first integers being really used. The strings are stored from the highest to the lowest. 43 | * **Port**: `integer`. The number of the MIDI port used. 44 | * **Channel**: `integer`. The number of the MIDI channel used. The channel 10 is the drums channel. 45 | * **ChannelE**: `integer`. The number of the MIDI channel used for effects. 46 | * **Number of frets**: `integer`. The number of frets of the instrument. 47 | * **Height of the capo**: `integer`. The number of the fret on which a capo is present. If no capo is used, the value is `0x00000000`. 48 | * **Track's color**: `color`. The track's displayed color in Guitar Pro. 49 | 50 | ## Measures-tracks pairs 51 | 52 | The list of beats per track and per measure is then written to the file in the following order: 53 | 54 | * Measure 1/Track 1 55 | * Measure 1/Track 2 56 | * ... 57 | * Measure 1/Track M 58 | * Measure 2/Track 1 59 | * ... 60 | * Measure 2/Track M 61 | * ... 62 | * Measure N/Track 1 63 | * Measure N/Track 2 64 | * ... 65 | * Measure N/Track M 66 | 67 | A measure-track pair is written in two steps. We first write the number of beats in the current pair: 68 | 69 | * **Number of beats**: `integer`. Integer indicating the number of beats the measure-track pair contains. 70 | 71 | After what we directly write the beat details (which are described in the next section). 72 | 73 | ## A beat 74 | 75 | The first byte is the beat header. It lists the data present in the current beat: 76 | 77 | | **Bit 7** | **Bit 6** | **Bit 5** | **Bit 4** | **Bit 3** | **Bit 2** | **Bit 1** | **Bit 0** | 78 | |-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------| 79 | | Blank bit | Status: `True` if the beat is empty of if it is a rest | The beat is a n-tuplet | Presence of a mix table change event | Presence of effects | Presence of a text | Presence of a chord diagram | Dotted notes | 80 | 81 | * **Status**: `byte`. If the bit 6 is `true`, we start by writing the beat status. The value is `0x00` if the beat is empty and 0x02 if it is a rest. 82 | * **Beat duration**: `byte`. The basic beat duration is a eighth note, which is equivalent to a time duration of 1. We thus obtain the following relations: 83 | * -2: Whole note 84 | * -1: Half note 85 | * 0: Quarter note 86 | * 1: Eighth note 87 | * 2: Sixteenth note 88 | * 3: Thirty-second note 89 | * ... 90 | * *N-tuplet*: `integer`. If the bit 5 of the header byte is true, this integer corresponds to the'n' in 'n-tuplet': 3, 5, 6, 7, 9, 10, 11, 12 and 13. 91 | * *Chord diagram*: If the presence of a chord diagram is indicated by the bit 1 of the header, it is then written here. The detail of this operation is [here](FILE-STRUCTURE-CHORD-DIAGRAMS.md). 92 | * **Text**: If the presence of a text is indicated by the bit 2 of the header, it is written here. It behaves like most of the previous strings. We first find an integer coding the text length + 1, followed by the string containing the text (at the format described in [GENERAL](FILE-STRUCTURE.md)). 93 | * **Effect on beats**: If the presence of an effect is indicated by the bit 3 of the header, it is written at this place. The detail of this operation is specified [here](FILE-STRUCTURE-EFFECTS.md). Effects on beats include tremolo bars, bends... 94 | * **Mix table change event**: If the presence of an event linked to the mix table is indicated by the bit 4 of the header, it is written here. The detail of this operation is specified in Mix table change event. 95 | * **Note**: The note itself is written here. The detail of this operation is specified in [Note](FILE-STRUCTURE-NOTE.md). 96 | 97 | ## Mix table change event 98 | 99 | * **Instrument**: `byte`. Gives the number of the new instrument. The value is -1 if no instrument change occurs. 100 | * **Volume**: `byte`. Gives the new volume value. The value is -1 if no volume change occurs. 101 | * **Pan**: `byte`. Gives the new pan value. The value is -1 if no pan change occurs. 102 | * **Chorus**: `byte`. Gives the new chorus value. The value is -1 if no chorus change occurs. 103 | * **Reverb**: `byte`. Gives the new reverb value. The value is -1 if no reverb change occurs. 104 | * **Phaser**: `byte`. Gives the new phaser value. The value is -1 if no phaser change occurs. 105 | * **Tremolo**: `byte`. Gives the new tremolo value. The value is -1 if no tremolo change occurs. 106 | * **Tempo**: `integer`. Gives the new tempo value. The value is -1 if no tempo change occurs. 107 | * **Volume change duration**: `byte`. Gives the volume change duration in beats. 108 | * **Pan change duration**: `byte`. Gives the pan change duration in beats. 109 | * **Chorus change duration**: `byte`. Gives the chorus change duration in beats. 110 | * **Reverb change duration**: `byte`. Gives the reverb change duration in beats. 111 | * **Phaser change duration**: `byte`. Gives the phaser change duration in beats. 112 | * **Tremolo change duration**: `byte`. Gives the tremolo change duration in beats. 113 | * **Tempo change duration**: `byte`. Gives the tempo change duration in beats. 114 | 115 | The next byte precises if the changes apply only to the current track (if the matching bit is 0), or to every track (if it is 1). 116 | 117 | | **Bit 7** | **Bit 6** | **Bit 5** | **Bit 4** | **Bit 3** | **Bit 2** | **Bit 1** | **Bit 0** | 118 | |-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------| 119 | | Blank bit | Blank bit | Tremolo | Phaser | Reverb | Chorus | Pan | Volume | 120 | -------------------------------------------------------------------------------- /lib/src/midi.rs: -------------------------------------------------------------------------------- 1 | use fraction::ToPrimitive; 2 | 3 | use crate::{io::*, gp::*}; 4 | 5 | //MIDI channels 6 | 7 | pub const CHANNEL_DEFAULT_NAMES: [&str; 128] = ["Piano", "Bright Piano", "Electric Grand", "Honky Tonk Piano", "Electric Piano 1", "Electric Piano 2", 8 | "Harpsichord", "Clavinet", "Celesta", 9 | "Glockenspiel", 10 | "Music Box", 11 | "Vibraphone", "Marimba", "Xylophone", "Tubular Bell", 12 | "Dulcimer", 13 | "Hammond Organ", "Perc Organ", "Rock Organ", "Church Organ", "Reed Organ", 14 | "Accordion", 15 | "Harmonica", 16 | "Tango Accordion", 17 | "Nylon Str Guitar", "Steel String Guitar", "Jazz Electric Gtr", "Clean Guitar", "Muted Guitar", "Overdrive Guitar", "Distortion Guitar", "Guitar Harmonics", 18 | "Acoustic Bass", "Fingered Bass", "Picked Bass", "Fretless Bass", "Slap Bass 1", "Slap Bass 2", "Syn Bass 1", "Syn Bass 2", 19 | "Violin", "Viola", "Cello", "Contrabass", 20 | "Tremolo Strings", "Pizzicato Strings", 21 | "Orchestral Harp", 22 | "Timpani", 23 | "Ensemble Strings", "Slow Strings", "Synth Strings 1", "Synth Strings 2", 24 | "Choir Aahs", "Voice Oohs", "Syn Choir", 25 | "Orchestra Hit", 26 | "Trumpet", "Trombone", "Tuba", "Muted Trumpet", "French Horn", "Brass Ensemble", "Syn Brass 1", "Syn Brass 2", 27 | "Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax", 28 | "Oboe", "English Horn", "Bassoon", "Clarinet", "Piccolo", "Flute", "Recorder", "Pan Flute", "Bottle Blow", "Shakuhachi", "Whistle", "Ocarina", 29 | "Syn Square Wave", "Syn Saw Wave", "Syn Calliope", "Syn Chiff", "Syn Charang", "Syn Voice", "Syn Fifths Saw", "Syn Brass and Lead", 30 | "Fantasia", "Warm Pad", "Polysynth", "Space Vox", "Bowed Glass", "Metal Pad", "Halo Pad", "Sweep Pad", "Ice Rain", "Soundtrack", "Crystal", "Atmosphere", 31 | "Brightness", "Goblins", "Echo Drops", "Sci Fi", 32 | "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", 33 | "Bag Pipe", 34 | "Fiddle", 35 | "Shanai", 36 | "Tinkle Bell", 37 | "Agogo", 38 | "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Syn Drum", "Reverse Cymbal", 39 | "Guitar Fret Noise", "Breath Noise", 40 | "Seashore", "Bird", "Telephone", "Helicopter", "Applause", "Gunshot"]; 41 | 42 | pub const DEFAULT_PERCUSSION_CHANNEL: u8 = 9; 43 | /// A MIDI channel describes playing data for a track. 44 | #[derive(Debug,Copy,Clone)] 45 | pub struct MidiChannel { 46 | pub channel: u8, 47 | pub effect_channel: u8, 48 | instrument: i32, 49 | pub volume: i8, 50 | pub balance: i8, 51 | pub chorus: i8, 52 | pub reverb: i8, 53 | pub phaser: i8, 54 | pub tremolo: i8, 55 | pub bank: u8, 56 | } 57 | impl Default for MidiChannel { 58 | fn default() -> Self { MidiChannel { channel: 0, effect_channel: 1, instrument: 25, volume: 104, balance: 64, chorus: 0, reverb: 0, phaser: 0, tremolo: 0, bank: 0, }} 59 | } 60 | impl MidiChannel { 61 | pub(crate) fn is_percussion_channel(self) -> bool { 62 | (self.channel % 16) == DEFAULT_PERCUSSION_CHANNEL 63 | } 64 | pub(crate) fn set_instrument(mut self, instrument: i32) { 65 | if instrument == -1 && self.is_percussion_channel() { self.instrument = 0; } 66 | else {self.instrument = instrument;} 67 | } 68 | 69 | pub(crate) fn _get_instrument(self) -> i32 {self.instrument} 70 | pub(crate) fn get_instrument_name(&self) -> String {String::from(CHANNEL_DEFAULT_NAMES[self.instrument.to_usize().unwrap()])} //TODO: FIXME: does not seems OK 71 | } 72 | 73 | impl Song{ 74 | /// Read all the MIDI channels 75 | pub(crate) fn read_midi_channels(&mut self, data: &[u8], seek: &mut usize) { for i in 0u8..64u8 { self.channels.push(self.read_midi_channel(data, seek, i)); } } 76 | /// Read MIDI channels. Guitar Pro format provides 64 channels (4 MIDI ports by 16 hannels), the channels are stored in this order: 77 | ///`port1/channel1`, `port1/channel2`, ..., `port1/channel16`, `port2/channel1`, ..., `port4/channel16`. 78 | /// 79 | /// Each channel has the following form: 80 | /// 81 | /// * **Instrument**: `int` 82 | /// * **Volume**: `byte` 83 | /// * **Balance**: `byte` 84 | /// * **Chorus**: `byte` 85 | /// * **Reverb**: `byte` 86 | /// * **Phaser**: `byte` 87 | /// * **Tremolo**: `byte` 88 | /// * **blank1**: `byte` => Backward compatibility with version 3.0 89 | /// * **blank2**: `byte` => Backward compatibility with version 3.0 90 | pub(crate) fn read_midi_channel(&self, data: &[u8], seek: &mut usize, channel: u8) -> MidiChannel { 91 | let instrument = read_int(data, seek); 92 | let mut c = MidiChannel{channel, effect_channel: channel, ..Default::default()}; 93 | c.volume = read_signed_byte(data, seek); c.balance = read_signed_byte(data, seek); 94 | c.chorus = read_signed_byte(data, seek); c.reverb = read_signed_byte(data, seek); c.phaser = read_signed_byte(data, seek); c.tremolo = read_signed_byte(data, seek); 95 | c.set_instrument(instrument); 96 | //println!("Channel: {}\t Volume: {}\tBalance: {}\tInstrument={}, {}, {}", c.channel, c.volume, c.balance, instrument, c.get_instrument(), c.get_instrument_name()); 97 | *seek += 2; //Backward compatibility with version 3.0 98 | c 99 | } 100 | 101 | /// Read MIDI channel. MIDI channel in Guitar Pro is represented by two integers. First is zero-based number of channel, second is zero-based number of channel used for effects. 102 | pub(crate) fn read_channel(&mut self, data: &[u8], seek: &mut usize) -> usize { //TODO: fixme for writing 103 | let index = read_int(data, seek) - 1; 104 | let effect_channel = read_int(data, seek) - 1; 105 | if 0 <= index && index < self.channels.len().to_i32().unwrap() { 106 | if self.channels[index.to_usize().unwrap()].instrument < 0 {self.channels[index.to_usize().unwrap()].instrument = 0;} 107 | if !self.channels[index.to_usize().unwrap()].is_percussion_channel() {self.channels[index.to_usize().unwrap()].effect_channel = effect_channel.to_u8().unwrap();} 108 | } 109 | index.to_usize().unwrap() 110 | } 111 | 112 | pub(crate) fn write_midi_channels(&self, data: &mut Vec) { 113 | for i in 0..self.channels.len() { 114 | println!("writing channel: {:?}", self.channels[i]); 115 | if self.channels[i].is_percussion_channel() && self.channels[i].instrument == 0 {write_i32(data, -1);} 116 | else {write_i32(data, self.channels[i].instrument);} 117 | write_signed_byte(data, Self::from_channel_short(self.channels[i].volume)); 118 | write_signed_byte(data, Self::from_channel_short(self.channels[i].balance)); 119 | write_signed_byte(data, Self::from_channel_short(self.channels[i].chorus)); 120 | write_signed_byte(data, Self::from_channel_short(self.channels[i].reverb)); 121 | write_signed_byte(data, Self::from_channel_short(self.channels[i].phaser)); 122 | write_signed_byte(data, Self::from_channel_short(self.channels[i].tremolo)); 123 | write_placeholder_default(data, 2); //Backward compatibility with version 3.0 124 | } 125 | } 126 | 127 | fn from_channel_short(data: i8) -> i8 { ((data >> 3) - 1).clamp(-128, 127) + 1 } 128 | } -------------------------------------------------------------------------------- /lib/src/key_signature.rs: -------------------------------------------------------------------------------- 1 | use fraction::ToPrimitive; 2 | use crate::io::*; 3 | 4 | pub const DURATION_QUARTER_TIME: i64 = 960; 5 | //pub const DURATION_WHOLE: u8 = 1; 6 | //pub const DURATION_HALF: u8 = 2; 7 | pub const DURATION_QUARTER: u8 = 4; 8 | pub const DURATION_EIGHTH: u8 = 8; 9 | pub const DURATION_SIXTEENTH: u8 = 16; 10 | pub const DURATION_THIRTY_SECOND: u8 = 32; 11 | pub const DURATION_SIXTY_FOURTH: u8 = 64; 12 | pub const DURATION_HUNDRED_TWENTY_EIGHTH: u8 = 128; 13 | 14 | /// A time signature 15 | #[derive(Debug,Clone, PartialEq,Eq)] 16 | pub struct TimeSignature { 17 | pub numerator: i8, 18 | pub denominator: Duration, 19 | pub beams: Vec, 20 | } 21 | impl Default for TimeSignature { 22 | fn default() -> Self { TimeSignature { numerator: 4, denominator:Duration::default(), beams: vec![2,2,2,2]}} 23 | } 24 | 25 | pub const KEY_SIGNATURES: [&str; 34] = ["F♭ major", "C♭ major", "G♭ major", "D♭ major", "A♭ major", "E♭ major", "B♭ major", 26 | "F major", "C major", "G major", "D major", "A major", "E major", "B major", 27 | "F# major", "C# major", "G# major", 28 | "D♭ minor", "A♭ minor", "E♭ minor", "B♭ minor", 29 | "F minor", "C minor", "G minor", "D minor", "A minor", "E minor", "B minor", 30 | "F# minor", "C# minor", "G# minor", "D# minor", "A# minor", "E# minor"]; 31 | #[derive(Debug,Clone,Default,PartialEq,Eq)] 32 | pub struct KeySignature { 33 | pub key: i8, 34 | pub is_minor: bool, 35 | } 36 | //impl Default for KeySignature { fn default() -> Self { KeySignature { key: 0, is_minor: false, }} } 37 | impl std::fmt::Display for KeySignature { 38 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 39 | let index: usize = if self.is_minor {(23i8 + self.key).to_usize().unwrap()} else {(8i8 + self.key).to_usize().unwrap()}; 40 | write!(f, "{}", KEY_SIGNATURES[index]) 41 | } 42 | } 43 | 44 | 45 | const SUPPORTED_TUPLETS: [(u8, u8); 10] = [(1,1), (3,2), (5,4), (6,4), (7,4), (9,8), (10,8), (11,8), (12,8), (13,8)]; 46 | 47 | #[derive(Debug,Clone,PartialEq,Eq)] 48 | pub struct Duration { 49 | pub value:u16, 50 | pub dotted: bool, 51 | pub double_dotted:bool, 52 | /// The time resulting with a 64th note and a 3/2 tuplet 53 | pub min_time: u8, 54 | //Tuplet division type 55 | pub tuplet_enters:u8, pub tuplet_times:u8 56 | } 57 | impl Default for Duration { 58 | fn default() -> Self { Duration { 59 | value: DURATION_QUARTER.to_u16().unwrap(), dotted: false, double_dotted: false, 60 | tuplet_enters:1, tuplet_times:1, 61 | min_time: 0 62 | }} 63 | } 64 | impl Duration { 65 | //fn convert_time(&self, time: u64) -> u64 { time * self.division_times as u64 / self.division_enters as u64 } 66 | 67 | pub(crate) fn is_supported(&self) -> bool { SUPPORTED_TUPLETS.contains(&(self.tuplet_enters, self.tuplet_times))} 68 | 69 | pub(crate) fn convert_time(&self, time: u32) -> u32 { 70 | let result = fraction::Fraction::new(time * self.tuplet_enters.to_u32().unwrap(), self.tuplet_times.to_u32().unwrap()); 71 | if *result.denom().unwrap() == 1 {(*result.numer().unwrap()).to_u32().unwrap()} 72 | else {result.trunc().to_u32().unwrap()} 73 | } 74 | 75 | pub(crate) fn time(&self) -> u32 { 76 | let mut result = (f64::from(DURATION_QUARTER_TIME.to_i32().unwrap()) * 4f64 / f64::from(self.value)).trunc(); 77 | //println!("\tDuration.time(): result: {}", result); 78 | if self.dotted { result += (result/2f64).trunc(); } 79 | //if self.dotted { result += (result/4f64).trunc() * 3f64; } 80 | //println!("\tDuration.time(): result: {}", result); 81 | self.convert_time(result.to_u32().unwrap()) 82 | } 83 | 84 | pub(crate) fn _index(&self) -> u8 { 85 | let mut index = 0u8; 86 | let mut value = self.value; 87 | loop { 88 | value >>= 1; 89 | if value > 0 {index += 1;} 90 | else {break;} 91 | } 92 | index 93 | } 94 | pub(crate) fn is_default_tuplet(&self) -> bool { self.tuplet_times == 1 && self.tuplet_enters == 1} 95 | //@classmethod def fromFraction(cls, frac): return cls(frac.denominator, frac.numerator) 96 | 97 | pub(crate) fn write_duration(&self, data: &mut Vec, flags: u8) { 98 | let value = (16 - self.value.leading_zeros()).to_i8().unwrap() - 3; //value = duration.value.bit_length() - 3 99 | write_signed_byte(data, value); 100 | if (flags & 0x20) == 0x20 { 101 | if !self.is_supported() {return;} 102 | write_i32(data, self.tuplet_enters.to_i32().unwrap()); //write iTuplet 103 | } 104 | } 105 | } 106 | /// Read beat duration. 107 | /// Duration is composed of byte signifying duration and an integer that maps to `Tuplet`. The byte maps to following values: 108 | /// 109 | /// * *-2*: whole note 110 | /// * *-1*: half note 111 | /// * *0*: quarter note 112 | /// * *1*: eighth note 113 | /// * *2*: sixteenth note 114 | /// * *3*: thirty-second note 115 | /// 116 | /// If flag at *0x20* is true, the tuplet is read 117 | pub(crate) fn read_duration(data: &[u8], seek: &mut usize, flags: u8) -> Duration { 118 | //println!("read_duration()"); 119 | let mut d = Duration{value: 1 << (read_signed_byte(data, seek) + 2), ..Default::default()}; 120 | //let b = read_signed_byte(data, seek); println!("B: {}", b); d.value = 1 << (b + 2); 121 | d.dotted = (flags & 0x01) == 0x01; 122 | if (flags & 0x20) == 0x20 { 123 | let i_tuplet = read_int(data, seek); 124 | if i_tuplet == 3 {d.tuplet_enters = 3; d.tuplet_times = 2;} 125 | else if i_tuplet == 5 {d.tuplet_enters = 5; d.tuplet_times = 4;} 126 | else if i_tuplet == 6 {d.tuplet_enters = 6; d.tuplet_times = 4;} 127 | else if i_tuplet == 7 {d.tuplet_enters = 7; d.tuplet_times = 4;} 128 | else if i_tuplet == 9 {d.tuplet_enters = 9; d.tuplet_times = 8;} 129 | else if i_tuplet == 10 {d.tuplet_enters = 10; d.tuplet_times = 8;} 130 | else if i_tuplet == 11 {d.tuplet_enters = 11; d.tuplet_times = 8;} 131 | else if i_tuplet == 12 {d.tuplet_enters = 12; d.tuplet_times = 8;} 132 | else if i_tuplet == 13 {d.tuplet_enters = 13; d.tuplet_times = 8;} 133 | } 134 | d 135 | } 136 | 137 | /*/// A *n:m* tuplet. 138 | #[derive(Clone)] 139 | struct Tuplet { 140 | enters: u8, 141 | times: u8, 142 | }*/ 143 | /*impl Default for Tuplet { fn default() -> Self { Tuplet { enters: 1, times: 1 }}} 144 | impl Tuplet { 145 | fn is_supported(self) -> bool { SUPPORTED_TUPLETS.contains(&(self.enters, self.times)) } 146 | fn convert_time(self) -> u8 { 147 | let result = fraction::Fraction::new(self.enters, self.times); 148 | if result.denom().expect("Cannot get fraction denominator") == &1 {1} 149 | else {result.to_u8().expect("Cannot get fraction result")} 150 | } 151 | }*/ 152 | 153 | /* Enum ?: const KEY_F_MAJOR_FLAT: (i8, bool) = (-8, false); 154 | const KEY_C_MAJOR_FLAT: (i8, bool) = (-7, false); 155 | const KEY_G_MAJOR_FLAT: (i8, bool) = (-6, false); 156 | const KEY_D_MAJOR_FLAT: (i8, bool) = (-5, false); 157 | const KEY_A_MAJOR_FLAT: (i8, bool) = (-4, false); 158 | const KEY_E_MAJOR_FLAT: (i8, bool) = (-3, false); 159 | const KEY_B_MAJOR_FLAT: (i8, bool) = (-2, false); 160 | const KEY_F_MAJOR: (i8, bool) = (-1, false); 161 | const KEY_C_MAJOR: (i8, bool) = (0, false); 162 | const KEY_G_MAJOR: (i8, bool) = (1, false); 163 | const KEY_D_MAJOR: (i8, bool) = (2, false); 164 | const KEY_A_MAJOR: (i8, bool) = (3, false); 165 | const KEY_E_MAJOR: (i8, bool) = (4, false); 166 | const KEY_B_MAJOR: (i8, bool) = (5, false); 167 | const KEY_F_MAJOR_SHARP: (i8, bool) = (6, false); 168 | const KEY_C_MAJOR_SHARP: (i8, bool) = (7, false); 169 | const KEY_G_MAJOR_SHARP: (i8, bool) = (8, false); 170 | const KEY_D_MINOR_FLAT: (i8, bool) = (-8, true); 171 | const KEY_A_MINOR_FLAT: (i8, bool) = (-7, true); 172 | const KEY_E_MINOR_FLAT: (i8, bool) = (-6, true); 173 | const KEY_B_MINOR_FLAT: (i8, bool) = (-5, true); 174 | const KEY_F_MINOR: (i8, bool) = (-4, true); 175 | const KEY_C_MINOR: (i8, bool) = (-3, true); 176 | const KEY_G_MINOR: (i8, bool) = (-2, true); 177 | const KEY_D_MINOR: (i8, bool) = (-1, true); 178 | const KEY_A_MINOR: (i8, bool) = (0, true); 179 | const KEY_E_MINOR: (i8, bool) = (1, true); 180 | const KEY_B_MINOR: (i8, bool) = (2, true); 181 | const KEY_F_MINOR_SHARP: (i8, bool) = (3, true); 182 | const KEY_C_MINOR_SHARP: (i8, bool) = (4, true); 183 | const KEY_G_MINOR_SHARP: (i8, bool) = (5, true); 184 | const KEY_D_MINOR_SHARP: (i8, bool) = (6, true); 185 | const KEY_A_MINOR_SHARP: (i8, bool) = (7, true); 186 | const KEY_E_MINOR_SHARP: (i8, bool) = (8, true);*/ 187 | -------------------------------------------------------------------------------- /lib/src/rse.rs: -------------------------------------------------------------------------------- 1 | use fraction::ToPrimitive; 2 | 3 | use crate::{io::*, gp::*, enums::*, track::*}; 4 | 5 | /// Equalizer found in master effect and track effect. 6 | /// 7 | /// Attribute :attr:`RSEEqualizer.knobs` is a list of values in range from -6.0 to 5.9. Master effect has 10 knobs, track effect has 3 8 | /// knobs. Gain is a value in range from -6.0 to 5.9 which can be found in both master and track effects and is named as "PRE" in Guitar Pro 5. 9 | #[derive(Debug,Clone)] 10 | pub struct RseEqualizer { 11 | pub knobs: Vec, 12 | pub gain: f32, 13 | } 14 | impl Default for RseEqualizer {fn default() -> Self { RseEqualizer { knobs: Vec::with_capacity(10), gain:0.0 }}} 15 | 16 | /// Master effect as seen in "Score information" 17 | #[derive(Debug,Clone)] 18 | pub struct RseMasterEffect { 19 | pub volume: f32, 20 | pub reverb: f32, 21 | pub equalizer: RseEqualizer, 22 | } 23 | impl Default for RseMasterEffect { fn default() -> Self { RseMasterEffect {volume:0.0, reverb:0.0, equalizer:RseEqualizer{knobs:vec![0.0;10], ..Default::default()} }}} 24 | 25 | #[derive(Debug, Clone, PartialEq, Eq)] 26 | pub struct RseInstrument { 27 | pub instrument: i16, 28 | pub unknown: i16, 29 | pub sound_bank: i16, 30 | pub effect_number: i16, 31 | pub effect_category: String, 32 | pub effect: String, 33 | } 34 | impl Default for RseInstrument { fn default() -> Self { RseInstrument { instrument:-1, unknown:-1, sound_bank:-1, effect_number:-1, effect_category:String::new(), effect:String::new()}}} 35 | 36 | #[derive(Debug,Clone)] 37 | pub struct TrackRse { 38 | pub instrument: RseInstrument, 39 | pub equalizer: RseEqualizer, 40 | pub humanize: u8, 41 | pub auto_accentuation: Accentuation, 42 | } 43 | impl Default for TrackRse { fn default() -> Self { TrackRse {instrument:RseInstrument::default(), humanize:0, auto_accentuation: Accentuation::None, equalizer:RseEqualizer{knobs:vec![0.0;3], ..Default::default()} }}} 44 | 45 | impl Song { 46 | /// Read RSE master effect. Persistence of RSE master effect was introduced in Guitar Pro 5.1. It is read as: 47 | /// - Master volume: `int`. Values are in range from 0 to 200. 48 | /// - 10-band equalizer. See `read_equalizer()`. 49 | pub(crate) fn read_rse_master_effect(&self, data: &[u8], seek: &mut usize) -> RseMasterEffect { 50 | let mut me = RseMasterEffect::default(); 51 | if self.version.number > (5,0,0) { 52 | me.volume = read_int(data, seek).to_f32().unwrap(); 53 | read_int(data, seek); //??? 54 | me.equalizer = self.read_rse_equalizer(data, seek, 11); 55 | //println!("read_rse_master_effect(): {:?}", me); 56 | } 57 | me 58 | } 59 | /// Read equalizer values. Equalizers are used in RSE master effect and Track RSE. They consist of *n* `SignedBytes ` for each *n* bands and one `signed-byte` for gain (PRE) fader. 60 | /// Volume values are stored as opposite to actual value. See `unpack_volume_value()`. 61 | fn read_rse_equalizer(&self, data: &[u8], seek: &mut usize, knobs: u8) -> RseEqualizer { 62 | let mut e = RseEqualizer::default(); 63 | for _ in 0..knobs {e.knobs.push(self.unpack_volume_value(read_signed_byte(data, seek)));} //knobs = list(map(self.unpackVolumeValue, self.readSignedByte(count=knobsNumber))) 64 | e //return gp.RSEEqualizer(knobs=knobs[:-1], gain=knobs[-1]) 65 | } 66 | /// Unpack equalizer volume value. Equalizer volumes are float but stored as `SignedBytes `. 67 | fn unpack_volume_value(&self, value: i8) -> f32 { -value.to_f32().unwrap() / 10.0 } 68 | 69 | /// Read track RSE. In GuitarPro 5.1 track RSE is read as follows: 70 | /// - Humanize: :`byte`. 71 | /// - Unknown space: 6 `Ints `. 72 | /// - RSE instrument. See `readRSEInstrument`. 73 | /// - 3-band track equalizer. See `read_equalizer()`. 74 | /// - RSE instrument effect. See `read_rse_instrument_effect()`. 75 | pub(crate) fn read_track_rse(&mut self, data: &[u8], seek: &mut usize, track: &mut Track) { 76 | track.rse.humanize = read_byte(data, seek); 77 | //println!("read_track_rse(), humanize: {} \t\t seek: {}", track.rse.humanize, *seek); 78 | *seek += 12; //read_int(data, seek); read_int(data, seek); read_int(data, seek); //??? 4 bytes*3 //*seek += 12; 79 | *seek += 12; //??? 80 | track.rse.instrument = self.read_rse_instrument(data, seek); 81 | if self.version.number > (5,0,0) { 82 | track.rse.equalizer = self.read_rse_equalizer(data, seek, 4); 83 | self.read_rse_instrument_effect(data, seek, &mut track.rse.instrument); 84 | } 85 | } 86 | /// Read RSE instrument. 87 | /// - MIDI instrument number: `int`. 88 | /// - Unknown `int`. 89 | /// - Sound bank: `int`. 90 | /// - Effect number: `int`. Vestige of Guitar Pro 5.0 format. 91 | pub(crate) fn read_rse_instrument(&mut self, data: &[u8], seek: &mut usize) -> RseInstrument { 92 | let mut instrument = RseInstrument{instrument: read_int(data, seek).to_i16().unwrap(), ..Default::default()}; 93 | instrument.unknown = read_int(data, seek).to_i16().unwrap(); //??? mostly 1 94 | instrument.sound_bank = read_int(data, seek).to_i16().unwrap(); 95 | //println!("read_rse_instrument(), instrument: {} {} {} \t\t seek: {}", instrument.instrument, instrument.unknown, instrument.sound_bank, *seek); 96 | if self.version.number == (5,0,0) { 97 | instrument.effect_number = read_short(data, seek); 98 | *seek += 1; 99 | } else {instrument.effect_number = read_int(data, seek).to_i16().unwrap();} 100 | //println!("read_rse_instrument(), instrument.effect_number: {} \t\t seek: {}", instrument.effect_number, *seek); 101 | instrument 102 | } 103 | /// Read RSE instrument effect name. This feature was introduced in Guitar Pro 5.1. 104 | /// - Effect name: `int-byte-size-string`. 105 | /// - Effect category: `int-byte-size-string`. 106 | pub(crate) fn read_rse_instrument_effect(&mut self, data: &[u8], seek: &mut usize, instrument: &mut RseInstrument) { 107 | if self.version.number > (5,0,0) { 108 | instrument.effect = read_int_byte_size_string(data, seek); 109 | instrument.effect_category = read_int_byte_size_string(data, seek); 110 | } 111 | } 112 | 113 | pub(crate) fn write_rse_master_effect(&self, data: &mut Vec) { 114 | write_i32(data, if self.master_effect.volume == 0.0 {100} else {self.master_effect.volume.ceil().to_i32().unwrap()}); 115 | write_i32(data, 0); //reverb? 116 | self.write_equalizer(data, &self.master_effect.equalizer); 117 | 118 | } 119 | fn write_equalizer(&self, data: &mut Vec, equalizer: &RseEqualizer) { 120 | for i in 0..equalizer.knobs.len() { write_signed_byte(data, self.pack_volume_value(equalizer.knobs[i])); } 121 | write_signed_byte(data, self.pack_volume_value(equalizer.gain)); 122 | } 123 | fn pack_volume_value(&self, value: f32) -> i8 { 124 | (-value * 10f32).round().to_i8().unwrap() //int(-round(value, 1) * 10) 125 | } 126 | pub(crate) fn write_master_reverb(&self, data: &mut Vec) { 127 | write_i32(data, self.master_effect.reverb.to_i32().unwrap()); 128 | } 129 | 130 | pub(crate) fn write_track_rse(&self, data: &mut Vec, rse: &TrackRse, version: &(u8,u8,u8)) { 131 | write_byte(data, rse.humanize); 132 | write_i32(data, 0); write_i32(data, 0); write_i32(data, 100); 133 | write_placeholder_default(data, 12); 134 | self.write_rse_instrument(data, &rse.instrument, version); 135 | if version > &(5,0,0) { 136 | self.write_equalizer(data, &rse.equalizer); 137 | self.write_rse_instrument_effect(data, &rse.instrument); 138 | } 139 | } 140 | pub(crate) fn write_rse_instrument(&self, data: &mut Vec, instrument: &RseInstrument, version: &(u8,u8,u8)) { 141 | write_i32(data, instrument.instrument.to_i32().unwrap()); 142 | write_i32(data, instrument.unknown.to_i32().unwrap()); 143 | write_i32(data, instrument.sound_bank.to_i32().unwrap()); 144 | if version == &(5,0,0) { 145 | write_i16(data, instrument.effect_number); 146 | write_placeholder_default(data, 1); 147 | } else {write_i32(data, instrument.effect_number.to_i32().unwrap());} 148 | } 149 | pub(crate) fn write_rse_instrument_effect(&self, data: &mut Vec, instrument: &RseInstrument) { //version>5.0.0 150 | write_int_byte_size_string(data, &instrument.effect); 151 | write_int_byte_size_string(data, &instrument.effect_category); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /lib/src/io.rs: -------------------------------------------------------------------------------- 1 | use fraction::ToPrimitive; 2 | use encoding_rs::*; 3 | 4 | //reading functions 5 | 6 | /// Read a byte and increase the cursor position by 1 7 | /// * `data` - array of bytes 8 | /// * `seek` - start position to read 9 | /// * returns the read byte as u8 10 | pub(crate) fn read_byte(data: &[u8], seek: &mut usize ) -> u8 { 11 | if data.len() < *seek {panic!("End of filee reached");} 12 | let b = data[*seek]; 13 | *seek += 1; 14 | b 15 | } 16 | 17 | /// Read a signed byte and increase the cursor position by 1 18 | /// * `data` - array of bytes 19 | /// * `seek` - start position to read 20 | /// * returns the read byte as u8 21 | pub(crate) fn read_signed_byte(data: &[u8], seek: &mut usize ) -> i8 { 22 | if data.len() < *seek {panic!("End of file reached");} 23 | let b = data[*seek] as i8; 24 | *seek += 1; 25 | b 26 | } 27 | 28 | /// Read a boolean and increase the cursor position by 1 29 | /// * `data` - array of bytes 30 | /// * `seek` - start position to read 31 | /// * returns boolean value 32 | pub(crate) fn read_bool(data: &[u8], seek: &mut usize ) -> bool { 33 | if data.len() < *seek {panic!("End of file reached");} 34 | let b = data[*seek]; 35 | *seek += 1; 36 | b != 0 37 | } 38 | 39 | /// Read a short and increase the cursor position by 2 (2 little-endian bytes) 40 | /// * `data` - array of bytes 41 | /// * `seek` - start position to read 42 | /// * returns the short value 43 | pub(crate) fn read_short(data: &[u8], seek: &mut usize ) -> i16 { 44 | if data.len() < *seek + 2 {panic!("End of file reached");} 45 | let n = i16::from_le_bytes([data[*seek], data[*seek+1]]); 46 | *seek += 2; 47 | n 48 | } 49 | 50 | /// Read an integer and increase the cursor position by 4 (4 little-endian bytes) 51 | /// * `data` - array of bytes 52 | /// * `seek` - start position to read 53 | /// * returns the integer value 54 | pub(crate) fn read_int(data: &[u8], seek: &mut usize ) -> i32 { 55 | if data.len() < *seek + 4 {panic!("End of file reached");} 56 | let n = i32::from_le_bytes([data[*seek], data[*seek+1], data[*seek+2], data[*seek+3]]); 57 | *seek += 4; 58 | n 59 | } 60 | 61 | /*/// Read a float and increase the cursor position by 4 (4 little-endian bytes) 62 | /// * `data` - array of bytes 63 | /// * `seek` - start position to read 64 | /// * returns the float value 65 | pub(crate) fn read_float(data: &[u8], seek: &mut usize ) -> f32 { 66 | let n = f32::from_le_bytes([data[*seek], data[*seek+1], data[*seek+2], data[*seek+3]]); 67 | *seek += 4; 68 | n 69 | }*/ 70 | 71 | /// Read a double and increase the cursor position by 8 (8 little-endian bytes) 72 | /// * `data` - array of bytes 73 | /// * `seek` - start position to read 74 | /// * returns the float value 75 | pub(crate) fn read_double(data: &[u8], seek: &mut usize ) -> f64 { 76 | let n = f64::from_le_bytes([data[*seek], data[*seek+1], data[*seek+2], data[*seek+3], data[*seek+4], data[*seek+5], data[*seek+6], data[*seek+7]]); 77 | *seek += 8; 78 | n 79 | } 80 | 81 | /// Read length of the string stored in 1 integer and followed by character bytes. 82 | pub(crate) fn read_int_size_string(data: &[u8], seek: &mut usize) -> String { 83 | let size = read_int(data, seek).to_usize().unwrap(); 84 | read_string(data, seek, size, None) 85 | } 86 | 87 | /// Read length of the string increased by 1 and stored in 1 integer followed by length of the string in 1 byte and finally followed by character bytes. 88 | pub(crate) fn read_int_byte_size_string(data: &[u8], seek: &mut usize) -> String { 89 | let s = (read_int(data, seek) - 1).to_usize().unwrap(); 90 | read_byte_size_string(data, seek, s) 91 | } 92 | 93 | /// Read length of the string stored in 1 byte and followed by character bytes. 94 | /// * `size`: string length that we should attempt to read. 95 | pub(crate) fn read_byte_size_string(data: &[u8], seek: &mut usize, size: usize) -> String { 96 | //println!("read_int_byte_size_string(), size={}", size); 97 | let length = read_byte(data, seek).to_usize().unwrap(); 98 | read_string(data, seek, size, Some(length)) 99 | } 100 | 101 | /// Read a string 102 | /// * `size`: real string length 103 | /// * `length`: optionnal provided length (in case of blank chars after the string) 104 | fn read_string(data: &[u8], seek: &mut usize, size: usize, length: Option) -> String { 105 | //println!("read_string(), size={} \t length={:?}", size, length); 106 | let length = length.unwrap_or(size); 107 | //let count = if size > 0 {size} else {length}; 108 | let (cow, _encoding_used, had_errors) = WINDOWS_1252.decode(&data[*seek..*seek+length]); 109 | if had_errors { 110 | let parse = std::str::from_utf8(&data[*seek..*seek+length]); 111 | if parse.is_err() {panic!("Unable to read string");} 112 | *seek += size; 113 | return parse.unwrap().to_string(); 114 | } 115 | *seek += size; 116 | cow.to_string() 117 | } 118 | 119 | pub const VERSIONS: [((u8,u8,u8), bool, &str); 10] = [((3, 0, 0), false, "FICHIER GUITAR PRO v3.00"), 120 | 121 | ((4, 0, 0), false, "FICHIER GUITAR PRO v4.00"), 122 | ((4, 0, 6), false, "FICHIER GUITAR PRO v4.06"), 123 | ((4, 0, 6), true, "CLIPBOARD GUITAR PRO 4.0 [c6]"), 124 | 125 | ((5, 0, 0), false, "FICHIER GUITAR PRO v5.00"), 126 | ((5, 1, 0), false, "FICHIER GUITAR PRO v5.10"), 127 | ((5, 2, 0), false, "FICHIER GUITAR PRO v5.10"), // sic 128 | ((5, 0, 0), true, "CLIPBOARD GP 5.0"), 129 | ((5, 1, 0), true, "CLIPBOARD GP 5.1"), 130 | ((5, 2, 0), true, "CLIPBOARD GP 5.2")]; 131 | 132 | /// Read the file version. It is on the first 31 bytes (1st byte is the real length, the following 30 bytes contain the version string) of the file. 133 | /// * `data` - array of bytes 134 | /// * `seek` - cursor that will be incremented 135 | /// * returns version 136 | pub(crate) fn read_version_string(data: &[u8], seek: &mut usize) -> crate::headers::Version { 137 | let mut v = crate::headers::Version {data: read_byte_size_string(data, seek, 30), number: (5,2,0), clipboard: false}; 138 | //println!("Version {} {}", n, s); 139 | //get the version 140 | for x in VERSIONS { 141 | if v.data == x.2 { 142 | v.number = x.0; 143 | v.clipboard = x.1; 144 | break; 145 | } 146 | } 147 | //println!("########################## Version: {:?}", v); 148 | v 149 | } 150 | 151 | /// Read a color. Colors are used by `Marker` and `Track`. They consist of 3 consecutive bytes and one blank byte. 152 | pub(crate) fn read_color(data: &[u8], seek: &mut usize) -> i32 { 153 | let r = read_byte(data, seek).to_i32().unwrap(); 154 | let g = read_byte(data, seek).to_i32().unwrap(); 155 | let b = read_byte(data, seek).to_i32().unwrap(); 156 | *seek += 1; 157 | r * 65536 + g * 256 + b 158 | } 159 | 160 | //writing functions 161 | fn write_placeholder(data: &mut Vec, count: usize, byte: u8) { for _ in 0..count {data.push(byte);} } 162 | 163 | pub(crate) fn write_placeholder_default(data: &mut Vec, count: usize) {write_placeholder(data, count, 0x00);} 164 | pub(crate) fn write_byte(data: &mut Vec, value: u8) {data.push(value);} 165 | pub(crate) fn write_signed_byte(data: &mut Vec, value: i8) {data.extend(value.to_le_bytes());} 166 | pub(crate) fn write_bool(data: &mut Vec, value: bool) {data.push(u8::from(value));} 167 | pub(crate) fn write_i32(data: &mut Vec, value: i32) {data.extend(value.to_le_bytes());} 168 | //pub(crate) fn write_u32(data: &mut Vec, value: u32) {data.extend(value.to_le_bytes());} 169 | pub(crate) fn write_i16(data: &mut Vec, value: i16) {data.extend(value.to_le_bytes());} 170 | //pub(crate) fn write_u16(data: &mut Vec, value: u16) {data.extend(value.to_le_bytes());} 171 | //pub(crate) fn write_f32(data: &mut Vec, value: f32) {data.extend(value.to_le_bytes());} 172 | pub(crate) fn write_f64(data: &mut Vec, value: f64) {data.extend(value.to_le_bytes());} 173 | pub(crate) fn write_color(data: &mut Vec, value: i32) { 174 | let r: u8 = ((value & 0xff0000) >> 16).to_u8().unwrap(); 175 | let g: u8 = ((value & 0x00ff00) >> 8).to_u8().unwrap(); 176 | let b: u8 = (value & 0x0000ff).to_u8().unwrap(); 177 | write_byte(data, r); 178 | write_byte(data, g); 179 | write_byte(data, b); 180 | write_placeholder_default(data, 1); 181 | } 182 | pub(crate) fn write_byte_size_string(data: &mut Vec, value: &str) { 183 | write_byte(data, value.chars().count().to_u8().unwrap()); 184 | data.extend(value.as_bytes()); 185 | } 186 | pub(crate) fn write_int_size_string(data: &mut Vec, value: &str) { 187 | let count = value.chars().count(); 188 | write_i32(data, count.to_i32().unwrap()+1); 189 | data.extend(value.as_bytes()); 190 | } 191 | 192 | pub(crate) fn write_int_byte_size_string(data: &mut Vec, value: &str) { 193 | let count = value.chars().count(); 194 | write_i32(data, count.to_i32().unwrap()+1); //write_i32( (value.getBytes(charset).length + 1) ); 195 | write_byte(data, count.to_u8().unwrap()); 196 | data.extend(value.as_bytes()); 197 | } 198 | 199 | pub(crate) fn write_version(data: &mut Vec, version: (u8,u8,u8)) { 200 | for v in VERSIONS { 201 | if version == v.0 { 202 | write_byte_size_string(data, v.2); 203 | write_placeholder_default(data, 30 - v.2.len()); 204 | break; 205 | } 206 | } 207 | } 208 | 209 | #[cfg(test)] 210 | mod test { 211 | use crate::io::*; 212 | 213 | #[test] 214 | fn test_read_byte_size_string() { 215 | let data: Vec = vec![0x18,0x46,0x49,0x43,0x48,0x49,0x45,0x52, 216 | 0x20,0x47,0x55,0x49,0x54,0x41,0x52,0x20, 217 | 0x50,0x52,0x4f,0x20,0x76,0x33,0x2e,0x30, 218 | 0x30]; 219 | let mut seek = 0usize; 220 | assert_eq!(read_byte_size_string(&data, &mut seek, 30), "FICHIER GUITAR PRO v3.00"); 221 | } 222 | 223 | #[test] 224 | fn test_read_int_size_string() { 225 | let data: Vec = vec![0x08,0x00,0x00,0x00, 0x25,0x41,0x52,0x54,0x49,0x53,0x54,0x25]; 226 | let mut seek = 0usize; 227 | assert_eq!(read_int_size_string(&data, &mut seek), "%ARTIST%"); 228 | } 229 | 230 | #[test] 231 | fn test_read_int_byte_size_string() { 232 | let data: Vec = vec![0x09,0x00,0x00,0x00, 0x08, 0x25,0x41,0x52,0x54,0x49,0x53,0x54,0x25]; 233 | let mut seek = 0usize; 234 | assert_eq!(read_int_byte_size_string(&data, &mut seek), "%ARTIST%"); 235 | } 236 | 237 | #[test] 238 | fn test_write_byte_size_string() { 239 | let mut out: Vec = Vec::with_capacity(32); 240 | write_byte_size_string(&mut out, "FICHIER GUITAR PRO v3.00"); 241 | let expected_result: Vec = vec![0x18,0x46,0x49,0x43,0x48,0x49,0x45,0x52, 242 | 0x20,0x47,0x55,0x49,0x54,0x41,0x52,0x20, 243 | 0x50,0x52,0x4f,0x20,0x76,0x33,0x2e,0x30, 244 | 0x30]; 245 | assert_eq!(out, expected_result); 246 | } 247 | #[test] 248 | fn test_write_int_size_string() { 249 | let mut out: Vec = Vec::with_capacity(16); 250 | write_int_size_string(&mut out, "%ARTIST%"); 251 | let expected_result: Vec = vec![0x09,0x00,0x00,0x00, 0x08,0x25,0x41,0x52,0x54,0x49,0x53,0x54,0x25]; 252 | assert_eq!(out, expected_result); 253 | } 254 | #[test] 255 | fn test_write_int_byte_size_string() { 256 | let mut out: Vec = Vec::with_capacity(16); 257 | write_int_byte_size_string(&mut out, "%ARTIST%"); 258 | let expected_result: Vec = vec![0x09,0x00,0x00,0x00, 0x08, 0x25,0x41,0x52,0x54,0x49,0x53,0x54,0x25]; 259 | assert_eq!(out, expected_result); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /lib/src/song.rs: -------------------------------------------------------------------------------- 1 | 2 | use fraction::ToPrimitive; 3 | 4 | use crate::enums::*; 5 | use crate::io::*; 6 | use crate::headers::*; 7 | use crate::page::*; 8 | use crate::track::*; 9 | use crate::key_signature::*; 10 | use crate::lyric::*; 11 | use crate::midi::*; 12 | use crate::rse::*; 13 | 14 | 15 | // Struct utility to read file: https://stackoverflow.com/questions/55555538/what-is-the-correct-way-to-read-a-binary-file-in-chunks-of-a-fixed-size-and-stor 16 | #[derive(Debug,Clone)] 17 | pub struct Song { 18 | pub version: Version, 19 | pub clipboard: Option, 20 | 21 | pub name: String, 22 | pub subtitle: String, //Guitar Pro 23 | pub artist: String, 24 | pub album: String, 25 | pub words: String, //GP 26 | pub author: String, //music by 27 | pub date: String, 28 | pub copyright: String, 29 | /// Tab writer 30 | pub writer: String, 31 | pub transcriber: String, 32 | pub instructions: String, 33 | pub comments: String, 34 | pub notice: Vec, 35 | 36 | pub tracks: Vec, 37 | pub measure_headers: Vec, 38 | pub channels: Vec, 39 | pub lyrics: Lyrics, 40 | pub tempo: i16, 41 | pub hide_tempo: bool, 42 | pub tempo_name:String, 43 | pub key: KeySignature, 44 | 45 | pub triplet_feel: TripletFeel, 46 | pub master_effect: RseMasterEffect, 47 | 48 | pub page_setup: PageSetup, 49 | 50 | //Used to read the file 51 | pub current_measure_number: Option, 52 | pub current_track: Option, 53 | pub current_voice_number: Option, 54 | pub current_beat_number: Option, 55 | } 56 | 57 | impl Default for Song { 58 | fn default() -> Self { Song { 59 | version: Version {data: String::with_capacity(30), clipboard: false, number: (5,1,0)}, clipboard: None, 60 | name:String::new(), subtitle: String::new(), artist:String::new(), album: String::new(), 61 | words: String::new(), author:String::new(), date:String::new(), 62 | copyright:String::new(), writer:String::new(), transcriber:String::new(), comments:String::new(), 63 | notice:Vec::new(), 64 | instructions: String::new(), 65 | tracks:Vec::new(), 66 | measure_headers:Vec::new(), 67 | channels:Vec::with_capacity(64), 68 | lyrics: Lyrics::default(), 69 | tempo: 120, hide_tempo: false, tempo_name:String::from("Moderate"), 70 | key: KeySignature::default(), 71 | 72 | triplet_feel: TripletFeel::None, 73 | current_measure_number: None, current_track: None, current_voice_number: None, current_beat_number: None, 74 | 75 | page_setup: PageSetup::default(), 76 | 77 | master_effect: RseMasterEffect::default(), 78 | }} 79 | } 80 | impl Song { 81 | /// Read the song. A song consists of score information, triplet feel, tempo, song key, MIDI channels, measure and track count, measure headers, tracks, measures. 82 | /// - Version: `byte-size-string` of size 30. 83 | /// - Score information. See `readInfo`. 84 | /// - Triplet feel: `bool`. If value is true, then triplet feel is set to eigth. 85 | /// - Tempo: `int`. 86 | /// - Key: `int`. Key signature of the song. 87 | /// - MIDI channels. See `readMidiChannels`. 88 | /// - Number of measures: `int`. 89 | /// - Number of tracks: `int`. 90 | /// - Measure headers. See `readMeasureHeaders`. 91 | /// - Tracks. See `read_tracks()`. 92 | /// - Measures. See `read_measures()`. 93 | pub fn read_gp3(&mut self, data: &[u8]) { 94 | let mut seek: usize = 0; 95 | self.version = read_version_string(data, &mut seek); 96 | self.read_info(data, &mut seek); 97 | self.triplet_feel = if read_bool(data, &mut seek) {TripletFeel::Eighth} else {TripletFeel::None}; 98 | //println!("Triplet feel: {}", self.triplet_feel); 99 | self.tempo = read_int(data, &mut seek).to_i16().unwrap(); 100 | self.key.key = read_int(data, &mut seek).to_i8().unwrap(); 101 | //println!("Tempo: {} bpm\t\tKey: {}", self.tempo, self.key.to_string()); 102 | self.read_midi_channels(data, &mut seek); 103 | let measure_count = read_int(data, &mut seek).to_usize().unwrap(); 104 | let track_count = read_int(data, &mut seek).to_usize().unwrap(); 105 | //println!("Measures count: {}\tTrack count: {}", measure_count, track_count); 106 | // Read measure headers. The *measures* are written one after another, their number have been specified previously. 107 | self.read_measure_headers(data, &mut seek, measure_count); 108 | self.current_measure_number = Some(0); 109 | self.read_tracks(data, &mut seek, track_count); 110 | self.read_measures(data, &mut seek); 111 | } 112 | /// Read the song. A song consists of score information, triplet feel, tempo, song key, MIDI channels, measure and track count, measure headers, tracks, measures. 113 | /// - Version: `byte-size-string` of size 30. 114 | /// - Score information. See `readInfo`. 115 | /// - Triplet feel: `bool`. If value is true, then triplet feel is set to eigth. 116 | /// - Lyrics. See `read_lyrics()`. 117 | /// - Tempo: `int`. 118 | /// - Key: `int`. Key signature of the song. 119 | /// - Octave: `signed-byte`. Reserved for future uses. 120 | /// - MIDI channels. See `readMidiChannels`. 121 | /// - Number of measures: `int`. 122 | /// - Number of tracks: `int`. 123 | /// - Measure headers. See `readMeasureHeaders`. 124 | /// - Tracks. See `read_tracks()`. 125 | /// - Measures. See `read_measures()`. 126 | pub fn read_gp4(&mut self, data: &[u8]) { 127 | let mut seek: usize = 0; 128 | self.version = read_version_string(data, &mut seek); 129 | self.read_clipboard(data, &mut seek); 130 | self.read_info(data, &mut seek); 131 | self.triplet_feel = if read_bool(data, &mut seek) {TripletFeel::Eighth} else {TripletFeel::None}; 132 | //println!("Triplet feel: {}", self.triplet_feel); 133 | self.lyrics = self.read_lyrics(data, &mut seek); //read lyrics 134 | self.tempo = read_int(data, &mut seek).to_i16().unwrap(); 135 | self.key.key = read_int(data, &mut seek).to_i8().unwrap(); 136 | //println!("Tempo: {} bpm\t\tKey: {}", self.tempo, self.key.to_string()); 137 | read_signed_byte(data, &mut seek); //octave 138 | self.read_midi_channels(data, &mut seek); 139 | let measure_count = read_int(data, &mut seek).to_usize().unwrap(); 140 | let track_count = read_int(data, &mut seek).to_usize().unwrap(); 141 | //println!("Measures count: {}\tTrack count: {}", measure_count, track_count); 142 | // Read measure headers. The *measures* are written one after another, their number have been specified previously. 143 | self.read_measure_headers(data, &mut seek, measure_count); 144 | //self.current_measure_number = Some(0); 145 | self.read_tracks(data, &mut seek, track_count); 146 | self.read_measures(data, &mut seek); 147 | } 148 | pub fn read_gp5(&mut self, data: &[u8]) { 149 | let mut seek: usize = 0; 150 | self.version = read_version_string(data, &mut seek); 151 | self.read_clipboard(data, &mut seek); 152 | self.read_info(data, &mut seek); 153 | self.lyrics = self.read_lyrics(data, &mut seek); //read lyrics 154 | self.master_effect = self.read_rse_master_effect(data, &mut seek); 155 | self.read_page_setup(data, &mut seek); 156 | self.tempo_name = read_int_size_string(data, &mut seek); 157 | self.tempo = read_int(data, &mut seek).to_i16().unwrap(); 158 | self.hide_tempo = if self.version.number > (5,0,0) {read_bool(data, &mut seek)} else {false}; 159 | self.key.key = read_signed_byte(data, &mut seek); 160 | read_int(data, &mut seek); //octave 161 | self.read_midi_channels(data, &mut seek); 162 | let directions = self.read_directions(data, &mut seek); 163 | self.master_effect.reverb = read_int(data, &mut seek).to_f32().unwrap(); 164 | let measure_count = read_int(data, &mut seek).to_usize().unwrap(); 165 | let track_count = read_int(data, &mut seek).to_usize().unwrap(); 166 | //println!("{} {} {} {:?}", self.tempo_name, self.tempo, self.hide_tempo, self.key.key); //OK 167 | println!("Track count: {} \t Measure count: {}", track_count, measure_count); //OK 168 | self.read_measure_headers_v5(data, &mut seek, measure_count, &directions); 169 | self.read_tracks_v5(data, &mut seek, track_count); 170 | println!("read_gp5(), after tracks \t seek: {}", seek); 171 | self.read_measures(data, &mut seek); 172 | println!("read_gp5(), after measures \t seek: {}", seek); 173 | } 174 | 175 | /// Read information (name, artist, ...) 176 | fn read_info(&mut self, data: &[u8], seek: &mut usize) { 177 | self.name = read_int_byte_size_string(data, seek);//.replace("\r", " ").replace("\n", " ").trim().to_owned(); 178 | self.subtitle = read_int_byte_size_string(data, seek); 179 | self.artist = read_int_byte_size_string(data, seek); 180 | self.album = read_int_byte_size_string(data, seek); 181 | self.words = read_int_byte_size_string(data, seek); //music 182 | self.author = if self.version.number.0 < 5 {self.words.clone()} else {read_int_byte_size_string(data, seek)}; 183 | self.copyright = read_int_byte_size_string(data, seek); 184 | self.writer = read_int_byte_size_string(data, seek); //tabbed by 185 | self.instructions= read_int_byte_size_string(data, seek); //instructions 186 | //notices 187 | let nc = read_int(data, seek).to_usize().unwrap(); //notes count 188 | if nc > 0 { for i in 0..nc { self.notice.push(read_int_byte_size_string(data, seek)); println!(" {}\t\t{}",i, self.notice[self.notice.len()-1]); }} 189 | } 190 | 191 | /*pub const _MAX_STRINGS: i32 = 25; 192 | pub const _MIN_STRINGS: i32 = 1; 193 | pub const _MAX_OFFSET: i32 = 24; 194 | pub const _MIN_OFFSET: i32 = -24;*/ 195 | 196 | /// Write data to a Vec, you are free to use the encoded data to write it in a file or in a database or do something else. 197 | pub fn write(&self, version: (u8,u8,u8), clipboard: Option) ->Vec { 198 | let mut data: Vec = Vec::with_capacity(8388608); //capacity of 8MB, should be sufficient 199 | write_version(&mut data, version); 200 | if clipboard.is_some() && clipboard.unwrap() && version.0 >= 4 {self.write_clipboard(&mut data, &version);} 201 | self.write_info(&mut data, version); 202 | if version.0 < 5 {write_bool(&mut data, self.triplet_feel != TripletFeel::None);} 203 | if version.0 >= 4 {self.write_lyrics(&mut data);} 204 | if version > (5,0,0) {self.write_rse_master_effect(&mut data);} 205 | if version.0 >= 5 { 206 | self.write_page_setup(&mut data); 207 | write_int_byte_size_string(&mut data, &self.tempo_name); 208 | } 209 | write_i32(&mut data, self.tempo.to_i32().unwrap()); 210 | if version > (5,0,0) {write_bool(&mut data, self.hide_tempo);} 211 | write_i32(&mut data, self.key.key.to_i32().unwrap()); 212 | 213 | if version.0 >= 4 {write_signed_byte(&mut data, 0);} //octave 214 | self.write_midi_channels(&mut data); //TODO: fixme for writing 215 | //return data; 216 | 217 | if version.0 == 5 { 218 | self.write_directions(&mut data); 219 | self.write_master_reverb(&mut data); 220 | } 221 | 222 | write_i32(&mut data, self.tracks[0].measures.len().to_i32().unwrap()); 223 | write_i32(&mut data, self.tracks.len().to_i32().unwrap()); 224 | self.write_measure_headers(&mut data, &version); 225 | self.write_tracks(&mut data, &version); 226 | self.write_measures(&mut data, &version); 227 | write_i32(&mut data, 0); 228 | data 229 | } 230 | fn write_info(&self, data: &mut Vec, version: (u8,u8,u8)) { 231 | write_int_byte_size_string(data, &self.name); 232 | write_int_byte_size_string(data, &self.subtitle); 233 | write_int_byte_size_string(data, &self.artist); 234 | write_int_byte_size_string(data, &self.album); 235 | if version.0 < 5 {write_int_byte_size_string(data, &self.pack_author());} 236 | else { 237 | write_int_byte_size_string(data, &self.words); 238 | write_int_byte_size_string(data, &self.author); 239 | } 240 | write_int_byte_size_string(data, &self.copyright); 241 | write_int_byte_size_string(data, &self.writer); 242 | write_int_byte_size_string(data, &self.instructions); 243 | write_i32(data, self.notice.len().to_i32().unwrap()); 244 | for i in 0..self.notice.len() {write_int_byte_size_string(data, &self.notice[i]);} 245 | } 246 | fn pack_author(&self) -> String { 247 | if !self.words.is_empty() && !self.author.is_empty() { 248 | if self.words != self.author { 249 | let mut s = self.words.clone(); 250 | s.push_str(", "); 251 | s.push_str(&self.author); 252 | s 253 | } else {self.words.clone()} 254 | } else { 255 | let mut s = self.words.clone(); 256 | s.push_str(&self.author); 257 | s 258 | } 259 | } 260 | } -------------------------------------------------------------------------------- /lib/src/mix_table.rs: -------------------------------------------------------------------------------- 1 | use fraction::ToPrimitive; 2 | 3 | use crate::rse::*; 4 | use crate::io::*; 5 | use crate::gp::*; 6 | 7 | /// A mix table item describes a mix parameter, e.g. volume or reverb 8 | #[derive(Debug,Clone,PartialEq,Eq,Default)] 9 | pub struct MixTableItem { 10 | pub value: u8, 11 | pub duration: u8, 12 | pub all_tracks: bool, 13 | } 14 | //impl Default for MixTableItem { fn default() -> Self { MixTableItem { value: 0, duration: 0, all_tracks: false }}} 15 | 16 | const WAH_EFFECT_OFF: i8 = -2; 17 | const WAH_EFFECT_NONE: i8 = -1; 18 | #[derive(Debug,Clone,PartialEq,Eq)] 19 | pub struct WahEffect { 20 | value: i8, 21 | display: bool, 22 | } 23 | impl Default for WahEffect { fn default() -> Self { WahEffect { value: WAH_EFFECT_NONE, display: false }}} 24 | impl WahEffect { 25 | pub(crate) fn _check_value(value: i8) { 26 | if !(WAH_EFFECT_OFF..=100).contains(&value) {panic!("Value for a wah effect must be in range from -2 to 100")} 27 | } 28 | pub(crate) fn _is_on(&self) -> bool {self.value <= 0 && self.value <= 100} 29 | pub(crate) fn _is_off(&self) -> bool {self.value == WAH_EFFECT_OFF} 30 | pub(crate) fn _is_none(&self) -> bool {self.value == WAH_EFFECT_NONE} 31 | } 32 | 33 | /// A MixTableChange describes a change in mix parameters 34 | #[derive(Debug,Clone,PartialEq,Eq)] 35 | pub struct MixTableChange { 36 | pub instrument: Option, 37 | pub rse: RseInstrument, 38 | pub volume: Option, 39 | pub balance: Option, 40 | pub chorus: Option, 41 | pub reverb: Option, 42 | pub phaser: Option, 43 | pub tremolo: Option, 44 | pub tempo_name: String, 45 | pub tempo: Option, 46 | pub hide_tempo: bool, 47 | pub wah: Option, 48 | pub use_rse: bool, 49 | } 50 | impl Default for MixTableChange { fn default() -> Self { MixTableChange { instrument:None, rse:RseInstrument::default(), volume:None, balance:None, chorus:None, reverb:None, phaser:None, tremolo:None, 51 | tempo_name:String::new(), tempo:None, hide_tempo:true, wah:None, use_rse:false, 52 | }}} 53 | impl MixTableChange { 54 | pub(crate) fn is_just_wah(&self) -> bool { 55 | self.instrument.is_none() && self.volume.is_none() && self.balance.is_none() && self.chorus.is_none() && self.reverb.is_none() && self.phaser.is_none() && self.tremolo.is_none() && self.tempo.is_none() && self.wah.is_none() 56 | } 57 | } 58 | 59 | impl Song { 60 | /// Read mix table change. List of values is read first. See `read_values()`. 61 | /// 62 | /// List of values is followed by the list of durations for parameters that have changed. See `read_durations()`. 63 | /// 64 | /// Mix table change in Guitar Pro 4 format extends Guitar Pro 3 format. It constists of `values `, 65 | /// `durations `, and, new to GP3, `flags `. 66 | /// 67 | /// Mix table change was modified to support RSE instruments. It is read as in Guitar Pro 3 and is followed by: 68 | /// - Wah effect. See :meth:`read_wah_effect()`. 69 | /// - RSE instrument effect. See :meth:`read_rse_instrument_effect()`. 70 | pub(crate) fn read_mix_table_change(&mut self, data: &[u8], seek: &mut usize) -> MixTableChange { 71 | let mut tc = MixTableChange::default(); 72 | self.read_mix_table_change_values(data, seek, &mut tc); 73 | self.read_mix_table_change_durations(data, seek, &mut tc); 74 | //println!("read_mix_table_change()"); 75 | if self.version.number >= (4,0,0) { 76 | let flags = self.read_mix_table_change_flags(data, seek, &mut tc); 77 | if self.version.number >= (5,0,0) { 78 | tc.wah = Some(self.read_wah_effect(data, seek, flags)); 79 | self.read_rse_instrument_effect(data, seek, &mut tc.rse); 80 | } 81 | } 82 | tc 83 | } 84 | /// Read mix table change values. Mix table change values consist of 7 `signed-byte` and an `int`, which correspond to: 85 | /// - instrument 86 | /// - RSE instrument. See `read_rse_instrument()` (GP5). 87 | /// - volume 88 | /// - balance 89 | /// - chorus 90 | /// - reverb 91 | /// - phaser 92 | /// - tremolo 93 | /// - Tempo name: `int-byte-size-string` (GP5). 94 | /// - tempo 95 | /// 96 | /// If signed byte is *-1* then corresponding parameter hasn't changed. 97 | fn read_mix_table_change_values(&mut self, data: &[u8], seek: &mut usize, mtc: &mut MixTableChange) { 98 | //instrument 99 | let b = read_signed_byte(data, seek); 100 | if b >= 0 {mtc.instrument = Some(MixTableItem{value: b.to_u8().unwrap(), ..Default::default()});} 101 | //RSE instrument GP5 102 | if self.version.number.0 == 5 {mtc.rse = self.read_rse_instrument(data, seek);} 103 | if self.version.number == (5,0,0) { *seek += 1; } 104 | //volume 105 | let b = read_signed_byte(data, seek); 106 | if b >= 0 {mtc.volume = Some(MixTableItem{value: b.to_u8().unwrap(), ..Default::default()});} 107 | //balance 108 | let b = read_signed_byte(data, seek); 109 | if b >= 0 {mtc.balance = Some(MixTableItem{value: b.to_u8().unwrap(), ..Default::default()});} 110 | //chorus 111 | let b = read_signed_byte(data, seek); 112 | if b >= 0 {mtc.chorus = Some(MixTableItem{value: b.to_u8().unwrap(), ..Default::default()});} 113 | //reverb 114 | let b = read_signed_byte(data, seek); 115 | if b >= 0 {mtc.reverb = Some(MixTableItem{value: b.to_u8().unwrap(), ..Default::default()});} 116 | //phaser 117 | let b = read_signed_byte(data, seek); 118 | if b >= 0 {mtc.phaser = Some(MixTableItem{value: b.to_u8().unwrap(), ..Default::default()});} 119 | //tremolo 120 | let b = read_signed_byte(data, seek); 121 | if b >= 0 {mtc.tremolo = Some(MixTableItem{value: b.to_u8().unwrap(), ..Default::default()});} 122 | //tempo 123 | if self.version.number >= (5,0,0) {mtc.tempo_name = read_int_byte_size_string(data, seek);} 124 | let b = read_int(data, seek); 125 | if b >= 0 {mtc.tempo = Some(MixTableItem{value: b.to_u8().unwrap(), ..Default::default()});} 126 | } 127 | /// Read mix table change durations. Durations are read for each non-null `MixTableItem`. Durations are encoded in `signed-byte`. 128 | /// 129 | /// If tempo did change, then one :ref:`bool` is read. If it's true, then tempo change won't be displayed on the score. 130 | fn read_mix_table_change_durations(&self, data: &[u8], seek: &mut usize, mtc: &mut MixTableChange) { 131 | if mtc.volume.is_some() {mtc.volume.take().unwrap().duration = read_signed_byte(data, seek).to_u8().unwrap();} 132 | if mtc.balance.is_some() {mtc.balance.take().unwrap().duration = read_signed_byte(data, seek).to_u8().unwrap();} 133 | if mtc.chorus.is_some() {mtc.chorus.take().unwrap().duration = read_signed_byte(data, seek).to_u8().unwrap();} 134 | if mtc.reverb.is_some() {mtc.reverb.take().unwrap().duration = read_signed_byte(data, seek).to_u8().unwrap();} 135 | if mtc.phaser.is_some() {mtc.phaser.take().unwrap().duration = read_signed_byte(data, seek).to_u8().unwrap();} 136 | if mtc.tremolo.is_some() {mtc.tremolo.take().unwrap().duration = read_signed_byte(data, seek).to_u8().unwrap();} 137 | if mtc.tempo.is_some() { 138 | let mut t = mtc.tempo.take().unwrap(); 139 | t.duration = read_signed_byte(data, seek).to_u8().unwrap(); 140 | mtc.tempo = Some(t); 141 | mtc.hide_tempo = false; 142 | if self.version.number >= (5,0,0) {mtc.hide_tempo = read_bool(data, seek);} 143 | } 144 | } 145 | 146 | /// Read mix table change flags (Guitar Pro 4). The meaning of flags: 147 | /// - *0x01*: change volume for all tracks 148 | /// - *0x02*: change balance for all tracks 149 | /// - *0x04*: change chorus for all tracks 150 | /// - *0x08*: change reverb for all tracks 151 | /// - *0x10*: change phaser for all tracks 152 | /// - *0x20*: change tremolo for all tracks 153 | /// 154 | /// In GP5, there is one additional flag: 155 | /// - *0x40*: use RSE 156 | /// - *0x80*: show wah-wah 157 | fn read_mix_table_change_flags(&self, data: &[u8], seek: &mut usize, mtc: &mut MixTableChange) -> i8 { 158 | let flags = read_signed_byte(data, seek); 159 | //println!("read_mix_table_change_flags(), flags: {}", flags); 160 | if mtc.volume.is_some() { 161 | let mut e = mtc.volume.take().unwrap(); 162 | e.all_tracks = (flags & 0x01) == 0x01; 163 | mtc.volume = Some(e); 164 | } 165 | if mtc.balance.is_some() { 166 | let mut e = mtc.balance.take().unwrap(); 167 | e.all_tracks = (flags & 0x01) == 0x01; 168 | mtc.balance = Some(e); 169 | } 170 | if mtc.chorus.is_some() { 171 | let mut e = mtc.chorus.take().unwrap(); 172 | e.all_tracks = (flags & 0x01) == 0x01; 173 | mtc.chorus = Some(e); 174 | } 175 | if mtc.reverb.is_some() { 176 | let mut e = mtc.reverb.take().unwrap(); 177 | e.all_tracks = (flags & 0x01) == 0x01; 178 | mtc.reverb = Some(e); 179 | } 180 | if mtc.phaser.is_some() { 181 | let mut e = mtc.phaser.take().unwrap(); 182 | e.all_tracks = (flags & 0x01) == 0x01; 183 | mtc.phaser = Some(e); 184 | } 185 | if mtc.tremolo.is_some() { 186 | let mut e = mtc.tremolo.take().unwrap(); 187 | e.all_tracks = (flags & 0x01) == 0x01; 188 | mtc.tremolo = Some(e); 189 | } 190 | if self.version.number >= (5,0,0) {mtc.use_rse = (flags & 0x40) == 0x40;} 191 | flags 192 | } 193 | 194 | /// Read wah-wah. 195 | /// - Wah value: :ref:`signed-byte`. See `WahEffect` for value mapping. 196 | fn read_wah_effect(&self, data: &[u8], seek: &mut usize, flags: i8) -> WahEffect {WahEffect{value: read_signed_byte(data, seek), display: (flags & -0x80) == -0x80 /*(flags & 0x80) == 0x80*/}} 197 | 198 | pub(crate) fn write_mix_table_change(&self, data: &mut Vec, mix_table_change: &Option, version: &(u8,u8,u8)) { 199 | if let Some(mtc) = mix_table_change { 200 | self.write_mix_table_change_values(data, mtc, version); 201 | self.write_mix_table_change_durations(data, mtc, version); 202 | if version.0 == 4 {self.write_mix_table_change_flags_v4(data, mtc);} 203 | if version.0 == 5 { 204 | self.write_mix_table_change_flags_v5(data, mtc); 205 | if let Some(w) = &mtc.wah {write_signed_byte(data, w.value);} else {write_signed_byte(data, WAH_EFFECT_NONE);} //write wah effect 206 | self.write_rse_instrument_effect(data, &mtc.rse); 207 | } 208 | } 209 | } 210 | fn write_mix_table_change_values(&self, data: &mut Vec, mix_table_change: &MixTableChange, version: &(u8,u8,u8)) { 211 | //instrument 212 | if let Some(i) = &mix_table_change.instrument {write_signed_byte(data, i.value.to_i8().unwrap());} 213 | else {write_signed_byte(data, -1);} 214 | if version.0 >= 5 {self.write_rse_instrument(data, &mix_table_change.rse, version);} 215 | if version == &(5,0,0) {write_placeholder_default(data, 1);} 216 | //volume 217 | if let Some(i) = &mix_table_change.volume {write_signed_byte(data, i.value.to_i8().unwrap());} 218 | else {write_signed_byte(data, -1);} 219 | //balance 220 | if let Some(i) = &mix_table_change.balance {write_signed_byte(data, i.value.to_i8().unwrap());} 221 | else {write_signed_byte(data, -1);} 222 | //chorus 223 | if let Some(i) = &mix_table_change.chorus {write_signed_byte(data, i.value.to_i8().unwrap());} 224 | else {write_signed_byte(data, -1);} 225 | //reverb 226 | if let Some(i) = &mix_table_change.reverb {write_signed_byte(data, i.value.to_i8().unwrap());} 227 | else {write_signed_byte(data, -1);} 228 | //phaser 229 | if let Some(i) = &mix_table_change.phaser {write_signed_byte(data, i.value.to_i8().unwrap());} 230 | else {write_signed_byte(data, -1);} 231 | //tremolo 232 | if let Some(i) = &mix_table_change.tremolo {write_signed_byte(data, i.value.to_i8().unwrap());} 233 | else {write_signed_byte(data, -1);} 234 | //tempo 235 | if let Some(i) = &mix_table_change.tempo {write_signed_byte(data, i.value.to_i8().unwrap());} 236 | else {write_signed_byte(data, -1);} 237 | if version.0 >= 5 { 238 | write_int_byte_size_string(data, &mix_table_change.tempo_name); 239 | if let Some(t) = &mix_table_change.tempo {write_i32(data, t.value.to_i32().unwrap());} 240 | else {write_i32(data, -1);} 241 | } 242 | } 243 | fn write_mix_table_change_durations(&self, data: &mut Vec, mix_table_change: &MixTableChange, version: &(u8,u8,u8)) { 244 | //volume 245 | if let Some(i) = &mix_table_change.volume {write_signed_byte(data, i.duration.to_i8().unwrap());} 246 | else {write_signed_byte(data, -1);} 247 | //balance 248 | if let Some(i) = &mix_table_change.balance {write_signed_byte(data, i.duration.to_i8().unwrap());} 249 | else {write_signed_byte(data, -1);} 250 | //chorus 251 | if let Some(i) = &mix_table_change.chorus {write_signed_byte(data, i.duration.to_i8().unwrap());} 252 | else {write_signed_byte(data, -1);} 253 | //reverb 254 | if let Some(i) = &mix_table_change.reverb {write_signed_byte(data, i.duration.to_i8().unwrap());} 255 | else {write_signed_byte(data, -1);} 256 | //phaser 257 | if let Some(i) = &mix_table_change.phaser {write_signed_byte(data, i.duration.to_i8().unwrap());} 258 | else {write_signed_byte(data, -1);} 259 | //tremolo 260 | if let Some(i) = &mix_table_change.tremolo {write_signed_byte(data, i.duration.to_i8().unwrap());} 261 | else {write_signed_byte(data, -1);} 262 | //tempo 263 | if let Some(i) = &mix_table_change.tempo { 264 | write_signed_byte(data, i.duration.to_i8().unwrap()); 265 | if version > &(5,0,0) {write_bool(data, mix_table_change.hide_tempo);} 266 | } else {write_signed_byte(data, -1);} 267 | } 268 | fn write_mix_table_change_flags_v4(&self, data: &mut Vec, mix_table_change: &MixTableChange) { 269 | let mut flags = 0i8; 270 | if let Some(i) = &mix_table_change.volume {if i.all_tracks {flags |= 0x01;}} 271 | if let Some(i) = &mix_table_change.balance {if i.all_tracks {flags |= 0x02;}} 272 | if let Some(i) = &mix_table_change.chorus {if i.all_tracks {flags |= 0x04;}} 273 | if let Some(i) = &mix_table_change.reverb {if i.all_tracks {flags |= 0x08;}} 274 | if let Some(i) = &mix_table_change.phaser {if i.all_tracks {flags |= 0x10;}} 275 | if let Some(i) = &mix_table_change.tremolo {if i.all_tracks {flags |= 0x20;}} 276 | write_signed_byte(data, flags); 277 | } 278 | fn write_mix_table_change_flags_v5(&self, data: &mut Vec, mix_table_change: &MixTableChange) { 279 | let mut flags = 0u8; 280 | if let Some(i) = &mix_table_change.volume {if i.all_tracks {flags |= 0x01;}} 281 | if let Some(i) = &mix_table_change.balance {if i.all_tracks {flags |= 0x02;}} 282 | if let Some(i) = &mix_table_change.chorus {if i.all_tracks {flags |= 0x04;}} 283 | if let Some(i) = &mix_table_change.reverb {if i.all_tracks {flags |= 0x08;}} 284 | if let Some(i) = &mix_table_change.phaser {if i.all_tracks {flags |= 0x10;}} 285 | if let Some(i) = &mix_table_change.tremolo {if i.all_tracks {flags |= 0x20;}} 286 | if mix_table_change.use_rse {flags |= 0x40;} 287 | if let Some(w) = &mix_table_change.wah {if w.display {flags |= 0x80;}} 288 | write_byte(data, flags); 289 | } 290 | } -------------------------------------------------------------------------------- /lib/src/track.rs: -------------------------------------------------------------------------------- 1 | use fraction::ToPrimitive; 2 | 3 | use crate::{io::*, gp::*, enums::*, rse::*, measure::*}; 4 | 5 | /// Settings of the track. 6 | #[derive(Debug,Clone)] 7 | pub struct TrackSettings { 8 | pub tablature: bool, 9 | pub notation: bool, 10 | pub diagram_are_below: bool, 11 | pub show_rythm: bool, 12 | pub force_horizontal: bool, 13 | pub force_channels: bool, 14 | pub diagram_list: bool, 15 | pub diagram_in_score: bool, 16 | pub auto_let_ring: bool, 17 | pub auto_brush: bool, 18 | pub extend_rythmic: bool, 19 | } 20 | impl Default for TrackSettings { fn default() -> Self { TrackSettings { 21 | tablature: true, 22 | notation: true, 23 | diagram_are_below: false, 24 | show_rythm: false, 25 | force_horizontal: false, 26 | force_channels: false, 27 | diagram_list: true, 28 | diagram_in_score: false, 29 | auto_let_ring: false, 30 | auto_brush: false, 31 | extend_rythmic: false, 32 | }}} 33 | 34 | 35 | #[derive(Debug,Clone)] 36 | pub struct Track { 37 | pub number: i32, 38 | pub offset: i32, 39 | pub channel_index: usize, //pub channel_id: i32, 40 | pub solo: bool, 41 | pub mute: bool, 42 | pub visible: bool, 43 | pub name: String, 44 | /// A guitar string with a special tuning. 45 | pub strings: Vec<(i8, i8)>, 46 | pub color: i32, 47 | pub percussion_track: bool, 48 | pub twelve_stringed_guitar_track: bool, 49 | pub banjo_track: bool, 50 | pub port: u8, 51 | pub fret_count: u8, 52 | pub indicate_tuning: bool, 53 | pub use_rse: bool, 54 | pub rse: TrackRse, 55 | pub measures: Vec, 56 | pub settings: TrackSettings, 57 | } 58 | impl Default for Track { 59 | fn default() -> Self { Track { 60 | number: 1, 61 | offset: 0, 62 | channel_index: 0, //channel_id: 25, 63 | solo: false, mute: false, visible: true, 64 | name: String::from("Track 1"), 65 | strings: vec![(1, 64), (2, 59), (3, 55), (4, 50), (5, 45), (6, 40)], 66 | banjo_track: false, twelve_stringed_guitar_track: false, percussion_track: false, 67 | fret_count: 24, 68 | color: 0xff0000, 69 | port: 1, 70 | indicate_tuning: false, 71 | use_rse: false, rse: TrackRse::default(), 72 | measures: Vec::new(), 73 | settings: TrackSettings::default(), 74 | }} 75 | } 76 | impl Song { 77 | /// Read tracks. The tracks are written one after another, their number having been specified previously in :meth:`GP3File.readSong`. 78 | /// - `track_count`: number of tracks to expect. 79 | pub(crate) fn read_tracks(&mut self, data: &[u8], seek: &mut usize, track_count: usize) { 80 | //println!("read_tracks()"); 81 | for i in 0..track_count {self.read_track(data, seek, i);} 82 | } 83 | 84 | pub(crate) fn read_tracks_v5(&mut self, data: &[u8], seek: &mut usize, track_count: usize) { 85 | //println!("read_tracks_v5(): {:?} {}", self.version.number, self.version.number == (5,1,0)); 86 | for i in 0..track_count { self.read_track_v5(data, seek, i); } 87 | *seek += if self.version.number == (5,0,0) {2} else {1}; 88 | } 89 | 90 | /// Read a track. The first byte is the track's flags. It presides the track's attributes: 91 | /// 92 | /// | **bit 7 to 3** | **bit 2** | **bit 1** | **bit 0** | 93 | /// |----------------|-------------|--------------------------|-------------| 94 | /// | Blank bits | Banjo track | 12 stringed guitar track | Drums track | 95 | /// 96 | /// Flags are followed by: 97 | /// 98 | /// * **Name**: `string`. A 40 characters long string containing the track's name. 99 | /// * **Number of strings**: `integer`. An integer equal to the number of strings of the track. 100 | /// * **Tuning of the strings**: Table of integers. The tuning of the strings is stored as a 7-integers table, the "Number of strings" first integers being really used. The strings are stored from the highest to the lowest. 101 | /// * **Port**: `integer`. The number of the MIDI port used. 102 | /// * **Channel**: `integer`. The number of the MIDI channel used. The channel 10 is the drums channel. 103 | /// * **ChannelE**: `integer`. The number of the MIDI channel used for effects. 104 | /// * **Number of frets**: `integer`. The number of frets of the instrument. 105 | /// * **Height of the capo**: `integer`. The number of the fret on which a capo is present. If no capo is used, the value is `0x00000000`. 106 | /// * **Track's color**: `color`. The track's displayed color in Guitar Pro. 107 | fn read_track(&mut self, data: &[u8], seek: &mut usize, number: usize) { 108 | let mut track = Track{number: number.to_i32().unwrap(), ..Default::default()}; 109 | //read the flag 110 | let flags = read_byte(data, seek); 111 | //println!("read_track(), flags: {}", flags); 112 | track.percussion_track = (flags & 0x01) == 0x01; //Drums track 113 | track.twelve_stringed_guitar_track = (flags & 0x02) == 0x02; //12 stringed guitar track 114 | track.banjo_track = (flags & 0x04) == 0x04; //Banjo track 115 | 116 | track.name = read_byte_size_string(data, seek, 40); 117 | let string_count = read_int(data, seek).to_u8().unwrap(); 118 | track.strings.clear(); 119 | for i in 0..7i8 { 120 | let i_tuning = read_int(data, seek).to_i8().unwrap(); 121 | if string_count.to_i8().unwrap() > i { track.strings.push((i + 1, i_tuning)); } 122 | } 123 | //println!("tuning: {:?}", track.strings); 124 | track.port = read_int(data, seek).to_u8().unwrap(); 125 | let index = self.read_channel(data, seek); 126 | if self.channels[index].channel == 9 {track.percussion_track = true;} 127 | track.fret_count = read_int(data, seek).to_u8().unwrap(); 128 | track.offset = read_int(data, seek); 129 | track.color = read_color(data, seek); 130 | println!("\tInstrument: {} \t Strings: {}/{} ({:?})", self.channels[index].get_instrument_name(), string_count, track.strings.len(), track.strings); 131 | self.tracks.push(track); 132 | } 133 | 134 | /// Read track. If it's Guitar Pro 5.0 format and track is first then one blank byte is read. Then go track's flags. It presides the track's attributes: 135 | /// - *0x01*: drums track 136 | /// - *0x02*: 12 stringed guitar track 137 | /// - *0x04*: banjo track 138 | /// - *0x08*: track visibility 139 | /// - *0x10*: track is soloed 140 | /// - *0x20*: track is muted 141 | /// - *0x40*: RSE is enabled 142 | /// - *0x80*: show tuning in the header of the sheet. 143 | /// 144 | /// Flags are followed by: 145 | /// - Name: `String`. A 40 characters long string containing the track's name. 146 | /// - Number of strings: :ref:`int`. An integer equal to the number of strings of the track. 147 | /// - Tuning of the strings: `Table of integers`. The tuning of the strings is stored as a 7-integers table, the "Number of strings" first integers being really used. The strings are stored from the highest to the lowest. 148 | /// - Port: :ref:`int`. The number of the MIDI port used. 149 | /// - Channel. See `GP3File.readChannel`. 150 | /// - Number of frets: :ref:`int`. The number of frets of the instrument. 151 | /// - Height of the capo: :ref:`int`. The number of the fret on which a capo is set. If no capo is used, the value is 0. 152 | /// - Track's color. The track's displayed color in Guitar Pro. 153 | /// 154 | /// The properties are followed by second set of flags stored in a :ref:`short`: 155 | /// - *0x0001*: show tablature 156 | /// - *0x0002*: show standard notation 157 | /// - *0x0004*: chord diagrams are below standard notation 158 | /// - *0x0008*: show rhythm with tab 159 | /// - *0x0010*: force horizontal beams 160 | /// - *0x0020*: force channels 11 to 16 161 | /// - *0x0040*: diagram list on top of the score 162 | /// - *0x0080*: diagrams in the score 163 | /// - *0x0200*: auto let-ring 164 | /// - *0x0400*: auto brush 165 | /// - *0x0800*: extend rhythmic inside the tab 166 | /// 167 | /// Then follow: 168 | /// - Auto accentuation: :ref:`byte`. See :class:`guitarpro.models.Accentuation`. 169 | /// - MIDI bank: :ref:`byte`. 170 | /// - Track RSE. See `readTrackRSE`. 171 | fn read_track_v5(&mut self, data: &[u8], seek: &mut usize, number: usize) { 172 | let mut track = Track{number: number.to_i32().unwrap(), ..Default::default()}; 173 | if number == 0 || self.version.number == (5,0,0) {*seek += 1;} //always 0 //missing 3 skips? 174 | let flags1 = read_byte(data, seek); 175 | //println!("read_track_v5(), flags1: {} \t seek: {}", flags1, *seek); 176 | track.percussion_track = (flags1 & 0x01) == 0x01; 177 | track.banjo_track = (flags1 & 0x02) == 0x02; 178 | track.visible = (flags1 & 0x04) == 0x04; 179 | track.solo = (flags1 & 0x10) == 0x10; 180 | track.mute = (flags1 & 0x20) == 0x20; 181 | track.use_rse = (flags1 & 0x40) == 0x40; 182 | track.indicate_tuning = (flags1 & 0x80) == 0x80; 183 | track.name = read_byte_size_string(data, seek, 40); 184 | //let string_count = read_int(data, seek).to_u8().unwrap(); 185 | let sc = read_int(data, seek); 186 | //println!("read_track_v5(), track:name: \"{}\", string count: {}", track.name, sc); 187 | let string_count = sc.to_u8().unwrap(); 188 | track.strings.clear(); 189 | for i in 0i8..7i8 { 190 | let i_tuning = read_int(data, seek).to_i8().unwrap(); 191 | if string_count.to_i8().unwrap() > i { track.strings.push((i + 1, i_tuning)); } 192 | } 193 | track.port = read_int(data, seek).to_u8().unwrap(); 194 | self.read_channel(data, seek); 195 | if self.channels[number].channel == 9 {track.percussion_track = true;} 196 | track.fret_count = read_int(data, seek).to_u8().unwrap(); 197 | track.offset = read_int(data, seek); 198 | track.color = read_color(data, seek); 199 | 200 | let flags2 = read_short(data, seek); 201 | //println!("read_track_v5(), flags2: {}", flags2); 202 | track.settings.tablature = (flags2 & 0x0001) == 0x0001; 203 | track.settings.notation = (flags2 & 0x0002) == 0x0002; 204 | track.settings.diagram_are_below = (flags2 & 0x0004) == 0x0004; 205 | track.settings.show_rythm = (flags2 & 0x0008) == 0x0008; 206 | track.settings.force_horizontal = (flags2 & 0x0010) == 0x0010; 207 | track.settings.force_channels = (flags2 & 0x0020) == 0x0020; 208 | track.settings.diagram_list = (flags2 & 0x0040) == 0x0040; 209 | track.settings.diagram_in_score = (flags2 & 0x0080) == 0x0080; 210 | //0x0100 ??? 211 | track.settings.auto_let_ring = (flags2 & 0x0200) == 0x0200; 212 | track.settings.auto_brush = (flags2 & 0x0400) == 0x0400; 213 | track.settings.extend_rythmic = (flags2 & 0x0800) == 0x0800; 214 | 215 | track.rse.auto_accentuation = get_accentuation(read_byte(data, seek)); 216 | self.channels[number].bank = read_byte(data, seek); 217 | self.read_track_rse(data, seek, &mut track); 218 | self.tracks.push(track); 219 | } 220 | 221 | pub(crate) fn write_tracks(&self, data: &mut Vec, version: &(u8,u8,u8)) { 222 | for i in 0..self.tracks.len() { 223 | //self.current_track = Some(i); 224 | if version.0 < 5 {self.write_track(data, i);} 225 | else {self.write_track_v5(data, i, version);} 226 | } 227 | if version.0 == 5 {write_placeholder_default(data, if version == &(5,0,0) {2} else {1});} 228 | //self.current_track = None; 229 | } 230 | fn write_track(&self, data: &mut Vec, number: usize) { 231 | let mut flags = 0x00; 232 | if self.tracks[number].percussion_track {flags |= 0x01;} 233 | if self.tracks[number].twelve_stringed_guitar_track {flags |= 0x02;} 234 | if self.tracks[number].banjo_track {flags |= 0x04;} 235 | write_byte(data, flags); 236 | write_byte_size_string(data, &self.tracks[number].name); 237 | write_placeholder_default(data, 30 - self.tracks[number].name.len()); 238 | write_i32(data, self.tracks[number].strings.len().to_i32().unwrap()); 239 | for i in 0..7usize { 240 | let mut tuning = 0i8; 241 | if i < self.tracks[number].strings.len() { tuning = self.tracks[number].strings[i].1;} 242 | write_i32(data, tuning.to_i32().unwrap()); 243 | } 244 | write_i32(data, self.tracks[number].port.to_i32().unwrap()); 245 | //write channel 246 | write_i32(data, self.channels[self.tracks[number].channel_index].channel.to_i32().unwrap() + 1); 247 | write_i32(data, self.channels[self.tracks[number].channel_index].effect_channel.to_i32().unwrap() + 1); 248 | //end write channel 249 | write_i32(data, self.tracks[number].fret_count.to_i32().unwrap()); 250 | write_i32(data, self.tracks[number].offset); 251 | write_color(data, self.tracks[number].color); 252 | } 253 | fn write_track_v5(&self, data: &mut Vec, number: usize, version: &(u8,u8,u8)) { 254 | if number == 1 || version == &(5,0,0) {write_placeholder_default(data, 1);} 255 | let mut flags1 = 0u8; 256 | if self.tracks[number].percussion_track {flags1 |= 0x01;} 257 | if self.tracks[number].twelve_stringed_guitar_track {flags1 |= 0x02;} 258 | if self.tracks[number].banjo_track {flags1 |= 0x04;} 259 | if self.tracks[number].visible {flags1 |= 0x08;} 260 | if self.tracks[number].solo {flags1 |= 0x10;} 261 | if self.tracks[number].mute {flags1 |= 0x20;} 262 | if self.tracks[number].use_rse {flags1 |= 0x40;} 263 | if self.tracks[number].indicate_tuning {flags1 |= 0x80;} 264 | write_byte(data, flags1); 265 | 266 | write_byte_size_string(data, &self.tracks[number].name); 267 | write_placeholder_default(data, 40 - self.tracks[number].name.len()); 268 | 269 | write_i32(data, self.tracks[number].strings.len().to_i32().unwrap()); 270 | for i in 0..7usize { 271 | let mut tuning = 0i8; 272 | if i < self.tracks[number].strings.len() { tuning = self.tracks[number].strings[i].1;} 273 | write_i32(data, tuning.to_i32().unwrap()); 274 | } 275 | write_i32(data, self.tracks[number].port.to_i32().unwrap()); 276 | //write channel 277 | write_i32(data, self.channels[self.tracks[number].channel_index].channel.to_i32().unwrap() + 1); 278 | write_i32(data, self.channels[self.tracks[number].channel_index].effect_channel.to_i32().unwrap() + 1); 279 | //end write channel 280 | write_i32(data, self.tracks[number].fret_count.to_i32().unwrap()); 281 | write_i32(data, self.tracks[number].offset); 282 | write_color(data, self.tracks[number].color); 283 | 284 | let mut flags2 = 0i16; 285 | if self.tracks[number].settings.tablature {flags2 |= 0x0001;} 286 | if self.tracks[number].settings.notation {flags2 |= 0x0002;} 287 | if self.tracks[number].settings.diagram_are_below {flags2 |= 0x0004;} 288 | if self.tracks[number].settings.show_rythm {flags2 |= 0x0008;} 289 | if self.tracks[number].settings.force_horizontal {flags2 |= 0x0010;} 290 | if self.tracks[number].settings.force_channels {flags2 |= 0x0020;} 291 | if self.tracks[number].settings.diagram_list {flags2 |= 0x0040;} 292 | if self.tracks[number].settings.diagram_in_score {flags2 |= 0x0080;} 293 | if self.tracks[number].settings.auto_let_ring {flags2 |= 0x0200;} 294 | if self.tracks[number].settings.auto_brush {flags2 |= 0x0400;} 295 | if self.tracks[number].settings.extend_rythmic {flags2 |= 0x0800;} 296 | write_i16(data, flags2); 297 | 298 | write_byte(data, from_accentuation(&self.tracks[number].rse.auto_accentuation)); 299 | write_byte(data, self.channels[self.tracks[number].channel_index].bank); 300 | self.write_track_rse(data, &self.tracks[number].rse, version); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /lib/src/enums.rs: -------------------------------------------------------------------------------- 1 | 2 | /// An enumeration of different triplet feels. 3 | #[repr(u8)] 4 | #[derive(Debug,Clone,PartialEq,Eq)] 5 | pub enum TripletFeel { None, Eighth, Sixteenth } 6 | pub(crate) fn get_triplet_feel(value: i8) -> TripletFeel { 7 | match value { 8 | 0 => TripletFeel::None, 9 | 1 => TripletFeel::Eighth, 10 | 2 => TripletFeel::Sixteenth, 11 | _ => panic!("Invalid triplet feel"), 12 | } 13 | } 14 | pub(crate) fn from_triplet_feel(value: &TripletFeel) -> u8 { 15 | match value { 16 | TripletFeel::None => 0, 17 | TripletFeel::Eighth => 1, 18 | TripletFeel::Sixteenth => 2, 19 | } 20 | } 21 | 22 | /// An enumeration of available clefs 23 | #[allow(dead_code)] 24 | #[repr(u8)] 25 | #[derive(Debug,Clone)] 26 | pub enum MeasureClef { Treble, Bass, Tenor, Alto } 27 | /// A line break directive: `NONE: no line break`, `BREAK: break line`, `Protect the line from breaking`. 28 | #[repr(u8)] 29 | #[derive(Debug,Clone)] 30 | pub enum LineBreak { None, Break, Protect } 31 | pub(crate) fn get_line_break(value: u8) -> LineBreak { 32 | match value { 33 | 1 => LineBreak::Break, 34 | 2 => LineBreak::Protect, 35 | _ => LineBreak::None, 36 | } 37 | } 38 | pub(crate) fn from_line_break(value: &LineBreak) -> u8 { 39 | match value { 40 | LineBreak::None => 0, 41 | LineBreak::Break => 1, 42 | LineBreak::Protect => 2, 43 | } 44 | } 45 | 46 | /// An enumeration of all supported slide types. 47 | #[repr(i8)] 48 | #[derive(Debug,Clone,PartialEq,Eq)] 49 | pub enum SlideType { 50 | IntoFromAbove = -2, //-2 51 | IntoFromBelow = -1, //-1 52 | None = 0, //0 53 | ShiftSlideTo, 54 | LegatoSlideTo, 55 | OutDownwards, 56 | OutUpWards 57 | } 58 | pub(crate) fn get_slide_type(value: i8) -> SlideType { 59 | match value { 60 | -2 => SlideType::IntoFromAbove, 61 | -1 => SlideType::IntoFromBelow, 62 | 0 => SlideType::None, 63 | 1 => SlideType::ShiftSlideTo, 64 | 2 => SlideType::LegatoSlideTo, 65 | 3 => SlideType::OutDownwards, 66 | 4 => SlideType::OutUpWards, 67 | _ => panic!("Invalid slide type"), 68 | } 69 | } 70 | pub(crate) fn from_slide_type(value: &SlideType) -> i8 { 71 | match value { 72 | SlideType::IntoFromAbove => -2, 73 | SlideType::IntoFromBelow => -1, 74 | SlideType::None => 0, 75 | SlideType::ShiftSlideTo => 1, 76 | SlideType::LegatoSlideTo => 2, 77 | SlideType::OutDownwards => 3, 78 | SlideType::OutUpWards => 4, 79 | } 80 | } 81 | 82 | /// An enumeration of all supported slide types. 83 | #[repr(u8)] 84 | #[derive(Debug,Clone,PartialEq,Eq)] 85 | pub enum NoteType { 86 | Rest, //0 87 | Normal, Tie, Dead, 88 | Unknown(u8), 89 | } 90 | pub(crate) fn get_note_type(value: u8) -> NoteType { 91 | match value { 92 | 0 => NoteType::Rest, 93 | 1 => NoteType::Normal, 94 | 2 => NoteType::Tie, 95 | 3 => NoteType::Dead, 96 | _ => NoteType::Unknown(value), //panic!("Cannot read note type"), 97 | } 98 | } 99 | pub(crate) fn from_note_type(value: &NoteType) -> u8 { 100 | match value { 101 | NoteType::Rest => 0, 102 | NoteType::Normal => 1, 103 | NoteType::Tie => 2, 104 | NoteType::Dead => 3, 105 | NoteType::Unknown(value) => *value, //panic!("Cannot read note type"), 106 | } 107 | } 108 | 109 | #[repr(u8)] 110 | #[derive(Debug,Clone,PartialEq,Eq)] 111 | pub enum BeatStatus {Empty, Normal, Rest} 112 | pub(crate) fn get_beat_status(value: u8) -> BeatStatus { 113 | match value { 114 | 0 => BeatStatus::Empty, 115 | 1 => BeatStatus::Normal, 116 | 2 => BeatStatus::Rest, 117 | _ => BeatStatus::Normal, //panic!("Cannot get beat status"), 118 | } 119 | } 120 | pub(crate) fn from_beat_status(value: &BeatStatus) -> u8 { 121 | match value { 122 | BeatStatus::Empty => 0, 123 | BeatStatus::Normal => 1, 124 | BeatStatus::Rest => 2, 125 | } 126 | } 127 | 128 | #[repr(u8)] 129 | #[derive(Debug,Clone,PartialEq,Eq)] 130 | pub enum TupletBracket {None, Start, End} 131 | 132 | /// Octave signs 133 | #[repr(u8)] 134 | #[derive(Debug,Clone,PartialEq,Eq)] 135 | pub enum Octave { None, Ottava, Quindicesima, OttavaBassa, QuindicesimaBassa } 136 | pub(crate) fn get_octave(value: u8) -> Octave { 137 | match value { 138 | 0 => Octave::None, 139 | 1 => Octave::Ottava, 140 | 2 => Octave::Quindicesima, 141 | 3 => Octave::OttavaBassa, 142 | 4 => Octave::QuindicesimaBassa, 143 | _ => panic!("Cannot get octave value"), 144 | } 145 | } 146 | pub(crate) fn from_octave(value: &Octave) -> u8 { 147 | match value { 148 | Octave::None => 0, 149 | Octave::Ottava => 1, 150 | Octave::Quindicesima => 2, 151 | Octave::OttavaBassa => 3, 152 | Octave::QuindicesimaBassa => 4, 153 | } 154 | } 155 | 156 | /// All beat stroke directions 157 | #[repr(u8)] 158 | #[derive(Debug,Clone,PartialEq,Eq)] 159 | pub enum BeatStrokeDirection { None, Up, Down } 160 | pub(crate) fn get_beat_stroke_direction(value: i8) -> BeatStrokeDirection { 161 | match value { 162 | 0 => BeatStrokeDirection::None, 163 | 1 => BeatStrokeDirection::Up, 164 | 2 => BeatStrokeDirection::Down, 165 | _ => panic!("Cannot read beat stroke direction"), 166 | } 167 | } 168 | pub(crate) fn from_beat_stroke_direction(value: &BeatStrokeDirection) -> i8 { 169 | match value { 170 | BeatStrokeDirection::None => 0, 171 | BeatStrokeDirection::Up => 1, 172 | BeatStrokeDirection::Down => 2, 173 | } 174 | } 175 | /// Characteristic of articulation 176 | #[repr(u8)] 177 | #[derive(Debug,Clone,PartialEq,Eq)] 178 | pub enum SlapEffect { None, Tapping, Slapping, Popping } 179 | pub(crate) fn get_slap_effect(value: u8) -> SlapEffect { 180 | match value { 181 | 0 => SlapEffect::None, 182 | 1 => SlapEffect::Tapping, 183 | 2 => SlapEffect::Slapping, 184 | 3 => SlapEffect::Popping, 185 | _ => panic!("Cannot read slap effect for the beat effects"), 186 | } 187 | } 188 | pub(crate) fn from_slap_effect(value: &SlapEffect) -> u8 { 189 | match value { 190 | SlapEffect::None => 0, 191 | SlapEffect::Tapping => 1, 192 | SlapEffect::Slapping => 2, 193 | SlapEffect::Popping => 3, 194 | } 195 | } 196 | 197 | 198 | /// Voice directions indicating the direction of beams 199 | #[repr(u8)] 200 | #[derive(Debug,Clone,PartialEq,Eq)] 201 | pub enum VoiceDirection { None, Up, Down } 202 | 203 | /// Type of the chord. 204 | #[repr(u8)] 205 | #[derive(Debug,Clone,PartialEq,Eq)] 206 | pub enum ChordType { 207 | /// Major chord. 208 | Major, 209 | /// Dominant seventh chord. 210 | Seventh, 211 | /// Major seventh chord. 212 | MajorSeventh, 213 | /// Add sixth chord. 214 | Sixth, 215 | /// Minor chord. 216 | Minor, 217 | /// Minor seventh chord. 218 | MinorSeventh, 219 | /// Minor major seventh chord. 220 | MinorMajor, 221 | /// Minor add sixth chord. 222 | MinorSixth, 223 | /// Suspended second chord. 224 | SuspendedSecond, 225 | /// Suspended fourth chord. 226 | SuspendedFourth, 227 | /// Seventh suspended second chord. 228 | SeventhSuspendedSecond, 229 | /// Seventh suspended fourth chord. 230 | SeventhSuspendedFourth, 231 | /// Diminished chord. 232 | Diminished, 233 | /// Augmented chord. 234 | Augmented, 235 | /// Power chord. 236 | Power, 237 | 238 | Unknown(u8), 239 | } 240 | pub(crate) fn get_chord_type(value: u8) -> ChordType { 241 | match value { 242 | 0 => ChordType::Major, 243 | 1 => ChordType::Seventh, 244 | 2 => ChordType::MajorSeventh, 245 | 3 => ChordType::Sixth, 246 | 4 => ChordType::Minor, 247 | 5 => ChordType::MinorSeventh, 248 | 6 => ChordType::MinorMajor, 249 | 7 => ChordType::MinorSixth, 250 | 8 => ChordType::SuspendedSecond, 251 | 9 => ChordType::SuspendedFourth, 252 | 10 => ChordType::SeventhSuspendedSecond, 253 | 11 => ChordType::SeventhSuspendedFourth, 254 | 12 => ChordType::Diminished, 255 | 13 => ChordType::Augmented, 256 | 14 => ChordType::Power, 257 | _ => ChordType::Unknown(value), //panic!("Cannot read chord type (new format)"), 258 | } 259 | } 260 | pub(crate) fn from_chord_type(value: &ChordType) -> u8 { 261 | match value { 262 | ChordType::Major => 0, 263 | ChordType::Seventh => 1, 264 | ChordType::MajorSeventh => 2, 265 | ChordType::Sixth => 3, 266 | ChordType::Minor => 4, 267 | ChordType::MinorSeventh => 5, 268 | ChordType::MinorMajor => 6, 269 | ChordType::MinorSixth => 7, 270 | ChordType::SuspendedSecond => 8, 271 | ChordType::SuspendedFourth => 9, 272 | ChordType::SeventhSuspendedSecond => 10, 273 | ChordType::SeventhSuspendedFourth => 11, 274 | ChordType::Diminished => 12, 275 | ChordType::Augmented => 13, 276 | ChordType::Power => 14, 277 | ChordType::Unknown(value) => *value, 278 | } 279 | } 280 | 281 | /// Tonality of the chord 282 | #[repr(u8)] 283 | #[derive(Debug,Clone,PartialEq,Eq)] 284 | pub enum ChordAlteration { 285 | /// Perfect. 286 | Perfect, 287 | /// Diminished. 288 | Diminished, 289 | /// Augmented. 290 | Augmented, 291 | } 292 | pub(crate) fn get_chord_alteration(value: u8) -> ChordAlteration { 293 | match value { 294 | 0 => ChordAlteration::Perfect, 295 | 1 => ChordAlteration::Diminished, 296 | 2 => ChordAlteration::Augmented, 297 | _ => panic!("Cannot read chord fifth (new format)"), 298 | } 299 | } 300 | pub(crate) fn from_chord_alteration(value: &ChordAlteration) -> u8 { 301 | match value { 302 | ChordAlteration::Perfect => 0, 303 | ChordAlteration::Diminished => 1, 304 | ChordAlteration::Augmented => 2, 305 | } 306 | } 307 | 308 | /// Extension type of the chord 309 | #[repr(u8)] 310 | #[derive(Debug,Clone,PartialEq,Eq)] 311 | pub enum ChordExtension { 312 | None, 313 | /// Ninth chord. 314 | Ninth, 315 | /// Eleventh chord. 316 | Eleventh, 317 | /// Thirteenth chord. 318 | Thirteenth, 319 | Unknown(u8) 320 | } 321 | pub(crate) fn get_chord_extension(value: u8) -> ChordExtension { 322 | match value { 323 | 0 => ChordExtension::None, 324 | 1 => ChordExtension::Ninth, 325 | 2 => ChordExtension::Eleventh, 326 | 3 => ChordExtension::Thirteenth, 327 | _ => ChordExtension::Unknown(value), //panic!("Cannot read chord type (new format)"), 328 | } 329 | } 330 | pub(crate) fn from_chord_extension(value: &ChordExtension) -> u8 { 331 | match value { 332 | ChordExtension::None => 0, 333 | ChordExtension::Ninth => 1, 334 | ChordExtension::Eleventh => 2, 335 | ChordExtension::Thirteenth => 3, 336 | ChordExtension::Unknown(value) => *value, //panic!("Cannot read chord type (new format)"), 337 | } 338 | } 339 | 340 | /// Left and right hand fingering used in tabs and chord diagram editor. 341 | #[repr(i8)] 342 | #[derive(Debug,Clone,PartialEq,Eq)] 343 | pub enum Fingering { 344 | /// Open or muted. 345 | Open = -1, //-1? 346 | /// Thumb. 347 | Thumb = 0, 348 | /// Index finger. 349 | Index, 350 | /// Middle finger. 351 | Middle, 352 | /// Annular finger. 353 | Annular, 354 | /// Little finger. 355 | Little, 356 | 357 | Unknown(i8), 358 | } 359 | 360 | pub(crate) fn get_fingering(value: i8) -> Fingering { 361 | match value { 362 | -1 => Fingering::Open, 363 | 0 => Fingering::Thumb, 364 | 1 => Fingering::Index, 365 | 2 => Fingering::Middle, 366 | 3 => Fingering::Annular, 367 | 4 => Fingering::Little, 368 | _ => Fingering::Unknown(value), //panic!("Cannot get fingering! How can you have more than 5 fingers per hand?!?"), 369 | } 370 | } 371 | pub(crate) fn from_fingering(value: &Fingering) -> i8 { 372 | match value { 373 | Fingering::Open => -1, 374 | Fingering::Thumb => 0, 375 | Fingering::Index => 1, 376 | Fingering::Middle => 2, 377 | Fingering::Annular => 3, 378 | Fingering::Little => 4, 379 | Fingering::Unknown(value) => *value, //panic!("Cannot get fingering! How can you have more than 5 fingers per hand?!?"), 380 | } 381 | } 382 | 383 | /// All Bend presets 384 | #[repr(u8)] 385 | #[derive(Debug,Clone,PartialEq,Eq)] 386 | pub enum BendType { 387 | /// No Preset. 388 | None, 389 | 390 | //Bends 391 | /// A simple bend. 392 | Bend, 393 | /// A bend and release afterwards. 394 | BendRelease, 395 | /// A bend, then release and rebend. 396 | BendReleaseBend, 397 | /// Prebend. 398 | Prebend, 399 | /// Prebend and then release. 400 | PrebendRelease, 401 | 402 | //Tremolo Bar 403 | /// Dip the bar down and then back up. 404 | Dip, 405 | /// Dive the bar. 406 | Dive, 407 | /// Release the bar up. 408 | ReleaseUp, 409 | /// Dip the bar up and then back down. 410 | InvertedDip, 411 | /// Return the bar. 412 | Return, 413 | /// Release the bar down. 414 | ReleaseDown 415 | } 416 | pub(crate) fn get_bend_type(value: i8) -> BendType { 417 | match value { 418 | 0 => BendType::None, 419 | 1 => BendType::Bend, 420 | 2 => BendType::BendRelease, 421 | 3 => BendType::BendReleaseBend, 422 | 4 => BendType::Prebend, 423 | 5 => BendType::PrebendRelease, 424 | 6 => BendType::Dip, 425 | 7 => BendType::Dive, 426 | 8 => BendType::ReleaseUp, 427 | 9 => BendType::InvertedDip, 428 | 10 => BendType::Return, 429 | 11 => BendType::ReleaseDown, 430 | _ => panic!("Cannot read bend type"), 431 | } 432 | } 433 | pub(crate) fn from_bend_type(value: &BendType) -> i8 { 434 | match value { 435 | BendType::None => 0, 436 | BendType::Bend => 1, 437 | BendType::BendRelease => 2, 438 | BendType::BendReleaseBend => 3, 439 | BendType::Prebend => 4, 440 | BendType::PrebendRelease => 5, 441 | BendType::Dip => 6, 442 | BendType::Dive => 7, 443 | BendType::ReleaseUp => 8, 444 | BendType::InvertedDip => 9, 445 | BendType::Return => 10, 446 | BendType::ReleaseDown => 11, 447 | } 448 | } 449 | 450 | /// All transition types for grace notes. 451 | #[repr(i8)] 452 | #[derive(Debug,Clone,PartialEq,Eq)] 453 | pub enum GraceEffectTransition { 454 | ///No transition 455 | None = 0, 456 | ///Slide from the grace note to the real one. 457 | Slide, 458 | ///Perform a bend from the grace note to the real one. 459 | Bend, 460 | ///Perform a hammer on. 461 | Hammer 462 | } 463 | pub(crate) fn get_grace_effect_transition(value: i8) -> GraceEffectTransition { 464 | match value { 465 | 0 => GraceEffectTransition::None, 466 | 1 => GraceEffectTransition::Slide, 467 | 2 => GraceEffectTransition::Bend, 468 | 3 => GraceEffectTransition::Hammer, 469 | _ => panic!("Cannot get transition for the grace effect"), 470 | } 471 | } 472 | pub(crate) fn from_grace_effect_transition(value: &GraceEffectTransition) -> i8 { 473 | match value { 474 | GraceEffectTransition::None => 0, 475 | GraceEffectTransition::Slide => 1, 476 | GraceEffectTransition::Bend => 2, 477 | GraceEffectTransition::Hammer => 3, 478 | } 479 | } 480 | 481 | #[repr(u8)] 482 | #[derive(Debug,Clone,PartialEq,Eq)] 483 | pub enum HarmonicType { 484 | Natural = 1, //1 485 | Artificial, 486 | Tapped, 487 | Pinch, 488 | Semi, //5 489 | } 490 | pub(crate) fn from_harmonic_type(value: &HarmonicType) -> i8 { 491 | match value { 492 | HarmonicType::Natural => 1, 493 | HarmonicType::Artificial => 2, 494 | HarmonicType::Tapped => 3, 495 | HarmonicType::Pinch => 4, 496 | HarmonicType::Semi => 5, 497 | } 498 | } 499 | 500 | /// Values of auto-accentuation on the beat found in track RSE settings 501 | #[repr(u8)] 502 | #[derive(Debug,Clone)] 503 | pub enum Accentuation { None, VerySoft, Soft, Medium, Strong, VeryStrong } 504 | pub(crate) fn get_accentuation(value: u8) -> Accentuation { 505 | match value { 506 | 0 => Accentuation::None, 507 | 1 => Accentuation::VerySoft, 508 | 2 => Accentuation::Soft, 509 | 3 => Accentuation::Medium, 510 | 4 => Accentuation::Strong, 511 | 5 => Accentuation::VeryStrong, 512 | _ => panic!("Cannot get accentuation"), 513 | } 514 | } 515 | pub(crate) fn from_accentuation(value: &Accentuation) -> u8 { 516 | match value { 517 | Accentuation::None => 0, 518 | Accentuation::VerySoft => 1, 519 | Accentuation::Soft => 2, 520 | Accentuation::Medium => 3, 521 | Accentuation::Strong => 4, 522 | Accentuation::VeryStrong => 5, 523 | } 524 | } 525 | 526 | /// A navigation sign like *Coda* (𝄌: U+1D10C) or *Segno* (𝄋 or 𝄉: U+1D10B or U+1D109). 527 | #[repr(u8)] 528 | #[derive(Debug,Clone,PartialEq,Eq,Hash)] 529 | pub enum DirectionSign { 530 | Coda, DoubleCoda, 531 | Segno, SegnoSegno, 532 | Fine, 533 | DaCapo, DaCapoAlCoda, DaCapoAlDoubleCoda, DaCapoAlFine, 534 | DaSegno, DaSegnoAlCoda, DaSegnoAlDoubleCoda, DaSegnoAlFine, DaSegnoSegno, DaSegnoSegnoAlCoda, DaSegnoSegnoAlDoubleCoda, DaSegnoSegnoAlFine, 535 | DaCoda, DaDoubleCoda, 536 | } 537 | --------------------------------------------------------------------------------