Java sound only playback once? - java

I use the following method to playback a recorded sound, it works fine, but why it only plays once, the second time I click the play button, it does nothing, how to reset the data ?
// Write data to the OutputChannel.
public class Playback implements Runnable
{
SourceDataLine line;
Thread thread;
public void start()
{
errStr=null;
thread=new Thread(this);
thread.setName("Playback");
thread.start();
}
public void stop() { thread=null; }
private void shutDown(String message)
{
if ((errStr=message)!=null)
{
System.err.println(errStr);
samplingGraph.repaint();
}
if (thread!=null)
{
thread=null;
samplingGraph.stop();
if (Java_Sound.Running_In_All_Permissions_Mode) captB.setEnabled(true);
pausB.setEnabled(false);
playB.setText("Play");
}
}
public void run()
{
AudioFormat format=formatControls.getFormat(); // get an AudioInputStream of the desired format for playback
AudioInputStream playbackInputStream=AudioSystem.getAudioInputStream(format,audioInputStream);
if (playbackInputStream==null)
{
shutDown("Unable to convert stream of format "+audioInputStream+" to format "+format);
return;
}
SourceDataLine.Info info=new DataLine.Info(SourceDataLine.class,format); // define the required attributes for our line,and make sure a compatible line is supported.
if (AudioSystem.isLineSupported(info))
{
try // get and open the source data line for playback.
{
line=(SourceDataLine)AudioSystem.getLine(info);
line.open(format,bufSize);
}
catch (LineUnavailableException ex)
{
shutDown("Unable to open the line: "+ex);
return;
}
int frameSizeInBytes=format.getFrameSize(); // play back the captured audio data
int bufferLengthInFrames=line.getBufferSize()/8;
int bufferLengthInBytes=bufferLengthInFrames*frameSizeInBytes;
byte[] data=new byte[bufferLengthInBytes];
int numBytesRead=0;
line.start(); // start the source data line
while (thread!=null)
{
try
{
if ((numBytesRead=playbackInputStream.read(data))==-1) break;
int numBytesRemaining=numBytesRead;
while (numBytesRemaining>0) { numBytesRemaining-=line.write(data,0,numBytesRemaining); }
}
catch (Exception e)
{
shutDown("Error during playback: "+e);
break;
}
}
}
if (thread!=null) line.drain(); // we reached the end of the stream. Let the data play out,then stop and close the line.
line.stop();
line.close();
line=null;
shutDown(null);
}
}
After my test, I found this line is causing the problem
"if ((numBytesRead=playbackInputStream.read(data))==-1) break;"
The first time I played back, there were data, it worked fine, but the second time, it broke. Why ? How to fix it ?

Never worked with java audio, but since you are using stream, you need to either reset the stream if that option is available, or need to create new stream every time you read it.

I figured it out, add the following line after "shutDown(null)"
if (file==null)
try { audioInputStream.reset(); }
catch (Exception e) { e.printStackTrace(); }
Now it works perfect.

Related

Java Sound API locks audio file after playing it

I am trying to use Javax.sound to play a .wav file.
Everything works fine, the file plays as expected and in the end I close the Clip and I close the AudioInputStream. However, the file remains locked (in use) after that and I cannot touch it without getting an exception: java.nio.file.FileSystemException: alerting.wav: The process cannot access the file because it is being used by another process.
A sample of code is below:
static private class SoundThread extends Thread implements LineListener {
private boolean playCompleted;
private int cycles;
public SoundThread(int repeats) {
cycles = repeats;
}
#Override
public void run() {
Clip clip;
AudioInputStream inputStream;
File soundFile = new File("alerting.wav");
try {
inputStream = AudioSystem.getAudioInputStream(soundFile);
try {
clip = AudioSystem.getClip();
clip.addLineListener(this);
clip.open(inputStream);
while(cycles > 0) {
playCompleted = false;
clip.setFramePosition(0);
clip.start();
while(!playCompleted) {
Thread.sleep(1000);
}
Thread.sleep(audioRepeatTime * 1000);
cycles--;
}
//clip.drain();
clip.close();
inputStream.close();
System.out.println("All closed");
try {
this.finalize();
} catch (Throwable ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (Exception ex) {
Main.syslog(Level.WARNING, "E1001 could not play alert sound", ex);
} finally {
inputStream.close();
}
} catch (UnsupportedAudioFileException ex) {
Main.syslog(Level.WARNING, "E1001 could not play alert sound", ex);
} catch (IOException ex) {
Main.syslog(Level.WARNING, "E1001 could not play alert sound", ex);
}
}
#Override
public void update(LineEvent event) {
LineEvent.Type type = event.getType();
System.out.println("Event: " + type);
if(type == LineEvent.Type.STOP) {
playCompleted = true;
} else if (type == LineEvent.Type.CLOSE) {
System.out.println("listener closed");
}
}
}
public static void PlayAlertSound() {
if(enableAudio) {
SoundThread st = new SoundThread(audioLoops);
st.start();
}
}
public static void PlayAlertSound(int repeats) {
if(enableAudio) {
SoundThread st = new SoundThread(repeats);
st.start();
}
}
In the Java threads list I see "Java Sound Event Dispatcher" running. I think this is what keeps the file locked.
Any idea how can I fix this? Thanks
The API for Clip states:
Note that some lines, once closed, cannot be reopened. Attempts to
reopen such a line will always result in a LineUnavailableException.
I'm going to make a couple additional suggestions.
Instead of using File, a better way to load audio resources is with the class.getResource method. This method returns a URL which you can then pass as your argument to the AudioSystem.getAudioInputStream method.
I'm not clear what you are trying to do, but I also recommend some further changes to your code. Initializing and playing a Clip in the same method is not generally done, as it goes against the intended use of the Clip. A Clip is meant for sounds that can be held in memory. So, make your Clip an instance variable. Then, place the code that loads and opens the Clip in its own method. And put the code that calls start or loop in a separate method or methods, and don't close the Clip at the end of playing unless you are sure you are not going to ever play it again.
If you use clip.loop, you don't have to bother with listeners and count iterations.
Instead of:
//...
AudioInputStream inputStream;
File soundFile = new File("alerting.wav");
try {
inputStream = AudioSystem.getAudioInputStream(soundFile);
// ...
}
// ...
Try this:
//...
AudioInputStream inputStream;
File soundFile = new File("alerting.wav");
try {
byte[] bytes = Files.readAllBytes(soundFile.toPath());
inputStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(bytes));
// ...
}
// ...

How to increase record and play volume with Java sound?

I use the following code to record and play sounds with Java, but the volume is too low, how to make it louder, at least 2,3 times louder ?
public void Record_Audio(String File_Path,AudioFileFormat.Type File_Type)
{
try
{
audioFormat=getAudioFormat(); // Get things set up for capture
DataLine.Info dataLineInfo=new DataLine.Info(TargetDataLine.class,audioFormat);
targetDataLine=(TargetDataLine)AudioSystem.getLine(dataLineInfo);
//Create a thread to capture the microphone data into an audio file and start the thread running. It will run
// until the Stop button is clicked. This method will return after starting the thread.
new Record_Thread(File_Path,File_Type).start();
}
catch (Exception e)
{
System.out.println(e);
System.exit(0);
}
}
private void Play_Audio_Recording()
{
File Audio_File=new File(Current_Folder_Path+File_Name_ComboBox.getSelectedItem().toString().trim()+"."+Get_Audio_File_Type());
try
{
Audio_Clip=Applet.newAudioClip(Audio_File.toURI().toURL());
Audio_Clip.play();
}
catch (Exception e) { e.printStackTrace(); }
}
class Record_Thread extends Thread
{
String File_Path;
AudioFileFormat.Type File_Type;
Record_Thread(String File_Path) { this(File_Path,AudioFileFormat.Type.WAVE); }
Record_Thread(String File_Path,AudioFileFormat.Type File_Type)
{
this.File_Path=File_Path;
this.File_Type=File_Type;
}
public void run()
{
Audio_File=new File(File_Path);
try
{
targetDataLine.open(audioFormat);
targetDataLine.start();
AudioSystem.write(new AudioInputStream(targetDataLine),File_Type,Audio_File);
}
catch (Exception e) { e.printStackTrace(); }
}
}
You would use the FloatControl.Type (https://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/FloatControl.Type.html) to set either the volume or the master gain. Something like:
targetDataLine=(TargetDataLine)AudioSystem.getLine(dataLineInfo);
javax.sound.sampled.FloatControl c = (FloatControl)targetDataLine.getControl(FloatControl.Type.VOLUME);
c.setValue(c.getMaximum());
might work.

Bluetooth data loss between Android and Arduino

Straight to the point - my application due to some mysterious reason looses part of a data (String) when receiving an InputStream. I am talking about Bluetooth connection here. Most of the time, I am receiving correct string but once in while it is shortened. Weirdest thing in here is that I am printing every InputStream into my ScrollView and I can tell that the whole string is there... Nevertheless here is the code:
#Override
public void run() {
InputStream inputStream;
try {
inputStream = mBTSocket.getInputStream();
while (!bStop) {
byte[] buffer = new byte[256];
int bytes;
if (inputStream.available() > 0) {
bytes = inputStream.read(buffer);
final String strInput = new String(buffer, 0, bytes);;
mTxtReceive.post(new Runnable() {
#Override
public void run() {
mTxtReceive.append(strInput);
int txtLength = mTxtReceive.getEditableText().length();
if (txtLength > mMaxChars) {
mTxtReceive.getEditableText().delete(0, txtLength - mMaxChars);
}
scrollView.post(new Runnable() {
#Override
public void run() {
scrollView.fullScroll(View.FOCUS_DOWN);
}
});
}
});
runOnUiThread(new Runnable() {
#Override
public void run() {
weather = strInput.split(",");
mTxtHumidity.setText(weather[0]);
mTxtTemperatureDHT.setText(weather[1]);
mTxtPressure.setText(weather[2]);
mTxtLux.setText(weather[3]);
mTxtRainMM.setText(weather[4]);
mTxtRainDaily.setText(weather[5]);
mTxtWSKPH.setText(weather[6]);
mTxtWGKPH.setText(weather[7]);
mTxtWSAVG2.setText(weather[8]);
mTxtWGAVG10.setText(weather[9]);
}
});
}
Thread.sleep(500);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The problem in here is null exception on array weather as according to my app I am sometimes accessing items out of boundry (weather length is 6/7/8 most of the time once it errors). App crashes in 10% of the time.
Any reason behind it?
EDIT: while receiving InputStream sometimes instead of receiving 56 bytes I get 33 and 22 separetely
Answer to be found here: Android InputStream dropping first two bytes (modified BluetoothChat)
Adding
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
solves only for non-dynamic data exchange between Android and Arduino due to sleep method. It turns out that even sleep(50) works in this condition. For some reason after this short sleep buffer is never divided nor lost. If it is bad coding please explain before downvoting.

Java shutdown hook and writing to an Excel Sheet

So high level I have a program that reads data from an Excel SpreadSheet stores it in a list of hash maps, does dome operations, gets a status, and then writes the information back the Excel SpreadSheet. As of right now if an Exception of some sort causes the program to crash or the program finishes normally everything is fine.
Now I am trying to handle what if a user terminates the run by pressing Ctrl + C. I figured the best way to do this would be implementing my own shutdown hook.
private static class TerminationThread extends Thread
{
public void run() {
for(UserCredential user : creds)
{
Accel.setRow(user.getMap(), user.getRowIndex());
}
try {
Accel.writeToFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
In my Main() I register this as a shutdown hook
TerminationThread sH = new TerminationThread();
Runtime.getRuntime().addShutdownHook(sH);
When the program enters the shutdown hook everything is fine until it gets to the portion that writes to the file then it starts throwing a bunch of exceptions and at the end i get a corrupted spreadsheet.
Here is my write code:
public void writeToFile() throws IOException
{
try
{
fileOut = new FileOutputStream(theFile);
theWorkbook.write(fileOut);
}
catch (IOException e)
{
throw new IOException("Exception in writeToFile(). " + e);
}
finally
{
try
{
fileOut.flush();
fileOut.close();
}
catch(IOException e)
{
throw new IOException("Exception closing FileOutputStream. " + e);
}
}
}
What I am assume is happening is that the object that handles my excel spreadsheet is getting wiped out before the write is completed or the list of hash maps where i store the data before writing it is getting wiped out.
I have looked at Thread Joins as a possible solution to my problem but upon looking at them I don't think they are the solution to my problem.
So I guess my question is how do i get this to work the way I would like it to work
Cheers,
Meinert
EDIT: Here is the exception
Exception in thread "Thread-4" org.apache.xmlbeans.impl.values.XmlValueDisconnectedException
at org.apache.xmlbeans.impl.values.XmlObjectBase.check_orphaned(XmlObjectBase.java:1213)
at org.apache.xmlbeans.impl.values.XmlObjectBase.newCursor(XmlObjectBase.java:243)
at org.apache.xmlbeans.impl.values.XmlComplexContentImpl.arraySetterHelper(XmlComplexContentImpl.java:1073)
at org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTDefinedNamesImpl.setDefinedNameArray(Unknown Source)
at org.apache.poi.xssf.usermodel.XSSFWorkbook.saveNamedRanges(XSSFWorkbook.java:1270)
at org.apache.poi.xssf.usermodel.XSSFWorkbook.commit(XSSFWorkbook.java:1291)
at org.apache.poi.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:313)
at org.apache.poi.POIXMLDocument.write(POIXMLDocument.java:173)
at com.cba.statuschecker.ExcelManager.writeToFile(ExcelManager.java:179)
at com.cba.statuschecker.Main$TerminationThread.run(Main.java:495)
EDIT 2: Well I figured out my own problem. I was approaching this the wrong way. My code is correct the only problem was that in my finally block I also have a file write. So when my program would execute all the way through without interruption it would execute the write twice which was the cause of the exception. I fixed this by checking if the finally block was executed and if it was the write in the shutdown hook is skipped.
Finally:
finally
{
// Print out statuses
printStatus();
for(UserCredential user : creds)
{
Accel.setRow(user.getMap(), user.getRowIndex());
}
try {
Accel.writeToFile();
} catch (IOException e) {
e.printStackTrace();
}
didFinally = true;
LOGGER.info(Log("Fin."));
System.exit(0);
}
Shutdown Hook
private static class TerminationThread extends Thread
{
public void run() {
System.out.println("Shutdown Initiated...");
if(!didFinally){
for(UserCredential user : creds)
{
Accel.setRow(user.getMap(), user.getRowIndex());
}
System.out.println("Writing to file...");
try {
Accel.writeToFile();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("Terminated!");
}
}
Well I figured out my own problem. I was approaching this the wrong way. My code is correct the only problem was that in my finally block I also have a file write. So when my program would execute all the way through without interruption it would execute the write twice which was the cause of the exception. I fixed this by checking if the finally block was executed and if it was the write in the shutdown hook is skipped.
Finally:
finally
{
// Print out statuses
printStatus();
for(UserCredential user : creds)
{
Accel.setRow(user.getMap(), user.getRowIndex());
}
try {
Accel.writeToFile();
} catch (IOException e) {
e.printStackTrace();
}
didFinally = true;
LOGGER.info(Log("Fin."));
System.exit(0);
}
Shutdown Hook
private static class TerminationThread extends Thread
{
public void run() {
System.out.println("Shutdown Initiated...");
if(!didFinally){
for(UserCredential user : creds)
{
Accel.setRow(user.getMap(), user.getRowIndex());
}
System.out.println("Writing to file...");
try {
Accel.writeToFile();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("Terminated!");
}
}

IllegalStateException [start called in an invalid state: 1] on restarting Android MediaRecorder

I am trying to implement simple logic to start/stop recording with MediaRecorder of Android.
The cycle is
connect to localSocket / set options / mRecorder.prepare();
mRecorder.start();
mRecorder.stop(); mRecorder.reset();
Then, loop between 2 and 3.
In the first cycle, 1,2,3 works fine as intended, however, I've got an error on the second start(restart) after the first stop.
com.example.app E/MediaRecorder﹕ start called in an invalid state: 1
What is the MediaRecorder state 1? What do I miss?
Thanks for your input.
if (cmd.equals("connect"))
{
try
{
sender.connect(new LocalSocketAddress(SOCKET_ADDRESS));
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setOutputFile(sender.getFileDescriptor());
mRecorder.prepare();
}
catch (IOException e)
{ e.printStackTrace(); }
}
if (cmd.equals("start"))
{
try
{
mRecorder.start();
}
catch (IllegalStateException e)
{ e.printStackTrace(); }
}
if (cmd.equals("stop"))
{
try
{
mRecorder.stop();
mRecorder.reset();
}
catch (Exception e)
{ e.printStackTrace(); }
}
I'd had the same problem. I had to make a function initRecorder that sets up and prepares the media recorder. Then I called this function each time after the start button was pressed but before start was called. recreate() after stop also works.
StartRecording.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 100);
try {
try {
initRecorder(mHolder.getSurface());
} catch (IOException e) {
e.printStackTrace();
}
mMediaRecorder.start();
Log.e("mRecorder", "Started");
} catch (RuntimeException e) {
Log.e("mRecorder", "Start Failure");
e.printStackTrace();
}
}
});
private void initRecorder(Surface surface) throws IOException {
toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 1000);
if (mMediaRecorder == null) mMediaRecorder = new MediaRecorder();
// mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
// mMediaRecorder.setOutputFormat(8);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
//mMediaRecorder.setVideoEncodingBitRate(512 * 1000);
mMediaRecorder.setVideoFrameRate(30);
// mMediaRecorder.setVideoSize(640,480);
mMediaRecorder.setPreviewDisplay(surface);
mMediaRecorder.setOutputFile(path);
// mMediaRecorder.setPreviewDisplay(mHolder.getSurface());
mMediaRecorder.setMaxDuration(10000); // 10 seconds
try {
mMediaRecorder.prepare();
Log.e("mRecorder", "Prepared");
} catch (IOException e) {
Log.e("mRecorder", "Prepare Failure");
e.printStackTrace();
}
mInitSuccesful = true;
}
In the scond cycle you have not called prepare, you need to call that before you can call start on media recorder
This is a self-answer, but I would not check as the answer because it's just work-around.
According to #Pulkit Sethi, state-1 means either MediaRecorder does not start properly or stop properly.
Perhaps, it's due to the local socket object sender.getFileDescriptor() as the target of setOutputFile.
So far, it's way too complicated and I could not find a way to stop gracefully enough to re-start or re-use MediaRecorder, I chose to dispose all everytime.
So
The cycle is
start localSocket/Server
connect to localSocket / set options / mRecorder.prepare();
mRecorder.start();
stop/close/release whole
This looks not the smartest way, but at least simple and stable, and I am happy with the result to start/stop/ & re-start as intended.
if (cmd.equals("stop"))
try
{
if (sender != null)
{
sender.close();
}
if (receiver != null)
{
receiver.close();
}
if (server != null)
{
server.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
sender = null;
receiver = null;
server = null;
}
for MediaRecorder
mRecorder.release();
The output file needs to be an actual file, not a socket. This is because MediaRecorder usually needs to be able to seek back in the file to update the header when the recording ends, and you can't seek in a socket.

Categories

Resources