Problem description: I have an array of sounds files with different length. Every time the Play button is clicked, I want one sound be played. The problem is that when the Play button is clicked twice (or more times), the next sound starts and overlaps the currently playing sound. I don't want to clip.stop()the current sound, I want the next sound not to start until the current sound is finished. I have tried different approaches, like boolean flag, clickCount and checking the Clip.isRunning();status, but they not really getting me to where I want. I also saw someone else having similar issue in OS (with MultiPlayer() though), but his problem was resolved AFAIK. Any suggestion would be appreciated!
Here is related code:
public class PlaySounds {
private AudioInputStream audioStream;
public static Clip clip;
private URL url;
public static int nextSound;
public void playAllSounds() {
for (String str : soundList) { // arrayList of sounds
str = soundList.get(nextSound);
url = getClass().getResource(str);
}
try {
audioStream = AudioSystem.getAudioInputStream(url);
clip = AudioSystem.getClip();
clip.open(audioStream);
clip.start();
} catch (Exception e) {
}
}
}
Main class file:
PlaySounds ps = new PlaySounds();
int next = 0;
List<Integer> positions = new ArrayList<>();
/*Some other methods....
....
*/
private void getshuffledPositions() {
for (int i = 0, i <=12; i++) {
positions.add(i);
}
Collections.shuffle(positions);
}
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == playButton) {
//Some codes.....Here I tried boolean flag, etc
ps.nextSound = positions.get(next);
ps.playAllSounds();
//Some more codes.....
Related
I have a button which when clicked plays the song but it doesnt stop and also I cant press any other button until the music stops on its own,i.e. the song gets over. Please help!
My code is as follows:
public class Mp3Player {
private String filename;
private Player player;
public Mp3Player(String filename) {
this.filename = filename;
}
public void play() {
try {
BufferedInputStream buffer = new BufferedInputStream(new FileInputStream(filename));
player = new Player(buffer);
player.play();
}
catch (FileNotFoundException | JavaLayerException e) {
System.out.println(e);
}
}
}
And this is in the button:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
Mp3Player mp3 = new Mp3Player("C:\\Users\\USER\\Desktop\\Echo.mp3");
mp3.play();
}
Call Player#play on another thread, this will allow your song to play while allowing your GUI to still work (a common approach in Swing).
Once it's running on it's own thread, you can kill that thread to stop it, there are a lot of different ways to do this...which I'll leave for homework =)
Create a thread to play the song without pausing the whole program.
PS. I would have written this as a comment but I don't have enough reputation points.
You are executing a blocking call (Player::play) in the event dispatcher thread, which means that you are blocking the processing of events (buttons clicked &co.) until player.play() returns.
Instead, call it in woker thread. First define a SwingWorker:
class PlayerWorker extends SwingWorker<Void, Void> {
private final Mp3Player mp3;
public PlayerWorker(String filename) {
mp3 = new Mp3Player(filename);
}
#Override
protected Void doInBackground() {
mp3.play();
return null;
}
}
please mind the use of Void (capital V) and return null. Then, call it like:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
(new PlayerWorker("C:\\Users\\USER\\Desktop\\Echo.mp3")).execute();
}
I've found a working Sound class online and am using it to play sound in a game I'm making. However, I wanted to continuously play the file so I decided just to use a Swing Timer and it works, the only problem is my game window freezes and won't even let you exit it out without using task manager.
Can you please take a look at it and tell me where I went wrong? By the way main is just my object variable for my Sound class, and maindelay is just my int for the delay.
public static void loopSound(){
Timer maintime = new Timer(maindelay, new ActionListener(){
public void actionPerformed(ActionEvent e){
main.run();
}
});
maintime.start();
}
The Sound class I've found:
public class Sound extends Thread {
private String filename;
private Position curPosition;
private final int EXTERNAL_BUFFER_SIZE = 524288; // 128Kb
enum Position {
LEFT, RIGHT, NORMAL
};
public Sound(String wavfile) {
filename = wavfile;
curPosition = Position.NORMAL;
}
public Sound(String wavfile, Position p) {
filename = wavfile;
curPosition = p;
}
public void run() {
File soundFile = new File(filename);
if (!soundFile.exists()) {
System.err.println("Wave file not found: " + filename);
return;
}
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
} catch (UnsupportedAudioFileException e1) {
e1.printStackTrace();
return;
} catch (IOException e1) {
e1.printStackTrace();
return;
}
AudioFormat format = audioInputStream.getFormat();
SourceDataLine auline = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
try {
auline = (SourceDataLine) AudioSystem.getLine(info);
auline.open(format);
} catch (LineUnavailableException e) {
e.printStackTrace();
return;
} catch (Exception e) {
e.printStackTrace();
return;
}
if (auline.isControlSupported(FloatControl.Type.PAN)) {
FloatControl pan = (FloatControl) auline
.getControl(FloatControl.Type.PAN);
if (curPosition == Position.RIGHT)
pan.setValue(1.0f);
else if (curPosition == Position.LEFT)
pan.setValue(-1.0f);
}
auline.start();
int nBytesRead = 0;
byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
try {
while (nBytesRead != -1) {
nBytesRead = audioInputStream.read(abData, 0, abData.length);
if (nBytesRead >= 0)
auline.write(abData, 0, nBytesRead);
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
auline.drain();
auline.close();
}
}
}
I'm guessing the sound last longer than 1 second. The int you pass to Timer is in milliseconds. It would seem to me that you are creating the sounds quicker than they can be played and this is clogging your application and blocking the UI (hence the "freeze").
I think using a Timer is the wrong way to repeatedly play the sound. You need instead to modify the sound file.
// as Sound
class RepeatSound {
...
public void run() {
// modify this condition to something meaningful
while (true) {
// as original Sound.run() in here
...
}
}
...
}
And call RepeatSound.start(). You must change that while(true) to exit with your application e.g. make it while (myFrame.visible()) to stop playing once the window is closed.
Some hints to solve the problem:
Swing Timer API states that a timer will repeat the notification to its listeners by default. It means you have an endless loop caused by this timer. See isRepeats() javadoc.
Consequently in each iteration a new thread playing a song is created which is not correct at all.
However as you don't have any interaction between swing components and played song you don't need a Swing timer actually. Just run your Song class in a separate thread than the EDT (Event Dispatch Thread) and it should be all right.
However, I wanted to continuously play the file...
As far as I can see in SourceDataLine interface API it extends from Line interface which allows you add LineListeners to listen LineEvents. Having said all this you may add a listener to restart the data line when it stops. I'd say it should look like something like this:
public class Sound extends Thread {
...
public void run()
...
auline.addLineListener(new LineListener() {
#Override
public void update(LineEvent e) {
if(e.getType() == LineEvent.Type.STOP && e.getLine().isOpen()) {
e.getLine().start();
}
}
});
auline.start();
...
}
...
}
Note: I really don't have time enough to play with this API so it's up to you test if it works. However I made an MP3 player for my cellphone using Java Mobile Media API and the approach was quite similar when I wanted to repeat the same song over again when it finishes.
I'm working at an Audio Player, which is written in Java with GUI. For playing the mp3 files, I've chosen JLayer library from javazoom because I saw it's very popular and used. I made the GUI, managed to play the selected mp3 file from the playlist.
My problem is that if I press many times on the play button or on the file from the playlist it will start playing the song as many times as I press it and I want to play it one the same thread ; if I press again play button I want to play again not to start the same song while the current one is playing .
Here is my code which play the mp3 file:
public class Playing implements Runnable{
private Player mp3Player;
private Thread playerThread;
public void createPlayer(FileInputStream file) throws JavaLayerException{
mp3Player = new Player(file);
playerThread = new Thread(this);
playerThread.start();
}
#Override
public void run(){
try {
mp3Player.play();
}
catch (JavaLayerException ex) {
Logger.getLogger(Playing.class.getName()).log(Level.SEVERE, null, ex);
}
}
This is my method for the play button:
public void createPlayButton(){
play = new JButton();
playButton = new ImageIcon("D:/Audio Player/Images/playButton.png");
play.setBounds(125, 100, 50, 50);
play.setIcon(playButton);
play.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < select.getFilesPath().size(); i++){
if (select.getFilesPath().get(i).toString().contains(playlistBody.getSongName())){
try {
mp3Player.createPlayer(new FileInputStream(new File(select.getFilesPath().get(i).toString())));
} catch (JavaLayerException ex) {
Logger.getLogger(PlayerBody.class.getName()).log(Level.SEVERE, null, ex);
} catch (FileNotFoundException ex) {
Logger.getLogger(PlayerBody.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
});
}
I mention that I'm new to multithreading, so dont be so hard on me. If I cannot do this with JLayer, please recommend me a good library with which I can play mp3 files. Thank you in advance and I'm waiting for your suggestions.
I fixed my issue with the threads; I'll put the solution, maybe will help someone.
static int fileRunning = 0;
public void playMp3(FileInputStream file) throws JavaLayerException{
if (fileRunning == 0){
mp3Player = new Player(file);
playerThread = new Thread(this);
playerThread.start();
fileRunning = 1;
}
}
So the main idea is that when I start playing a song, that int will take value 1, and it won't be 0, so no more threads can be made.
use this after you press you play button and the thread has been started once player start the button will disable and let it enable again once the song is being completed
yourplaybutton.setEnabled(false);
ok so im working on my program which will play a song once a button is clicked, i have the code down, and my audio is playing when the button is clicked,but the only problem now is that my program is going completely bonkers and just playing the audio when the button is clicked!!!! the program just freezes the audio keeps playing but the program will not do it's other functions! What am i doing wrong? and how do i fix it?
heres the code for the action listeners button
in myFrame.java
sound sound = new sound();
private File pokemonBattle = new File(".\\audio\\"+"sound.wav");
private void dualButtonActionPerformed(java.awt.event.ActionEvent evt)
{
// TODO add your handling code here:
for (int i = 0; i<1; i++){
c = deck.getNextCard();
p1hand[i] = c;
p1.setCard(c); //ignore all this
c = deck.getNextCard();
p2hand[i] = c;
p2.setCard(c);
}
pokemon1.setEnabled(true);
pokemon2.setEnabled(true);
pokemon1.setIcon(p1hand[0].getImage()); //ignore all this as well
pokemon2.setIcon(p2hand[0].getImage());
textArea.setText("Pokemon 1:"+p1hand[0].toString()+"\nPokemon 2:
"+p2hand[0].toString());
p1ResultLabel.setText("");
p2ResultLabel.setText("");
//this is where im calling my audio
sound.playAudio(pokemonBattle);//this is where im calling my play audio method
}
sound.java where i have playAudio()
import java.io.File;
import javax.sound.sampled.*;
public class sound {
public void playAudio(File sf){
AudioFormat audioFormat;
AudioInputStream audioInputStream;
SourceDataLine sourceDataLine;
try
{
audioInputStream = AudioSystem.getAudioInputStream(sf);
audioFormat = audioInputStream.getFormat();
System.out.println(audioFormat);
DataLine.Info dataLineInfo = new
DataLine.Info(SourceDataLine.class,audioFormat);
sourceDataLine =(SourceDataLine)AudioSystem.getLine(dataLineInfo);
byte tempBuffer[]=new byte[100000];
int cnt;
sourceDataLine.open(audioFormat);
sourceDataLine.start();
while((cnt=audioInputStream.read
(tempBuffer,0,tempBuffer.length))!=-1){
if(cnt>0){
sourceDataLine.write(tempBuffer,0,cnt);
}
}
}
catch (Exception e)
{
e.printStackTrace();
System.exit(0);
}
}
}
You need to play the audio in a separate thread. The program becomes unresponsive because it is playing your audio on the event dispatch thread instead of drawing the UI and responding to user interaction.
instead of:
sound.playAudio(pokemonBattle);
do:
Thread t = new Thread( new SoundPlayer( sound, pokemonBattle );
t.start();
and also:
public class SoundPlayer implements Runnable
{
private final sound sound;
private final File soundFile;
public SoundPlayer( sound sound, File soundFile )
{
this.sound = sound;
this.soundFile = soundFile;
}
public void run()
{
sound.playAudio( soundFile );
}
}
Joseph is right. But in this instance the problem is
while((cnt=audioInputStream.read
(tempBuffer,0,tempBuffer.length))!=-1){
if(cnt>0){
sourceDataLine.write(tempBuffer,0,cnt);
}
}
You while loop will run infinitely hence freezing up your application. Since you are reading and writing the entire buffer at once there is no need for the while loop. Do this outside of a loop and you will be fine.
I'm building a drum machine/sequencer application, where the user selects a pattern of checkboxes (that are in a grid format: each column is a different beat, each row is a different instrument), and on clicking "play" an audio sample should hopefully play on each selected beat in the pattern.
Currently, I'm using a java.swing timer to run the note() method in a loop. However, this makes it play on every "beat", whereas I only want it to play on selected beats. I have a feeling I need to use timer.stop() and timer.start() somewhere, so that note() won't execute on the unselected beats but will start again when it encounters the next selected beat.
I created the array int pattern[] = {1,0,1,0}; to hold the pattern I want the sound to play in. I was thinking there might be a way to cycle through the array and execute note() each time the value is 1, but I don't really know how it would be done.
So I guess the main question is: how to I get timer to trigger (or not trigger) an event in a pattern? Are any of my inclinations correct, or am I approaching this in the wrong way?
Grateful for any suggestions/advice :)
Here's what I have so far:
import java.io.*;
import javax.sound.sampled.*;
import javax.swing.Timer;
import java.awt.event.*;
public class PlaySound extends JFrame {
static String folderPath = "folder/path/goes/here/";
public static String fileName = "kick_01.wav";
int pattern[] = {1,0,1,0};
int c = 0;
// Constructor
public PlaySound() {
Timer timer = new Timer(500, new ActionListener() { //500 == 120bpm
public void actionPerformed(ActionEvent e) {
note(fileName); //Play audio file
}
});
timer.start();
}
public static void note(String f) {
try {
try {
// Open an audio input stream.
File soundFile = new File(folderPath + f);
AudioInputStream audioIn = AudioSystem.getAudioInputStream(soundFile);
// Get a soundjava clip resource.
Clip clip = AudioSystem.getClip();
// Open audio clip and load samples from the audio input stream.
clip.open(audioIn);
clip.start();
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
} catch (Exception e) {}
}
}
Keep track of where you are in the pattern every time the Timer fires. Something like:
if (pattern[index] == 1)
playSound();
index++;
if (index == pattern.length)
index = 0;