I am trying to stop an intro song from playing when pressing the start button. I tried doing so using this code. Note that this code does not entail all my code. The GUI looks fine, the Actionlisteners work fine too. Only the music does not stop playing when the start button is pressed.
File introPath = new File("src/BattleshipGUI/423499__soundflakes__epic-heroic-orchestral-
dramatic.wav");
File buttonPressedPath = new File("src/BattleshipGUI/sfx_patcher_button_launch.wav");
static Clip introWAV;
Menu() {
super("BattleBoard");
this.setContentPane(this.panelMain);
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
this.setLocationRelativeTo(null);
this.pack();
play(introPath); // playing when launching
// when the game starts, the sound should stop
ButtonStartGame.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
play(buttonPressedPath);
try {
if (random) {
currentCols = (Integer) spinnerColumns.getValue();
currentRows = (Integer) spinnerRows.getValue();
if (currentCols < 5 || currentRows < 5) {
throw (new IllegalArgumentException());
} else {
BoardFrame b = new BoardFrame(currentRows, currentCols);
b.SetFrame(currentRows, currentCols);
b.AddRandomShips(currentRows, currentCols);
b.ScoreMethod(adjustedScoreMethod);
introWAV.stop();
introWAV.flush();
introWAV.close();
dispose();
public static void SetIntroWAV(Clip clip){
introWAV=clip;
}
public static void play(File file) {
try {
Clip sound = AudioSystem.getClip();
sound.open(AudioSystem.getAudioInputStream(file));
SetIntroWAV(sound);
sound.start();
} catch (Exception e) {
System.out.println(e);
}
}
I tried other ways, like using while loops in the Play-class, 'if-else'-statements,... Does someone know how to fix this? Thanks in advance!
The culprit is part of your play method.
Whenever you want to play any sound you also call SetIntroWAV internally. This results in your introWAV variable being set.
Here's why that's a problem:
The first time you call play, your intro sound is played back and introWAV has the correct value.
However, once you start your game and play a different sound (namely using buttonPressedPath) your introWAV variable is set to a different value: the sound that was most recently started.
When you then try to stop your sound from playing, you're using introWAV which doesn't actually contain a reference to your intro sound anymore. Instead, this will result in your most recently played sound to be stopped since this is what introWAV is holding now.
To fix this, it's simply a case of only setting your introWAV variable once and not every time play is called. There are multiple ways of doing this, including these:
You could let your play method return the resulting Clip that will be played afterwards:
public static Clip play(File file) {
Clip sound = null;
try {
sound = AudioSystem.getClip();
sound.open(AudioSystem.getAudioInputStream(file));
sound.start();
} catch (Exception e) {
System.out.println(e);
} finally {
return sound;
}
}
You can then use this returned value to call SetIntroWAV once: SetIntroWAV(play(introPath));
You could also use this return value for other purposes like keeping local references to your sounds. However, you don't have to use it every time and can still ignore it whenever you don't need that reference.
You could rewrite your play method to also contain a parameter telling the method whether the sound you're trying to play is the intro sound:
public static void play(File file, boolean intro) {
try {
Clip sound = AudioSystem.getClip();
sound.open(AudioSystem.getAudioInputStream(file));
if(intro) {
SetIntroWAV(sound);
}
sound.start();
} catch (Exception e) {
System.out.println(e);
}
}
This will also result in SetIntroWAV only being called once.
I'd also recommend you use more of an object-oriented style of programming for this as it can make things like these much more obvious and easier to fix.
For example, you could create separate classes for audio playback and your gameplay.
IMHO the best practice with Clip variables is to load and open, and then hold them in memory. This can be done in a class that manages your sound effects. In that class, have a Clip as an instance variable and preload and open it in the constructor.
This class can also have two methods that are called from your game.
public void play() {
clip.setFramePosition(0); // ensures Clip will start from the beginning
clip.start();
}
public void stop() {
clip.stop();
}
With this sort of structure, it also become easier to manage multiple sounds. For example, you can have two instances of this sound-managing class, and set each to a different sound source. Then, you can readily stop one and start another.
Related
I am trying to make a game and attempting to make a "mute" and "unmute" button, but I cant figure out how to make it so when you push the button it stops the same clip that is being played at the beginning of the program. (using different methods of course).
I attempted to make the clip and audio public, but I keep getting an error and I'm not sure why.
public class TowerDefense
{
String filepath = "MenuTheme.wav";
private Clip clip;
void playMusic(String musicLocation)
{
try{
File musicPath = new File(musicLocation);
if(musicPath.exists())
{
AudioInputStream audioInput = AudioSystem.getAudioInputStream(musicPath);
clip = AudioSystem.getClip();
clip.open(audioInput);
clip.start();
clip.loop(Clip.`LOOP_CONTINUOUSLY`);
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
void pauseMusic(String musicLocation2)
{
long clipTimePosition = clip.getMicrosecondPosition();
clip.stop();
}
==============
//this is in a different private method called Options
panel.setButtonsActionListener2(**new** ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
frame.dispose();
TowerDefense musicObject = new TowerDefense();
musicObject.pauseMusic(filepath);
Options();
}
});
I was expecting an output of the clip stopping and then you can either make it play again or keep it muted. In this case it just says error when I press the "mute" button.
So, in the code that runs when you click your mute button, you are making a new TowerDefense object that is supplied the music's filepath. Here is the issue with that. The clip that is already playing exists in the program. Creating a new TowerDefense object will not automatically give you access to the clip that is playing.
Ensure that you call the pauseMusic method on the same object where you called the playMusic method.
Thus, if you already have created a TowerDefense object in the program and called playMusic, then give your action listener access to that object and use the existing object to call pauseMusic.
I want to make a program that plays sounds and displays png images onto the JFrame. I am trying to put the png and sound files (.wav) into the package that the class that's displaying it is in. I can't seem to get it working though. I've looked up many methods on how to do it, it every time they all pop up NullPointer errors. Or that it couldn't find the file, even though the file path specified was exactly where it was when I went into File Explorer. So if anyone can help me find a way to play music and display the picture (getting the png file and making it an ImageIcon), that would be great.
Here is java code
Play Button action Performed
private void play_btnActionPerformed(java.awt.event.ActionEvent evt) {
SetImage();
PlaySound();
}
Play audio
void PlaySound() {
try (InputStream in = getClass().getResourceAsStream("sam.wav")) {
InputStream bufferedInS = new BufferedInputStream(in);
try (AudioInputStream audioInS = AudioSystem.getAudioInputStream(bufferedInS)) {
Clip clip = AudioSystem.getClip();
clip.open(audioInS);
clip.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
set image
void SetImage() {
audio_icon.setIcon(new javax.swing.ImageIcon(getClass().getResource("/player/player.png")));
}
I'm relatively new to java (I took a 1 semester online class, so I know the basic structure of the language but my knowledge has lots of gaps). I'm trying to write a simple ear training application for a class in microtonal music I'm taking, and I obviously need to be able to play sound with this application. Looking on the web, a lot of the info I've found is out of date and I'm having trouble figuring out the APIs for Clip, Dataline, etc. (again, I'm new!) Is there a simple way to load sounds onto some kind of object (like an AudioClip maybe?) so they can be played back when necessary? If it's more complicated than that, I would appreciate being directed to resources that would help me figure how this process works.
You can use this method to play a audio clip using java application
public static synchronized void playSound(final String url) {
new Thread(new Runnable() {
// The wrapper thread is unnecessary, unless it blocks on the
// Clip finishing; see comments.
public void run() {
try {
Clip clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(
Main.class.getResourceAsStream("/path/to/sounds/" + url));
clip.open(inputStream);
clip.start();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}).start();
}
Here's another tutorial on java how to add audio clips. You can check this too Tutorial in playing sounds in Java
You will need to import the following:
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import java.io.File;
import java.io.FileNotFoundException;
I created a method for playing the audio clip, as shown below:
public static void myMusic() throws FileNotFoundException {
try {
File wavFile = new File(/*file path*/);
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(wavFile));
clip.start();
} catch (Exception e) {
System.out.println(e);
}
}
Let's say for example, if your audio clip's file name is music.wav, your file path may be:
File wavFile = new File("music.wav");
Finally, to play the audio clip, you will need to call the method.
I have a problem with the Android MediaPlayer when changing the dataSource of the player. According the specification of the MediaPlayer (http://developer.android.com/reference/android/media/MediaPlayer.html) I have to reset the player when changing the dataSource. This works fine, but as soon as the channelChanged method is called twice in quick succession the MediaPlayer.reset freezes the UI. I profile the code as seen here:
public void channelChanged(String streamingUrl)
{
long m1 = System.currentTimeMillis();
mMediaPlayer.reset();
long m2 = System.currentTimeMillis();
try
{
mMediaPlayer.setDataSource(streamingUrl);
}
catch (IOException e)
{
e.printStackTrace();
}
long m3 = System.currentTimeMillis();
mMediaPlayer.prepareAsync();
long m4 = System.currentTimeMillis();
Log.d("MEDIAPLAYER", "reset: " + (m2 - m1));
Log.d("MEDIAPLAYER", "setDataSource: " + (m3 - m2));
Log.d("MEDIAPLAYER", "preparing: " + (m4 - m3));
}
reset: 3
setDataSource: 1
preparing: 0
reset: 3119
setDataSource: 2
preparing: 1
So apparently the reset is blocked by the asynchronous preparing of the first call (when I wait until the first stream starts and then call channelChanged() again, everything is fine).
Any ideas how to solve the problems? Should I execute the whole method in a separate thread? Basically I want to avoid that, because it seems not to be a good coding style and can possibly cause some further issues, e.g. when the user tries to start the player again, but the player is still in the reset method, which on the other hand seems to wait for the asyncPrepare method. It is not clear how the player would behave...
Is there any other good solution?
MediaPlayer is a tricky bastard. I recommend you take a look at the sample app where the MediaPlayer bad design is made evident by looking at the mess of code you have to write around it to have a consistent media playback experience.
If anything, after looking at the sample, you see that when they want to skip a track, they essentially reset and release…
mPlayer.reset();
mPlayer.release();
…and later when they are ready to load a new track…
try {
mPlayer.reset();
mPlayer.setDataSource(someUrl);
mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
//bam!
}
});
mPlayer.prepareAsync();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
I have added the try/catch because on some devices/OS versions, the MediaPlayer is worse than others and sometimes it just does weird stuff. You should have an Interface/Listener that is capable of reacting to these situations
UPDATE:
This is a method I use when I stop (or pause) my Music Playback (mostly taken from the sample app, this is running in a service and it has been modified to suit my own app but still).
The first method is used by both stop and pause, the former passes true, the later false
/**
* Releases resources used by the service for playback. This includes the "foreground service"
* status and notification, the wake locks and possibly the MediaPlayer.
*
* #param releaseMediaPlayer Indicates whether the Media Player should also be released or not
*/
void relaxResources(boolean releaseMediaPlayer) {
stopForeground(true);
stopMonitoringPlaybackProgress();
// stop and release the Media Player, if it's available
if (releaseMediaPlayer && mPlayer != null) {
mPlayer.reset();
mPlayer.release();
mPlayer = null;
}
// we can also release the Wifi lock, if we're holding it
if (mWifiLock.isHeld()) {
mWifiLock.release();
}
}
This is part of the processPauseRequest():
if (mState == State.Playing) {
// Pause media player and cancel the 'foreground service' state.
mState = State.Paused;
mPlayer.pause();
dispatchBroadcastEvent(ServiceConstants.EVENT_AUDIO_PAUSE);//notify broadcast receivers
relaxResources(false); // while paused, we always retain the mp and notification
And this is part of the processStopRequest() (simplified):
void processStopRequest(boolean force, final boolean stopSelf) {
if (mState == State.Playing || mState == State.Paused || force) {
mState = State.Stopped;
// let go of all resources...
relaxResources(true);
currentTrackNotification = null;
giveUpAudioFocus();
}
}
Now the core part is the next/skip…
This is what I do…
void processNextRequest(final boolean isSkipping) {
processStopRequest(true, false); // THIS IS IMPORTANT, WE RELEASE THE MP HERE
mState = State.Retrieving;
dispatchBroadcastEvent(ServiceConstants.EVENT_TRACK_INFO_LOAD_START);
// snipped but here you retrieve your next track and when it's ready…
// you just processPlayRequest() and "start from scratch"
This is how the MediaPlayer sample does it (found in the samples folder) and I haven't had problems with it.
That being said, i know what you mean when you say you get the whole thing blocked, I've seen it and it's the MP buggyness. If you get an ANR I'd like to see the log for it.
For the record here's how I "begin playing" (a lot of custom code has been omited but you get to see the MP stuff):"
/**
* Starts playing the next song.
*/
void beginPlaying(Track track) {
mState = State.Stopped;
relaxResources(false); // release everything except MediaPlayer
try {
if (track != null) {
createMediaPlayerIfNeeded();
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setDataSource(track.audioUrl);
} else {
processStopRequest(true, false); // stop everything!
return;
}
mState = State.Preparing;
setUpAsForeground(); //service
/* STRIPPED ALL CODE FROM REMOTECONTROLCLIENT, AS IT ADDS A LOT OF NOISE :) */
// starts preparing the media player in the background. When it's done, it will call
// our OnPreparedListener (that is, the onPrepared() method on this class, since we set
// the listener to 'this').
// Until the media player is prepared, we *cannot* call start() on it!
mPlayer.prepareAsync();
// We are streaming from the internet, we want to hold a Wifi lock, which prevents
// the Wifi radio from going to sleep while the song is playing.
if (!mWifiLock.isHeld()) {
mWifiLock.acquire();
}
} catch (IOException ex) {
Log.e("MusicService", "IOException playing next song: " + ex.getMessage());
ex.printStackTrace();
}
}
As a final note, I've noticed that the "media player blocking everything" happens when the audio stream or source is unavailable or unreliable.
Good luck! Let me know if there's anything specific you'd like to see.
The newest phones and Android API works much butter, reset method takes only 5-20 ms when fast switching between songs (next or prev)
So there is no solution for older phones, it just how it works
How can I add sound to my basic 'Sound Adventure' game?
I am a beginner and I want to make a basic sound adventure game. (Yes you heard it right, Sound Adventure.)
I know basics of Java and I can code text adventures, but I don't know how Sound works in Java quite yet. I've seen tutorials over the internet and they don't seem to work.
I am ready to change the format of my sound. (It's currently .mp3)
Also, I am using JDK 7 with Eclipse Kepler. (If that helps.)
Here's my code so far:
package everything;
import java.util.Scanner;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
public class Main {
public static void main(String[] args) {
// Declarations
System.out.println("Please Enter Your Name To Start...");
Scanner temp = new Scanner(System.in);
String name = temp.nextLine();
System.out.println("Okay " + name + ", Let's Get Started!");
System.out.println("Press N To Start The Game...");
while(!"N".equals(temp.nextLine())){
System.out.println("I Asked For The Letter N, Was It So Hard? Try Again!");
}
}
}
There's literally tons of resources with a simple Google Search.
Using JavaFX Framework
Simply use an instance of AudioClip. This one is very suitable for just playing single short sounds.
AudioClip plonkSound = new AudioClip("http://somehost/path/plonk.aiff");
plonkSound.play();
Using Standard Java API
Standard Java API is a little more painful, I don't have any experience with it but this piece of code has 60+ Upvotes on this related question.
public static synchronized void playSound(final String url) {
new Thread(new Runnable() {
// The wrapper thread is unnecessary, unless it blocks on the
// Clip finishing; see comments.
public void run() {
try {
Clip clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(
Main.class.getResourceAsStream("/path/to/sounds/" + url));
clip.open(inputStream);
clip.start();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}).start();
}