Java/Development Class/MIDI
Содержание
A Swing component that can load and play a sound clip, displaying progress and controls
<source lang="java">
/*
* Copyright (c) 2004 David Flanagan. All rights reserved. * This code is from the book Java Examples in a Nutshell, 3nd Edition. * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied. * You may study, use, and modify it for any non-commercial purpose, * including teaching and use in open-source projects. * You may distribute it non-commercially as long as you retain this notice. * For a commercial use license, or to purchase the book, * please visit http://www.davidflanagan.ru/javaexamples3. */
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Receiver; import javax.sound.midi.Sequence; import javax.sound.midi.Sequencer; import javax.sound.midi.Synthesizer; import javax.sound.midi.Track; import javax.sound.midi.Transmitter; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.DataLine; import javax.sound.sampled.FloatControl; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JSlider; import javax.swing.Timer; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /**
* This class is a Swing component that can load and play a sound clip, * displaying progress and controls. The main() method is a test program. This * component can play sampled audio or MIDI files, but handles them differently. * For sampled audio, time is reported in microseconds, tracked in milliseconds * and displayed in seconds and tenths of seconds. For midi files time is * reported, tracked, and displayed in MIDI "ticks". This program does no * transcoding, so it can only play sound files that use the PCM encoding. */
public class SoundPlayer extends JComponent {
boolean midi; // Are we playing a midi file or a sampled one? Sequence sequence; // The contents of a MIDI file Sequencer sequencer; // We play MIDI Sequences with a Sequencer Clip clip; // Contents of a sampled audio file boolean playing = false; // whether the sound is current playing // Length and position of the sound are measured in milliseconds for // sampled sounds and MIDI "ticks" for MIDI sounds int audioLength; // Length of the sound. int audioPosition = 0; // Current position within the sound // The following fields are for the GUI JButton play; // The Play/Stop button JSlider progress; // Shows and sets current position in sound JLabel time; // Displays audioPosition as a number Timer timer; // Updates slider every 100 milliseconds // The main method just creates an SoundPlayer in a Frame and displays it public static void main(String[] args) throws IOException, UnsupportedAudioFileException, LineUnavailableException, MidiUnavailableException, InvalidMidiDataException { SoundPlayer player; File file = new File(args[0]); // This is the file we"ll be playing // Determine whether it is midi or sampled audio boolean ismidi; try { // We discard the return value of this method; we just need to know // whether it returns successfully or throws an exception MidiSystem.getMidiFileFormat(file); ismidi = true; } catch (InvalidMidiDataException e) { ismidi = false; } // Create a SoundPlayer object to play the sound. player = new SoundPlayer(file, ismidi); // Put it in a window and play it JFrame f = new JFrame("SoundPlayer"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.getContentPane().add(player, "Center"); f.pack(); f.setVisible(true); } // Create an SoundPlayer component for the specified file. public SoundPlayer(File f, boolean isMidi) throws IOException, UnsupportedAudioFileException, LineUnavailableException, MidiUnavailableException, InvalidMidiDataException { if (isMidi) { // The file is a MIDI file midi = true; // First, get a Sequencer to play sequences of MIDI events // That is, to send events to a Synthesizer at the right time. sequencer = MidiSystem.getSequencer(); // Used to play sequences sequencer.open(); // Turn it on. // Get a Synthesizer for the Sequencer to send notes to Synthesizer synth = MidiSystem.getSynthesizer(); synth.open(); // acquire whatever resources it needs // The Sequencer obtained above may be connected to a Synthesizer // by default, or it may not. Therefore, we explicitly connect it. Transmitter transmitter = sequencer.getTransmitter(); Receiver receiver = synth.getReceiver(); transmitter.setReceiver(receiver); // Read the sequence from the file and tell the sequencer about it sequence = MidiSystem.getSequence(f); sequencer.setSequence(sequence); audioLength = (int) sequence.getTickLength(); // Get sequence length } else { // The file is sampled audio midi = false; // Getting a Clip object for a file of sampled audio data is kind // of cumbersome. The following lines do what we need. AudioInputStream ain = AudioSystem.getAudioInputStream(f); try { DataLine.Info info = new DataLine.Info(Clip.class, ain.getFormat()); clip = (Clip) AudioSystem.getLine(info); clip.open(ain); } finally { // We"re done with the input stream. ain.close(); } // Get the clip length in microseconds and convert to milliseconds audioLength = (int) (clip.getMicrosecondLength() / 1000); } // Now create the basic GUI play = new JButton("Play"); // Play/stop button progress = new JSlider(0, audioLength, 0); // Shows position in sound time = new JLabel("0"); // Shows position as a # // When clicked, start or stop playing the sound play.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (playing) stop(); else play(); } }); // Whenever the slider value changes, first update the time label. // Next, if we"re not already at the new position, skip to it. progress.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { int value = progress.getValue(); // Update the time label if (midi) time.setText(value + ""); else time.setText(value / 1000 + "." + (value % 1000) / 100); // If we"re not already there, skip there. if (value != audioPosition) skip(value); } }); // This timer calls the tick() method 10 times a second to keep // our slider in sync with the music. timer = new javax.swing.Timer(100, new ActionListener() { public void actionPerformed(ActionEvent e) { tick(); } }); // put those controls in a row Box row = Box.createHorizontalBox(); row.add(play); row.add(progress); row.add(time); // And add them to this component. setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); this.add(row); // Now add additional controls based on the type of the sound if (midi) addMidiControls(); else addSampledControls(); } /** Start playing the sound at the current position */ public void play() { if (midi) sequencer.start(); else clip.start(); timer.start(); play.setText("Stop"); playing = true; } /** Stop playing the sound, but retain the current position */ public void stop() { timer.stop(); if (midi) sequencer.stop(); else clip.stop(); play.setText("Play"); playing = false; } /** Stop playing the sound and reset the position to 0 */ public void reset() { stop(); if (midi) sequencer.setTickPosition(0); else clip.setMicrosecondPosition(0); audioPosition = 0; progress.setValue(0); } /** Skip to the specified position */ public void skip(int position) { // Called when user drags the slider if (position < 0 || position > audioLength) return; audioPosition = position; if (midi) sequencer.setTickPosition(position); else clip.setMicrosecondPosition(position * 1000); progress.setValue(position); // in case skip() is called from outside } /** Return the length of the sound in ms or ticks */ public int getLength() { return audioLength; } // An internal method that updates the progress bar. // The Timer object calls it 10 times a second. // If the sound has finished, it resets to the beginning void tick() { if (midi && sequencer.isRunning()) { audioPosition = (int) sequencer.getTickPosition(); progress.setValue(audioPosition); } else if (!midi && clip.isActive()) { audioPosition = (int) (clip.getMicrosecondPosition() / 1000); progress.setValue(audioPosition); } else reset(); } // For sampled sounds, add sliders to control volume and balance void addSampledControls() { try { FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN); if (gainControl != null) this.add(createSlider(gainControl)); } catch (IllegalArgumentException e) { // If MASTER_GAIN volume control is unsupported, just skip it } try { // FloatControl.Type.BALANCE is probably the correct control to // use here, but it doesn"t work for me, so I use PAN instead. FloatControl panControl = (FloatControl) clip.getControl(FloatControl.Type.PAN); if (panControl != null) this.add(createSlider(panControl)); } catch (IllegalArgumentException e) { } } // Return a JSlider component to manipulate the supplied FloatControl // for sampled audio. JSlider createSlider(final FloatControl c) { if (c == null) return null; final JSlider s = new JSlider(0, 1000); final float min = c.getMinimum(); final float max = c.getMaximum(); final float width = max - min; float fval = c.getValue(); s.setValue((int) ((fval - min) / width * 1000)); java.util.Hashtable labels = new java.util.Hashtable(3); labels.put(new Integer(0), new JLabel(c.getMinLabel())); labels.put(new Integer(500), new JLabel(c.getMidLabel())); labels.put(new Integer(1000), new JLabel(c.getMaxLabel())); s.setLabelTable(labels); s.setPaintLabels(true); s.setBorder(new TitledBorder(c.getType().toString() + " " + c.getUnits())); s.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { int i = s.getValue(); float f = min + (i * width / 1000.0f); c.setValue(f); } }); return s; } // For Midi files, create a JSlider to control the tempo, // and create JCheckBoxes to mute or solo each MIDI track. void addMidiControls() { // Add a slider to control the tempo final JSlider tempo = new JSlider(50, 200); tempo.setValue((int) (sequencer.getTempoFactor() * 100)); tempo.setBorder(new TitledBorder("Tempo Adjustment (%)")); java.util.Hashtable labels = new java.util.Hashtable(); labels.put(new Integer(50), new JLabel("50%")); labels.put(new Integer(100), new JLabel("100%")); labels.put(new Integer(200), new JLabel("200%")); tempo.setLabelTable(labels); tempo.setPaintLabels(true); // The event listener actually changes the tmpo tempo.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { sequencer.setTempoFactor(tempo.getValue() / 100.0f); } }); this.add(tempo); // Create rows of solo and checkboxes for each track Track[] tracks = sequence.getTracks(); for (int i = 0; i < tracks.length; i++) { final int tracknum = i; // Two checkboxes per track final JCheckBox solo = new JCheckBox("solo"); final JCheckBox mute = new JCheckBox("mute"); // The listeners solo or mute the track solo.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { sequencer.setTrackSolo(tracknum, solo.isSelected()); } }); mute.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { sequencer.setTrackMute(tracknum, mute.isSelected()); } }); // Build up a row Box box = Box.createHorizontalBox(); box.add(new JLabel("Track " + tracknum)); box.add(Box.createHorizontalStrut(10)); box.add(solo); box.add(Box.createHorizontalStrut(10)); box.add(mute); box.add(Box.createHorizontalGlue()); // And add it to this component this.add(box); } }
}
</source>
Play Piano
<source lang="java">
/*
* Copyright (c) 2004 David Flanagan. All rights reserved. * This code is from the book Java Examples in a Nutshell, 3nd Edition. * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied. * You may study, use, and modify it for any non-commercial purpose, * including teaching and use in open-source projects. * You may distribute it non-commercially as long as you retain this notice. * For a commercial use license, or to purchase the book, * please visit http://www.davidflanagan.ru/javaexamples3. */
import java.io.File; import java.io.IOException; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MetaEventListener; import javax.sound.midi.MetaMessage; import javax.sound.midi.MidiEvent; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Sequence; import javax.sound.midi.Sequencer; import javax.sound.midi.ShortMessage; import javax.sound.midi.Synthesizer; import javax.sound.midi.Track; public class PlayerPiano {
// These are some MIDI constants from the spec. They aren"t defined // for us in javax.sound.midi. public static final int DAMPER_PEDAL = 64; public static final int DAMPER_ON = 127; public static final int DAMPER_OFF = 0; public static final int END_OF_TRACK = 47; public static void main(String[] args) throws MidiUnavailableException, InvalidMidiDataException, IOException { int instrument = 0; int tempo = 120; String filename = null; // Parse the options // -i <instrument number> default 0, a piano. Allowed values: 0-127 // -t <beats per minute> default tempo is 120 quarter notes per minute // -o <filename> save to a midi file instead of playing int a = 0; while (a < args.length) { if (args[a].equals("-i")) { instrument = Integer.parseInt(args[a + 1]); a += 2; } else if (args[a].equals("-t")) { tempo = Integer.parseInt(args[a + 1]); a += 2; } else if (args[a].equals("-o")) { filename = args[a + 1]; a += 2; } else break; } char[] notes = args[a].toCharArray(); // 16 ticks per quarter note. Sequence sequence = new Sequence(Sequence.PPQ, 16); // Add the specified notes to the track addTrack(sequence, instrument, tempo, notes); if (filename == null) { // no filename, so play the notes // Set up the Sequencer and Synthesizer objects Sequencer sequencer = MidiSystem.getSequencer(); sequencer.open(); Synthesizer synthesizer = MidiSystem.getSynthesizer(); synthesizer.open(); sequencer.getTransmitter().setReceiver(synthesizer.getReceiver()); // Specify the sequence to play, and the tempo to play it at sequencer.setSequence(sequence); sequencer.setTempoInBPM(tempo); // Let us know when it is done playing sequencer.addMetaEventListener(new MetaEventListener() { public void meta(MetaMessage m) { // A message of this type is automatically sent // when we reach the end of the track if (m.getType() == END_OF_TRACK) System.exit(0); } }); // And start playing now. sequencer.start(); } else { // A file name was specified, so save the notes int[] allowedTypes = MidiSystem.getMidiFileTypes(sequence); if (allowedTypes.length == 0) { System.err.println("No supported MIDI file types."); } else { MidiSystem.write(sequence, allowedTypes[0], new File(filename)); System.exit(0); } } } static final int[] offsets = { // add these amounts to the base value // A B C D E F G -4, -2, 0, 1, 3, 5, 7 }; /* * This method parses the specified char[] of notes into a Track. The musical * notation is the following: A-G: A named note; Add b for flat and # for * sharp. +: Move up one octave. Persists. -: Move down one octave. Persists. * /1: Notes are whole notes. Persists "till changed /2: Half notes /4: * Quarter notes /n: N can also be, 8, 16, 32, 64. s: Toggle sustain pedal on * or off (initially off) * >: Louder. Persists <: Softer. Persists .: Rest. Length depends on current * length setting Space: Play the previous note or notes; notes not separated * by spaces are played at the same time */ public static void addTrack(Sequence s, int instrument, int tempo, char[] notes) throws InvalidMidiDataException { Track track = s.createTrack(); // Begin with a new track // Set the instrument on channel 0 ShortMessage sm = new ShortMessage(); sm.setMessage(ShortMessage.PROGRAM_CHANGE, 0, instrument, 0); track.add(new MidiEvent(sm, 0)); int n = 0; // current character in notes[] array int t = 0; // time in ticks for the composition // These values persist and apply to all notes "till changed int notelength = 16; // default to quarter notes int velocity = 64; // default to middle volume int basekey = 60; // 60 is middle C. Adjusted up and down by octave boolean sustain = false; // is the sustain pedal depressed? int numnotes = 0; // How many notes in current chord? while (n < notes.length) { char c = notes[n++]; if (c == "+") basekey += 12; // increase octave else if (c == "-") basekey -= 12; // decrease octave else if (c == ">") velocity += 16; // increase volume; else if (c == "<") velocity -= 16; // decrease volume; else if (c == "/") { char d = notes[n++]; if (d == "2") notelength = 32; // half note else if (d == "4") notelength = 16; // quarter note else if (d == "8") notelength = 8; // eighth note else if (d == "3" && notes[n++] == "2") notelength = 2; else if (d == "6" && notes[n++] == "4") notelength = 1; else if (d == "1") { if (n < notes.length && notes[n] == "6") notelength = 4; // 1/16th note else notelength = 64; // whole note } } else if (c == "s") { sustain = !sustain; // Change the sustain setting for channel 0 ShortMessage m = new ShortMessage(); m .setMessage(ShortMessage.CONTROL_CHANGE, 0, DAMPER_PEDAL, sustain ? DAMPER_ON : DAMPER_OFF); track.add(new MidiEvent(m, t)); } else if (c >= "A" && c <= "G") { int key = basekey + offsets[c - "A"]; if (n < notes.length) { if (notes[n] == "b") { // flat key--; n++; } else if (notes[n] == "#") { // sharp key++; n++; } } addNote(track, t, notelength, key, velocity); numnotes++; } else if (c == " ") { // Spaces separate groups of notes played at the same time. // But we ignore them unless they follow a note or notes. if (numnotes > 0) { t += notelength; numnotes = 0; } } else if (c == ".") { // Rests are like spaces in that they force any previous // note to be output (since they are never part of chords) if (numnotes > 0) { t += notelength; numnotes = 0; } // Now add additional rest time t += notelength; } } } // A convenience method to add a note to the track on channel 0 public static void addNote(Track track, int startTick, int tickLength, int key, int velocity) throws InvalidMidiDataException { ShortMessage on = new ShortMessage(); on.setMessage(ShortMessage.NOTE_ON, 0, key, velocity); ShortMessage off = new ShortMessage(); off.setMessage(ShortMessage.NOTE_OFF, 0, key, velocity); track.add(new MidiEvent(on, startTick)); track.add(new MidiEvent(off, startTick + tickLength)); }
}
</source>
Plays sounds streaming from a URL
<source lang="java">
/*
* Copyright (c) 2004 David Flanagan. All rights reserved. * This code is from the book Java Examples in a Nutshell, 3nd Edition. * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied. * You may study, use, and modify it for any non-commercial purpose, * including teaching and use in open-source projects. * You may distribute it non-commercially as long as you retain this notice. * For a commercial use license, or to purchase the book, * please visit http://www.davidflanagan.ru/javaexamples3. */
import java.io.IOException; import java.net.URL; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MetaEventListener; import javax.sound.midi.MetaMessage; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Sequencer; import javax.sound.midi.Synthesizer; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.UnsupportedAudioFileException; /**
* This class plays sounds streaming from a URL: it does not have to preload the * entire sound into memory before playing it. It is a command-line application * with no gui. It includes code to convert ULAW and ALAW audio formats to PCM * so they can be played. Use the -m command-line option before MIDI files. */
public class PlaySoundStream {
// Create a URL from the command-line argument and pass it to the // right static method depending on the presence of the -m (MIDI) option. public static void main(String[] args) throws Exception { if (args[0].equals("-m")) streamMidiSequence(new URL(args[1])); else streamSampledAudio(new URL(args[0])); // Exit explicitly. // This is needed because the audio system starts background threads. System.exit(0); } /** Read sampled audio data from the specified URL and play it */ public static void streamSampledAudio(URL url) throws IOException, UnsupportedAudioFileException, LineUnavailableException { AudioInputStream ain = null; // We read audio data from here SourceDataLine line = null; // And write it here. try { // Get an audio input stream from the URL ain = AudioSystem.getAudioInputStream(url); // Get information about the format of the stream AudioFormat format = ain.getFormat(); DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); // If the format is not supported directly (i.e. if it is not PCM // encoded, then try to transcode it to PCM. if (!AudioSystem.isLineSupported(info)) { // This is the PCM format we want to transcode to. // The parameters here are audio format details that you // shouldn"t need to understand for casual use. AudioFormat pcm = new AudioFormat(format.getSampleRate(), 16, format.getChannels(), true, false); // Get a wrapper stream around the input stream that does the // transcoding for us. ain = AudioSystem.getAudioInputStream(pcm, ain); // Update the format and info variables for the transcoded data format = ain.getFormat(); info = new DataLine.Info(SourceDataLine.class, format); } // Open the line through which we"ll play the streaming audio. line = (SourceDataLine) AudioSystem.getLine(info); line.open(format); // Allocate a buffer for reading from the input stream and writing // to the line. Make it large enough to hold 4k audio frames. // Note that the SourceDataLine also has its own internal buffer. int framesize = format.getFrameSize(); byte[] buffer = new byte[4 * 1024 * framesize]; // the buffer int numbytes = 0; // how many bytes // We haven"t started the line yet. boolean started = false; for (;;) { // We"ll exit the loop when we reach the end of stream // First, read some bytes from the input stream. int bytesread = ain.read(buffer, numbytes, buffer.length - numbytes); // If there were no more bytes to read, we"re done. if (bytesread == -1) break; numbytes += bytesread; // Now that we"ve got some audio data, to write to the line, // start the line, so it will play that data as we write it. if (!started) { line.start(); started = true; } // We must write bytes to the line in an integer multiple of // the framesize. So figure out how many bytes we"ll write. int bytestowrite = (numbytes / framesize) * framesize; // Now write the bytes. The line will buffer them and play // them. This call will block until all bytes are written. line.write(buffer, 0, bytestowrite); // If we didn"t have an integer multiple of the frame size, // then copy the remaining bytes to the start of the buffer. int remaining = numbytes - bytestowrite; if (remaining > 0) System.arraycopy(buffer, bytestowrite, buffer, 0, remaining); numbytes = remaining; } // Now block until all buffered sound finishes playing. line.drain(); } finally { // Always relinquish the resources we use if (line != null) line.close(); if (ain != null) ain.close(); } } // A MIDI protocol constant that isn"t defined by javax.sound.midi public static final int END_OF_TRACK = 47; /* MIDI or RMF data from the specified URL and play it */ public static void streamMidiSequence(URL url) throws IOException, InvalidMidiDataException, MidiUnavailableException { Sequencer sequencer = null; // Converts a Sequence to MIDI events Synthesizer synthesizer = null; // Plays notes in response to MIDI events try { // Create, open, and connect a Sequencer and Synthesizer // They are closed in the finally block at the end of this method. sequencer = MidiSystem.getSequencer(); sequencer.open(); synthesizer = MidiSystem.getSynthesizer(); synthesizer.open(); sequencer.getTransmitter().setReceiver(synthesizer.getReceiver()); // Specify the InputStream to stream the sequence from sequencer.setSequence(url.openStream()); // This is an arbitrary object used with wait and notify to // prevent the method from returning before the music finishes final Object lock = new Object(); // Register a listener to make the method exit when the stream is // done. See Object.wait() and Object.notify() sequencer.addMetaEventListener(new MetaEventListener() { public void meta(MetaMessage e) { if (e.getType() == END_OF_TRACK) { synchronized (lock) { lock.notify(); } } } }); // Start playing the music sequencer.start(); // Now block until the listener above notifies us that we"re done. synchronized (lock) { while (sequencer.isRunning()) { try { lock.wait(); } catch (InterruptedException e) { } } } } finally { // Always relinquish the sequencer, so others can use it. if (sequencer != null) sequencer.close(); if (synthesizer != null) synthesizer.close(); } }
}
</source>
Program the MIDI percussion channel with a Swing window
<source lang="java">
/*
* Copyright (c) 2004 David Flanagan. All rights reserved. * This code is from the book Java Examples in a Nutshell, 3nd Edition. * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied. * You may study, use, and modify it for any non-commercial purpose, * including teaching and use in open-source projects. * You may distribute it non-commercially as long as you retain this notice. * For a commercial use license, or to purchase the book, * please visit http://www.davidflanagan.ru/javaexamples3. */
import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import javax.sound.midi.MidiChannel; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Synthesizer; import javax.swing.JFrame; /**
* This program the MIDI percussion channel with a Swing window. It monitors * keystrokes and mouse motion in the window and uses them to create music. * Keycodes between 35 and 81, inclusive, generate different percussive sounds. * See the VK_ constants in java.awt.event.KeyEvent, or just experiment. Mouse * position controls volume: move the mouse to the right of the window to * increase the volume. */
public class Drums extends JFrame {
MidiChannel channel; // The channel we play on: 10 is for percussion int velocity = 64; // Default volume is 50% public static void main(String[] args) throws MidiUnavailableException { // We don"t need a Sequencer in this example, since we send MIDI // events directly to the Synthesizer instead. Synthesizer synthesizer = MidiSystem.getSynthesizer(); synthesizer.open(); JFrame frame = new Drums(synthesizer); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(50, 128); // We use window width as volume control frame.setVisible(true); } public Drums(Synthesizer synth) { super("Drums"); // Channel 10 is the GeneralMidi percussion channel. In Java code, we // number channels from 0 and use channel 9 instead. channel = synth.getChannels()[9]; addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key >= 35 && key <= 81) { channel.noteOn(key, velocity); } } public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); if (key >= 35 && key <= 81) channel.noteOff(key); } }); addMouseMotionListener(new MouseMotionAdapter() { public void mouseMoved(MouseEvent e) { velocity = e.getX(); } }); }
}
</source>