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.
Related
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?
I am writing a java program to record sound(using Java Sound API), When I run my code while headphones are plugged in, the program works correctly, but if I remove headphones in the middle the program is neither capturing the sound nor displaying a notification to show that capturing device is not found.
My aim here is to detect the removal of the external microphone and automatically switch to the built-in microphone of my laptop computer.
Here is my code:
public void capAud() {
try {
af = getAudioFormate();
DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class,af);
td = (TargetDataLine)AudioSystem.getLine(dataLineInfo);
td.addLineListener(new MyListener());
new newthread().start();
} catch(Exception e) {
e.printStackTrace();
}
}
And the "newThread" class
class newthread extends Thread {
public void run() {
baos=new ByteArrayOutputStream();
int len=32*1024;
try {
File file=new File("C:\\Users\\Programmer\\Desktop\\my.wav");
td.open(af);
td.start();
byte []temp=new byte[len];
while(td!=null) {
int num=td.read(temp, 0, len);
if(num>0) {
baos.write(temp, 0, num);
}
}
baos.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
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();
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;