package org.herac.tuxguitar.io.lilypond; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import org.herac.tuxguitar.song.managers.TGSongManager; import org.herac.tuxguitar.song.models.Clef; import org.herac.tuxguitar.song.models.Direction; import org.herac.tuxguitar.song.models.TGBeat; import org.herac.tuxguitar.song.models.TGChord; import org.herac.tuxguitar.song.models.TGDivisionType; import org.herac.tuxguitar.song.models.TGDuration; import org.herac.tuxguitar.song.models.TGMeasure; import org.herac.tuxguitar.song.models.TGMeasureHeader; import org.herac.tuxguitar.song.models.TGNote; import org.herac.tuxguitar.song.models.TGNoteEffect; import org.herac.tuxguitar.song.models.TGSong; import org.herac.tuxguitar.song.models.TGString; import org.herac.tuxguitar.song.models.TGTempo; import org.herac.tuxguitar.song.models.TGTimeSignature; import org.herac.tuxguitar.song.models.TGTrack; import org.herac.tuxguitar.song.models.TGVoice; import org.herac.tuxguitar.song.models.effects.TGEffectGrace; public class LilypondOutputStream { protected class LilypondTempData { private boolean divisionTypeOpen; private boolean multipleVoices; private int repeatAlternativeNumber; private boolean repeatAlternativeOpen; private int repeatCount; private boolean repeatOpen; private List<String> skippedLyricBeats; protected LilypondTempData() { this.skippedLyricBeats = new ArrayList<String>(); this.reset(); } public void addSkippedLyricBeat(String duration) { this.skippedLyricBeats.add(duration); } public int getRepeatAlternativeNumber() { return this.repeatAlternativeNumber; } public int getRepeatCount() { return this.repeatCount; } public List<String> getSkippedLyricBeats() { return this.skippedLyricBeats; } public boolean isDivisionTypeOpen() { return this.divisionTypeOpen; } public boolean isMultipleVoices() { return this.multipleVoices; } public boolean isRepeatAlternativeOpen() { return this.repeatAlternativeOpen; } public boolean isRepeatOpen() { return this.repeatOpen; } public void reset() { this.multipleVoices = false; this.repeatCount = 0; this.repeatOpen = false; this.divisionTypeOpen = false; this.skippedLyricBeats.clear(); } public void setDivisionTypeOpen(boolean divisionTypeOpen) { this.divisionTypeOpen = divisionTypeOpen; } public void setMultipleVoices(boolean multipleVoices) { this.multipleVoices = multipleVoices; } public void setRepeatAlternativeNumber(int repeatAlternativeNumber) { this.repeatAlternativeNumber = repeatAlternativeNumber; } public void setRepeatAlternativeOpen(boolean repeatAlternativeOpen) { this.repeatAlternativeOpen = repeatAlternativeOpen; } public void setRepeatCount(int repeatCount) { this.repeatCount = repeatCount; } public void setRepeatOpen(boolean repeatOpen) { this.repeatOpen = repeatOpen; } } private static final String INDENT = new String(" "); private static final String[] LILYPOND_FLAT_NOTES = new String[] { "c", "des", "d", "ees", "e", "f", "ges", "g", "aes", "a", "bes", "b" }; private static final String[] LILYPOND_KEY_SIGNATURES = new String[] { "c", "g", "d", "a", "e", "b", "fis", "cis", "f", "bes", "ees", "aes", "des", "ges", "ces" }; private static final String[] LILYPOND_SHARP_NOTES = new String[] { "c", "cis", "d", "dis", "e", "f", "fis", "g", "gis", "a", "ais", "b" }; private static final String LILYPOND_VERSION = "2.10.5"; private TGSongManager manager; private LilypondSettings settings; private LilypondTempData temp; private PrintWriter writer; public LilypondOutputStream(OutputStream stream, LilypondSettings settings) { this.writer = new PrintWriter(stream); this.temp = new LilypondTempData(); this.settings = settings; } private void addBeat(int key, TGBeat beat, TGVoice voice) { if (voice.isRestVoice()) { boolean skip = false; for (int v = 0; v < beat.countVoices(); v++) { if (!skip && v != voice.getIndex()) { TGVoice current = beat.getVoice(v); if (!current.isEmpty() && current.getDuration().isEqual(voice.getDuration())) { skip = (!current.isRestVoice() || current.getIndex() < voice .getIndex()); } } } this.writer.print((skip ? "\\skip " : "r")); this.addDuration(voice.getDuration()); } else { this.addEffectsBeforeBeat(voice); this.writer.print("<"); int size = voice.getNotes().size(); for (int i = 0; i < size; i++) { TGNote note = voice.getNote(i); this.addEffectsBeforeNote(note); this.addKey(key, (beat.getMeasure().getTrack().getString( note.getString()).getValue() + note.getValue())); if (this.isAnyTiedTo(note)) { this.writer.print("~"); } this.addString(note.getString()); this.addEffectsOnNote(note.getEffect()); if (size > 1) { this.writer.print(" "); } } this.writer.print(">"); this.addDuration(voice.getDuration()); this.addEffectsOnDuration(voice); this.addEffectsOnBeat(voice); } // Add Chord, if was not previously added in another voice if (beat.isChordBeat() && !voice.isRestVoice()) { boolean skip = false; for (int v = 0; v < voice.getIndex(); v++) { TGVoice current = beat.getVoice(v); skip = (skip || (!current.isEmpty() && !current.isRestVoice())); } if (!skip) { this.writer.print("-\\tag #'chords ^\\markup \\fret-diagram #\""); TGChord chord = beat.getChord(); for (int i = 0; i < chord.countStrings(); i++) { this.writer.print((i + 1) + "-" + getLilypondChordFret(chord.getFretValue(i)) + ";"); } this.writer.print("\""); } } // Add Text, if was not previously added in another voice if (beat.isTextBeat()) { boolean skip = false; for (int v = 0; v < voice.getIndex(); v++) { skip = (skip || !beat.getVoice(v).isEmpty()); } if (!skip) { this.writer.print("-\\tag #'texts ^\\markup {\"" + beat.getText().getValue() + "\"}"); } } // Check if it's a lyric beat to skip // For now we only support lyrics for first voice. if (voice.getIndex() == 0 && !voice.isRestVoice()) { if (beat.getMeasure().getTrack().getLyrics().getFrom() > beat .getMeasure().getNumber()) { this.temp.addSkippedLyricBeat(getLilypondDuration(voice.getDuration())); } } this.writer.print(" "); } private void addClef(Clef clef, int indent) { String clefName = null; switch (clef) { case TREBLE: clefName = "treble"; break; case BASS: clefName = "bass"; break; case TENOR: clefName = "tenor"; break; case ALTO: clefName = "alto"; break; } if (clefName != null) { this.writer.println(indent(indent) + "\\clef #(if $inTab \"tab\" \"" + clefName + "_8\")"); } } private void addCommands() { // deadNote this.writer .println("deadNote = #(define-music-function (parser location note) (ly:music?)"); this.writer.println(indent(1) + "(set! (ly:music-property note 'tweaks)"); this.writer.println(indent(2) + "(acons 'stencil ly:note-head::print"); this.writer.println(indent(3) + "(acons 'glyph-name \"2cross\""); this.writer.println(indent(4) + "(acons 'style 'special"); this.writer.println(indent(5) + "(ly:music-property note 'tweaks)))))"); this.writer.println(indent(1) + "note)"); this.writer.println(); // palmMute this.writer .println("palmMute = #(define-music-function (parser location note) (ly:music?)"); this.writer.println(indent(1) + "(set! (ly:music-property note 'tweaks)"); this.writer.println(indent(2) + "(acons 'style 'do (ly:music-property note 'tweaks)))"); this.writer.println(indent(1) + "note)"); this.writer.println(); } private void addComponents(TGMeasure measure, int vIndex) { int key = measure.getKeySignature(); TGBeat previous = null; for (int i = 0; i < measure.countBeats(); i++) { TGBeat beat = measure.getBeat(i); TGVoice voice = beat.getVoice(vIndex); if (!voice.isEmpty()) { TGDivisionType divisionType = voice.getDuration().getDivision(); if (previous != null && this.temp.isDivisionTypeOpen() && !divisionType.isEqual(previous.getVoice(vIndex).getDuration() .getDivision())) { this.writer.print("} "); this.temp.setDivisionTypeOpen(false); } if (!this.temp.isDivisionTypeOpen() && !divisionType.isEqual(TGDivisionType.NORMAL)) { this.writer.print("\\times " + divisionType.getTimes() + "/" + divisionType.getEnters() + " {"); this.temp.setDivisionTypeOpen(true); } this.addBeat(key, beat, voice); previous = beat; } } // It Means that all voice beats are empty if (previous == null) { this.writer.print("\\skip "); this.addDuration(measure.getTimeSignature().getDenominator()); this.writer.print("*" + measure.getTimeSignature().getNumerator() + " "); } if (this.temp.isDivisionTypeOpen()) { this.writer.print("} "); this.temp.setDivisionTypeOpen(false); } } private void addDuration(TGDuration duration) { this.writer.print(getLilypondDuration(duration)); } private void addEffectsBeforeBeat(TGVoice voice) { List<TGNote> graceNotes = new ArrayList<TGNote>(); for (int i = 0; i < voice.getNotes().size(); i++) { TGNote note = voice.getNote(i); if (note.getEffect().isGrace()) { graceNotes.add(note); } } if (!graceNotes.isEmpty()) { this.writer.print("\\grace "); this.writer.print("<"); int duration = 0; for (int i = 0; i < graceNotes.size(); i++) { TGNote note = (TGNote) graceNotes.get(i); TGMeasure measure = voice.getBeat().getMeasure(); TGString string = measure.getTrack().getString(note.getString()); TGEffectGrace grace = note.getEffect().getGrace(); if (duration < TGDuration.SIXTY_FOURTH && grace.getDuration() == 1) { duration = TGDuration.SIXTY_FOURTH; } else if (duration < TGDuration.THIRTY_SECOND && grace.getDuration() == 2) { duration = TGDuration.THIRTY_SECOND; } else if (duration < TGDuration.SIXTEENTH && grace.getDuration() == 3) { duration = TGDuration.SIXTEENTH; } if (i > 0) { this.writer.print(" "); } this.addKey(measure.getKeySignature(), (string.getValue() + grace .getFret())); this.addString(note.getString()); } this.writer.print(">"); this.writer.print(duration); this.writer.print(" "); } } private void addEffectsBeforeNote(TGNote note) { TGNoteEffect effect = note.getEffect(); if (effect.isDeadNote()) { this.writer.print("\\deadNote "); } if (effect.isPalmMute()) { this.writer.print("\\palmMute "); } if (effect.isGhostNote()) { this.writer.print("\\parenthesize "); } if (effect.isBend()) { this.writer.print("\\bendAfter #+6 "); } } private void addEffectsOnBeat(TGVoice voice) { boolean trill = false; boolean vibrato = false; boolean staccato = false; boolean accentuatedNote = false; boolean heavyAccentuatedNote = false; boolean arpeggio = !voice.getBeat().getStroke().getDirection().equals( Direction.NONE); for (int i = 0; i < voice.getNotes().size(); i++) { TGNoteEffect effect = voice.getNote(i).getEffect(); trill = (trill || effect.isTrill()); vibrato = (vibrato || effect.isVibrato()); staccato = (staccato || effect.isStaccato()); accentuatedNote = (accentuatedNote || effect.isAccentuatedNote()); heavyAccentuatedNote = (heavyAccentuatedNote || effect .isHeavyAccentuatedNote()); } if (trill) { this.writer.print("\\trill"); } if (vibrato) { this.writer.print("\\prall"); } if (staccato) { this.writer.print("\\staccato"); } if (accentuatedNote) { this.writer.print("->"); } if (heavyAccentuatedNote) { this.writer.print("-^"); } if (arpeggio) { this.writer.print("\\arpeggio"); } } private void addEffectsOnDuration(TGVoice voice) { int tremoloPicking = -1; for (int i = 0; i < voice.getNotes().size(); i++) { TGNote note = voice.getNote(i); if (tremoloPicking == -1 && note.getEffect().isTremoloPicking()) { tremoloPicking = note.getEffect().getTremoloPicking().getDuration() .getValue(); } } if (tremoloPicking != -1) { this.writer.print(":" + tremoloPicking); } } private void addEffectsOnNote(TGNoteEffect effect) { if (effect.isHarmonic()) { this.writer.print("\\harmonic"); } } private void addFunctions() { // tab-clear-tied-fret-numbers this.writer.println("#(define (tie::tab-clear-tied-fret-numbers grob)"); this.writer.println(indent(1) + "(let* ((tied-fret-nr (ly:spanner-bound grob RIGHT)))"); this.writer.println(indent(2) + "(ly:grob-set-property! tied-fret-nr 'transparent #t)))"); this.writer.println(); } private void addHeader(TGSong song, String instrument, int indent) { this.writer.println(indent(indent) + "\\header {"); this.writer.println(indent(indent + 1) + "title = \"" + song.getName() + "\" "); this.writer.println(indent(indent + 1) + "composer = \"" + song.getAuthor() + "\" "); if (this.settings.isTrackNameEnabled() && !this.addTrackTitleOnGroup(song) && instrument != null) { this.writer.println(indent(indent + 1) + "instrument = \"" + instrument + "\" "); } this.writer.println(indent(indent) + "}"); } private void addKey(int keySignature, int value) { this.writer.print(getLilypondKey(keySignature, value)); } private void addKeySignature(int keySignature, int indent) { if (keySignature >= 0 && keySignature < LILYPOND_KEY_SIGNATURES.length) { this.writer.println(indent(indent) + "\\key " + LILYPOND_KEY_SIGNATURES[keySignature] + " \\major"); } } private void addLayout() { this.writer.println("\\layout {"); this.writer.println(indent(1) + "\\context { \\Score"); this.writer.println(indent(2) + "\\override MetronomeMark #'padding = #'5"); this.writer.println(indent(1) + "}"); this.writer.println(indent(1) + "\\context { \\Staff"); this.writer.println(indent(2) + "\\override TimeSignature #'style = #'numbered"); this.writer.println(indent(2) + "\\override StringNumber #'transparent = #" + getLilypondBoolean(true)); this.writer.println(indent(1) + "}"); this.writer.println(indent(1) + "\\context { \\TabStaff"); this.writer.println(indent(2) + "\\override TimeSignature #'style = #'numbered"); this.writer.println(indent(2) + "\\override Stem #'transparent = #" + getLilypondBoolean(this.settings.isScoreEnabled())); this.writer.println(indent(2) + "\\override Beam #'transparent = #" + getLilypondBoolean(this.settings.isScoreEnabled())); this.writer .println(indent(2) + "\\override Tie #'after-line-breaking = #tie::tab-clear-tied-fret-numbers"); this.writer.println(indent(1) + "}"); if (this.settings.isScoreEnabled()) { this.writer.println(indent(1) + "\\context { \\TabVoice"); this.writer.println(indent(2) + "\\override Tie #'stencil = ##f"); this.writer.println(indent(1) + "}"); } this.writer.println(indent(1) + "\\context { \\StaffGroup"); this.writer.println(indent(2) + "\\consists \"Instrument_name_engraver\""); this.writer.println(indent(1) + "}"); this.writer.println("}"); } private void addLyrics(TGTrack track, String id) { this.writer.println(id + "Lyrics = \\lyricmode {"); this.writer.println(indent(1) + "\\set ignoreMelismata = #" + getLilypondBoolean(true)); int skippedCount = this.temp.getSkippedLyricBeats().size(); if (skippedCount > 0) { this.writer.print(indent(1)); for (int i = 0; i < skippedCount; i++) { this.writer.print("\\skip " + ((String) this.temp.getSkippedLyricBeats().get(i)) + " "); } this.writer.println(); } this.writer.println(indent(1) + track.getLyrics().getLyrics()); this.writer.println(indent(1) + "\\unset ignoreMelismata"); this.writer.println("}"); } private void addMeasure(TGMeasure measure, TGMeasure previous, int voice, int indent, boolean isLast) { if (previous == null || measure.getTempo().getValue() != previous.getTempo().getValue()) { this.addTempo(measure.getTempo(), indent); } if (previous == null || measure.getClef() != previous.getClef()) { this.addClef(measure.getClef(), indent); } if (previous == null || measure.getKeySignature() != previous.getKeySignature()) { this.addKeySignature(measure.getKeySignature(), indent); } if (previous == null || !measure.getTimeSignature().isEqual(previous.getTimeSignature())) { this.addTimeSignature(measure.getTimeSignature(), indent); } // Set the specific voice this.addMeasureVoice(measure, voice, (previous == null), indent); // Open repeat if (measure.isRepeatOpen()) { this.addRepeatOpen(measure.getHeader(), indent); } // If is first measure, and it don't have a repeat-open, // We check on next measures if should open it. else if (measure.getNumber() == 1) { this.checkRepeatCount(measure.getHeader()); if (this.temp.getRepeatCount() > 0) { this.addRepeatOpen(measure.getHeader(), indent); } } // Open a repeat alternative only if this measure isn't who openned the // repeat. if (!measure.isRepeatOpen() && measure.getHeader().getRepeatAlternative() > 0) { this.addRepeatAlternativeOpen(indent); } this.addMeasureComponents(measure, voice, (this.temp.isRepeatOpen() || this.temp.isRepeatAlternativeOpen() ? (indent + 1) : indent)); // If is last alternative, we can close it now if (this.temp.isRepeatAlternativeOpen() && this.temp.getRepeatAlternativeNumber() >= this.temp.getRepeatCount()) { this.addRepeatClose(indent); this.addRepeatAlternativeClose(indent); } // Close repeat if (measure.getRepeatClose() > 0) { this.addRepeatClose(indent); } // If is last, we close any openned repeat if (isLast) { this.addRepeatClose(indent); this.addRepeatAlternativeClose(indent); } } private void addMeasureComponents(TGMeasure measure, int voice, int indent) { this.writer.print(indent(indent)); this.addComponents(measure, voice); this.writer.println(); } private void addMeasureVoice(TGMeasure measure, int voice, boolean force, int indent) { boolean multipleVoices = hasMultipleVoices(measure); if (force || multipleVoices != this.temp.isMultipleVoices()) { this.writer.println(indent(indent) + getLilypondVoice(multipleVoices ? voice : -1)); } this.temp.setMultipleVoices(multipleVoices); } private void addMusic(TGTrack track, String id) { for (int voice = 0; voice < TGBeat.MAX_VOICES; voice++) { this.writer.println(trackVoiceID(voice, id, "Music") + " = #(define-music-function (parser location inTab) (boolean?)"); this.writer.println("#{"); if (this.isVoiceAvailable(track, voice)) { TGMeasure previous = null; int count = track.countMeasures(); for (int i = 0; i < count; i++) { TGMeasure measure = track.getMeasure(i); int measureFrom = this.settings.getMeasureFrom(); int measureTo = this.settings.getMeasureTo(); if ((measureFrom <= measure.getNumber() || measureFrom == LilypondSettings.FIRST_MEASURE) && (measureTo >= measure.getNumber() || measureTo == LilypondSettings.LAST_MEASURE)) { this.addMeasure(measure, previous, voice, 1, (i == (count - 1))); previous = measure; } } this.writer.println(indent(1) + "\\bar \"|.\""); this.writer.println(indent(1) + "\\pageBreak"); } this.writer.println("#})"); } } private void addPaper(TGSong song) { this.writer.println("\\paper {"); this.writer.println(indent(1) + "indent = #" + (this.addTrackTitleOnGroup(song) ? 30 : 0)); this.writer.println(indent(1) + "printallheaders = #" + getLilypondBoolean(true)); this.writer.println(indent(1) + "print-all-headers = #" + getLilypondBoolean(true)); this.writer.println(indent(1) + "ragged-right = #" + getLilypondBoolean(false)); this.writer.println(indent(1) + "ragged-bottom = #" + getLilypondBoolean(true)); this.writer.println("}"); } private void addRepeatAlternativeClose(int indent) { if (this.temp.isRepeatAlternativeOpen()) { if (this.temp.getRepeatAlternativeNumber() > 0) { this.writer.println(indent(indent) + "}"); } this.writer.println(indent(indent) + "}"); } this.temp.setRepeatAlternativeOpen(false); this.temp.setRepeatAlternativeNumber(0); if (!this.temp.isRepeatOpen()) { this.temp.setRepeatCount(0); } } private void addRepeatAlternativeOpen(int indent) { if (this.temp.isRepeatOpen() && !this.temp.isRepeatAlternativeOpen()) { this.temp.setRepeatAlternativeOpen(true); this.addRepeatClose(indent); this.writer.println(indent(indent) + "\\alternative {"); } if (this.temp.isRepeatAlternativeOpen()) { if (this.temp.getRepeatAlternativeNumber() > 0) { this.writer.println(indent(indent) + "}"); } this.writer.println(indent(indent) + "{"); this.temp.setRepeatAlternativeNumber(this.temp .getRepeatAlternativeNumber() + 1); } } private void addRepeatClose(int indent) { if (this.temp.isRepeatOpen()) { this.writer.println(indent(indent) + "}"); } this.temp.setRepeatOpen(false); if (!this.temp.isRepeatAlternativeOpen()) { this.temp.setRepeatCount(0); } } private void addRepeatOpen(TGMeasureHeader measure, int indent) { // Close any existent first this.addRepeatClose(indent); this.addRepeatAlternativeClose(indent); this.checkRepeatCount(measure); this.writer.println(indent(indent) + "\\repeat volta " + this.temp.getRepeatCount() + " {"); this.temp.setRepeatOpen(true); } private void addScoreStaff(TGTrack track, String id) { boolean addLyrics = (this.settings.isLyricsEnabled() && !this.settings.isTablatureEnabled() && !track.getLyrics() .getLyrics().isEmpty()); boolean addChordDiagrams = this.settings.isChordDiagramEnabled(); boolean addTexts = this.settings.isTextEnabled(); this.writer.println(id + "Staff = \\new Staff <<"); for (int v = 0; v < TGBeat.MAX_VOICES; v++) { String vId = trackVoiceID(v, id, "Music"); this.writer.println(indent(1) + "\\context Voice = \"" + vId + "\" {"); if (!addChordDiagrams) { this.writer.println(indent(2) + "\\removeWithTag #'chords"); } if (!addTexts) { this.writer.println(indent(2) + "\\removeWithTag #'texts"); } this.writer.println(indent(2) + "\\" + vId + " #" + getLilypondBoolean(false)); this.writer.println(indent(1) + "}"); } if (addLyrics) { this.writer.println(indent(1) + "\\new Lyrics \\lyricsto \"" + trackVoiceID(0, id, "Music") + "\" \\" + id + "Lyrics"); } this.writer.println(">>"); } private void addSong(TGSong song) { int trackCount = song.countTracks(); if (this.settings.isTrackGroupEnabled() && trackCount > 1) { this.writer.println("\\score {"); if (this.settings.getTrack() == LilypondSettings.ALL_TRACKS) { this.writer.println(indent(1) + "<<"); } } for (int i = 0; i < trackCount; i++) { TGTrack track = song.getTrack(i); if (this.settings.getTrack() == LilypondSettings.ALL_TRACKS || this.settings.getTrack() == track.getNumber()) { if (!this.settings.isTrackGroupEnabled() || trackCount == 1) { this.writer.println("\\score {"); } this.writer.println(indent(1) + "\\" + this.trackID(i, "StaffGroup")); if (!this.settings.isTrackGroupEnabled() || trackCount == 1) { this.addHeader(song, track.getName(), 1); this.writer.println("}"); } } } if (this.settings.isTrackGroupEnabled() && trackCount > 1) { if (this.settings.getTrack() == LilypondSettings.ALL_TRACKS) { this.writer.println(indent(1) + ">>"); } this.addHeader(song, null, 1); this.writer.println("}"); } } private void addSongDefinitions(TGSong song) { for (int i = 0; i < song.countTracks(); i++) { TGTrack track = song.getTrack(i); String id = this.trackID(i, ""); this.temp.reset(); this.addMusic(track, id); this.addLyrics(track, id); this.addScoreStaff(track, id); this.addTabStaff(track, id); this.addStaffGroup(track, id); } } private void addStaffGroup(TGTrack track, String id) { this.writer.println(id + "StaffGroup = \\new StaffGroup <<"); if (this.addTrackTitleOnGroup(track.getSong())) { this.writer.println(indent(1) + "\\set StaffGroup.instrumentName = #\"" + track.getName() + "\""); } if (this.settings.isScoreEnabled()) { this.writer.println(indent(1) + "\\" + id + "Staff"); } if (this.settings.isTablatureEnabled()) { this.writer.println(indent(1) + "\\" + id + "TabStaff"); } this.writer.println(">>"); } private void addString(int string) { this.writer.print("\\" + string); } private void addTabStaff(TGTrack track, String id) { boolean addLyrics = (this.settings.isLyricsEnabled() && !track.getLyrics() .getLyrics().isEmpty()); boolean addChordDiagrams = (this.settings.isChordDiagramEnabled() && !this.settings .isScoreEnabled()); boolean addTexts = (this.settings.isTextEnabled() && !this.settings .isScoreEnabled()); this.writer.println(id + "TabStaff = \\new TabStaff " + getLilypondTuning(track) + " <<"); for (int v = 0; v < TGBeat.MAX_VOICES; v++) { String vId = trackVoiceID(v, id, "Music"); this.writer.println(indent(1) + "\\context TabVoice = \"" + vId + "\" {"); if (!addChordDiagrams) { this.writer.println(indent(2) + "\\removeWithTag #'chords"); } if (!addTexts) { this.writer.println(indent(2) + "\\removeWithTag #'texts"); } this.writer.println(indent(2) + "\\" + vId + " #" + getLilypondBoolean(true)); this.writer.println(indent(1) + "}"); } if (addLyrics) { this.writer.println(indent(1) + "\\new Lyrics \\lyricsto \"" + trackVoiceID(0, id, "Music") + "\" \\" + id + "Lyrics"); } this.writer.println(">>"); } private void addTempo(TGTempo tempo, int indent) { this.writer.println(indent(indent) + "\\tempo 4=" + tempo.getValue()); } private void addTimeSignature(TGTimeSignature ts, int indent) { this.writer.println(indent(indent) + "\\time " + ts.getNumerator() + "/" + ts.getDenominator().getValue()); } private boolean addTrackTitleOnGroup(TGSong song) { if (this.settings.isTrackNameEnabled() && this.settings.isTrackGroupEnabled()) { if (this.settings.getTrack() == LilypondSettings.ALL_TRACKS && song.countTracks() > 1) { return true; } } return false; } private void addVersion() { this.writer.println("\\version \"" + LILYPOND_VERSION + "\""); } private void checkRepeatCount(TGMeasureHeader header) { boolean alternativePresent = false; TGMeasureHeader next = header; while (next != null) { if (next.isRepeatOpen() && next.getNumber() != header.getNumber()) { break; } if (next.getNumber() > header.getNumber() && next.getRepeatAlternative() > 0) { alternativePresent = true; this.temp.setRepeatCount((this.temp.getRepeatCount() + 1)); } else if (!alternativePresent && next.getRepeatClose() > 0) { this.temp.setRepeatCount((next.getRepeatClose() + 1)); break; } next = this.manager.getNextMeasureHeader(next); } } private String getLilypondBoolean(boolean value) { return (value ? "#t" : "#f"); } private String getLilypondChordFret(int value) { if (value < 0) { return ("x"); } if (value == 0) { return ("o"); } return Integer.toString(value); } private String getLilypondDuration(TGDuration value) { String duration = Integer.toString(value.getValue()); if (value.isDotted()) { duration += ("."); } else if (value.isDoubleDotted()) { duration += (".."); } return duration; } private String getLilypondKey(int keySignature, int value) { String[] LILYPOND_NOTES = (keySignature <= 7 ? LILYPOND_SHARP_NOTES : LILYPOND_FLAT_NOTES); String key = (LILYPOND_NOTES[value % 12]); for (int i = 4; i < (value / 12); i++) { key += ("'"); } for (int i = (value / 12); i < 4; i++) { key += (","); } return key; } private String getLilypondTuning(TGTrack track) { String tuning = ("\\with { stringTunings = #'( "); for (final TGString string : track.getStrings()) { // Lilypond relates string tuning to MIDI middle C (note 60) tuning += ((string.getValue() - 60) + " "); } tuning += (") }"); return tuning; } private String getLilypondVoice(int voice) { if (voice == -1) { return "\\oneVoice"; } return (voice == 0 ? "\\voiceOne" : "\\voiceTwo"); } private boolean hasMultipleVoices(TGMeasure measure) { int voiceCount = 0; for (int voice = 0; voice < TGBeat.MAX_VOICES; voice++) { if (isVoiceAvailable(measure, voice)) { voiceCount++; } } return (voiceCount > 1); } private String indent(int level) { String indent = new String(); for (int i = 0; i < level; i++) { indent += INDENT; } return indent; } private boolean isAnyTiedTo(TGNote note) { TGMeasure measure = note.getVoice().getBeat().getMeasure(); TGBeat beat = this.manager.getMeasureManager().getNextBeat( measure.getBeats(), note.getVoice().getBeat()); while (measure != null) { while (beat != null) { TGVoice voice = beat.getVoice(note.getVoice().getIndex()); // If is a rest beat, all voice sounds must be stopped. if (voice.isRestVoice()) { return false; } // Check if is there any note at same string. for (final TGNote current : voice.getNotes()) { if (current.getString() == note.getString()) { return current.isTiedNote(); } } beat = this.manager.getMeasureManager().getNextBeat(measure.getBeats(), beat); } measure = this.manager.getTrackManager().getNextMeasure(measure); if (measure != null) { beat = this.manager.getMeasureManager() .getFirstBeat(measure.getBeats()); } } return false; } private boolean isVoiceAvailable(TGMeasure measure, int voice) { for (int i = 0; i < measure.countBeats(); i++) { TGBeat beat = measure.getBeat(i); if (!beat.getVoice(voice).isEmpty()) { return true; } } return false; } private boolean isVoiceAvailable(TGTrack track, int voice) { for (int i = 0; i < track.countMeasures(); i++) { TGMeasure measure = track.getMeasure(i); if (isVoiceAvailable(measure, voice)) { return true; } } return false; } private String toBase26(int value) { String result = new String(); int base = value; while (base > 25) { result = ((char) ((base % 26) + 'A') + result); base = base / 26 - 1; } return ((char) (base + 'A') + result); } private String trackID(int index, String suffix) { return ("Track" + this.toBase26(index) + suffix); } private String trackVoiceID(int index, String prefix, String suffix) { return (prefix + "Voice" + this.toBase26(index) + suffix); } public void writeSong(TGSong song) { this.manager = new TGSongManager(); this.manager.setSong(song); this.addFunctions(); this.addVersion(); this.addPaper(song); this.addLayout(); this.addCommands(); this.addSongDefinitions(song); this.addSong(song); this.writer.flush(); this.writer.close(); } }