package org.herac.tuxguitar.io.tef;
import java.io.InputStream;
import java.util.Collections;
import java.util.Comparator;
import org.apache.log4j.Logger;
import org.herac.tuxguitar.gui.editors.tab.TGBeatImpl;
import org.herac.tuxguitar.gui.editors.tab.TGChordImpl;
import org.herac.tuxguitar.gui.editors.tab.TGNoteImpl;
import org.herac.tuxguitar.io.base.TGFileFormat;
import org.herac.tuxguitar.io.base.TGFileFormatException;
import org.herac.tuxguitar.io.base.TGLocalFileImporter;
import org.herac.tuxguitar.io.tef.base.TEChord;
import org.herac.tuxguitar.io.tef.base.TEComponent;
import org.herac.tuxguitar.io.tef.base.TEComponentChord;
import org.herac.tuxguitar.io.tef.base.TEComponentNote;
import org.herac.tuxguitar.io.tef.base.TESong;
import org.herac.tuxguitar.io.tef.base.TETimeSignature;
import org.herac.tuxguitar.io.tef.base.TETrack;
import org.herac.tuxguitar.song.managers.TGSongManager;
import org.herac.tuxguitar.song.models.TGBeat;
import org.herac.tuxguitar.song.models.TGChannel;
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.TGSong;
import org.herac.tuxguitar.song.models.TGString;
import org.herac.tuxguitar.song.models.TGTimeSignature;
import org.herac.tuxguitar.song.models.TGTrack;
public class TESongImporter implements TGLocalFileImporter {
/** The Logger for this class. */
public static final transient Logger LOG = Logger
.getLogger(TESongImporter.class);
private static final int[][] PERCUSSION_TUNINGS = new int[][] {
new int[] { 49, 41, 32 }, new int[] { 49, 51, 42, 50 },
new int[] { 49, 42, 50, 37, 32 }, new int[] { 49, 51, 42, 50, 45, 37 },
new int[] { 49, 51, 42, 50, 45, 37, 41 }, };
protected TGSongManager manager;
protected InputStream stream;
public TESongImporter() {
super();
}
private void addChord(TEChord[] chords, TEComponentChord component,
TGTrack tgTrack, TGMeasure tgMeasure) {
if (component.getChord() >= 0 && component.getChord() < chords.length) {
TEChord chord = chords[component.getChord()];
byte[] strings = chord.getStrings();
TGChord tgChord = new TGChordImpl(tgTrack.stringCount());
tgChord.setName(chord.getName());
for (int i = 0; i < tgChord.countStrings(); i++) {
int value = ((i < strings.length) ? strings[i] : -1);
tgChord.addFretValue(i, value);
}
if (tgChord.countNotes() > 0) {
TGBeat tgBeat = getBeat(tgMeasure, getStart(null, tgMeasure, component
.getPosition()));
tgBeat.setChord(tgChord);
}
}
}
private void addComponents(TESong song) {
for (final TEComponent component : song.getComponents()) {
if (component.getMeasure() >= 0
&& component.getMeasure() < this.manager.getSong()
.countMeasureHeaders()) {
int offset = 0;
TETrack[] tracks = song.getTracks();
for (int i = 0; i < tracks.length; i++) {
int strings = tracks[i].getStrings().length;
int string = (component.getString() - offset);
if (string >= 0 && string < strings && string < 7) {
TGTrack tgTrack = this.manager.getSong().getTrack(i);
TGMeasure tgMeasure = tgTrack.getMeasure(component.getMeasure());
if (component instanceof TEComponentNote) {
addNote(tracks[i], (TEComponentNote) component, string, strings,
tgMeasure);
} else if (component instanceof TEComponentChord) {
addChord(song.getChords(), (TEComponentChord) component, tgTrack,
tgMeasure);
}
}
offset += strings;
}
}
}
}
private void addMeasureValues(TESong song) {
TGTimeSignature timeSignature = new TGTimeSignature();
for (int i = 0; i < this.manager.getSong().countMeasureHeaders(); i++) {
TGMeasureHeader header = this.manager.getSong().getMeasureHeader(i);
TETimeSignature ts = song.getTimeSignature(i);
timeSignature.setNumerator(ts.getNumerator());
timeSignature.getDenominator().setValue(ts.getDenominator());
this.manager.changeTimeSignature(header, timeSignature, false);
}
}
private void addNote(TETrack track, TEComponentNote note, int string,
int strings, TGMeasure tgMeasure) {
int value = note.getFret();
if (track.isPercussion()) {
int tuning = (Math.min((strings - 2), (PERCUSSION_TUNINGS.length)) - 1);
if (string >= 0 && string < PERCUSSION_TUNINGS[tuning].length) {
value += PERCUSSION_TUNINGS[tuning][string];
}
}
TGNote tgNote = new TGNoteImpl();
tgNote.setString(string + 1);
tgNote.setValue(value);
TGDuration tgDuration = getDuration(note.getDuration());
TGBeat tgBeat = getBeat(tgMeasure, getStart(tgDuration, tgMeasure, note
.getPosition()));
tgBeat.getVoice(0).setDuration(tgDuration.clone());
tgBeat.getVoice(0).addNote(tgNote);
}
private void addTrackValues(TETrack[] tracks) {
for (int i = 0; i < tracks.length; i++) {
TGTrack track = this.manager.getSong().getTrack(i);
track.getChannel().setVolume(
(short) (((15 - tracks[i].getVolume()) * 127) / 15));
track.getChannel().setBalance((short) ((tracks[i].getPan() * 127) / 15));
track.getChannel().setInstrument((short) tracks[i].getInstrument());
if (tracks[i].isPercussion()) {
TGChannel.setPercussionChannel(track.getChannel());
}
track.getStrings().clear();
int strings[] = tracks[i].getStrings();
for (int j = 0; j < strings.length; j++) {
if (j >= 7) {
break;
}
track.getStrings().add(
new TGString(j + 1, tracks[i].isPercussion() ? 0
: (96 - strings[j])));
}
}
}
public boolean configure(boolean setDefaults) {
return true;
}
private TGBeat getBeat(TGMeasure measure, long start) {
TGBeat beat = this.manager.getMeasureManager().getBeat(measure, start);
if (beat == null) {
beat = new TGBeatImpl();
beat.setStart(start);
measure.addBeat(beat);
}
return beat;
}
private TGDuration getDuration(int duration) {
TGDuration tgDuration = new TGDuration();
int value = TGDuration.WHOLE;
for (int i = 0; i < (duration / 3); i++) {
value = (value * 2);
}
if ((duration % 3) == 1) {
value = (value * 2);
tgDuration.setDotted(true);
} else if ((duration % 3) == 2) {
tgDuration.setDivision(TGDivisionType.DEFAULT);
}
tgDuration.setValue(value);
return tgDuration;
}
public TGFileFormat getFileFormat() {
return new TGFileFormat("Tef", "*.tef");
}
public String getImportName() {
return "Tef";
}
private long getStart(TGDuration duration, TGMeasure measure, int position) {
float fixedPosition = position;
if (duration != null
&& !duration.getDivision().isEqual(TGDivisionType.NORMAL)) {
fixedPosition = ((fixedPosition - (fixedPosition % 64)) + ((((fixedPosition % 64) * 2) * 2) / 3));
}
long start = ((long) (measure.getStart() + ((fixedPosition * TGDuration.QUARTER_TIME) / 64)));
return start;
}
public TGSong importSong() throws TGFileFormatException {
try {
if (this.manager != null && this.stream != null) {
return this.parseSong(new TEInputStream(this.stream).readSong());
}
} catch (Exception e) {
LOG.error(e);
}
throw new TGFileFormatException();
}
public void init(InputStream stream) {
this.manager = new TGSongManager();
this.stream = stream;
}
private void newTGSong(int tracks, int measures, int tempo) {
this.manager.setSong(this.manager.newSong());
this.manager.getFirstMeasureHeader().getTempo().setValue(tempo);
while (this.manager.getSong().countTracks() < tracks) {
this.manager.createTrack();
}
while (this.manager.getSong().countMeasureHeaders() < measures) {
this.manager.addNewMeasureBeforeEnd();
}
}
private TGSong parseSong(TESong song) {
this.sortComponents(song);
this.newTGSong(song.getTracks().length, song.getMeasures(), song.getTempo()
.getValue());
this.addMeasureValues(song);
this.addTrackValues(song.getTracks());
this.addComponents(song);
return new TGSongAdjuster(this.manager).process();
}
public void sortComponents(TESong song) {
Collections.sort(song.getComponents(), new Comparator<TEComponent>() {
public int compare(TEComponent c1, TEComponent c2) {
if (c1.getMeasure() < c2.getMeasure()) {
return -1;
}
if (c1.getMeasure() > c2.getMeasure()) {
return 1;
}
if (c1.getPosition() < c2.getPosition()) {
return -1;
}
if (c1.getPosition() > c2.getPosition()) {
return 1;
}
if ((c1 instanceof TEComponentNote) && !(c2 instanceof TEComponentNote)) {
return -1;
}
if ((c2 instanceof TEComponentNote) && !(c1 instanceof TEComponentNote)) {
return 1;
}
return 0;
}
});
}
}
class TGSongAdjuster {
protected TGSongManager manager;
public TGSongAdjuster(TGSongManager manager) {
this.manager = manager;
}
public void adjustBeats(TGMeasure measure) {
TGBeat previous = null;
boolean finish = true;
long measureStart = measure.getStart();
long measureEnd = (measureStart + measure.getLength());
for (int i = 0; i < measure.countBeats(); i++) {
TGBeat beat = measure.getBeat(i);
long beatStart = beat.getStart();
long beatLength = beat.getVoice(0).getDuration().getTime();
if (previous != null) {
long previousStart = previous.getStart();
long previousLength = previous.getVoice(0).getDuration().getTime();
// check for a chord in a rest beat
if (beat.getVoice(0).isRestVoice() && beat.isChordBeat()) {
TGBeat candidate = null;
TGBeat next = this.manager.getMeasureManager().getFirstBeat(
measure.getBeats());
while (next != null) {
if (candidate != null && next.getStart() > beat.getStart()) {
break;
}
if (!next.getVoice(0).isRestVoice() && !next.isChordBeat()) {
candidate = next;
}
next = this.manager.getMeasureManager().getNextBeat(
measure.getBeats(), next);
}
if (candidate != null) {
candidate.setChord(beat.getChord());
}
measure.removeBeat(beat);
finish = false;
break;
}
// check the duration
if (previousStart < beatStart
&& (previousStart + previousLength) > beatStart) {
if (beat.getVoice(0).isRestVoice()) {
measure.removeBeat(beat);
finish = false;
break;
}
TGDuration duration = TGDuration
.fromTime((beatStart - previousStart));
previous.getVoice(0).setDuration(duration.clone());
}
}
if ((beatStart + beatLength) > measureEnd) {
if (beat.getVoice(0).isRestVoice()) {
measure.removeBeat(beat);
finish = false;
break;
}
TGDuration duration = TGDuration.fromTime((measureEnd - beatStart));
beat.getVoice(0).setDuration(duration.clone());
}
previous = beat;
}
if (!finish) {
adjustBeats(measure);
}
}
public TGSong process() {
for (final TGTrack track : this.manager.getSong().getTracks()) {
for (final TGMeasure measure : track.getMeasures()) {
this.process(measure);
}
}
return this.manager.getSong();
}
public void process(TGMeasure measure) {
this.manager.getMeasureManager().orderBeats(measure);
this.adjustBeats(measure);
}
}