package org.herac.tuxguitar.midiinput;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.SortedSet;
import javax.sound.midi.ShortMessage;
import javax.swing.Timer;
import org.apache.log4j.Logger;
import org.herac.tuxguitar.gui.TuxGuitar;
import org.herac.tuxguitar.gui.actions.ActionLock;
import org.herac.tuxguitar.gui.editors.TablatureEditor;
import org.herac.tuxguitar.gui.editors.fretboard.FretBoard;
import org.herac.tuxguitar.gui.editors.tab.Caret;
import org.herac.tuxguitar.gui.editors.tab.TGBeatImpl;
import org.herac.tuxguitar.gui.editors.tab.TGMeasureImpl;
import org.herac.tuxguitar.gui.editors.tab.TGNoteImpl;
import org.herac.tuxguitar.gui.editors.tab.TGTrackImpl;
import org.herac.tuxguitar.gui.tools.scale.ScaleManager;
import org.herac.tuxguitar.gui.undo.undoables.measure.UndoableMeasureGeneric;
import org.herac.tuxguitar.gui.util.MessageDialog;
import org.herac.tuxguitar.song.managers.TGSongManager;
import org.herac.tuxguitar.song.models.TGBeat;
import org.herac.tuxguitar.song.models.TGChord;
import org.herac.tuxguitar.song.models.TGDuration;
import org.herac.tuxguitar.song.models.TGNote;
import org.herac.tuxguitar.song.models.TGString;
import org.herac.tuxguitar.song.models.TGVoice;
import org.herac.tuxguitar.util.TGSynchronizer;
public class MiProvider {
/** The Logger for this class. */
public static final transient Logger LOG = Logger.getLogger(MiProvider.class);
static private MiProvider s_Instance;
static int getFret(int inPitch, int inString) {
// returns the 0-based fret index corresponding to the specified note and
// string
// or -1 if an error occurred
int stringFirstPitch = getStringFirstPitch(inString);
if (stringFirstPitch != -1) {
int fret = inPitch - stringFirstPitch;
if (fret >= 0 && fret < FretBoard.MAX_FRETS)
return (fret);
}
return (-1);
}
static int getStringFirstPitch(int inString) {
// returns the note corresponding to the free vibrating string
// or -1 if the current track does not have such string
TGTrackImpl track = TuxGuitar.instance().getTablatureEditor()
.getTablature().getCaret().getTrack();
if (track != null && track.getStrings().size() >= inString)
return (track.getString(inString).getValue());
return (-1);
}
static public MiProvider instance() {
if (s_Instance == null)
s_Instance = new MiProvider();
return s_Instance;
}
private final int DEVICE_CHANNELS_COUNT = 6; // number of MIDI channels
// supported by the input device
private int f_BaseChannel = 0; // 0-based MIDI channel corresponding to the
// first string
private MiBuffer f_Buffer = new MiBuffer(); // input notes buffer
private int f_ChordMode = MiConfig.CHORD_MODE_DIAGRAM; // current chord mode
private TGBeat f_EchoBeat = null; // beat for echo rendering
private boolean f_EchoLastWasOn = false; // indicates if last note message was
// NOTE_ON
private int[] f_EchoNotes = new int[6]; // list of notes for echo
private int f_EchoTimeOut = MiConfig.DEF_ECHO_TIMEOUT; // time out for echo
// rendering [msec]
private Timer f_EchoTimer = null; // timer for echo rendering
private int f_InputTimeOut = MiConfig.DEF_INPUT_TIMEOUT; // time out for
// chord/scale input
// [msec]
private Timer f_InputTimer = null; // timer for chord/scale input
private long f_MinDuration = MiConfig.DEF_DURATION_THRESHOLD; // notes with
// duration
// lower than
// this
// threshold are
// considered
// unwanted
// noise
private byte f_MinVelocity = MiConfig.DEF_VELOCITY_THRESHOLD; // notes with
// velocity
// lower than
// this
// threshold are
// considered
// unwanted
// noise
private int f_Mode = MiConfig.MODE_FRETBOARD_ECHO; // current mode
private MiProvider() {
echo_ResetNotes();
}
private void chord_AddNote(byte inString, byte inFret, byte inPitch,
byte inVelocity, long inTimeStamp) {
if (this.f_InputTimer == null) {
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
f_InputTimer.stop();
f_InputTimer = null;
f_Buffer.stopRecording(MiPort.getNotesPortTimeStamp());
// LOG.debug("Chord ended");
if (f_Buffer.finalize(f_MinVelocity, f_MinDuration * 1000) > 0) {
if (!TuxGuitar.instance().getPlayer().isRunning()
&& !TuxGuitar.instance().isLocked() && !ActionLock.isLocked()) {
TablatureEditor editor = TuxGuitar.instance()
.getTablatureEditor();
Caret caret = editor.getTablature().getCaret();
TGTrackImpl track = caret.getTrack();
TGMeasureImpl measure = caret.getMeasure();
TGBeat beat = caret.getSelectedBeat();
TGSongManager songMgr = TuxGuitar.instance().getSongManager();
TGChord chord = f_Buffer
.toChord(measure.getTrack().stringCount());
// TGBeat _beat = this.f_Buffer.toBeat();
// emulates InsertChordAction
ActionLock.lock();
UndoableMeasureGeneric undoable = UndoableMeasureGeneric
.startUndo();
if (f_ChordMode == MiConfig.CHORD_MODE_ALL) {
songMgr.getMeasureManager().cleanBeat(beat);
TGVoice voice = beat.getVoice(caret.getVoice());
Iterator it = track.getStrings().iterator();
while (it.hasNext()) {
TGString string = (TGString) it.next();
int value = chord.getFretValue(string.getNumber() - 1);
if (value >= 0) {
TGNote note = new TGNoteImpl();
note.setValue(value);
note.setVelocity(editor.getTablature().getCaret()
.getVelocity());
note.setString(string.getNumber());
TGDuration duration = voice.getDuration().clone();
songMgr.getMeasureManager().addNote(beat, note, duration,
voice.getIndex());
}
}
}
songMgr.getMeasureManager().addChord(beat, chord);
TuxGuitar.instance().getFileHistory().setUnsavedFile();
editor.getTablature().getViewLayout().fireUpdate(
measure.getNumber());
TuxGuitar.instance().getUndoableManager().addEdit(
undoable.endUndo());
ActionLock.unlock();
TuxGuitar.instance().updateCache(true);
}
}
}
};
if (inVelocity > 0) {
// LOG.debug("New chord");
this.f_Buffer.startRecording(MiPort.getNotesPortTimeStamp());
this.f_Buffer.addEvent(inString, inFret, inPitch, inVelocity,
inTimeStamp);
this.f_InputTimer = new Timer(f_InputTimeOut, taskPerformer);
this.f_InputTimer.start();
}
} else {
this.f_Buffer
.addEvent(inString, inFret, inPitch, inVelocity, inTimeStamp);
if (inVelocity > 0)
this.f_InputTimer.restart();
}
}
private void echo(int inString, int inFret, boolean inIsNoteOn) {
if (f_EchoTimer == null) {
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (f_EchoLastWasOn) {
f_EchoTimer.restart();
} else {
f_EchoTimer.stop();
f_EchoTimer = null;
if (f_Mode != MiConfig.MODE_SCALES_RECOGNITION)
echo_UpdateExternalBeat(true);
}
}
};
echo_ResetNotes();
echo_BuildAndShowBeat(inString, inFret, inIsNoteOn);
this.f_EchoTimer = new Timer(this.f_EchoTimeOut, taskPerformer);
this.f_EchoTimer.start();
} else {
echo_BuildAndShowBeat(inString, inFret, inIsNoteOn);
this.f_EchoTimer.restart();
}
}
private void echo_BuildAndShowBeat(int inString, int inFret, boolean inIsOn) {
if (this.f_Mode == MiConfig.MODE_SCALES_RECOGNITION
&& this.f_InputTimer == null)
return;
this.f_EchoNotes[inString - 1] = (inIsOn ? inFret : -1);
this.f_EchoLastWasOn = inIsOn;
TGSongManager songMgr = TuxGuitar.instance().getSongManager();
this.f_EchoBeat = new TGBeatImpl();
for (int s = 0; s < this.f_EchoNotes.length; s++) {
if (this.f_EchoNotes[s] != -1) {
TGNote note = new TGNoteImpl();
note.setString(s + 1);
note.setValue(this.f_EchoNotes[s]);
this.f_EchoBeat.getVoice(0).addNote(note);
}
}
echo_UpdateExternalBeat(false);
}
public void echo_ResetNotes() {
this.f_EchoLastWasOn = false;
for (int s = 0; s < this.f_EchoNotes.length; s++)
this.f_EchoNotes[s] = -1;
}
private void echo_UpdateExternalBeat(boolean inIsEmpty) {
TGSynchronizer.TGRunnable task;
if (inIsEmpty) {
task = new TGSynchronizer.TGRunnable() {
public void run() throws Throwable {
TuxGuitar.instance().hideExternalBeat();
}
};
} else {
task = new TGSynchronizer.TGRunnable() {
public void run() throws Throwable {
TuxGuitar.instance().showExternalBeat(f_EchoBeat);
}
};
}
try {
TGSynchronizer.instance().runLater(task);
} catch (Throwable t) {
MessageDialog.errorMessage(t);
}
}
int getMode() {
return this.f_Mode;
}
private int getString(int inChannel) {
// returns the 1-based string index corresponding to the 0-based specified
// MIDI channel
// or -1 if the current track does not have such string
TGTrackImpl track = TuxGuitar.instance().getTablatureEditor()
.getTablature().getCaret().getTrack();
if (track != null) {
int stringsCount = track.getStrings().size(), stringIndex = inChannel
- (this.f_BaseChannel + (this.DEVICE_CHANNELS_COUNT - stringsCount))
+ 1;
if (stringIndex > 0 && stringIndex <= stringsCount)
return (stringIndex);
}
return (-1);
}
public void noteReceived(ShortMessage inMessage, long inTimeStamp) {
byte pitch = (byte) inMessage.getData1(), velocity = (byte) inMessage
.getData2(), stringIndex = (byte) getString(inMessage.getChannel());
if (stringIndex != -1) {
byte fretIndex = (byte) getFret(pitch, stringIndex);
if (fretIndex != -1) {
switch (inMessage.getCommand()) {
case ShortMessage.NOTE_ON: {
switch (this.f_Mode) {
case MiConfig.MODE_FRETBOARD_ECHO:
if (velocity == 0 || velocity > this.f_MinVelocity) // questo VA
// MODIFICATO!!!
echo(stringIndex, fretIndex, velocity > 0);
break;
case MiConfig.MODE_CHORDS_RECORDING:
if (velocity == 0 || velocity > this.f_MinVelocity) // questo VA
// MODIFICATO!!!
echo(stringIndex, fretIndex, velocity > 0);
chord_AddNote(stringIndex, fretIndex, pitch, velocity, inTimeStamp);
break;
case MiConfig.MODE_SCALES_RECOGNITION:
if (velocity == 0 || velocity > this.f_MinVelocity) // questo VA
// MODIFICATO!!!
echo(stringIndex, fretIndex, velocity > 0);
scale_AddNote(stringIndex, fretIndex, pitch, velocity, inTimeStamp);
break;
case MiConfig.MODE_SONG_RECORDING:
if (velocity == 0 || velocity > this.f_MinVelocity) // questo VA
// MODIFICATO!!!
echo(stringIndex, fretIndex, velocity > 0);
MiRecorder.instance().addNote(stringIndex, fretIndex, pitch,
velocity, inTimeStamp);
break;
}
}
break;
case ShortMessage.NOTE_OFF:
switch (this.f_Mode) {
case MiConfig.MODE_FRETBOARD_ECHO:
echo(stringIndex, fretIndex, false);
break;
case MiConfig.MODE_CHORDS_RECORDING:
echo(stringIndex, fretIndex, false);
chord_AddNote(stringIndex, fretIndex, pitch, (byte) 0, inTimeStamp);
break;
case MiConfig.MODE_SCALES_RECOGNITION:
echo(stringIndex, fretIndex, false);
scale_AddNote(stringIndex, fretIndex, pitch, (byte) 0, inTimeStamp);
break;
case MiConfig.MODE_SONG_RECORDING:
echo(stringIndex, fretIndex, false);
MiRecorder.instance().addNote(stringIndex, fretIndex, pitch,
(byte) 0, inTimeStamp);
break;
}
break;
}
}
}
}
private void scale_AddNote(byte inString, byte inFret, byte inPitch,
byte inVelocity, long inTimeStamp) {
if (f_InputTimer == null) {
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
f_InputTimer.stop();
f_InputTimer = null;
f_Buffer.stopRecording(MiPort.getNotesPortTimeStamp());
// LOG.debug("Scale ended");
if (f_Buffer.finalize(f_MinVelocity, f_MinDuration * 1000) > 0) {
TGBeat beat = f_Buffer.toBeat();
SortedSet<Byte> pitches = f_Buffer.toPitchesSet();
MiScaleFinder.findMatchingScale(pitches);
TuxGuitar.instance().showExternalBeat(beat);
} else {
TuxGuitar.instance().hideExternalBeat();
MiScaleFinder.selectScale(ScaleManager.NONE_SELECTION, 0);
}
TuxGuitar.instance().updateCache(true);
}
};
if (inVelocity > 0) {
// LOG.debug("New scale");
this.f_Buffer.startRecording(MiPort.getNotesPortTimeStamp());
this.f_Buffer.addEvent(inString, inFret, inPitch, inVelocity,
inTimeStamp);
this.f_InputTimer = new Timer(f_InputTimeOut, taskPerformer);
this.f_InputTimer.start();
}
} else {
this.f_Buffer
.addEvent(inString, inFret, inPitch, inVelocity, inTimeStamp);
if (inVelocity > 0)
this.f_InputTimer.restart();
}
}
public void setBaseChannel(int inValue) {
this.f_BaseChannel = inValue;
}
public void setChordMode(int inValue) {
this.f_ChordMode = inValue;
}
public void setEchoTimeOut(int inValue) {
this.f_EchoTimeOut = inValue;
}
public void setInputTimeOut(int inValue) {
this.f_InputTimeOut = inValue;
}
public void setMinDuration(long inValue) {
this.f_MinDuration = inValue;
}
public void setMinVelocity(byte inValue) {
this.f_MinVelocity = inValue;
}
public void setMode(int inValue) {
this.f_Mode = inValue;
echo_ResetNotes();
}
}