I am trying to make a text to speech thread stop whenever the talk(String text, boolean voiceEnabled) method is called from an ActionEvent using buttons.
When these buttons are pressed different text Strings are passed to the method, which runs the audio on a new thread. If the current thread is still running but a new ActionEvent occurs I need the current thread to stop (i.e the text-to-speech) so that the new text-to-speech audio can be played without the current audio clip and new clip playing over the top of eachother.
This is what I currently have but the TTS audio are playing over the top of eachother. I need the current TTS to stop as soon as a new TTS is triggered. I believe my main problem is that a new Thread is being made each time the method is called.
Any help greatly appreciated. Thanks!
public void talk(String text, boolean voiceEnabled) {
System.out.println(text);
// Create a new Thread as JLayer is running on the current Thread and will
// make the application lag
Thread thread = new Thread(() -> {
try {
// Create a JLayer instance
AdvancedPlayer player = new AdvancedPlayer(synthesizer.getMP3Data(text));
if (voiceEnabled) {
player.play(); //Plays the TTS audio
System.out.println("Successfully retrieved synthesizer data");
}
else {
}
} catch (IOException | JavaLayerException e) {
e.printStackTrace();
}
});
// We don't want the application to terminate before this Thread terminates
thread.setDaemon(false);
// Start the Thread
thread.start();
}
You appear to be burying key references inside of anonymous inner classes, and I don't see how you can get to them when and if needed. Why do this? Why not create an instance of a non-anonymous class, one with an AdvancedPlayer field, one whose reference is held by some collection, perhaps a List<...> or a HashMap, or by a variable if only one to two are running, where you can extract the object, get its AdvancedPlayer field and call .stop() on it?
e.g.,
public class RunnablePlayer implements Runnable {
private AdvancedPlayer player;
private String text;
private boolean voiceEnabled;
public RunnablePlayer(String text, boolean voiceEnabled) {
this.text = text;
this.voiceEnabled = voiceEnabled;
}
#Override
public void run() {
try {
// Create a JLayer instance
player = new AdvancedPlayer(synthesizer.getMP3Data(text));
if (voiceEnabled) {
player.play(); //Plays the TTS audio
System.out.println("Successfully retrieved synthesizer data");
}
} catch (IOException | JavaLayerException e) {
e.printStackTrace();
}
}
public AdvancedPlayer getPlayer() {
return player;
}
public void stop() {
// perhaps do a null check here first?
if (player != null) {
player.stop();
}
}
}
Then you could have a field of the class, something like:
// field of the class
private RunnablePlayer runnablePlayer;
and use this in your talk method:
public void talk(String text, boolean voiceEnabled) {
if (runnablePlayer != null) {
runnablePlayer.stop(); // not calling this on a Thread
}
runnablePlayer = new RunnablePlayer(text, voiceEnabled);
Thread thread = new Thread(runnablePlayer);
//.....
thread.start();
}
Code not compiled or tested, but is presented to just give a general idea.
Related
I'm new to Java so sorry if the problem is trivial. My code is probably a mess. I created a online radio player for Raspberry Pi in Java and I have to do a POST request every 10 seconds to check what is the current song playing and if the song has changed, I have to do some things (like scrobble old song, update display with new song data, store play session to my server etc.). I created a class for this:
public class Scrobbler implements Runnable, Commands, Observer {
private TimeCounter counter;
private Timer timer;
private Radio radio;
private List<Observer> observers;
private final Object MUTEX= new Object();
private int currentPlaySession;
// And all other variables which I deleted before posting
// Constructor
public Scrobbler(TimeCounter _counter, Radio _radio)
{
currentTrack = null;
counter = _counter;
radio = _radio;
timer = null;
observers = new ArrayList<>();
currentPlaySession = 0;
}
private void GetInfo()
{
// POST request to get current song playing
// If song has changed, notify observers
}
public void Scrobble()
{
// POST request - Scrobble song to my website and last.fm
}
public void StartPlaySession()
{
// Again POST request to my website
}
public void EndPlaySession()
{
// Again POST request to my website
}
#Override
public void start() {
new Thread(this).start();
}
public void stop()
{
timer.cancel();
timer.purge();
timer = null;
}
#Override
public void register(Observer obj) {
if(obj == null) throw new NullPointerException("Null Observer");
synchronized (MUTEX) {
if(!observers.contains(obj)) observers.add(obj);
}
}
#Override
public void unregister(Observer obj) {
synchronized (MUTEX) {
observers.remove(obj);
}
}
#Override
public void notifyObservers(String command) {
for (Observer obj : observers) {
obj.update(command);
}
}
#Override
public void run() {
timer = new Timer();
timer.schedule(new TimeOut(), 0, 10000);
GetInfo();
StartPlaySession();
}
public class TimeOut extends TimerTask
{
#Override
public void run() {
EndPlaySession();
GetInfo();
}
}
#Override
public void update(String command) {
if ("RADIO_STARTED".equals(command))
{
this.start();
}
if ("RADIO_STOPPED".equals(command))
{
this.stop();
if (this.currentTrack != null && this.counter.getTrackTime() >= 60)
this.Scrobble();
EndPlaySession();
}
}
private void handleException(String message)
{
try {
String timeStamp = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime());
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("/home/pi/OnlineRadio/error_log.txt", true)));
out.println("(" + timeStamp + ") [SCROBBLER] - " + message + "\n\n\r");
out.close();
} catch (IOException e) {}
this.stop();
this.start();
}
}
I create a new instance of Scrobbler in my main method and register it as an observable to player. Once I start the radio, player will notify its observers (including instance of Scrobbler class) by calling update() method and forward "RADIO_STARTED" or "RADIO_STOPPED" (after starting or stopping the radio). As you can see in code, on RADIO_STARTED, a start() method is called where new Thread is started. Starting a new thread is probably unnecessary here since it only starts the timer but it shouldn't be a problem neither. After starting the timer, every 10 seconds method run() in class TimeOut will be called which then calls necessary methods.
This code works but sometimes it just stops for no reason. All other parts of application continue to work (music is still playing, application reacts on buttons etc.), just it doesn't update the song and there's no communication with my website at all so not even one of these methods is called, just like the timer stopped. When I stop and start the radio again or change the station, it works again (as you can see in code, the timer will be stoped every time the radio stops and the timer will start everytime the radio starts), but it will break again after some time.
Since I have a lot of try-catch blocks in those methods with POST requests, at first I thought that there must be an exception occuring and killing the timer thread so I created the handleException() method which logs exception message to a file and restarts this timer (you can see in code), and then I handle exceptions like this, for example:
try {
writer.close();
} catch (IOException e) {
this.handleException(e.getMessage());
}
But when the problem occured again, log file was empty, which means that not even one exception occured. I also tried to log all the data everytime the song changes and everything is fine, it just stops after a while for no reason. And I can't find a regularity in occuring, sometimes it occurs after a few minutes and sometimes if works for hours before it breaks.
This app runs on Raspberry Pi model B, and I don't know if this means something, but it's in JAR and it starts with the Pi (via cron #reboot) since I don't have any monitor nor keyboard/mouse on this Pi, so the app must start with it and run in background all the time. And I use SSH to transfer the JAR to Pi and to read log files.
Somewhere I read that it's not uncommon for the Java timer to stop without any reason. But how to solve this?
I just started using the Jlayer library to play MP3s. It works perfectly and I can play the song. My only problem is implementing pause and resume methods. With my limited amount of knowledge about multithreading, I though that making the thread where I play the MP3 wait, the sound would stop and in order to resume the song I would just have to notify the thread. Here is what I got:
import java.util.Scanner;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import javazoom.jl.player.Player;
public class MP3 extends Thread{
private String filename;
private Player player;
private Thread t;
private volatile boolean continuePlaying = true;
// constructor that takes the name of an MP3 file
public MP3(String filename) {
this.filename = filename;
}
public void close() { if (player != null) player.close(); }
// play the MP3 file to the sound card
public void play() {
try {
FileInputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis);
player = new Player(bis);
}
catch (Exception e) {
System.out.println("Problem playing file " + filename);
System.out.println(e);
}
}
public void run() {
play();
try {
while (true) {
synchronized(this) {
while(!continuePlaying)
wait();
player.play();
}
}
}
catch (Exception e) {
System.out.println(e);
}
}
private void pause() throws InterruptedException{
System.out.println("Pause");
continuePlaying = false;
}
private void resumeSong() throws InterruptedException{
synchronized(this) {
System.out.println("Resume");
continuePlaying = true;
notify();
}
}
// test client
public static void main(String[] args) throws InterruptedException{
String filename = ("Fall To Pieces.mp3");
MP3 mp3 = new MP3(filename);
mp3.start();
Scanner s = new Scanner(System.in);
s.nextLine();
mp3.pause();
s.nextLine();
mp3.resumeSong();
try {
mp3.join();
} catch (Exception e){
}
}
}
For some reason, however, wait() doesn't do anything and the program doesn't even reach notify(). Why is this happening?
I've read the previous SO questions about this, but I haven't been able to make them work. I am also interested in learning why this code doesn't work so I can further understand multithreading. Thank you!
It's late here so pardon me if I read your code wrong. But as far as I see it, you start your thread with continuePlaying = true; and the run method just calls play(); no initialize the new player and then straight enters a while (true) loop wich has to exit point. continuePlaying can't be changed by that thread still stuck in it's endless loop and even if you start another MP3 thread to access the volatile variable, it will enter the same loop before being able to change anything. Therefore wait() will never be reached.
Later on you are trying to notify() your waiting thread from within itself. Which is a bit of a paradoxon because it is waiting to be notified and in that state of waiting doing nothing, let alone notifying itself. It simply can't do anything until notified, that includes notifying itself or others. What I am trying to say is, that you should handle wait() but especially notify() from outside the thread that is being adressed/waiting.
Additionally your player.play(); is in an odd position. At the moment the player should only start playing after the thread has been paused(waited) at least once because it is behind the while(!continuePlaying) condition.
So for your case i'd rater go with methods in a different thread (or even the main thread for your tests) which call a wait() and notify() on and synchronized over the thread in question.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I have problem with mp 3 player. I'm using jLayer.
This is my code
private void formWindowOpened(java.awt.event.WindowEvent evt) {
new Thread (){
public void run(){
try
{
Player prehravac;
FileInputStream buff = new FileInputStream(Okno.filename);
prehravac = new Player(buff);
prehravac.play();
if (prehravac != null)
{
prehravac.play();
}
}
catch(Exception e)
{
}
}
}.start();
}
In my application I need to play song from the beginning to the end. So when song ends I need to start it again and when window closes I want to stop this song...
JLayer does not support continuous play, so you have to use a loop to repeatedly start a new player after the old one finished. For example:
try
{
do
{
FileInputStream buff = new FileInputStream(Okno.filename);
prehravac = new AdvancedPlayer(buff );
prehravac .play();
}while(loop);
}
catch(Exception ioe)
{
//TODO error handling
}
with loop being a boolean you can set true or false in a different method depending on if you want it to be played just once or repeatedly.
If you want to access the thread later you should at least declare it to a variable. Even better is writing a seperate class that extends thread. Doing so you can add method to the thread you can later call.
For your code it might look something like that:
import java.io.*;
import javazoom.jl.player.*;
public class MyAudioPlayer extends Thread {
private String fileLocation;
private boolean loop;
private Player prehravac;
public MyAudioPlayer(String fileLocation, boolean loop) {
this.fileLocation = fileLocation;
this.loop = loop;
}
public void run() {
try {
do {
FileInputStream buff = new FileInputStream(fileLocation);
prehravac = new Player(buff);
prehravac.play();
} while (loop);
} catch (Exception ioe) {
// TODO error handling
}
}
public void close(){
loop = false;
prehravac.close();
this.interrupt();
}
}
With this you can simply create the Thread when and wherever you want like this:
private MyAudioPlayer thePlayer;
[... some class code here...]
public void yourMethod(){
thePlayer = new MyAudioPlayer("path of the music file", true);
thePlayer.start();
}
and if you want to get rid of it at some point call thePlayer.close();
Note that thePlayer should be an instance variable so you can reuse it again. If you only declare it within a method it will disappear after the method is finished.
Hope this helps.
Hi I am trying to make a multiplayer game where it would find the opponent in another thread, but I am not sure why the code running in the thread does not update the model in the main class...
Here is the code in the main class. The call to the LoadTask starts another thread
// Start model, passing number of words, user name, and selected animal
model = new MultiPlayerModel(NUM_WORDS, username, anmID);
model.addObserver(this);
new LoadTask().execute();
setContentView(R.layout.activity_multi_player);
initialDisplay(animal, background, oppAnimal);
Here is the code for the thread class
private class LoadTask extends AsyncTask<Void, Integer, Void> {
// called before running code in a separate thread
private boolean quitFlag;
#Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(MultiPlayer.this,"Finding a Game...",
"Searching for opponent, please wait...", false, false);
}
#Override
protected Void doInBackground(Void... params) {
synchronized (this) {
try {
model.beginMatchMaking();
model.setWordsList();
// Get the opponent's animal from the model
oppAnimal = reverseDrawable(model.getOpponentAnimal());
// Display the multiplayer screen
} catch (InternetConnectionException e) {
e.fillInStackTrace();
quitFlag = true;
error(States.error.CONNECTION);
return null;
} catch (EmptyQueueException e) {
e.fillInStackTrace();
quitFlag = true;
error(States.error.NOOPPONENT);
return null;
} catch (InternalErrorException e) {
e.fillInStackTrace();
quitFlag = true;
error(States.error.INTERNAL);
return null;
}
}
return null;
}
#Override
protected void onPostExecute(Void result) {
if (!quitFlag) {
progressDialog.dismiss();
gameTimer = new GameTimer(START_TIME, INTERVAL);
gameTimer.start();
}
}
}
And it segfaults on initialDisplay after the thread is called because the fields in model class were not updated at all. it acts as though it was just created and nothing was done to it
I'm not sure about AsyncTask class
but if it's properly configurated Thread and next line
new LoadTask().execute();
will start this thread properly, in this case thread will execute inparallel but usualy after lines:
setContentView(R.layout.activity_multi_player);
initialDisplay(animal, background, oppAnimal);
to prove it you may print messages main methods.
To solve it you may simply add next line after starting thread:
new LoadTask().execute();
Thread.sleep(100)
I'm working on making an interface for a robot. My Robot class has methods that include movement, stopping movement and reading sensor data. If at all possible, I'd like to have certain methods run under a given thread and certain other methods run under another. I'd like to be able to send the command to move to the robot object, have the thread executing it sleep duration milliseconds and then stop movement, but I'd like the stop() method able to be called and interrupt the thread executing the movement. Any help is greatly appreciated.
public class robotTest
{
public static void main(String[] args) throws InterruptedException
{
Robot robot = new Robot(); //Instantiate new Robot object
robot.forward(255, 100, Robot.DIRECTION_RIGHT, 10); //Last argument representing duration
Thread.sleep(5000); //Wait 5 seconds
robot.stop(); //Stop movement prematurely
}
}
I would suggest instantiating your Robot class with an ExecutorService that you can use for moving asynchronusly. Submit the movement request to your service and use the Future returned to 'stop' the move request.
class Robot{
final ExecutorService movingService = Executors.newSingleThreadExecutor();
private volatile Future<?> request; //you can use a Deque or a List for multiple requests
public void forward(int... args){
request = movingService.submit(new Runnable(){
public void run(){
Robot.this.move(args);
}
});
}
public void stop(){
request.cancel(true);
}
}
If I'm understanding you correctly then yes, you can call methods on an object from any given thread. However, for this to work in a bug free fashion the robot class needs to be thread safe.
Make sure all your calls to Robot come from a thread (a class extending Thread that you create) with permissions to make the calls. Add this method to your call.
Note: this code is far from perfect. But it may give you some ideas you can use in your application.
public void stop() throws NoPermissionException {
checkStopPermission(); // throws NoPermissionException
// rest of stop here as normal
}
/**
* Alternatively you could return a boolean for has permission and then throw the NoPermissionException up there.
*/
private void checkStopPermission() throws NoPermissionException() {
try {
Thread t = Thread.currentThread();
RobotRunnableThread rrt = (RobotRunnableThread)t; // may throw cast exception
if(!rrt.hasPermission(RobotRunnableThread.STOP_PERMISSION)) { // assume Permission enum in RobotRunnableThread
throw new NoPermissionExeception();
}
} catch(Exception e) { // perhaps catch the individual exception(s)?
throw new NoPermissionException();
}
}
You have to start a new background thread when you instantiate a Robot that would handle movement. The thread would sit there, waiting for a signal from forward or stop and do the appropriate thing.
You will have to synchronize the threads using either semaphores, wait handles, or other inter thread communication elements.
The least robust solution that wastes the most CPU (this is pseudo code since I have not used Java in a while, might be intermixed with .NET APIs):
public class Robot implements IRunnable {
public Robot() {
new Thread(this).Start();
}
private int direction = 0;
private int duration = 0;
private bool go = false;
public void Run() {
DateTime moveStartedAt;
bool moving = false;
while(true) {
if(go) {
if(moving) {
// we are already moving
if((DateTime.Now - moveStartedAt).Seconds >= duration) {
moving = false;
}
} else {
moveStartedAt = DateTime.Now;
moving = true;
}
} else {
moving = false;
}
}
}
public void forward(int direction, int duration) {
this.direction = direction;
this.duration = duration;
this.go = true;
}
public void stop() {
this.go = false;
}
}
(the above code should be modified to be Java for better answer)
What is wrong with this code:
The Run() method consumes one whole Core (it has no sleeps)
Calling stop() and then forward() right away can result in a race condition (the Run() has not seen the stop yet, but you already gave it another forward)
There is no way for Run() to exit
You can call forward() to redirect the move that is already in progress
Others?