Fixed bug in octave changes.
MANUAL = 6,
INSERT_MEASURE = 7,
DELETE_MEASURE = 8,
- SET_TEMPO = 9;
+ SET_TEMPO = 9,
+ PREFERENCES = 10;
/**
* Constructor of the dialogs.
case INSERT_MEASURE: makeInsertMeasureDialog(pane); break;
case DELETE_MEASURE: makeDeleteMeasureDialog(pane); break;
case SET_TEMPO: makeSetTempoDialog(pane); break;
+ case PREFERENCES: makePrefsDialog(pane); break;
}
}
setVisible(true);
}
+ private void makePrefsDialog(Container pane) {
+ /*
+ MidiDevice.Info[] devInfo = MidiSystem.getMidiDeviceInfo();
+ for (int i = 0; i < devInfo.length; i++) {
+ if (MidiSystem.getMidiDevice(devInfo[i]) instanceof Sequencer) {
+
+ } else if (MidiSystem.getMidiDevice(devInfo[i]) instanceof Synthesizer) {
+
+ }
+ }
+ String[] seqNames, synthNames;
+ JPanel pane = (JPanel) this.getContentPane();
+ pane.add(new JLabel("Sequencer"));
+ JComboBox seqBox = new JComboBox(seqNames);
+ pane.add(new JLabel("Synthesizer"));
+ JComboBox synthBox = new JComboBox(synthNames);
+ */
+ }
+
private MooNote note;
private JOptionPane optionPane;
private JTextField pitch;
}
}};
am.put("Play", playAction);
- am.put("Change octave up", createOctaveAction(1));
- am.put("Change octave down", createOctaveAction(-1));
+ am.put("Change octave up", createOctaveAction(true));
+ am.put("Change octave down", createOctaveAction(false));
InputMap im = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
KeyStroke playKey = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0);
/**
* Creates an action for a specific octave.
- * @param octave The octave we want an action for.
+ * @param increase true for increase, false for decrease
*/
- private Action createOctaveAction(final int octave) {
+ private Action createOctaveAction(final boolean increase) {
Action octaveAction = new AbstractAction() {
public void actionPerformed(ActionEvent ae) {
- MooKeyboard.setOctave(octave);
+ MooKeyboard.setRelativeOctave(increase);
}};
return octaveAction;
}
* @param n the octave to start at
*/
public static void setOctave(int n) {
- if ((startNote == 0 && n == -1) || (startNote == 108 && n == 1)) return;
- startNote += n*12;
+ startNote = n * 12;
makeKeyboardMapping();
}
+ /**
+ * Increases or decreases the octave of the lower part of the keyboard (default = 4)
+ * @param increase true for increase, false for decrease
+ */
+ public static void setRelativeOctave(boolean increase) {
+ if (increase) {
+ if (startNote == 108) return;
+ setOctave((startNote/12) + 1);
+ } else {
+ if (startNote == 0) return;
+ setOctave((startNote/12) - 1);
+ }
+ }
+
/** Maps keycodes (array indices) to MIDI note numbers using the following layout:
*
* 2 3 5 6 7 9 0 => # # # # # # #
Sequence seq;
if(command == "New") {
- Moosique.clearSequence();
+ if (!Moosique.promptOnUnsavedChanges()) Moosique.clearSequence();
} else if (command == "Open...") {
// Shows a file chooser. If shown previously, starts in the current directory.
if (directory != null) {
* @author Einar Pehrson
*/
-public class MooNote extends MidiEvent {
+public class MooNote extends MidiEvent implements Cloneable {
- protected MidiEvent noteOffEvent;
- protected ShortMessage noteOnMsg, noteOffMsg;
-
- /**
- * Creates a MooNote from the given NoteOn event in the current track.
- * @param noteOnEvent the NoteOn event of the note
- */
- public MooNote (MidiEvent noteOnEvent) {
- super(noteOnEvent.getMessage(), noteOnEvent.getTick());
- noteOnMsg = (ShortMessage)getMessage();
- }
+ private MidiEvent noteOffEvent;
+ private ShortMessage noteOnMsg, noteOffMsg;
/**
* Creates a MooNote from the given NoteOn event in the current track and creates a reference to
public void setChannel(int channel) {
try {
noteOnMsg.setMessage(noteOnMsg.getCommand(), (byte)channel, noteOnMsg.getData1(), noteOnMsg.getData2());
- if(hasNoteOffEvent()) noteOffMsg.setMessage(noteOffMsg.getCommand(), (byte)channel, noteOffMsg.getData1(), noteOffMsg.getData2());
+ noteOffMsg.setMessage(noteOffMsg.getCommand(), (byte)channel, noteOffMsg.getData1(), noteOffMsg.getData2());
} catch (InvalidMidiDataException e) {}
}
public void setPitch(int pitch) {
try {
noteOnMsg.setMessage(noteOnMsg.getCommand(), noteOnMsg.getChannel(), (byte)pitch, noteOnMsg.getData2());
- if(hasNoteOffEvent()) noteOffMsg.setMessage(noteOffMsg.getCommand(), noteOffMsg.getChannel(), (byte)pitch, noteOffMsg.getData2());
+ noteOffMsg.setMessage(noteOffMsg.getCommand(), noteOffMsg.getChannel(), (byte)pitch, noteOffMsg.getData2());
} catch (InvalidMidiDataException e) {}
}
- /**
- * Transposes the current note the given number of halftones.
- * @param halftones the number of halftones to transpose - positive for up, negative for down
- */
- public void transpose(int halftones) {
- setPitch(getPitch() + halftones);
- }
-
/**
* Sets the velocity of the current note.
* @param vel the velocity of the note (0-127)
public void setVelocity(int vel) {
try {
noteOnMsg.setMessage(noteOnMsg.getCommand(), noteOnMsg.getChannel(), noteOnMsg.getData1(), (byte)vel);
- if(hasNoteOffEvent()) noteOffMsg.setMessage(noteOffMsg.getCommand(), noteOffMsg.getChannel(), noteOffMsg.getData1(), noteOffMsg.getData2());
+ noteOffMsg.setMessage(noteOffMsg.getCommand(), noteOffMsg.getChannel(), noteOffMsg.getData1(), noteOffMsg.getData2());
} catch (InvalidMidiDataException e) {}
}
* @param n the duration of the note in ticks (96 per beat)
*/
public void setDuration(int ticks) {
- if (hasNoteOffEvent()) noteOffEvent.setTick(getTick() + ticks);
+ noteOffEvent.setTick(getTick() + ticks);
}
/**
* @param tick the timestamp of the note in ticks (96 per beat)
*/
public void setTick(long tick) {
- if (hasNoteOffEvent()) noteOffEvent.setTick(tick + getDuration());
+ noteOffEvent.setTick(tick + getDuration());
super.setTick(tick);
}
+ /**
+ * Transposes the current note the given number of halftones.
+ * @param halftones the number of halftones to transpose - positive for up, negative for down
+ */
+ public void transpose(int halftones) {
+ setPitch(getPitch() + halftones);
+ }
+
/**
* Returns the channel of the current note.
* @return the channel of the note (1-16)
* @return the duration of the note (in ticks)
*/
public int getDuration() {
- if (!hasNoteOffEvent()) return 0;
return (int)(noteOffEvent.getTick() - getTick());
}
- /**
- * Returns whether the NoteOff event was found.
- * @return the note off MidiEvent
- */
- public boolean hasNoteOffEvent() {
- return noteOffEvent != null;
- }
-
/**
* Adds this note (both noteOn and noteOffEvents) to a track.
* @param track the track it'll be added to.
*/
- public void addTo(Track track){
+ public void addTo(Track track) {
track.add(this);
- if (hasNoteOffEvent()) track.add(noteOffEvent);
+ track.add(noteOffEvent);
}
/**
* Removes this note (both noteOn and noteOffEvents) from a track.
* @param track the track it'll be removed from.
*/
- public void removeFrom(Track track){
+ public void removeFrom(Track track) {
track.remove(this);
- if (hasNoteOffEvent()) track.remove(noteOffEvent);
+ track.remove(noteOffEvent);
+ }
+
+ /**
+ * Returns a clone of this note.
+ */
+ public Object clone() {
+ return new MooNote(
+ new MidiEvent((ShortMessage)getMessage().clone(), getTick()),
+ new MidiEvent((ShortMessage)noteOffEvent.getMessage().clone(), noteOffEvent.getTick())
+ );
}
}
repaint();
}
+ /**
+ * Transposes the current note element the given number of halftones.
+ * @param halftones the number of halftones to transpose - positive for up, negative for down
+ */
+ public void transpose(int halftones) {
+ note.transpose(halftones);
+ update();
+ }
+
/**
* Draws the string that shows the note's properties.
* @param g The Graphics object used to draw the strings.
}
/**
- * layout this changed elemnt
+ * Layout this changed elemnt.
*/
protected void newLayout(){
mtv.layoutElement(this,true);
}
+
/**
* Listener that checks the mouse actions on this element.
*/
} else if (source == popupRemove) {
remove();
} else if (source == popupTranspOctUp) {
- note.transpose(12);
- update();
+ transpose(12);
} else if (source == popupTranspOctDown) {
- note.transpose(-12);
- update();
+ transpose(-12);
}
}
}
private String trackName = "";
private int channel = 0;
+ /**
+ * Creates the title bar for an empty track, and therefore an initial channel is required.
+ * @param aTrack the track that this tracktitle is operating on.
+ * @param chan the initial channel
+ */
+ public MooTrackTitle (Track aTrack, int chan) {
+ this(aTrack);
+ channel = chan;
+ }
+
/**
* Creates the title bar.
* @param aTrack the track that this tracktitle is operating on.
private JPopupMenu popup, selPopup;
private JMenu selPopupTranspUp, selPopupTranspDown;
- private JMenuItem popupAdd;
- private JMenuItem selPopupRemove, selPopupTranspUpOct, selPopupTranspDownOct;
+ private JMenuItem popupAdd, popupPaste;
+ private JMenuItem selPopupCopy, selPopupCut, selPopupRemove, selPopupTranspUpOct, selPopupTranspDownOct;
- private ArrayList rects;
- private ArrayList selected;
+ private ArrayList coords, selection, copyBuffer;
private Insets insets;
private int ticksPerSixteenth, popupY = 0;
protected static int viewLength = 0;
super(true);
this.track = track;
this.title = title;
+
+ // Creates instance variables
insets = getInsets();
- selected = new ArrayList();
+ coords = new ArrayList(track.size() / 2);
+ selection = new ArrayList();
+ copyBuffer = new ArrayList();
+
+ // Creates temporary variables
+ MidiEvent note;
+ MooNoteElement elem;
+ extraHeight = Toolkit.getDefaultToolkit().getScreenSize().height - 150;
// Configures panel
setBackground(Color.white);
setLayout(null);
setPreferredSize(new Dimension(VIEW_WIDTH, 140 * NOTE_HEIGHT));
- // Creates temporary variables
- MidiEvent note;
- MooNoteElement elem;
- rects = new ArrayList(track.size() / 2);
- extraHeight = Toolkit.getDefaultToolkit().getScreenSize().height - 150;
-
// Places note elements
for (int i = 0; i < track.size(); i++) {
note = track.get(i);
popupAdd = new JMenuItem("Add note...");
popupAdd.addActionListener(pList);
popup.add(popupAdd);
+ popupPaste = new JMenuItem("Paste");
+ popupPaste.addActionListener(pList);
+ popup.add(popupPaste);
// Creates selection pop-up menu.
selPopup = new JPopupMenu();
+ selPopupCopy = new JMenuItem("Copy selection");
+ selPopupCopy.addActionListener(pList);
+ selPopup.add(selPopupCopy);
+ selPopupCut = new JMenuItem("Cut selection");
+ selPopupCut.addActionListener(pList);
+ selPopup.add(selPopupCut);
selPopupRemove = new JMenuItem("Remove selection");
selPopupRemove.addActionListener(pList);
selPopup.add(selPopupRemove);
Rectangle r = new Rectangle();
if (old){
r = elem.getBounds(r);
- for (Iterator i = rects.iterator(); i.hasNext();){
+ for (Iterator i = coords.iterator(); i.hasNext();){
Object ob = i.next();
if (r.equals(ob)){
- rects.remove(ob);
+ coords.remove(ob);
break;
}
}
// Places the element in the appropriate place.
while(isOccupied(r)) r.translate(NOTE_WIDTH, 0);
elem.setBounds(r);
- rects.add(r);
+ coords.add(r);
if (viewLength < (y + height)){
viewLength = y + height;
if(old)setPreferredSize(new Dimension(VIEW_WIDTH, viewLength + extraHeight));
}
- if(old)repaint();
+ if (old) {
+ validate();
+ repaint();
+ }
}
/**
* @return true if the position is occupied.
*/
private boolean isOccupied(Rectangle r) {
- Iterator it = rects.iterator();
+ Iterator it = coords.iterator();
while (it.hasNext()) {
if(r.intersects((Rectangle)it.next())) return true;
}
layoutElement(elem, false);
setPreferredSize(new Dimension(VIEW_WIDTH, viewLength + extraHeight));
Moosique.setEdited();
+ validate();
repaint();
}
* Adds a standard note to this track.
*/
private void addStandardNote() {
- int row = (popupY - insets.top) / NOTE_HEIGHT;
+ int row = (popupY - insets.top) / NOTE_HEIGHT;
long timestamp = (long)(ticksPerSixteenth * row);
addNote(new MooNote(title.getChannel(), 60, 100, timestamp, Moosique.getSequence().getResolution() / 4));
}
remove(elem);
Rectangle r = new Rectangle();
r = elem.getBounds(r);
- rects.remove(r);
+ coords.remove(r);
Moosique.setEdited();
+ validate();
repaint();
}
/**
- * Deselects all notes.
+ * Selects the given note
+ * @param the note to select
*/
public void selectNote(MooNoteElement elem) {
- selected.add(elem);
+ selection.add(elem);
}
/**
- * Deselects all notes.
+ * Deselects the given note
+ * @param the note to deselect
*/
public void deselectNote(MooNoteElement elem) {
- selected.remove(selected.indexOf(elem));
+ selection.remove(selection.indexOf(elem));
}
/**
* Deselects all notes.
*/
public void deselectAllNotes() {
- Iterator it = selected.iterator();
+ Iterator it = selection.iterator();
while(it.hasNext()) {
((MooNoteElement)it.next()).deselect();
}
- selected.clear();
+ selection.clear();
}
/**
* @return if the given element is the only selected one
*/
public boolean isTheOnlySelected(MooNoteElement elem) {
- Iterator it = selected.iterator();
+ Iterator it = selection.iterator();
while(it.hasNext()) {
if (!it.next().equals(elem)) return false;
}
}
/**
- * Shows a popup-menu with options for the current selection of note elements.
- * @param c the component over which to display the menu
- * @param x the x-coordinate in which to display the menu
- * @param y the y-coordinate in which to display the menu
+ * Copies the current selection.
*/
- public void showSelectionPopup(Component c, int x, int y) {
- selPopup.show(c, x, y);
+ public void copySelectedNotes() {
+ copyBuffer = new ArrayList(selection.size());
+ Iterator it = selection.iterator();
+ while(it.hasNext()) {
+ copyBuffer.add(((MooNoteElement)it.next()).getNote().clone());
+ }
+ Collections.sort(copyBuffer, new Moosique.NoteComparator());
+ }
+
+ /**
+ * Cuts the current selection.
+ */
+ public void cutSelectedNotes() {
+ copySelectedNotes();
+ removeSelectedNotes();
+ }
+
+ /**
+ * Pastes the current copy buffer at the given timestamp.
+ */
+ public void pasteCopiedNotes() {
+ int row = (popupY - insets.top) / NOTE_HEIGHT;
+ long timestamp = (long)(ticksPerSixteenth * row);
+ if (copyBuffer.size() > 0) {
+ long startTime = ((MooNote)copyBuffer.get(0)).getTick();
+ Iterator it = copyBuffer.iterator();
+ while(it.hasNext()) {
+ MooNote mn = (MooNote)((MooNote)it.next()).clone();
+ mn.setTick(mn.getTick() - startTime + timestamp);
+ addNote(mn);
+ }
+ }
+ }
+
+ /**
+ * Removes the current selection.
+ */
+ public void removeSelectedNotes() {
+ Iterator it = selection.iterator();
+ while(it.hasNext()) {
+ removeNote((MooNoteElement)it.next());
+ }
+ selection.clear();
}
/**
* Transposes all selected notes the given number of halftones.
*/
private void transposeSelectedNotes(int halftones) {
- Iterator it = selected.iterator();
+ Iterator it = selection.iterator();
while(it.hasNext()) {
MooNoteElement elem = (MooNoteElement)it.next();
- elem.getNote().transpose(halftones);
- elem.update();
+ elem.transpose(halftones);
}
}
+ /**
+ * Shows a popup-menu with options for the current selection of note elements.
+ * @param c the component over which to display the menu
+ * @param x the x-coordinate in which to display the menu
+ * @param y the y-coordinate in which to display the menu
+ */
+ public void showSelectionPopup(Component c, int x, int y) {
+ selPopup.show(c, x, y);
+ }
+
/**
* Draws the grid that is on the background.
* @param g The Graphics object used to draw the grid.
class PopupListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
+ // Handling panel popup actions.
if (source == popupAdd) {
addStandardNote();
+ } else if (source == popupPaste) {
+ pasteCopiedNotes();
+ // Handling selection popup actions.
+ } else if (source == selPopupCopy) {
+ copySelectedNotes();
+ } else if (source == selPopupCut) {
+ cutSelectedNotes();
} else if (source == selPopupRemove) {
- Iterator it = selected.iterator();
- while(it.hasNext()) {
- removeNote((MooNoteElement)it.next());
- }
- selected.clear();
+ removeSelectedNotes();
} else if (source == selPopupTranspUpOct) {
transposeSelectedNotes(12);
} else if (source == selPopupTranspDownOct) {
if (noteOns.size() == 0) emptyTracks.add(tracks[i]);
// Sorts the note lists by tick position.
- Comparator c = new Comparator() {
- public int compare(Object o1, Object o2) {
- return (int)(((MidiEvent)o1).getTick() - ((MidiEvent)o2).getTick());
- }
- };
+ Comparator c = new NoteComparator();
Collections.sort(noteOns, c);
Collections.sort(noteOffs, c);
if (off != null) {
tracks[i].add(new MooNote(on, off));
} else {
- tracks[i].add(new MooNote(on));
+ tracks[i].add(new MooNote(on, new MidiEvent((ShortMessage)on.getMessage().clone(), on.getTick() + 48)));
}
iOn.remove();
}
return true;
}
+ /**
+ * Prompts the user .
+ */
+ public static boolean promptOnUnsavedChanges() {
+ if (!isEdited) return false;
+ int exitOption = JOptionPane.showConfirmDialog(gui,
+ "The current sequence has been edited, but not saved.\nDo you wish to continue anyway?",
+ "File not saved - continue?",
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.WARNING_MESSAGE);
+ if (exitOption == JOptionPane.CANCEL_OPTION || exitOption == JOptionPane.CLOSED_OPTION) return true;
+ return false;
+ }
+
/**
* Saves the current sequence to the previously given filename.
*/
try {
MidiSystem.write(seq, 1, new File(file));
filename = file;
+ isEdited = false;
gui.setStatus("Saved " + file);
return true;
} catch (IOException e) {
* Releases all reserved devices and exits the program.
*/
public static void quit() {
- if (isEdited && gui != null) {
- int exitOption = JOptionPane.showConfirmDialog(gui,
- "The current sequence has been edited, but not saved.\nDo you wish to quit anyway?",
- "File not saved - really quit?",
- JOptionPane.OK_CANCEL_OPTION,
- JOptionPane.WARNING_MESSAGE);
- if (exitOption == JOptionPane.CANCEL_OPTION || exitOption == JOptionPane.CLOSED_OPTION) return;
+ if (gui != null) {
+ if (promptOnUnsavedChanges()) return;
}
if (sequencer.isOpen()) sequencer.close();
if (synthesizer.isOpen()) synthesizer.close();
System.exit(0);
}
+
+ /**
+ * A Comparator for sorting lists of MidiEvents.
+ */
+ public static class NoteComparator implements Comparator {
+ public int compare(Object o1, Object o2) {
+ return (int)(((MidiEvent)o1).getTick() - ((MidiEvent)o2).getTick());
+ }
+ }
}
+\f
+jar cmf manif Moosique.jar *.class
+manif: Main-Class: Moosique
+
+\f
+Presentation
+
+EDIT-huset vån 2 ES52
+Onsdag 19/5 10-12
+
+TID PRESENTATION OPPOSITION
+11:00 Group 03 Vi
+11:30 Vi Group 11
+
+\f
+Varför ritar den ut de tomma spåren i en ny fil? Rätt, men hur?
+Lägg till redigeringsfunktionerna i menyn. Metoderna flyttas till MooView?
+Fixa kanalnummer 1,2,10 i nya filer.
+Kopiera/flytta spår, möjligt?
+
\f
IO
x Implementera ljudlös körning med -m.
MooTrackView
-x Fixa markera, kopiera, klipp ut och klistra in.
x Highlighta noter som spelas? (Enligt kravspec.)
x Ändra längden på MooNoteElement (JPanel).
Känn av klick på panelens gräns, MouseMotionListener känner av när ny ruta nås.