/*
* Musical Skill Coach - An interactive midi device friendly program to help music students improve their skills
* Copyright (C) 2011 Paul-Emile Gaudet
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package music.ui;
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
import javax.swing.*;
import javax.swing.event.*;
import music.lib.LabeledInteger;
import music.lib.MusicScore;
import music.lib.instruments.Piano;
import music.midi.MidiReceiver;
import music.midi.MidiTransmitter;
import music.modules.NoteTiming;
import music.modules.SightRead;
import music.ui.instruments.BasicInstrumentCanvas;
import music.ui.instruments.PianoKeyCanvas;
import music.ui.lib.MetronomeCanvas; // import music.ui.instruments.RecorderKeyCanvas;
import music.ui.panels.AccuracyPanel;
import music.ui.panels.NoteSelectionPanel;
import music.ui.panels.OctaveSelectionPanel;
import music.ui.panels.Timing;
import music.ui.panels.TrebleBassStaff;
public class MusicalSkillCoach extends JFrame implements KeyListener
{
/**
*
*/
private static final long serialVersionUID = 1L;
protected static final int PIANO_TYPE = 0;
protected JMenuBar menuBar;
protected JMenu file;
protected JMenuItem filePlay;
protected JMenuItem fileExit;
protected JMenu exercises;
protected JMenu exercisePractice;
protected JMenu exerciseSightRead;
protected JMenu exerciseTiming;
protected JMenu keys;
protected JMenu chords;
protected JMenu scales;
protected JMenu instruments;
protected JMenu instrumentPiano;
protected JMenuItem keysEightyEight;
protected JMenuItem keysSixtyOne;
protected JMenuItem keysFortyOne;
protected JTabbedPane tabbedPane;
protected MetronomeCanvas metronome;
protected BasicInstrumentCanvas pianoKeys;
protected JComboBox midiSelector;
protected JLabel statusBar;
protected ActionListener actionPerformed;
protected ListSelectionListener selectionPerformed;
protected FocusListener focusChange;
protected ChangeListener changePerformed;
protected JPanel srPanel;
protected Timing timing;
protected TrebleBassStaff practicePanel;
protected TrebleBassStaff sightReadPanel;
protected AccuracyPanel accuracyPanel;
protected NoteSelectionPanel noteSelectionPanel;
protected OctaveSelectionPanel octaveSelectionPanel;
protected boolean processEvent;
protected MidiDevice midiInDevice;
protected MidiDevice midiOutDevice;
protected MidiTransmitter mt;
protected MidiReceiver mr;
protected SightRead sightReader;
protected NoteTiming noteTiming;
protected MusicScore musicScore;
protected Piano piano;
protected HashMap<JRadioButtonMenuItem, LabeledInteger> keyMenuToItemLut;
protected HashMap<JRadioButtonMenuItem, LabeledInteger> chordMenuToItemLut;
protected HashMap<JRadioButtonMenuItem, LabeledInteger> scaleMenuToItemLut;
protected HashMap<String, ArrayList<MidiDevice>> midiDeviceLookup;
public MusicalSkillCoach()
{
super("Musical Skill Coach");
keyMenuToItemLut = new HashMap<JRadioButtonMenuItem, LabeledInteger>();
chordMenuToItemLut = new HashMap<JRadioButtonMenuItem, LabeledInteger>();
scaleMenuToItemLut = new HashMap<JRadioButtonMenuItem, LabeledInteger>();
piano = new Piano();
actionPerformed = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
actionEvents(e);
}
};
selectionPerformed = new ListSelectionListener()
{
public void valueChanged(ListSelectionEvent e)
{
selectionEvents(e);
}
};
focusChange = new FocusListener()
{
public void focusGained(FocusEvent e)
{
gotFocus(e);
}
public void focusLost(FocusEvent e)
{
lostFocus(e);
}
};
changePerformed = new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
changeEvents(e);
}
};
menuBar = new JMenuBar();
file = new JMenu("File");
file.add(filePlay = new JMenuItem("Play"));
file.add(fileExit = new JMenuItem("Exit"));
exercises = new JMenu("Exercises");
exercises.add(exercisePractice = new JMenu("Practice"));
exercises.add(exerciseSightRead = new JMenu("Sight Read"));
exercises.add(exerciseTiming = new JMenu("Timing"));
keys = new JMenu("Keys");
chords = new JMenu("Chords");
scales = new JMenu("Scales");
instruments = new JMenu("Instrument");
instruments.add(instrumentPiano = new JMenu("Piano"));
instrumentPiano.add(keysEightyEight = new JMenuItem("88 Keys"));
instrumentPiano.add(keysSixtyOne = new JMenuItem("61 Keys"));
instrumentPiano.add(keysFortyOne = new JMenuItem("41 Keys"));
menuBar.add(file);
menuBar.add(exercises);
menuBar.add(keys);
menuBar.add(chords);
menuBar.add(scales);
menuBar.add(instruments);
filePlay.addActionListener(actionPerformed);
fileExit.addActionListener(actionPerformed);
keysEightyEight.addActionListener(actionPerformed);
keysSixtyOne.addActionListener(actionPerformed);
keysFortyOne.addActionListener(actionPerformed);
setJMenuBar(menuBar);
JPanel mainPane = new JPanel();
mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.Y_AXIS));
mainPane.add(tabbedPane = new JTabbedPane());
mainPane.add(metronome = new MetronomeCanvas());
mainPane.add(pianoKeys = new PianoKeyCanvas());
mainPane.add(midiSelector = new JComboBox());
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(0, 1));
panel.add(statusBar = new JLabel("Status messages go here"));
mainPane.add(panel);
practicePanel = new TrebleBassStaff();
sightReadPanel = new TrebleBassStaff();
accuracyPanel = new AccuracyPanel();
noteSelectionPanel = new NoteSelectionPanel();
octaveSelectionPanel = new OctaveSelectionPanel();
srPanel = new JPanel();
srPanel.setLayout(new BoxLayout(srPanel, BoxLayout.X_AXIS));
srPanel.add(sightReadPanel);
panel = new JPanel();
panel.setLayout(new GridLayout(3, 1));
panel.add(accuracyPanel);
panel.add(noteSelectionPanel);
panel.add(octaveSelectionPanel);
srPanel.add(panel);
tabbedPane.addTab("Practice", practicePanel);
tabbedPane.addTab("Sight Reading", srPanel);
tabbedPane.addTab("Timing", timing = new Timing());
setContentPane(mainPane);
pack();
statusBar.setAlignmentX(LEFT_ALIGNMENT);
processEvent = true;
midiSelector.addItem(new String("No MIDI device Selected"));
MidiDevice device = null;
MidiDevice.Info[] midiDevices = MidiSystem.getMidiDeviceInfo();
midiInDevice = midiOutDevice = null;
midiDeviceLookup = new HashMap<String, ArrayList<MidiDevice>>();
ArrayList<MidiDevice> devices;
for (int i = 0; i < midiDevices.length; i++)
{
try
{
device = MidiSystem.getMidiDevice(midiDevices[i]);
if ((!(device instanceof Sequencer) && !(device instanceof Synthesizer)))
{
devices = midiDeviceLookup.get(midiDevices[i].getName());
if (devices == null)
{
devices = new ArrayList<MidiDevice>();
}
devices.add(device);
midiDeviceLookup.put(midiDevices[i].getName(), devices);
}
}
catch (MidiUnavailableException e)
{
}
}
Set<String> s = midiDeviceLookup.keySet();
Iterator<String> iterator = s.iterator();
while (iterator.hasNext())
{
midiSelector.addItem(iterator.next());
}
mt = new MidiTransmitter();
mr = new MidiReceiver();
pianoKeys.setMidiReceiver(mr);
pianoKeys.setMidiTransmitter(mt);
sightReader = new SightRead();
sightReader.setmRec(mr);
sightReader.setmTrans(mt);
sightReader.setTbStaff(sightReadPanel);
sightReader.setAccuracyPanel(accuracyPanel);
sightReader.setStatusBar(statusBar);
sightReader.start();
noteTiming = new NoteTiming(metronome);
noteTiming.setmRec(mr);
noteTiming.setPanel(timing);
noteTiming.start();
noteTiming.setStatusBar(statusBar);
midiSelector.addActionListener(actionPerformed);
JOptionPane
.showMessageDialog(
this,
"This application is very early in development (version 0.7)\n(and now contains the first 8 bars of Beethoven's \"Moonlight Sonata\")\nPlease be patient as I continue to develop it when my time permits.\nFor now, please review this sourceforge project's readme.txt file for more information in 'view all files'");
metronome.setMidiTransmitter(mt);
noteSelectionPanel.setSightReader(sightReader);
octaveSelectionPanel.setSightReader(sightReader);
noteTiming.stop();
getJarFileData("/data/moonlightsonata.txt");
practicePanel.loadNotesFromMusicScore(musicScore);
setInstrumentType(PIANO_TYPE);
scales.setEnabled(false);
chords.setEnabled(false);
tabbedPane.setSelectedComponent(srPanel);
verifyObject(srPanel);
tabbedPane.addKeyListener(this);
tabbedPane.addChangeListener(changePerformed);
}
protected void setInstrumentType(int instrumentType)
{
JRadioButtonMenuItem menuItem;
Iterator<JRadioButtonMenuItem> iterator;
iterator = keyMenuToItemLut.keySet().iterator();
while (iterator.hasNext())
{
menuItem = iterator.next();
menuItem.removeActionListener(actionPerformed);
keys.remove(menuItem);
}
iterator = chordMenuToItemLut.keySet().iterator();
while (iterator.hasNext())
{
menuItem = iterator.next();
menuItem.removeActionListener(actionPerformed);
chords.remove(menuItem);
}
iterator = scaleMenuToItemLut.keySet().iterator();
while (iterator.hasNext())
{
menuItem = iterator.next();
menuItem.removeActionListener(actionPerformed);
scales.remove(menuItem);
}
keyMenuToItemLut.clear();
chordMenuToItemLut.clear();
scaleMenuToItemLut.clear();
ArrayList<LabeledInteger> labels;
Iterator<LabeledInteger> labelIterator;
LabeledInteger labeledInteger;
if (instrumentType == PIANO_TYPE)
{
labels = piano.getKeyLabels();
JRadioButtonMenuItem firstMenuItem;
labelIterator = labels.iterator();
firstMenuItem = null;
while (labelIterator.hasNext())
{
labeledInteger = labelIterator.next();
menuItem = new JRadioButtonMenuItem(labeledInteger.getLabel());
menuItem.addActionListener(actionPerformed);
keys.add(menuItem);
keyMenuToItemLut.put(menuItem, labeledInteger);
if (firstMenuItem == null)
firstMenuItem = menuItem;
}
labels = piano.getChordLabels();
labelIterator = labels.iterator();
firstMenuItem = null;
while (labelIterator.hasNext())
{
labeledInteger = labelIterator.next();
menuItem = new JRadioButtonMenuItem(labeledInteger.getLabel());
menuItem.addActionListener(actionPerformed);
chords.add(menuItem);
chordMenuToItemLut.put(menuItem, labeledInteger);
if (firstMenuItem == null)
firstMenuItem = menuItem;
}
labels = piano.getScaleLabels();
labelIterator = labels.iterator();
firstMenuItem = null;
while (labelIterator.hasNext())
{
labeledInteger = labelIterator.next();
menuItem = new JRadioButtonMenuItem(labeledInteger.getLabel());
menuItem.addActionListener(actionPerformed);
scales.add(menuItem);
scaleMenuToItemLut.put(menuItem, labeledInteger);
if (firstMenuItem == null)
firstMenuItem = menuItem;
}
}
}
public void getJarFileData(String filename)
{
MusicScore ms = new MusicScore();
InputStream jarStream = null;
BufferedReader bufferedReader = null;
try
{
jarStream = MusicalSkillCoach.class.getResourceAsStream(filename);
if (jarStream == null)
return;
bufferedReader = new BufferedReader(new InputStreamReader(jarStream));
String line;
while ((line = bufferedReader.readLine()) != null)
ms.parseLine(line);
}
catch (Exception e)
{
return;
}
finally
{
try
{
if (bufferedReader != null)
bufferedReader.close();
if (jarStream != null)
jarStream.close();
}
catch (IOException e)
{
return;
}
}
this.musicScore = ms;
}
protected void actionEvents(ActionEvent e)
{
Object obj = e.getSource();
if (processEvent == false)
return;
verifyObject(obj);
}
protected void selectionEvents(ListSelectionEvent e)
{
Object obj = e.getSource();
verifyObject(obj);
}
protected void changeEvents(ChangeEvent e)
{
Object obj = e.getSource();
verifyObject(obj);
}
protected void gotFocus(FocusEvent e)
{
Object obj = e.getSource();
if (obj instanceof JTextField)
{
JTextField tf = (JTextField) obj;
tf.setCaretPosition(0);
tf.moveCaretPosition(tf.getText().length());
}
}
protected void lostFocus(FocusEvent e)
{
Object obj = e.getSource();
verifyObject(obj);
}
protected void verifyObject(Object obj)
{
if (processEvent == false)
return;
processEvent = false;
if (obj instanceof JButton)
{
}
else if (obj == midiSelector)
{
selectMidiDevice();
}
else if (obj instanceof JMenuItem)
{
if (obj == filePlay)
{
if (musicScore != null)
{
musicScore.playScore(mt);
}
}
else if (obj == fileExit)
{
if (midiOutDevice != null)
midiOutDevice.close();
System.exit(0);
}
else if (obj == keysEightyEight)
{
pianoKeys.setInstrumentType(PianoKeyCanvas.KEYBOARD_LAYOUT_88KEYS);
pianoKeys.invalidate();
pianoKeys.repaint();
}
else if (obj == keysSixtyOne)
{
pianoKeys.setInstrumentType(PianoKeyCanvas.KEYBOARD_LAYOUT_61KEYS);
pianoKeys.invalidate();
pianoKeys.repaint();
}
else if (obj == keysFortyOne)
{
pianoKeys.setInstrumentType(PianoKeyCanvas.KEYBOARD_LAYOUT_41KEYS);
pianoKeys.invalidate();
pianoKeys.repaint();
}
else
{
JRadioButtonMenuItem jRadio = (JRadioButtonMenuItem) obj;
LabeledInteger li;
if ((li = keyMenuToItemLut.get(jRadio)) != null)
{
piano.setKey(li);
// no time yet to do the jradio button thingie right at the moment, so doing this instead
Iterator<JRadioButtonMenuItem> iterator = keyMenuToItemLut.keySet().iterator();
while (iterator.hasNext())
{
jRadio = iterator.next();
if (jRadio != obj)
jRadio.setSelected(false);
}
}
else if ((li = chordMenuToItemLut.get(jRadio)) != null)
{
piano.setChord(li);
// no time yet to do the jradio button thingie right at the moment, so doing this instead
Iterator<JRadioButtonMenuItem> iterator = chordMenuToItemLut.keySet().iterator();
while (iterator.hasNext())
{
jRadio = iterator.next();
if (jRadio != obj)
jRadio.setSelected(false);
}
}
else if ((li = scaleMenuToItemLut.get(jRadio)) != null)
{
piano.setScale(li);
// no time yet to do the jradio button thingie right at the moment, so doing this instead
Iterator<JRadioButtonMenuItem> iterator = scaleMenuToItemLut.keySet().iterator();
while (iterator.hasNext())
{
jRadio = iterator.next();
if (jRadio != obj)
jRadio.setSelected(false);
}
}
if (tabbedPane.getSelectedComponent() == srPanel)
{
// NOT YET...
}
else if (tabbedPane.getSelectedComponent() == timing)
{
noteTiming.stop();
noteTiming.setFixedScale(piano.getScales(5));
noteTiming.start();
timing.invalidate();
timing.repaint();
}
}
}
else if (obj == tabbedPane)
{
sightReader.stop();
noteTiming.stop();
if (tabbedPane.getSelectedComponent() == srPanel)
{
sightReader.start();
chords.setEnabled(false);
scales.setEnabled(false);
}
else if (tabbedPane.getSelectedComponent() == timing)
{
noteTiming.start();
chords.setEnabled(false);
scales.setEnabled(true);
}
else
{
chords.setEnabled(false);
scales.setEnabled(false);
}
}
processEvent = true;
}
protected void selectMidiDevice()
{
if (midiInDevice != null)
{
mt.clearMidiDevice();
midiInDevice.close();
midiInDevice = null;
}
if (midiOutDevice != null)
{
midiOutDevice.close();
midiOutDevice = null;
}
MidiDevice device;
String selectedDevice = (String) midiSelector.getSelectedItem();
ArrayList<MidiDevice> al = midiDeviceLookup.get(selectedDevice);
if (al == null)
{
return;
}
Iterator<MidiDevice> iterator = al.iterator();
while (iterator.hasNext())
{
device = iterator.next();
if (device.getMaxReceivers() != 0)
midiInDevice = device;
if (device.getMaxTransmitters() != 0)
midiOutDevice = device;
}
if ((midiInDevice != null) && (midiOutDevice != null))
{
try
{
midiInDevice.open();
mt.setDevice(midiInDevice);
}
catch (Exception e)
{
e.printStackTrace();
midiInDevice = midiOutDevice = null;
}
try
{
midiOutDevice.open();
midiOutDevice.getTransmitter().setReceiver(mr);
}
catch (Exception e)
{
e.printStackTrace();
midiInDevice = midiOutDevice = null;
}
}
if ((midiInDevice != null) && (midiOutDevice != null))
{
mt.setProgramChange(0, 0);
mt.setProgramChange(1, 0);
mt.setProgramChange(15, 0x73);
}
// midiSelector.setEnabled(false);
}
@Override
public void keyPressed(KeyEvent e)
{
// System.out.println("PRESS: " + e.getKeyChar());
}
@Override
public void keyReleased(KeyEvent e)
{
// System.out.println("RELEASE: " + e.getKeyChar());
}
@Override
public void keyTyped(KeyEvent e)
{
// System.out.println("TYPED: " + e.getKeyChar());
practicePanel.removeFirstObject();
practicePanel.invalidate();
practicePanel.repaint();
}
public static void main(String args[])
{
final MusicalSkillCoach mscMainWindow = new MusicalSkillCoach();
mscMainWindow.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
mscMainWindow.setLocation(10, 25);
mscMainWindow.setSize(new Dimension(mscMainWindow.getPreferredSize().width, mscMainWindow.getPreferredSize().height));
mscMainWindow.setVisible(true);
}
}