/* Author: James Pate Williams (c) 2001 White noise music usage: java WhiteNoise2 */ import java.awt.*; import java.awt.event.*; import java.util.*; import javax.sound.midi.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.table.*; import javax.swing.event.*; /* * @(#)MidiSynth.java 1.15 99/12/03 * * Copyright (c) 1999 Sun Microsystems, Inc. All Rights Reserved. * * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, * modify and redistribute this software in source and binary code form, * provided that i) this copyright notice and license appear on all copies of * the software; and ii) Licensee does not utilize the software in a manner * which is disparaging to Sun. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * This software is not designed or intended for use in on-line control of * aircraft, air traffic, aircraft navigation or aircraft communications; or in * the design, construction, operation or maintenance of any nuclear * facility. Licensee represents and warrants that it will not use or * redistribute the Software for such purposes. */ /** * Table for 128 general MIDI melody instuments. */ class InstrumentsTable extends JPanel { private String names[] = { "Piano", "Chromatic Perc.", "Organ", "Guitar", "Bass", "Strings", "Ensemble", "Brass", "Reed", "Pipe", "Synth Lead", "Synth Pad", "Synth Effects", "Ethnic", "Percussive", "Sound Effects" }; private int col = 0, row = 0; private int nRows = 8; private int nCols = names.length; // just show 128 instruments // Pate added the following private data private Instrument[] instruments; private Synthesizer synthesizer; private JTable table; public InstrumentsTable() { setLayout(new BorderLayout()); TableModel dataModel = new AbstractTableModel() { public int getColumnCount() { return nCols; } public int getRowCount() { return nRows;} public Object getValueAt(int r, int c) { if (instruments != null) { return instruments[c*nRows+r].getName(); } else { return Integer.toString(c*nRows+r); } } public String getColumnName(int c) { return names[c]; } public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } public boolean isCellEditable(int r, int c) {return false;} public void setValueAt(Object obj, int r, int c) {} }; table = new JTable(dataModel); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ListSelectionModel lsm = table.getSelectionModel(); // Listener for row changes //ListSelectionModel lsm = table.getSelectionModel(); lsm.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { ListSelectionModel sm = (ListSelectionModel) e.getSource(); if (!sm.isSelectionEmpty()) { row = sm.getMinSelectionIndex(); } programChange(col*nRows+row); } }); // Listener for column changes lsm = table.getColumnModel().getSelectionModel(); lsm.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { ListSelectionModel sm = (ListSelectionModel) e.getSource(); if (!sm.isSelectionEmpty()) { col = sm.getMinSelectionIndex(); } programChange(col*nRows+row); } }); table.setPreferredScrollableViewportSize(new Dimension(nCols*110, 200)); table.setCellSelectionEnabled(true); table.setColumnSelectionAllowed(true); for (int i = 0; i < names.length; i++) { TableColumn column = table.getColumn(names[i]); column.setPreferredWidth(110); } table.setAutoResizeMode(table.AUTO_RESIZE_OFF); JScrollPane sp = new JScrollPane(table); sp.setVerticalScrollBarPolicy(sp.VERTICAL_SCROLLBAR_NEVER); sp.setHorizontalScrollBarPolicy(sp.HORIZONTAL_SCROLLBAR_ALWAYS); add(sp); } public Dimension getPreferredSize() { return new Dimension(800,170); } public Dimension getMaximumSize() { return new Dimension(800,170); } private void programChange(int program) { if (instruments != null) { synthesizer.loadInstrument(instruments[program]); // Pate added the next three lines MidiChannel midiChannels[] = synthesizer.getChannels(); for (int i = 0; i < midiChannels.length; i++) midiChannels[i].programChange(program); } /*cc.channel.programChange(program); if (record) { createShortEvent(PROGRAM, program); }*/ } // Pate added this method void setData(Instrument[] i, Synthesizer s) { instruments = i; synthesizer = s; } public void initTable() { ListSelectionModel lsm = table.getSelectionModel(); lsm.setSelectionInterval(0,0); lsm = table.getColumnModel().getSelectionModel(); lsm.setSelectionInterval(0,0); } } // end InstrumentsTable /** * A collection of MIDI controllers. */ class Controls extends JPanel implements /*ActionListener,*/ ChangeListener, ItemListener { //public JButton recordB; //JMenu menu; //int fileNum = 0; final int REVERB = 91; int beats = 108, velocity = 64; JCheckBox soloCB, monoCB, muteCB/*, sustCB*/; JSlider veloS, presS, bendS, revbS, beatS; MidiChannel midiChannel; public Controls() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setBorder(new EmptyBorder(5,10,5,10)); JPanel p = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); veloS = createSlider("Velocity", p, 0, 127, 64); presS = createSlider("Pressure", p, 0, 127, 64); revbS = createSlider("Reverb", p, 0, 127, 64); beatS = createSlider("BPM", p, 40, 208, beats); // create a slider with a 14-bit range of values for pitch-bend bendS = create14BitSlider("Bend", p); p.add(Box.createHorizontalStrut(10)); add(p); p = new JPanel(); p.setBorder(new EmptyBorder(10,0,10,0)); p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); /*JComboBox combo = new JComboBox(); combo.setPreferredSize(new Dimension(120,25)); combo.setMaximumSize(new Dimension(120,25)); for (int i = 1; i <= 16; i++) { combo.addItem("Channel " + String.valueOf(i)); } combo.addItemListener(this); p.add(combo); p.add(Box.createHorizontalStrut(20));*/ muteCB = createCheckBox("Mute", p); soloCB = createCheckBox("Solo", p); monoCB = createCheckBox("Mono", p); //sustCB = createCheckBox("Sustain", p); /*createButton("All Notes Off", p); p.add(Box.createHorizontalStrut(10)); p.add(mouseOverCB); p.add(Box.createHorizontalStrut(10)); recordB = createButton("Record...", p); add(p);*/ } /*public JButton createButton(String name, JPanel p) { JButton b = new JButton(name); b.addActionListener(this); p.add(b); return b; }*/ private JCheckBox createCheckBox(String name, JPanel p) { JCheckBox cb = new JCheckBox(name); cb.addItemListener(this); p.add(cb); return cb; } private JSlider createSlider(String name, JPanel p, int min, int max, int value) { JSlider slider = new JSlider(JSlider.HORIZONTAL, min, max, value); slider.addChangeListener(this); TitledBorder tb = new TitledBorder(new EtchedBorder()); tb.setTitle(name + " = " + Integer.toString(value)); slider.setBorder(tb); p.add(slider); p.add(Box.createHorizontalStrut(5)); return slider; } private JSlider create14BitSlider(String name, JPanel p) { JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 16383, 8192); slider.addChangeListener(this); TitledBorder tb = new TitledBorder(new EtchedBorder()); tb.setTitle(name + " = 8192"); slider.setBorder(tb); p.add(slider); p.add(Box.createHorizontalStrut(5)); return slider; } public void stateChanged(ChangeEvent e) { JSlider slider = (JSlider) e.getSource(); int value = slider.getValue(); TitledBorder tb = (TitledBorder) slider.getBorder(); String s = tb.getTitle(); tb.setTitle(s.substring(0, s.indexOf('=')+1) + s.valueOf(value)); if (s.startsWith("Velocity")) { velocity = value; } else if (s.startsWith("Pressure")) { midiChannel.setChannelPressure(value); } else if (s.startsWith("Bend")) { midiChannel.setPitchBend(value); } else if (s.startsWith("Reverb")) { midiChannel.controlChange(REVERB, value); } else if (s.startsWith("BPM")) { beats = value; } slider.repaint(); } public void itemStateChanged(ItemEvent e) { /*if (e.getSource() instanceof JComboBox) { JComboBox combo = (JComboBox) e.getSource(); cc = channels[combo.getSelectedIndex()]; cc.setComponentStates(); } else {*/ JCheckBox cb = (JCheckBox) e.getSource(); String name = cb.getText(); if (name.startsWith("Mute")) { midiChannel.setMute(/*cc.mute = */cb.isSelected()); } else if (name.startsWith("Solo")) { midiChannel.setSolo(/*cc.solo = */cb.isSelected()); } else if (name.startsWith("Mono")) { midiChannel.setMono(/*cc.mono = */cb.isSelected()); }/* else if (name.startsWith("Sustain")) { cc.sustain = cb.isSelected(); cc.channel.controlChange(SUSTAIN, cc.sustain ? 127 : 0); }*/ //} } /*public void actionPerformed(ActionEvent e) { JButton button = (JButton) e.getSource(); if (button.getText().startsWith("All")) { for (int i = 0; i < channels.length; i++) { channels[i].channel.allNotesOff(); } for (int i = 0; i < keys.size(); i++) { ((Key) keys.get(i)).setNoteState(OFF); } } else if (button.getText().startsWith("Record")) { if (recordFrame != null) { recordFrame.toFront(); } else { recordFrame = new RecordFrame(); } } }*/ // Pate added the following methods public int getBeats() { return beats; } public int getVelocity() { return velocity; } public void setMidiChannel(MidiChannel mc) { midiChannel = mc; } } // End class Controls // end of Sun's contribution to the program class WhiteNoise2Panel extends JPanel { int deltaX, deltaY, number; int x0, x1, y0, y1; int[] note, duration; public WhiteNoise2Panel(int iWidth, int iHeight) { number = 0; x0 = iWidth / 8; x1 = 7 * x0; y0 = iHeight / 16; y1 = 2 * y0; deltaY = (y1 - y0) / 4; } public void paintComponent(Graphics g) { int i, x, y; super.paintComponent(g); y = y0; for (i = 0; i < 5; i++) { g.drawLine(x0, y, x1, y); y += deltaY; } g.drawLine(x0, y0, x0, y1); g.drawLine(x1, y0, x1, y1); if (number != 0) deltaX = (x1 - x0) / (2 * number + 1); x = x0 + deltaX; for (i = 0; i < number; i++) { y = (8 - note[i]) * deltaY / 2 + y0 - deltaY / 2; switch (duration[i]) { case 0: // whole note g.drawArc(x, y, deltaX, deltaY, 0, 360); break; case 1: // half note g.fillArc(x, y, deltaX, deltaY, 0, 360); break; case 2: // quarter note g.fillArc(x, y, deltaX, deltaY, 0, 360); g.drawLine(x + deltaX, y + deltaY / 2, x + deltaX, y - 2 * deltaY); break; case 3: // eighth note g.fillArc(x, y, deltaX, deltaY, 0, 360); g.drawLine(x + deltaX, y + deltaY / 2, x + deltaX, y - 2 * deltaY); g.drawLine(x + deltaX, y - 2 * deltaY, x + deltaX + deltaX / 2, y - 2 * deltaY); break; } x += 2 * deltaX; } } public void drawNote(boolean red, int i, int iNote, int iDuration) { int x = x0 + (2 * i + 1) * deltaX; int y = (8 - iNote) * deltaY / 2 + y0 - deltaY / 2; Graphics g = getGraphics(); if (red) g.setColor(Color.red); else g.setColor(Color.black); switch (iDuration) { case 0: // whole note g.drawArc(x, y, deltaX, deltaY, 0, 360); break; case 1: // half note g.fillArc(x, y, deltaX, deltaY, 0, 360); break; case 2: // quarter note g.fillArc(x, y, deltaX, deltaY, 0, 360); g.drawLine(x + deltaX, y + deltaY / 2, x + deltaX, y - 2 * deltaY); break; case 3: // eighth note g.fillArc(x, y, deltaX, deltaY, 0, 360); g.drawLine(x + deltaX, y + deltaY / 2, x + deltaX, y - 2 * deltaY); g.drawLine(x + deltaX, y - 2 * deltaY, x + deltaX + deltaX / 2, y - 2 * deltaY); break; } } public void setMusicAndDraw(int num, int[] not, int[] dur) { number = num; note = new int[number]; duration = new int[number]; for (int i = 0; i < number; i++) { note[i] = not[i]; duration[i] = dur[i]; } paintComponent(getGraphics()); } } class WhiteNoise2Frame extends JFrame { int guitar, iHeight, iWidth, number; int[] note, duration; JButton jButton1 = new JButton(); JButton jButton2 = new JButton(); JPanel jPanel = new JPanel(); GridLayout gridLayout = new GridLayout(4, 1); Controls controls; Date date = new Date(); Instrument[] instruments; InstrumentsTable instrumentsTable; MidiChannel[] midiChannels; Random random = new Random(date.getTime()); Receiver synthesizerReceiver; Synthesizer synthesizer; WhiteNoise2Panel whiteNoise2Panel; // step 3 - percentage size the window void setDesktopSize(JFrame frame, int wPerc, int hPerc) { Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); iWidth = screen.width * wPerc / 100; iHeight = screen.height * hPerc / 100; frame.setSize(iWidth, iHeight); } // step 4 - center the window void centerOnScreen(JFrame frame) { Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); Dimension window = frame.getSize(); int iCenterX = screen.width / 2; int iCenterY = screen.height / 2; frame.setLocation(iCenterX - window.width / 2, iCenterY - window.height / 2); } public WhiteNoise2Frame() { number = 0; String title = "WhiteNoise by James Pate Williams, Jr. (c) 2001"; jButton1.setToolTipText("Generate white noise music"); jButton1.setText("Generate Notes"); jButton1.setVerticalAlignment(SwingConstants.BOTTOM); jButton1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jButton1_actionPerformed(e); } }); jButton2.setText("Play Notes"); jButton2.setVerticalAlignment(SwingConstants.BOTTOM); jButton2.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jButton2_actionPerformed(e); } }); try { synthesizer = MidiSystem.getSynthesizer(); synthesizer.open(); Soundbank soundBank = synthesizer.getDefaultSoundbank(); if (soundBank != null) { instruments = synthesizer.getDefaultSoundbank().getInstruments(); //for (int i = 0; i < instruments.length; i++) // System.out.println(" strcpy(instrument[" + i + "], " + "\"" + instruments[i].getName() + "\");"); synthesizer.loadInstrument(instruments[0]); midiChannels = synthesizer.getChannels(); for (int i = 0; i < midiChannels.length; i++) midiChannels[i].programChange(guitar); synthesizerReceiver = synthesizer.getReceiver(); jButton2.setToolTipText(instruments[0].getName()); } } catch (Exception exception) { jButton2.setEnabled(false); } this.getContentPane().setLayout(gridLayout); setTitle(title); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent event) { synthesizer.close(); System.exit(0); } }); setDesktopSize(this, 100, 100); centerOnScreen(this); controls = new Controls(); controls.setMidiChannel(midiChannels[0]); Container contentPane = getContentPane(); jPanel.add(controls, BorderLayout.CENTER); jPanel.add(jButton1, BorderLayout.EAST); jPanel.add(jButton2, BorderLayout.WEST); whiteNoise2Panel = new WhiteNoise2Panel(iWidth, iHeight); instrumentsTable = new InstrumentsTable(); instrumentsTable.initTable(); instrumentsTable.setData(instruments, synthesizer); contentPane.add(whiteNoise2Panel); contentPane.add(controls); contentPane.add(instrumentsTable); contentPane.add(jPanel); this.show(); } void jButton1_actionPerformed(ActionEvent e) { number = 32; note = new int[number]; duration = new int[number]; for (int i = 0; i < number; i++) { note[i] = random.nextInt(9); duration[i] = random.nextInt(4); } whiteNoise2Panel.setMusicAndDraw(number, note, duration); } void jButton2_actionPerformed(ActionEvent e) { int beats = controls.getBeats(); int volume; int[] durationMap = new int[4]; int[] keyMap = new int[9]; durationMap[0] = 1; // whole note durationMap[1] = 2; // half note durationMap[2] = 4; // quarter note durationMap[3] = 8; // eigth note keyMap[0] = 64; // E above middle C keyMap[1] = 65; // F keyMap[2] = 67; // G keyMap[3] = 69; // A keyMap[4] = 71; // B keyMap[5] = 72; // C keyMap[6] = 74; // D keyMap[7] = 76; // E keyMap[8] = 77; // F try { volume = controls.getVelocity(); for (int i = 0; i < number; i++) { ShortMessage shortMessage = new ShortMessage(); shortMessage.setMessage(ShortMessage.NOTE_ON, 0, keyMap[note[i]], volume); synthesizerReceiver.send(shortMessage, - 1); int time = (int) (60000.0 / (beats * durationMap[duration[i]])); whiteNoise2Panel.drawNote(true, i, note[i], duration[i]); Thread.sleep(time); shortMessage = new ShortMessage(); shortMessage.setMessage(ShortMessage.NOTE_OFF, 0, keyMap[note[i]], volume); synthesizerReceiver.send(shortMessage, - 1); whiteNoise2Panel.drawNote(false, i, note[i], duration[i]); } } catch (Exception exception) { System.exit(0); } } } class WhiteNoise2 { public static void main(String[] args) { new WhiteNoise2Frame(); } }