I am currently creating virtual drum kit. Kinect is recording my moves and when I hit the virtual drum the program needs to play the sound of this drum. Currently my code to play music looks like that:
void playInstrumentSound(InstrumentModel instrument) {
if (instrument.getMedia() != null) {
new Thread() {
public void run() {
instrument.setPlaying(true);
MediaPlayer player = new MediaPlayer(instrument.getMedia());
player.setVolume(1);
player.play();
try {
Thread.sleep(properties.getSleepLength());
} catch (InterruptedException e) {
e.printStackTrace();
}
instrument.setPlaying(false);
player.dispose();
}
}.start();
}
}
InstrumentModel is my class that contains Media object that is initialized by using pathToSound which is path to .wav file in resources folder:
if (this.pathToSound != null) {
media = new Media(new File(this.pathToSound).toURI().toString());
}
Right now this code doesn't reach my expectations, because if I hit a drum then I can't hit it again for the properties.getSleepLength() value of time (now it is about 200ms). If I don`t make Thread sleep then I don't hear full sound.
For example, if the .wav file duration is 300ms and I make Thread.sleep(300) inside playInstrumentSound() method, then I can hear full sound but I can't play different sound for this 300ms. But if I make Thread.sleep(50) then I can hit it again almost instantly but I hear only 50ms of the .wav file.
I would like to be able to hit the drum almost instantly but also hear full sound of it. How can I reach that? Thanks in advance.
EDIT:
I just got an idea to change order inside playInstrumentSoundMethod:
void playInstrumentSound(InstrumentModel instrument) {
if (instrument.getMedia() != null) {
instrument.setPlaying(true);
MediaPlayer player = new MediaPlayer(instrument.getMedia());
player.setVolume(1);
player.play();
new Thread() {
public void run() {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
instrument.setPlaying(false);
}
}.start();
new Thread() {
public void run() {
try {
Thread.sleep(250);
} catch (InterruptedException e) {
e.printStackTrace();
}
player.dispose();
}
}.start();
}
Now it should turn the sound off after 250ms and changes isPlaying flag after 50ms (if isPlaying flag is true then you can't hit a drum again). You think that it might work?
Related
I have sound effects for a game. Upon the game frame opening, the first sound lags behind. After that sound plays, no more lag is experienced.
Here's my clip player:
public enum SoundEffect
{
WALL("ping_pong_8bit_plop"),
PADDLE("ping_pong_8bit_beeep"),
POINT("ping_pong_8bit_peeeeeep");
public static enum Volume
{
MUTE, UNMUTE
}
public static Volume volume = Volume.MUTE;
private Clip clip;
SoundEffect (String file)
{
try
{
AudioInputStream inputStream = AudioSystem.getAudioInputStream(this.getClass().getResource(file+".wav"));
AudioFormat format = inputStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
clip = (Clip)AudioSystem.getLine(info);
clip.open(inputStream);
}
catch (UnsupportedAudioFileException uae)
{
uae.printStackTrace();
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
catch (LineUnavailableException lue)
{
lue.printStackTrace();
}
}
public void play()
{
if (volume != Volume.MUTE)
{
if (clip.isRunning())
clip.stop();
clip.flush();
clip.setFramePosition(0);
clip.start();
}
}
static void init()
{
values();
}
}
So when I call SoundEffect.WALL.play() for example, it plays fine overall, but the very first time it plays there is a huge lag spike. What can I do to solve this, preferably still using Clips?
I had the same bug, the first play of a clip was about 1-2 sec delayed, no matter when the play()-method is called. What I did is I played a clip in the main() method which is a half seccond long and contains nothing (no sound). This means that the play() method of the actual sound isn't called the first time, and for me, it works. Hope it helped.
Use a separate Thread
Thread music = new Thread(new Runnable() {
#Override public void run() { your code }
};
music.start();
I am writing a programm and I added some background music to it.
However I would like to be able to stop the music by the click of a button.
I know how to add buttons and stuff however I don't know how to stop the music, I have tried a lot of things but just can't get it to work.
This is my code:
new Thread(new Runnable() {
public void run() {
try {
Clip Music = AudioSystem.getClip();
AudioInputStream BMG = AudioSystem.getAudioInputStream(
Main.class.getResourceAsStream("/Music/BackgroundMusic.wav"));
Music.open(BMG);
Music.start();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}).start();
If you could help that would be awesome!
Thank you in advance,
JS
Clip Music=null;
new Thread(new Runnable() {
public void run() {
try {
Clip Music = AudioSystem.getClip();
AudioInputStream BMG = AudioSystem.getAudioInputStream(
Main.class.getResourceAsStream("/Music/BackgroundMusic.wav"));
Music.open(BMG);
Music.start();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}).start();
//this goes inside the actionPerformed event of the button
if(Music!=null)Music.stop();
And as m0skit0 said you better start your variables with a lowercase letter,don't be a c# smug.
I am attempting to make an application that retrieves images and .mp3 files and transitions from one image to the next once the audio has finished. The underlying framework of how I transition between these images is a little convoluted, but I have managed to get an action in SWT that successfully enables me to manually transition from one to the next. However, a problem has arisen when I've tried to automate it; when placed into a loop, my playAudio() method begins before all of the calls I make in my displayShow() method have resolved, which results in a blank window, despite the audio still playing.
Here is the run method for the action that I want to start the show:
Action startAction = new Action("Start") {
public void run() {
//do {
displayShow();
playAudio();
//} while(true);
}
};
Here is playAudio(). I am able to PLAY the audio without incident:
public void playAudio() {
final String audio = "Happy_Birthday.mp3";
audioLatch = new CountDownLatch(1);
audioThread = new Thread() {
public void run() {
try {
Player player = new Player
(new BufferedInputStream
(new FileInputStream(audio)));
player.play();
audioLatch.countDown();
} catch (JavaLayerException e) {
} catch (FileNotFoundException e) {
} catch (Exception e) {
}
}
};
audioThread.start();
try {
audioLatch.await();
} catch (InterruptedException e) {
}
}
And here is displayShow():
private void displayShow() {
new Thread() {
public void run() {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
Control[] children = container.getChildren();
for (int i = 0; i < children.length; i++) {
children[i].dispose();
}
show.showSlide(container);
container.layout();
}
});
}
}.start();
}
show.showSlide returns a composite whose parent is container, which is the immediate child of the highest parent composite. Within the newly created composite, an image is added to a label and the label's parent is assigned to composite. I realize whether displayShow() is in a separate thread or not seems to be immaterial; this was just the last thing I tried.
It is not solely the addition of the loop that causes the refresh to not execute. The only way I can get the manual transition to work is if I remove the CountDownLatch from the playAudio() method. Were I to remove this latch, the only way to encase these two methods in a loop would be embedded while loops, which seem to hog a fair amount of the CPU and still does not solve my problem. Am I missing anything?
The audioLatch.await() is blocking the main program thread, this is the thread that all SWT operations run on so the Display.asyncExec runnables are just being queued until the thread is available.
If you really must wait in the playAudio method you could run the display event loop there until the background thread is finished:
while (! background thread finished)
{
if (!display.readAndDispatch())
display.sleep();
}
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);