permaculture playing cards*
The moose likes Java in General and the fly likes Progress Bar while using Streaming Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Java in General
Bookmark "Progress Bar while using Streaming" Watch "Progress Bar while using Streaming" New topic
Author

Progress Bar while using Streaming

Rebecca Witmer
Ranch Hand

Joined: Sep 10, 2004
Posts: 46
I have already created an application that will play a long audio file using SourceDataLine. Now I am looking for a way to do three things:

1) Display seconds elapsed on a progress bar or something similar
2) Display total seconds of audio available in the file (not just how many seconds it has available in the current buffer)
3) Start play from some point not at the beginning of the file, like from second 10 of the file.

getMicrosecondPosition() from DataLine looks useful on the first one, but I don't know about the other two. Can anyone point me to an example, tutorial or resource that deals with those questions? Thanks.


SCJP 1.4
Stephen Huey
Ranch Hand

Joined: Jul 15, 2003
Posts: 618
To solve this with Java, I'm pretty sure you'd have to use an applet. This isn't exactly what you want, but maybe you can use some of these concepts:
http://www.javaworld.com/javaworld/jw-12-1996/jw-12-progressbar.html

You'll probably need to dig deep just to get a good foundation in order to write original code on this:
http://java.sun.com/docs/books/tutorial/applet/

You might also need to read up on some threading:
http://java.sun.com/docs/books/tutorial/essential/threads/index.html
David Harkness
Ranch Hand

Joined: Aug 07, 2003
Posts: 1646
If you can post some of the code you're using, then maybe someone can help you. At the bare minimum, what classes are you using to populate your SourceDataLine? From the JavaDocs, SourceDataLine looks to be the output line for a sample, but it's hard to tell from the way it reads. If that's the case, it won't know the total length of the sample.

if you post the code that opens the file and attaches it to a Clip or the SourceDataLine, then we'll have more to go from. Or are you streaming from a SHOUTcast server? If that's the case, there is no such thing as a "total length."
Rebecca Witmer
Ranch Hand

Joined: Sep 10, 2004
Posts: 46
Sure. Fortunately, the class that handles the sound is not that huge. I'll just paste the whole thing. I started out many moons ago using Clip starting with code from the tutorial here:

http://www.onjava.com/pub/a/onjava/excerpt/jenut3_ch17/
http://www.onjava.com/onjava/excerpt/jenut3_ch17/examples/SoundPlayer.java

But I changed Clip to SourceDataLine when it started throwing exceptions on the longer files. So basically I need to figure out how to get it back to the functionality it had before as well as be able to play "segments," which is our official term for short pieces of the overall sound. We know segment boundaries by which seconds they start and end at.

Okay, anyway, here it comes.


package cvg.corpus.gui;

//Example 17-3. SoundPlayer.java


import java.io.*;
import java.awt.event.*;
import javax.swing.*;
import javax.sound.sampled.*;
/**
* @author rwitmer
*/

/**
*
* 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. This program
* does no transcoding, so it can only play sound files that use the PCM encoding.
*/
public class SoundPlayer extends JComponent {
Clip clip; // Contents of a sampled audio file
boolean playing = false; // whether the sound is currently 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 button
JButton stop; // The 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

File filename;
SourceDataLine line = null;
AudioInputStream audioInputStream = null;
PlayerThread pt;

// Create a SoundPlayer component for the specified file.
public SoundPlayer(File f)
throws IOException,
UnsupportedAudioFileException,
LineUnavailableException

{

// Now create the basic GUI
play = new JButton("Play"); // Play button
stop = new JButton("Stop"); // Stop button
play.setEnabled(false);
stop.setEnabled(false);

// When clicked, start playing the sound
play.addActionListener(new ActionListener( ) {
public void actionPerformed(ActionEvent e) {
if (!playing){
pt = new PlayerThread();
pt.start();
}
}
});

// When clicked, stop playing the sound
stop.addActionListener(new ActionListener( ) {
public void actionPerformed(ActionEvent e) {
if (playing) stop( );
}
});

// put those controls in a row
Box row = Box.createHorizontalBox( );
row.add(play);
row.add(stop);

// And add them to this component.
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.add(row);

}

public void setFile(File f) {

filename = f;

}

class PlayerThread extends Thread {
public void run() {
try
{
audioInputStream = AudioSystem.getAudioInputStream(filename);
}
catch (Exception e)
{
e.printStackTrace();
}

AudioFormat audioFormat = audioInputStream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat, AudioSystem.NOT_SPECIFIED);

try
{
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
}
catch (LineUnavailableException e)
{
e.printStackTrace();
System.exit(1);
}
catch (Exception e)
{
e.printStackTrace();
}

playing = true;

line.start();
int nBytesRead = 0;
byte[] abData = new byte[128000];
while (nBytesRead != -1)
{
try
{
nBytesRead = audioInputStream.read(abData, 0, abData.length);
int temp [] = new int[abData.length];
}
catch (IOException e)
{
e.printStackTrace();
}
if (nBytesRead >= 0)
{
int nBytesWritten = line.write(abData, 0, nBytesRead);
}
}

line.drain();
line.close();

}
}

/** Stop playing the sound*/
public void stop( ) {
line.stop();
line.flush();
line.close();
pt.interrupt();
pt = null;

playing = false;
}

}
Rebecca Witmer
Ranch Hand

Joined: Sep 10, 2004
Posts: 46
Okay, well I solved the first part of my problem. I figured out how to make the progress bar. First I figured out that the audio I am dealing with is headerless, so there are no random bytes hanging about on the front. Then I did some fancy math with the file size in bytes, the bytes per frame and the frames per second to figure out how long the audio was. Then I made a slider based on that. So, questions 1 and 2 answered, check!

Question 3 was:
How do I start play from some point not at the beginning of the file, like from second 10 of the file?

I am still trying to think of an intelligent suggestion to answer that question. If there were some way to take some piece data from a SourceDataLine and put it into another one that would be a start. I am still pondering it. Any thoughts on that subject would be appreciated.

Rebecca


Here is the code that includes the progress bar:

package cvg.corpus.gui;

//Example 17-3. SoundPlayer.java


import java.io.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.sound.sampled.*;

/**
*
* 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. This program
* does no transcoding, so it can only play sound files that use the PCM encoding.
*/
public class SoundPlayer extends JComponent {
Clip clip; // Contents of a sampled audio file
boolean playing = false; // whether the sound is currently playing

// Length and position of the sound are measured in milliseconds
int audioLength = 1000; // Length of the sound.
int audioPosition = 0; // Current position within the sound

// The following fields are for the GUI
JButton play; // The Play button
JButton stop; // The 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

File filename;
SourceDataLine line = null;
AudioInputStream audioInputStream = null;
PlayerThread pt;

// Create a SoundPlayer component for the specified file.
public SoundPlayer(File f)
throws IOException,
UnsupportedAudioFileException,
LineUnavailableException

{

// Now create the basic GUI
play = new JButton("Play"); // Play button
stop = new JButton("Stop"); // Stop button
progress = new JSlider(0, audioLength, 0); // Shows position in sound
time = new JLabel("0"); // Shows position as a #

play.setEnabled(false);
stop.setEnabled(false);

// When clicked, start playing the sound
play.addActionListener(new ActionListener( ) {
public void actionPerformed(ActionEvent e) {
if (!playing){
pt = new PlayerThread();
pt.start();
}
}
});

// When clicked, stop playing the sound
stop.addActionListener(new ActionListener( ) {
public void actionPerformed(ActionEvent e) {
if (playing) stop( );
}
});

// 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
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) {
//System.out.println("x");
tick( );
}
});

// put those controls in a row
Box row = Box.createHorizontalBox( );
row.add(play);
row.add(stop);
row.add(progress);
row.add(time);

// And add them to this component.
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.add(row);

}

public void setFile(File f) {

filename = f;

}

class PlayerThread extends Thread {
public void run() {
float length = 0;
try
{
audioInputStream = AudioSystem.getAudioInputStream(filename);
length = filename.length();
//System.out.println("File length is " + length + " bytes.");
}
catch (Exception e)
{
e.printStackTrace();
}

AudioFormat audioFormat = audioInputStream.getFormat();
int frameSize = audioFormat.getFrameSize();
float frameRate = audioFormat.getFrameRate();
float lengthInSeconds = ((length/frameSize)/frameRate);
audioLength = (int) lengthInSeconds;
audioLength = audioLength * 1000;
progress.setMaximum(audioLength);


DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat, AudioSystem.NOT_SPECIFIED);

timer.start( );

try
{
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
}
catch (LineUnavailableException e)
{
e.printStackTrace();
System.exit(1);
}
catch (Exception e)
{
e.printStackTrace();
}

playing = true;

line.start();
int nBytesRead = 0;
byte[] abData = new byte[128000];
while (nBytesRead != -1)
{
try
{
nBytesRead = audioInputStream.read(abData, 0, abData.length);
int temp [] = new int[abData.length];
}
catch (IOException e)
{
e.printStackTrace();
}
if (nBytesRead >= 0)
{
int nBytesWritten = line.write(abData, 0, nBytesRead);
}
}

line.drain();
line.close();

}
}

/** Stop playing the sound*/
public void stop( ) {
timer.stop( );
line.stop();
line.flush();
line.close();
pt.interrupt();
pt = null;

playing = false;
}

/** Skip to the specified position */
public void skip(int position) { // Called when user drags the slider
if (position < 0 || position > audioLength) return;
audioPosition = position;
//clip.setMicrosecondPosition(position * 1000);
progress.setValue(position); // in case skip( ) is called from outside
}

// 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 (playing == true) {
audioPosition = (int)(line.getMicrosecondPosition( )/1000);
progress.setValue(audioPosition);
time.setText(audioPosition/1000 + "." + (audioPosition%1000)/100);
}
}

}
 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
 
subject: Progress Bar while using Streaming