FileDocCategorySizeDatePackage
SoundPlayer.javaAPI DocExample11967Sat Jan 24 10:44:38 GMT 2004je3.sound

SoundPlayer

public class SoundPlayer extends JComponent
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.

Fields Summary
boolean
midi
Sequence
sequence
Sequencer
sequencer
Clip
clip
boolean
playing
int
audioLength
int
audioPosition
JButton
play
JSlider
progress
JLabel
time
Timer
timer
Constructors Summary
public SoundPlayer(File f, boolean isMidi)

	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();
    
Methods Summary
voidaddMidiControls()

	// 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);
	}
    
voidaddSampledControls()

	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) {}
    
javax.swing.JSlidercreateSlider(javax.sound.sampled.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;
    
public intgetLength()
Return the length of the sound in ms or ticks

 return audioLength; 
public static voidmain(java.lang.String[] args)

              // Updates slider every 100 milliseconds

    // The main method just creates an SoundPlayer in a Frame and displays it
         
	 
	       
	       
	       
	       
    
	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);
    
public voidplay()
Start playing the sound at the current position

	if (midi) sequencer.start();
	else clip.start();
	timer.start();
	play.setText("Stop");
	playing = true;
    
public voidreset()
Stop playing the sound and reset the position to 0

	stop();
	if (midi) sequencer.setTickPosition(0);
	else clip.setMicrosecondPosition(0);
	audioPosition = 0; 
	progress.setValue(0);
    
public voidskip(int position)
Skip to the specified 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
    
public voidstop()
Stop playing the sound, but retain the current position

	timer.stop();
	if (midi) sequencer.stop();
	else clip.stop();
	play.setText("Play");
	playing = false;
    
voidtick()

	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();