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();
}
}