From c31857b9fcb119f0d4c12b96222f66340b3dcc56 Mon Sep 17 00:00:00 2001 From: Einar Pehrson Date: Mon, 19 May 2003 23:51:27 +0000 Subject: [PATCH] Fixed recording!!! ..plus nested transpose popup menus and note-play on double-click etc. --- MooKeyboard.java | 47 ++++++++++--- MooNote.java | 75 +++++++++++--------- MooNoteElement.java | 80 ++++++++++++++++----- MooToolbar.java | 26 ++++--- MooTrackTitle.java | 104 ++++++++++++++++++--------- MooTrackView.java | 167 +++++++++++++++++++++++++++++--------------- MooView.java | 8 ++- Moosique.java | 125 ++++++++++++++++++++------------- To Do.txt | 5 +- 9 files changed, 423 insertions(+), 214 deletions(-) diff --git a/MooKeyboard.java b/MooKeyboard.java index ef4785e..29ffeec 100644 --- a/MooKeyboard.java +++ b/MooKeyboard.java @@ -9,14 +9,17 @@ import java.awt.event.*; public class MooKeyboard extends KeyAdapter { - private boolean[] isOn; private static int startNote; private static int[] keyToNote = new int[120]; + private boolean[] isOn; + private boolean recording; + private MooTrackTitle title; /** * Sets up the synthesizer emulation. */ - public MooKeyboard() { + public MooKeyboard(MooTrackTitle mtt) { + title = mtt; isOn = new boolean[120]; } @@ -34,9 +37,17 @@ public class MooKeyboard extends KeyAdapter { // Retrieves the notenumber from the conversion array. int noteNumber = keyToNote[e.getKeyCode()]; // If note is not already on and the key is mapped to a note, sends the NoteOn event. - if (!isOn[noteNumber] && noteNumber > 0) Moosique.getActiveChannel().noteOn(noteNumber, 127); + if (!isOn[noteNumber] && noteNumber > 0) { + if (recording) { + ShortMessage msg = new ShortMessage(); + msg.setMessage(ShortMessage.NOTE_ON, title.getChannel(), noteNumber, 100); + Moosique.getSequencer().getReceiver().send(msg, -1); + } else { + Moosique.getActiveChannel().noteOn(noteNumber, 127); + } + } isOn[noteNumber] = true; - } catch (ArrayIndexOutOfBoundsException x) { + } catch (Exception x) { return; } } @@ -51,16 +62,36 @@ public class MooKeyboard extends KeyAdapter { // Retrieves the notenumber from the conversion array. int noteNumber = keyToNote[e.getKeyCode()]; // Sends the NoteOff event. - Moosique.getActiveChannel().noteOff(noteNumber); + if (recording) { + ShortMessage msg = new ShortMessage(); + msg.setMessage(ShortMessage.NOTE_OFF, title.getChannel(), noteNumber, 0); + Moosique.getSequencer().getReceiver().send(msg, -1); + } else { + Moosique.getActiveChannel().noteOff(noteNumber); + } isOn[noteNumber] = false; - } catch (ArrayIndexOutOfBoundsException x) { + } catch (Exception x) { return; } } } /** - * Sets the octave of the lower part of the keyboard (default = 4) + * Prepares the keyboard for recording on the current channel. + */ + public void recordEnable() { + recording = true; + } + + /** + * Disables recording and returns to keyjazz mode. + */ + public void recordDisable() { + recording = false; + } + + /** + * Sets the octave of the lower part of the keyboard (default = 4). * @param n the octave to start at */ public static void setOctave(int n) { @@ -69,7 +100,7 @@ public class MooKeyboard extends KeyAdapter { } /** - * Increases or decreases the octave of the lower part of the keyboard (default = 4) + * 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) { diff --git a/MooNote.java b/MooNote.java index fb73341..57cf86c 100644 --- a/MooNote.java +++ b/MooNote.java @@ -44,6 +44,46 @@ public class MooNote extends MidiEvent implements Cloneable, Comparable { } catch (InvalidMidiDataException e) {System.out.println("Invalid data!");} } + /** + * Returns the note off event of this note. + * @return the note off event + */ + public MidiEvent getNoteOffEvent() { + return noteOffEvent; + } + + /** + * Returns the channel of the current note. + * @return the channel of the note (1-16) + */ + public int getChannel() { + return noteOnMsg.getChannel(); + } + + /** + * Returns the pitch of the current note. + * @return the pitch of the note (0-127) + */ + public int getPitch() { + return noteOnMsg.getData1(); + } + + /** + * Returns the velocity of the current note. + * @return the velocity of the note (0-127) + */ + public int getVelocity() { + return noteOnMsg.getData2(); + } + + /** + * Returns the duration of the current note. + * @return the duration of the note (in ticks) + */ + public int getDuration() { + return (int)(noteOffEvent.getTick() - getTick()); + } + /** * Sets the channel of the current note. * @param channel the channel of the note (1-16) @@ -102,38 +142,6 @@ public class MooNote extends MidiEvent implements Cloneable, Comparable { setPitch(getPitch() + halftones); } - /** - * Returns the channel of the current note. - * @return the channel of the note (1-16) - */ - public int getChannel() { - return noteOnMsg.getChannel(); - } - - /** - * Returns the pitch of the current note. - * @return the pitch of the note (0-127) - */ - public int getPitch() { - return noteOnMsg.getData1(); - } - - /** - * Returns the velocity of the current note. - * @return the velocity of the note (0-127) - */ - public int getVelocity() { - return noteOnMsg.getData2(); - } - - /** - * Returns the duration of the current note. - * @return the duration of the note (in ticks) - */ - public int getDuration() { - return (int)(noteOffEvent.getTick() - getTick()); - } - /** * Adds this note (both noteOn and noteOffEvents) to a track. * @param track the track it'll be added to. @@ -167,7 +175,6 @@ public class MooNote extends MidiEvent implements Cloneable, Comparable { * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object */ public int compareTo(Object o) { - return (int)(((MidiEvent)o).getTick() - getTick()); + return (int)(getTick() - ((MidiEvent)o).getTick()); } - } diff --git a/MooNoteElement.java b/MooNoteElement.java index 6080889..beed5c4 100644 --- a/MooNoteElement.java +++ b/MooNoteElement.java @@ -14,7 +14,9 @@ public class MooNoteElement extends JPanel implements Comparable{ private MooTrackView mtv; private MooNote note; private JPopupMenu popup; - private JMenuItem popupRemove, popupProp, popupTranspOctUp, popupTranspOctDown; + private JMenu popupTranspUp, popupTranspDown; + private JMenuItem popupRemove, popupProp; + private JMenuItem[] popupTranspUpItems, popupTranspDownItems; private Rectangle pitchRect, veloRect; private String notePitch, noteVelocity; private boolean selected = false, leftMouseButtonPressed = false, mouseIn = false; @@ -42,19 +44,12 @@ public class MooNoteElement extends JPanel implements Comparable{ // Creates pop-up menu. popup = new JPopupMenu(); - PopupListener pList = new PopupListener(); - popupProp = new JMenuItem("Preferences..."); - popupProp.addActionListener(pList); - popup.add(popupProp); - popupRemove = new JMenuItem("Remove"); - popupRemove.addActionListener(pList); - popup.add(popupRemove); - popupTranspOctUp = new JMenuItem("Transpose one octave up"); - popupTranspOctUp.addActionListener(pList); - popup.add(popupTranspOctUp); - popupTranspOctDown = new JMenuItem("Transpose one octave down"); - popupTranspOctDown.addActionListener(pList); - popup.add(popupTranspOctDown); + popupProp = addMenuItem(popup, "Preferences..."); + popupRemove = addMenuItem(popup, "Remove"); + popupTranspUpItems = new JMenuItem[12]; + popupTranspDownItems = new JMenuItem[12]; + popupTranspUp = createTransposeMenu(popup, popupTranspUpItems, "note up"); + popupTranspDown = createTransposeMenu(popup, popupTranspDownItems, "note down"); } /** @@ -178,11 +173,53 @@ public class MooNoteElement extends JPanel implements Comparable{ mtv.layoutElement(this,true); } + /** + * Adds a menu item with the given command to the given popup menu. + */ + private JMenuItem addMenuItem(JPopupMenu menu, String command) { + JMenuItem item = new JMenuItem(command); + item.addActionListener(new PopupListener()); + menu.add(item); + return item; + } + + /** + * Adds a menu item with the given command to the given menu. + */ + private JMenuItem addMenuItem(JMenu menu, String command) { + JMenuItem item = new JMenuItem(command); + item.addActionListener(new PopupListener()); + menu.add(item); + return item; + } + + /** + * Creates a transpose sub menu with the given title in the given popup menu, + * inserting the items into the given array. + */ + private JMenu createTransposeMenu(JPopupMenu menu, JMenuItem[] items, String title) { + JMenu trans = new JMenu("Transpose " + title); + menu.add(trans); + items[0] = addMenuItem(trans, "One octave"); + for (int i = 1; i < 12; i++) { + items[i] = addMenuItem(trans, (i) + " halftones"); + } + return trans; + } + /** * Listener that checks the mouse actions on this element. */ class MAdapter extends MouseAdapter { + /** + * Plays the note on double-click. + */ + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) + Moosique.getReceiver().send(note.getMessage(), -1); + } + /** * Selects the note if mouse entered with the left mouse button pressed. */ @@ -198,6 +235,7 @@ public class MooNoteElement extends JPanel implements Comparable{ */ public void mouseExited(MouseEvent e) { mouseIn = false; + Moosique.getReceiver().send(note.getNoteOffEvent().getMessage(), -1); } /** @@ -229,7 +267,6 @@ public class MooNoteElement extends JPanel implements Comparable{ } } else { select(); - // Play the note maybeShowPopup(e); } } @@ -257,7 +294,7 @@ public class MooNoteElement extends JPanel implements Comparable{ } /** - * Listens on the actions made to the popupmenu. + * Takes the appropriate action when a user selects an item on the popup menu. */ class PopupListener implements ActionListener { public void actionPerformed(ActionEvent e) { @@ -267,13 +304,18 @@ public class MooNoteElement extends JPanel implements Comparable{ new MooDialog(note); System.out.println("Duration: " + note.getDuration()); newLayout(); - repaint(); + update(); } else if (source == popupRemove) { remove(); - } else if (source == popupTranspOctUp) { + } else if (source == popupTranspUpItems[0]) { transpose(12); - } else if (source == popupTranspOctDown) { + } else if (source == popupTranspDownItems[0]) { transpose(-12); + } else { + for (int i = 1; i < 12; i++) { + if (source == popupTranspUpItems[i]) transpose(i); + else if (source == popupTranspDownItems[i]) transpose(-i); + } } } } diff --git a/MooToolbar.java b/MooToolbar.java index 50aaf4d..1ea47ab 100644 --- a/MooToolbar.java +++ b/MooToolbar.java @@ -102,11 +102,12 @@ public class MooToolbar extends JToolBar { } /** - * Creates a button with the specified image and tooltip. + * Creates a button with the specified image and action command / tooltip. */ - private JButton createButton(String imagelocation, String tooltip) { + private JButton createButton(String imagelocation, String command) { JButton button = new JButton (new ImageIcon(imagelocation)); - button.setToolTipText(tooltip); + button.setToolTipText(command); + button.setActionCommand(command); button.addMouseListener(mouseAdapter); return button; } @@ -133,20 +134,29 @@ public class MooToolbar extends JToolBar { class MAdapter extends MouseAdapter { public void mouseClicked(MouseEvent e) { if (e.getSource() instanceof JButton) { - String toolTip = ((JButton)e.getSource()).getToolTipText(); - if (toolTip == "Play") { + String command = ((JButton)e.getSource()).getActionCommand(); + if (command == "Play") { playpause.setIcon(pauseIcon); + playpause.setActionCommand("Pause"); playpause.setToolTipText("Pause"); Moosique.play(); - } else if (toolTip == "Pause") { + } else if (command == "Pause") { playpause.setIcon(playIcon); + playpause.setActionCommand("Resume"); playpause.setToolTipText("Resume"); Moosique.pause(); - } else if (toolTip == "Resume") { + } else if (command == "Resume") { playpause.setIcon(pauseIcon); + playpause.setActionCommand("Pause"); playpause.setToolTipText("Pause"); Moosique.resume(); - } else if (toolTip == "Stop") { + } else if (command == "Rewind") { + // Different implementation, perhaps? + Moosique.setEditPosition(0); + Moosique.stop(); + } else if (command == "Fast forward") { + + } else if (command == "Stop") { Moosique.stop(); } } else if (e.getSource() instanceof JLabel) { diff --git a/MooTrackTitle.java b/MooTrackTitle.java index 979aed2..6c629d9 100644 --- a/MooTrackTitle.java +++ b/MooTrackTitle.java @@ -15,12 +15,14 @@ public class MooTrackTitle extends JPanel { private Track track; private MetaMessage trackNameMessage; private ShortMessage programChangeMessage; + private MooTrackView mtv; private JTextField title; private MooInstrumentList instruments; private JComboBox channelBox; private JCheckBox mute; private JCheckBox solo; + private JButton record; private String trackName = ""; private int channel = 0; @@ -63,6 +65,7 @@ public class MooTrackTitle extends JPanel { // Creates and places components. setLayout(new GridLayout(4,1)); setBorder(BorderFactory.createLineBorder(Color.black)); + TitleListener tl = new TitleListener(); setPreferredSize(new Dimension(MooTrackView.VIEW_WIDTH,70)); title = new JTextField(trackName); @@ -84,19 +87,7 @@ public class MooTrackTitle extends JPanel { channelBox.setSelectedIndex(channel); - channelBox.addActionListener(new ActionListener(){ - public void actionPerformed(ActionEvent e){ - channel = channelBox.getSelectedIndex(); - MidiEvent me; - MooNote mn; - instruments.setChannel(channel); - for (int j = 0; j < track.size(); j++) { - me = track.get(j); - if (me instanceof MooNote){ - mn = (MooNote)me; - mn.setChannel(channel); - } - }}}); + channelBox.addActionListener(tl); add(channelBox); JPanel checkboxes = new JPanel(); @@ -106,33 +97,21 @@ public class MooTrackTitle extends JPanel { mute.setSelected(false); Moosique.setTrackMute(track, false); mute.setFont(Moosique.getGUI().FONT); - mute.addActionListener(new ActionListener(){ - public void actionPerformed(ActionEvent event){ - boolean selected = mute.isSelected(); - if (selected){ - solo.setSelected(false); - Moosique.setTrackSolo(track, false); - } - Moosique.setTrackMute(track, selected); - - }}); + mute.addActionListener(tl); checkboxes.add(mute); solo = new JCheckBox("Solo"); solo.setSelected(false); Moosique.setTrackSolo(track, false); solo.setFont(Moosique.getGUI().FONT); - solo.addActionListener(new ActionListener(){ - public void actionPerformed(ActionEvent event){ - //setSolo - boolean selected = solo.isSelected(); - if (selected){ - mute.setSelected(false); - Moosique.setTrackMute(track, false); - } - Moosique.setTrackSolo(track, selected); - }}); + solo.addActionListener(tl); checkboxes.add(solo); + + record = new JButton("Record"); + record.setFont(Moosique.getGUI().FONT); + record.addActionListener(tl); + checkboxes.add(record); + add(checkboxes); } @@ -144,6 +123,14 @@ public class MooTrackTitle extends JPanel { return channel; } + /** + * Sets the track view this title should update after recording. + * @param the track view + */ + public void setTrackView(MooTrackView tv) { + mtv = tv; + } + /** * Checks if the focus is lost. */ @@ -152,4 +139,55 @@ public class MooTrackTitle extends JPanel { // Update the MidiEvent containing the title of this track } } + + /** + * Takes the appropriate action when a user selects an item on the popup menu. + */ + class TitleListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + Object source = e.getSource(); + if (source == channelBox) { + channel = channelBox.getSelectedIndex(); + MidiEvent me; + MooNote mn; + instruments.setChannel(channel); + // Query the user before rechannelling??? + for (int j = 0; j < track.size(); j++) { + me = track.get(j); + if (me instanceof MooNote){ + mn = (MooNote)me; + mn.setChannel(channel); + } + } + } else if (source == solo) { + boolean selected = mute.isSelected(); + if (selected){ + solo.setSelected(false); + Moosique.setTrackSolo(track, false); + } + Moosique.setTrackMute(track, selected); + } else if (source == mute) { + boolean selected = solo.isSelected(); + if (selected){ + mute.setSelected(false); + Moosique.setTrackMute(track, false); + } + Moosique.setTrackSolo(track, selected); + } else if (source == record) { + Sequencer sequencer = Moosique.getSequencer(); + if (record.getText() == "Record") { + record.setText("Stop"); + mtv.enableKeyboardRecording(); + sequencer.recordEnable(track, channel); + sequencer.startRecording(); + } else { + record.setText("Record"); + mtv.disableKeyboardRecording(); + sequencer.stopRecording(); + sequencer.recordDisable(track); + mtv.placeNoteElements(); + } + } + } + } } diff --git a/MooTrackView.java b/MooTrackView.java index c1d8b58..f7e8335 100644 --- a/MooTrackView.java +++ b/MooTrackView.java @@ -15,16 +15,18 @@ public class MooTrackView extends JPanel { private Track track; private MooTrackTitle title; - private Rectangle box; + private MooKeyboard keyboard; private JPopupMenu popup, selPopup; private JMenu selPopupTranspUp, selPopupTranspDown; private JMenuItem popupAdd, popupPaste; - private JMenuItem selPopupCopy, selPopupCut, selPopupRemove, selPopupTranspUpOct, selPopupTranspDownOct; + private JMenuItem selPopupCopy, selPopupCut, selPopupRemove; + private JMenuItem[] selPopupTranspUpItems, selPopupTranspDownItems; - private ArrayList coords, copyBuffer; + private ArrayList coords; private TreeSet selection; private Insets insets; + private Rectangle box; private int ticksPerSixteenth, popupY = 0; private boolean leftMouseButtonPressed = false; protected static int viewLength = 0; @@ -38,26 +40,58 @@ public class MooTrackView extends JPanel { */ public MooTrackView (Track track, MooTrackTitle title) { super(true); + + // Defines instance variables this.track = track; this.title = title; - - // Creates instance variables insets = getInsets(); + + // Configures panel + setBackground(Color.white); + setBorder(BorderFactory.createLineBorder(Color.black)); + setLayout(null); + setPreferredSize(new Dimension(VIEW_WIDTH, 140 * NOTE_HEIGHT)); + + placeNoteElements(); + + // Creates panel pop-up menu. + popup = new JPopupMenu(); + popupAdd = addMenuItem(popup, "Add note..."); + popupPaste = addMenuItem(popup, "Paste"); + + // Creates selection pop-up menu. + selPopup = new JPopupMenu(); + selPopupCopy = addMenuItem(selPopup, "Copy selection"); + selPopupCut = addMenuItem(selPopup, "Cut selection"); + selPopupRemove = addMenuItem(selPopup, "Remove selection"); + selPopupTranspUpItems = new JMenuItem[12]; + selPopupTranspDownItems = new JMenuItem[12]; + selPopupTranspUp = createTransposeMenu(selPopup, selPopupTranspUpItems, "selection up"); + selPopupTranspDown = createTransposeMenu(selPopup, selPopupTranspDownItems, "selection down"); + + // Adds listeners for popup menu and keyboard synthesizer. + addMouseListener(new MAdapter()); + keyboard = new MooKeyboard(title); + addKeyListener(keyboard); + } + + /** + * Creates note elements for all MooNotes in the track, and places them in the appropriate place. + */ + public void placeNoteElements() { + // Converts the track. + Moosique.convertTrack(track); + + // Empties the container + removeAll(); coords = new ArrayList(track.size() / 2); selection = new TreeSet(); - copyBuffer = new ArrayList(); // Creates temporary variables MidiEvent note; MooNoteElement elem; extraHeight = Toolkit.getDefaultToolkit().getScreenSize().height - 150; - // Configures panel - setBackground(Color.white); - setBorder(BorderFactory.createLineBorder(Color.black)); - setLayout(null); - setPreferredSize(new Dimension(VIEW_WIDTH, 140 * NOTE_HEIGHT)); - // Places note elements for (int i = 0; i < track.size(); i++) { note = track.get(i); @@ -69,45 +103,8 @@ public class MooTrackView extends JPanel { layoutElement(elem, false); } setPreferredSize(new Dimension(VIEW_WIDTH, viewLength + extraHeight)); - } - - // Creates panel pop-up menu. - popup = new JPopupMenu(); - PopupListener pList = new PopupListener(); - 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); - selPopupTranspUp = new JMenu("Transpose selection up"); - selPopup.add(selPopupTranspUp); - selPopupTranspUpOct = new JMenuItem("One octave"); - selPopupTranspUpOct.addActionListener(pList); - selPopupTranspUp.add(selPopupTranspUpOct); - selPopupTranspDown = new JMenu("Transpose selection down"); - selPopup.add(selPopupTranspDown); - selPopupTranspDownOct = new JMenuItem("One octave"); - selPopupTranspDownOct.addActionListener(pList); - selPopupTranspDown.add(selPopupTranspDownOct); - - // Adds listeners for popup menu and keyboard synthesizer. - addMouseListener(new MAdapter()); - addKeyListener(new MooKeyboard()); - } + } /** * Layouts the element to the right place. @@ -264,12 +261,13 @@ public class MooTrackView extends JPanel { * Copies the current selection. */ public void copySelectedNotes() { - copyBuffer = new ArrayList(selection.size()); + ArrayList copyBuffer = new ArrayList(selection.size()); Iterator it = selection.iterator(); while(it.hasNext()) { copyBuffer.add(((MooNoteElement)it.next()).getNote().clone()); } Collections.sort(copyBuffer); + Moosique.setCopyBuffer(copyBuffer); } /** @@ -286,12 +284,14 @@ public class MooTrackView extends JPanel { public void pasteCopiedNotes() { int row = (popupY - insets.top) / NOTE_HEIGHT; long timestamp = (long)(ticksPerSixteenth * row); + ArrayList copyBuffer = Moosique.getCopyBuffer(); 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); + mn.setChannel(title.getChannel()); addNote(mn); } } @@ -324,8 +324,8 @@ public class MooTrackView extends JPanel { * @param ticks the number of ticks to move the selection. */ public void moveSelectedNotes(int ticks) { - if (ticks > 0) { - // If the selection should be moved downwards, traverses the list in the natural order. + if (ticks < 0) { + // If the selection should be moved upwards, traverses the list in the natural order. Iterator it = selection.iterator(); while(it.hasNext()) { MooNoteElement elem = (MooNoteElement)it.next(); @@ -333,7 +333,7 @@ public class MooTrackView extends JPanel { layoutElement(elem, true); } } else { - // If the selection should be moved upwards, traverses the list in the opposite order. + // If the selection should be moved downwards, traverses the list in the opposite order. ArrayList selectedList = new ArrayList(selection); ListIterator it = selectedList.listIterator(selectedList.size()); while(it.hasPrevious()) { @@ -352,6 +352,54 @@ public class MooTrackView extends JPanel { moveSelectedNotes(ticksPerSixteenth * (y / NOTE_HEIGHT)); } + /** + * Enables keyboard recording. + */ + public void enableKeyboardRecording() { + keyboard.recordEnable(); + } + + /** + * Disables keyboard recording. + */ + public void disableKeyboardRecording() { + keyboard.recordDisable(); + } + + /** + * Adds a menu item with the given command to the given popup menu. + */ + private JMenuItem addMenuItem(JPopupMenu menu, String command) { + JMenuItem item = new JMenuItem(command); + item.addActionListener(new PopupListener()); + menu.add(item); + return item; + } + + /** + * Adds a menu item with the given command to the given menu. + */ + private JMenuItem addMenuItem(JMenu menu, String command) { + JMenuItem item = new JMenuItem(command); + item.addActionListener(new PopupListener()); + menu.add(item); + return item; + } + + /** + * Creates a transpose sub menu with the given title in the given popup menu, + * inserting the items into the given array. + */ + private JMenu createTransposeMenu(JPopupMenu menu, JMenuItem[] items, String title) { + JMenu trans = new JMenu("Transpose " + title); + menu.add(trans); + items[0] = addMenuItem(trans, "One octave"); + for (int i = 1; i < 12; i++) { + items[i] = addMenuItem(trans, (i) + " halftones"); + } + return trans; + } + /** * Shows a popup-menu with options for the current selection of note elements. * @param c the component over which to display the menu @@ -434,7 +482,7 @@ public class MooTrackView extends JPanel { } /** - * Listens on actions on the popup menu and executes the appropriate action. + * Takes the appropriate action when a user selects an item on the popup menu. */ class PopupListener implements ActionListener { public void actionPerformed(ActionEvent e) { @@ -451,10 +499,15 @@ public class MooTrackView extends JPanel { cutSelectedNotes(); } else if (source == selPopupRemove) { removeSelectedNotes(); - } else if (source == selPopupTranspUpOct) { + } else if (source == selPopupTranspUpItems[0]) { transposeSelectedNotes(12); - } else if (source == selPopupTranspDownOct) { + } else if (source == selPopupTranspDownItems[0]) { transposeSelectedNotes(-12); + } else { + for (int i = 1; i < 12; i++) { + if (source == selPopupTranspUpItems[i]) transposeSelectedNotes(i); + else if (source == selPopupTranspDownItems[i]) transposeSelectedNotes(-i); + } } } } diff --git a/MooView.java b/MooView.java index c97a4a4..a5ebf90 100644 --- a/MooView.java +++ b/MooView.java @@ -60,7 +60,9 @@ public class MooView extends JScrollPane { trackPanel.setLayout(new FlowLayout()); MooTrackTitle title = new MooTrackTitle(tracks[0]); titlePanel.add(title); - trackPanel.add(new MooTrackView(tracks[0], title)); + MooTrackView mtv = new MooTrackView(tracks[0], title); + trackPanel.add(mtv); + title.setTrackView(mtv); } else { if (showProgress) { // Creates dialog for progress bar. @@ -85,7 +87,9 @@ public class MooView extends JScrollPane { if (Moosique.shouldBeDrawn(tracks[i])) { MooTrackTitle title = new MooTrackTitle(tracks[i]); titlePanel.add(title); - trackPanel.add(new MooTrackView(tracks[i], title)); + MooTrackView mtv = new MooTrackView(tracks[i], title); + trackPanel.add(mtv); + title.setTrackView(mtv); if (showProgress) progressBar.setValue(i); else System.out.print("."); } else { diff --git a/Moosique.java b/Moosique.java index 8de399f..9089a2f 100644 --- a/Moosique.java +++ b/Moosique.java @@ -22,7 +22,7 @@ public class Moosique { private static MidiChannel activeChannel; private static MidiEvent[] timeSignatures, tempoChanges; - private static ArrayList emptyTracks; + private static ArrayList copyBuffer, emptyTracks; private static Map trackMute = new HashMap(); private static Map trackSolo = new HashMap(); private static Thread player; @@ -141,6 +141,14 @@ public class Moosique { return channels; } + /** + * Returns the current copy buffer. + * @return the current copy buffer + */ + public static ArrayList getCopyBuffer() { + return copyBuffer; + } + /** * Returns the current editing position of the sequencer. * @return the tick position @@ -290,6 +298,14 @@ public class Moosique { activeChannel = channels[channel]; } + /** + * Sets the current copy buffer. + * @param the copy buffer + */ + public static void setCopyBuffer(ArrayList buffer) { + copyBuffer = buffer; + } + /** * Sets whether empty tracks should be drawn * @param state true if empty tracks should be drawn @@ -466,10 +482,65 @@ public class Moosique { emptyTracks = new ArrayList(); trackSolo = new HashMap(); trackMute = new HashMap(); + copyBuffer = new ArrayList(); } catch (InvalidMidiDataException e) {} // Sends sequence to GUI. if (gui != null) gui.setSequence(seq); } + + /** + * Wraps each NoteOn event in the track with its NoteOff event in a MooNote. + */ + public static void convertTrack(Track track) { + // Searches the track for NoteOn and NoteOff events + ArrayList noteOns = new ArrayList(track.size() / 2); + ArrayList noteOffs = new ArrayList(track.size() / 2); + MidiEvent event; + for (int j = 0; j < track.size(); j++) { + event = track.get(j); + if (event.getMessage().getStatus() >= 144 && + event.getMessage().getStatus() < 160) noteOns.add(event); + if (event.getMessage().getStatus() >= 128 && + event.getMessage().getStatus() < 144) noteOffs.add(event); + } + noteOns.trimToSize(); + noteOffs.trimToSize(); + if (noteOns.size() == 0) emptyTracks.add(track); + + // Sorts the note lists by tick position. + Comparator c = new MidiEventComparator(); + Collections.sort(noteOns, c); + Collections.sort(noteOffs, c); + + // Replaces each NoteOn event it with a MooNote containing a reference to the NoteOff event. + Iterator iOn = noteOns.iterator(), iOff; + MidiEvent on, off = null, nextOff; + ShortMessage onMsg, nextOffMsg; + while(iOn.hasNext()) { + on = (MidiEvent)iOn.next(); + onMsg = (ShortMessage)on.getMessage(); + iOff = noteOffs.iterator(); + while(iOff.hasNext()) { + nextOff = (MidiEvent)iOff.next(); + nextOffMsg = (ShortMessage)nextOff.getMessage(); + if(onMsg.getChannel() == nextOffMsg.getChannel() && + onMsg.getData1() == nextOffMsg.getData1() && + c.compare(nextOff, on) > 0) { + off = nextOff; + iOff.remove(); + break; + } + + } + track.remove(on); + if (off != null) { + track.add(new MooNote(on, off)); + } else { + track.add(new MooNote(on, new MidiEvent((ShortMessage)on.getMessage().clone(), on.getTick() + 48))); + } + iOn.remove(); + } + } /** * Loads a MIDI sequence from the given file. @@ -491,6 +562,7 @@ public class Moosique { emptyTracks = new ArrayList(); trackMute = new HashMap(); trackSolo = new HashMap(); + copyBuffer = new ArrayList(); // Searches track 0 for changes in tempo and time signature. MidiEvent event; @@ -509,56 +581,9 @@ public class Moosique { // timeSignatures = ts.toArray(timeSignatures); // tempoChanges = tc.toArray(tempoChanges); - // Wraps each NoteOn event with its NoteOff event in a MooNote - ArrayList noteOns, noteOffs; + // Converts tracks. for (int i = 0; i < tracks.length; i++) { - // Searches the sequence for NoteOn and NoteOff events - noteOns = new ArrayList(tracks[i].size() / 2); - noteOffs = new ArrayList(tracks[i].size() / 2); - for (int j = 0; j < tracks[i].size(); j++) { - event = tracks[i].get(j); - if (event.getMessage().getStatus() >= 144 && - event.getMessage().getStatus() < 160) noteOns.add(event); - if (event.getMessage().getStatus() >= 128 && - event.getMessage().getStatus() < 144) noteOffs.add(event); - } - noteOns.trimToSize(); - noteOffs.trimToSize(); - if (noteOns.size() == 0) emptyTracks.add(tracks[i]); - - // Sorts the note lists by tick position. - Comparator c = new MidiEventComparator(); - Collections.sort(noteOns, c); - Collections.sort(noteOffs, c); - - // Replaces each NoteOn event it with a MooNote containing a reference to the NoteOff event. - Iterator iOn = noteOns.iterator(), iOff; - MidiEvent on, off = null, nextOff; - ShortMessage onMsg, nextOffMsg; - while(iOn.hasNext()) { - on = (MidiEvent)iOn.next(); - onMsg = (ShortMessage)on.getMessage(); - iOff = noteOffs.iterator(); - while(iOff.hasNext()) { - nextOff = (MidiEvent)iOff.next(); - nextOffMsg = (ShortMessage)nextOff.getMessage(); - if(onMsg.getChannel() == nextOffMsg.getChannel() && - onMsg.getData1() == nextOffMsg.getData1() && - c.compare(nextOff, on) > 0) { - off = nextOff; - iOff.remove(); - break; - } - - } - tracks[i].remove(on); - if (off != null) { - tracks[i].add(new MooNote(on, off)); - } else { - tracks[i].add(new MooNote(on, new MidiEvent((ShortMessage)on.getMessage().clone(), on.getTick() + 48))); - } - iOn.remove(); - } + convertTrack(tracks[i]); } // Sends sequence to GUI and sequencer, then returns if (gui != null) gui.setSequence(seq); diff --git a/To Do.txt b/To Do.txt index 321bd18..f53632a 100644 --- a/To Do.txt +++ b/To Do.txt @@ -1,6 +1,6 @@ -jar cmf manif Moosique.jar *.class +jar cmf manif Moosique.jar *.class *.java midi\*.mid images\*.gif Manual.txt manif: Main-Class: Moosique @@ -23,12 +23,11 @@ Kopiera/flytta sp IO x Implementera ljudlös körning med -m. x Lägg till alla metameddelanden i filerna vi skapar. Annars är det lite dumt. -x Play hänger sig om man ändrar duration på en not.. +x Play hänger sig om man ändrar duration på en not. x Spara konfiguration? Arbetskatalog Fem senast öppnade filerna Valda MIDI-enheter -x Inspelning! TEMPO / TAKTART x Räkna ut tempo och taktart. Skicka (på nåt sätt) tempovektorn till MooToolbar. -- 2.39.2