Make a seek bar for media player. - java

I am making a media player using JMF, I want to use my own control components
Can anyone please help me in making a seek bar for media player so that it can play song according to the slider position.
Just suggest me some logic, I can figure out the coding part afterwards
if(player!=null){
long durationNanoseconds =
(player.getDuration().getNanoseconds());
durationbar.setMaximum((int) player.getDuration().getSeconds());
int duration=(int) player.getDuration().getSeconds();
int percent = durationbar.getValue();
long t = (durationNanoseconds / duration) * percent;
Time newTime = new Time(t);
player.stop();
player.setMediaTime(newTime);
player.start();
mousedrag=true;
Here is the code. Now how can I make the slider move along with the song?
Slider works when I drag/click on it, but it doesn't move with the song.

The problem with using a slider for this is that when the slider position is moved programmatically, it fires events. When an event is fired on a slider, it typically means the app. has to do something, such as move the song position. The effect is a never ending loop. There is probably a way around this by setting flags and ignoring some events, but I decided to go a different way.
Instead I used a JProgressBar to indicate the location in the track, and a MouseListener to detect when the user clicks on a separate position. Update the progress bar use a Swing Timer that checks the track location every 50-200 milliseconds. When a MouseEvent is detected, reposition the track.
The bar can be seen in the upper right of this GUI. Hovering over it will produce a tool tip showing the time in the track at that mouse position.

You could use a JSlider.
You can learn more from the Slider tutorial

You don't have to revalidate the container in order to change the slider.
Use these lines each time a new player is created:
slider.setMinimum(0);
slider.setMaximum(duration);
slider.setValue(0);
new UpdateWorker(duration).execute();
where duration is the variable holding the duration of the song in seconds.
And here is the code (used as inner class) which updates the slider:
private class UpdateWorker extends SwingWorker<Void, Integer> {
private int duration;
public UpdateWorker(int duration) {
this.duration = duration;
}
#Override
protected Void doInBackground() throws Exception {
for (int i = 1; i <= duration; i++) {
Thread.sleep(1000);
publish(i);
}
return null;
}
#Override
protected void process(List<Integer> chunks) {
slider.setValue(chunks.get(0));
}
}
Now the slider will move to the right until the end of the song.
Also note that unless you want to use a custom slider, JMF provides a simple (and working) slider via player.getVisualComponent() (see this example).
UPDATE
In order to pause/resume the worker thread (and thus the slider and the song), here is an example with a button that sets the appropriate flags.
private boolean isPaused = false;
JButton pause = new JButton("Pause");
pause.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JButton source = (JButton)e.getSource();
if (!isPaused) {
isPaused = true;
source.setText("Resume");
} else {
isPaused = false;
source.setText("Pause");
}
}
});
The method doInBackground should be changed to something like that:
#Override
protected Void doInBackground() throws Exception {
for (int i = 0; i <= duration; i++) {
if (!isPaused) {
publish(i);
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
while (isPaused) {
try {
Thread.sleep(50);
continue;
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
return null;
}
Modify it accordingly to pause/resume the song along with the slider.
You should also consider #AndrewThompson's answer.

Related

How to pause program execution without blocking a Timer (Java Swing)?

I have a Timer that shows an animation on the screen when a game event takes place. What I want to do is have the main execution pause until this animation finishes, but everything I try seems to block the Timer from running. I've tried using Thread.sleep() and calling wait() and notify() on a lock object, but with the same result. The Timer listener's actionPerformed() is never called.
This code sets up the timer:
protected void showMovingEffect(int steps, Direction dir, AnimatedImageSet imgs) {
effectsPane.runAnimation(imgs, dir, steps);
waiting = true;
while (waiting) {
synchronized(animationWaitLock) {
try {
animationWaitLock.wait();
} catch (InterruptedException e) {}
}
}
}
And the Timer and listener inside the EffectsPane object:
private void runAnimation(AnimatedImageSet ais, Direction dir, int iters) {
System.out.println("run");
imgs = ais;
top = 0;
left = 0;
topStep = dir.getRowIncrement() * 10;
leftStep = dir.getColIncrement() * 10;
iterations = iters;
index = 0;
active = true;
timer.start();
}
public void actionPerformed(ActionEvent e) {
System.out.println(index);
top += topStep;
left += leftStep;
index++;
currentImage = imgs.getImage(index);
repaint();
if (index == iterations) {
active = false;
timer.stop();
synchronized(animationWaitLock) {
animationWaitLock.notify();
}
waiting = false;
}
}
The System.out.println call at the start of the actionPerformed() is never called, so either the wait() call is also pausing the Timer, or something is blocking it. If I comment out the sleep/wait lines in showMovingEffect(), the animation runs, but the program does not pause.
One approach would be to display a modal dialog while the animation proceeds. As discussed here, user interaction will be foreclosed, but background GUI updates in response to the javax.swing.Timer will continue. You can allow the user to dismiss the dialog at any time, as shown here, but you may want to abandon the animation at that point.
no input from the user is needed.
You can block user interaction without displaying a dialog by entering a SecondaryLoop after the animation starts.
SecondaryLoop loop = Toolkit.getDefaultToolkit()
.getSystemEventQueue().createSecondaryLoop();
timer.start();
loop.enter();
When the animation concludes, exit() the SecondaryLoop:
public void actionPerformed(ActionEvent e) {
…
if (index == iterations) {
timer.stop();
loop.exit ();
…
}
}
You can try this:
timer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// your code
}
});

Java-Repositioning of object when timer increments

I have a JFrame with two buttons. One of the buttons when clicked moves (btnMove) the other button(shape) from the present position to another.I am using a thread as a timer to count in seconds but each time the counter increments, the button moves back to its original position.
public class FrameTh extends JFrame {
class count extends Thread {
public int p = 0;
public void run() {
for (int i = 1; i < 100; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e);
}
lblCounter.setText("Seconds: " + i);
}
}
}
count t1 = new count();
private void formWindowActivated(java.awt.event.WindowEvent evt) {
t1.start();
}
private void btnMoveActionPerformed(java.awt.event.ActionEvent evt) {
shape.setLocation(23, 44);
}
The core problem is you're fighting the layout management API, which when you call setText is causing the container to be invalidated and relayed out
You might consider using something like JLayeredPane, but remember, you become entirely responsible for the size and position of the component
The other problem you have is you're violating the single threaded nature of Swing, Swing is not thread safe, meaning you shouldn't update the ui from out of the Event Dispatching Thread.
To solve that particular problem you should use a Swing Timer instead of a thread, see How to use Swing Timers for more details

Continue once the thread returns true

I am having trouble getting my code to 'pause' until the thread is finished. My program is simply a JProgressBar inside a frame. I am using a loop located inside a thread, which loops every 10 milliseconds and adds +1 to the progress bar. The end result will make the bar look animated as its value increases from 0 to 100.
Now, the problem is getting the code to 'wait' until the progress bar hits value 100. As you can see in the code, I would like to have buttons re-enabled only when the progress bar reaches value 100. As of right now, the buttons seem to get re-enabled simultaneously as the progress bar is increasing.
disableButtons();
// loop and animate the progress bar
for (int i = 0; i <= 100; i++) {
Game.setProgBar_loading(i);
// wait a number of milliseconds
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
}
enableButtons();
You could nest Loading class in Program and then access Program's variables from there, and then pretty much copy this part of the code: // <TODO> re-enable all buttons into the end of the loop. Something like this:
class Program {
// assuming you have these buttons:
private JButton button1;
private JButton button2;
private JButton button3;
// snip
public Program() {
// assuming there's initialization code for the above buttons somewhere...
// button1 = new ...
// button2 = new ...
// button3 = new ...
// frame initializing
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setBounds(100, 100, 890, 480);
getContentPane().setLayout(null);
setVisible(true);
// create the loading bar
loadingBar = new JProgressBar();
loadingBar.setBounds(10, 11, 864, 23);
getContentPane().add(loadingBar);
// <TODO> disable all buttons
button1.setEnabled(false);
button2.setEnabled(false);
button3.setEnabled(false);
// animate the loading bar
Loading thread = new Loading();
new Thread(thread).start();
// do not re-enable the buttons here yet.
}
// snip
public class Loading implements Runnable {
#Override
public void run() {
// loop and increment loading bar's value
for (int i = 0; i <= 100; i++) {
Program.setProgressBar(i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
}
// re-enable the buttons here instead
button1.setEnabled(true);
button2.setEnabled(true);
button3.setEnabled(true);
}
}
}
EDIT: To generalize your Loading class and reuse it later you could create an interface that contains methods such as beforeExecute() and afterExecute(). Take that interface as an argument to Loading's constructor and save it as a member so you can later call the interface's methods from within run():
public interface LoadingHandler {
public void beforeExecute();
public void afterExecute();
}
public class Loading implements Runnable {
private LoadingHandler handler;
public Loading(LoadingHandler handler) {
this.handler = handler;
}
#Override
public void run() {
// disableButtons();
// use interface's methods instead:
handler.beforeExecute();
// loop and animate the progress bar
for (int i = 0; i <= 100; i++) {
Game.setProgBar_loading(i);
// wait a number of milliseconds
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
}
// enableButtons();
// use interface's method instead:
handler.afterExecute();
}
}
Then, in your GUI's thread you would do something like this:
Loading thread = new Loading(new LoadingHandler() {
#Override
public void beforeExecute() {
disableButtons();
}
#Override
public void afterExecute() {
enableButtons();
}
});
new Thread(thread).start();
Why not just enable the buttons after the loading bar hits the 100%? Either you do it via callback or just put the regarding code in the setProgressBar method
// set the progress bar's value
public static void setProgressBar(int num) {
loadingBar.setValue(num);
if(num >=100){
// <TODO> re-enable all buttons
}
}
I am not sur to understand why it is needed to use a thread to animate a progress bar if no parrallel actions are done meanwhile.
join method from Thread class is blocking until the thread execution terminates.
...
Thread animatedProgressBarThread = new Thread(thread);
animatedProgressBarThread.start();
// next line blocks until thread terminates its execution
animatedProgressBarThread.join();
The run method of the Loading class uses Catch block to ignore InterruptedException. According to documentation, the thread created won't be interruptable. the catch block should set thread's state as interrupted and terminate.

Rectangle colouring logic

I have one three rectangles in my canvas. I wanted to change the colours of three rectangles
in a slow manner one by one.
For example: When starting the application, user should be able to see three rectangles with the same colour (blue).
After 2 secons that rectangles colour should change to red.
Again after 2 secons the next rectangles colour should get changed.
The last one is also done the same way, that means after 2 seconds of the 2nd rectangle.
I wrote in my own way. But it is not working. All the rectanlges are changed together. I want one by one.
Could anyone give me the logic.
final Runnable timer = new Runnable() {
public void run() {
//list of rectangles size =3; each contain Rectangle.
for(int i = 0 ; i < rectangleList.size();i++){
if(rectangleListt.get(i).getBackgroundColor().equals(ColorConstants.blue)){
try {
rectangleList.get(i).setBackgroundColor(ColorConstants.yellow);
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//rectSubFigureList.get(i).setBorder(null);
}/*else{
rectSubFigureList.get(i).setBackgroundColor(ColorConstants.blue);
}*/
}
You're likely calling Thread.sleep inside of Swing's event thread or EDT (for event dispatch thread), and this will cause the thread itself to sleep. Since this thread is responsible for all of Swing's graphics and user interactions, this will in effect put your entire application to sleep, and is not what you want to have happen. Instead, read up on and use a Swing Timer for this.
References:
Swing Timer tutorial
Swing Event Dispatch Thread and Swingworker tutorial
To expand on Hidde's code, you could do:
// the timer:
Timer t = new Timer(2000, new ActionListener() {
private int changed = 0; // better to keep this private and in the class
#Override
public void actionPerformed(ActionEvent e) {
if (changed < rectangleList.size()) {
rectangleList.setBackgroundColor(someColor);
} else {
((Timer) e.getSource()).stop();
}
changed++;
}
});
t.start();
You can set a Timer:
// declaration:
static int changed = 0;
// the timer:
Timer t = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Change the colour here:
if (changed == 0) {
// change the first one
} else if (changed == 1) {
// change the second one
} else if (changed == 2) {
// change the last one
} else {
((Timer) e.getSource()).stop();
}
changed ++;
}
});
t.start();

Java: Double-Click a JSlider to Reset

I have a JSlider that sets the speed of my metronome, from 40 - 200, where 120 is the default, in the middle.
When the user clicks the metronome button, the metronome plays at the speed displayed on the JSlider - the user drags the slider to the right, the speed of the metronome increases, and it decreases if they slide it to the left.
How do I add functionality so that if the user double-clicks on the JSlider button, it defaults back to 120 - in the middle?
Here is my code:
public Metronome() {
tempoChooser = new JSlider();
metronomeButton = new JToggleButton();
JLabel metText = new JLabel("Metronome:");
add(metText);
...
tempoChooser.setMaximum(200);
tempoChooser.setMinimum(40);
tempoChooser.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
tempoChooserStateChanged(evt);
}
});
add(tempoChooser);
...
}
private void tempoChooserStateChanged(javax.swing.event.ChangeEvent evt) {
final int tempo = tempoChooser.getValue();
if (((JSlider) evt.getSource()).getValueIsAdjusting()) {
setMetronomeButtonText(tempo);
} else {
processTempoChange(tempo);
}
}
thanks in advance!
This should help you out: http://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html
You need to read up on that and implement MouseListener. You can use int getClickCount() to count how many times the user has clicked, which will help you read double clicks.
Hope this helps!
Even though I dont see a question, my gues is you are looking for MouseListener.
Not simple job, you have to add javax.swing.Timer and listening if during fixed period Mouse cliked once or twice times, for example
I recently wrote something similar so I could differentiate between single and double left mouse-button clicks:
private Timer timer;
#Override
public void mouseClicked(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1){
if (timer == null) {
timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() { // timer expired before another click received, therefore = single click
this.cancel();
timer = null;
/* single-click actions in here */
}
}, (Integer) Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval"));
}
else { // received another click before previous click (timer) expired, therefore = double click
timer.cancel();
timer = null;
/* double-click actions in here */
}
}
}

Categories