I'm doing a pet project (a kind of game). And I ran into a problem: when the application is called from the console with the command:
C:\java -jar MyGame.jar
short sounds are played, but long ones are not.
All sounds are inside the JAR in the "/assets" folder.
The path to the audio looks like this:
C:\MyGame.jar\assets\background_music.wav
Such as a shot or a jump are played. For long audio data, only the first 0.5 seconds are played.
For example: if you load the sound with 0.834 sec length, then it loops (background music), the sound is played in a loop! (WAV file, 0.843 sec, 48 KB).
But if you load a WAV file for 2 seconds and a size of 115 KB, only 0.5-1 seconds are played.
If you load a WAV background music file for 15 seconds (or 7 seconds) and a size of 110 - 2000 KB and more, the same 0.5 seconds will be played. EVERY 15 (or 7) seconds (if you say "play in a loop").
That is, the file is loaded, its length is loaded, markers are placed at the beginning and at the end, but I only hear the first 0.5 seconds of audio (every "x" -sec, where "x" is the length of the clip).
Audio upload method:
public static InputStream uploadAudio (String path){
InputStream sourceSound = null;
try{
final File jarFile = new File(ResourceLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath());
if(jarFile.isFile()) { // Run with JAR file
final JarFile jar = new JarFile(jarFile);
InputStream fileInputStreamReader =(jar.getInputStream(jar.getEntry(path)));
byte[] byteArray = new byte[fileInputStreamReader.available()];
fileInputStreamReader.read(byteArray);
InputStream newInputStreamFromArray = new BufferedInputStream(new ByteArrayInputStream(byteArray));
sourceSound = newInputStreamFromArray;
jar.close();
} else { // Run with IDE
URL url = ResourceLoader.class.getResource( "../" + path);
InputStream fileInputStreamReader = new BufferedInputStream(new FileInputStream(url.getPath()));
sourceSound = fileInputStreamReader;
}
}catch (IOException e){
e.printStackTrace();
}
return sourceSound;
}
Part of the audio playback class:
public class Sound implements AutoCloseable {
private boolean released = false;
private AudioInputStream stream = null;
private Clip clip = null;
private FloatControl volumeControl = null;
private boolean playing = false;
public Sound(InputStream inputStream) {
try {
stream = AudioSystem.getAudioInputStream(inputStream);
clip = AudioSystem.getClip();
clip.open(stream);
clip.addLineListener(new Listener());
volumeControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
released = true;
} catch (IOException | UnsupportedAudioFileException | LineUnavailableException exc) {
exc.printStackTrace();
released = false;
close();
}
}
public void playLoop(boolean breakOld){
if (released) {
if (breakOld) {
clip.stop();
clip.loop(Clip.LOOP_CONTINUOUSLY);
playing = true;
} else if (!isPlaying()) {
clip.loop(Clip.LOOP_CONTINUOUSLY);
playing = true;
}
}
}
public void playLoop(){
playLoop(true);
}
No error's. The program works. The background sound is played in a loop, but only 0.5 sec of the clip.
Short sounds (shot or jump sound) are played. Everything works in the IDE: short sounds and full background music.
Problem solved! (may not be the best way).
My problem is that I took the method
public static Image uploadImage (String path)
and rewrote part of it for audio.
In the method for images, there is a string for unpacking the JAR.
InputStream fileInputStreamReader = (jar.getInputStream (jar.getEntry (path)));
Next, I wrote the code for audio. It didn't work directly: it was not the same for
InputStream sourceSound = new BufferedInputStream (fileInputStreamReader);
... ...
return sourceSound;
Next step - I added:
InputStream fileInputStreamReader =(jar.getInputStream(jar.getEntry(path)));
byte[] byteArray = new byte[fileInputStreamReader.available()];
fileInputStreamReader.read(byteArray);
InputStream newInputStreamFromArray = new BufferedInputStream(
new ByteArrayInputStream(byteArray));
sourceSound = newInputStreamFromArray;
And it worked! (I thought so). After posting the question here, I started to think that not all inputStream are written in byte[] byteArray.
I made a copy of the byteArray and wrote it to a file "H:/1.wav".
And this is what I saw in the file:
RIFFp[) WAVEfmt D¬ ± dataäZ) ÿ1VÿEÿR¾ÿVëÿH .
.....................................................................................
.....................................................................................
And etс. The data was cut off. There was no content.
Working code for images:
BufferedImage sourceImage = null;
InputStream fileInputStreamReader = jar.getInputStream(jar.getJarEntry(path));
sourceImage = ImageIO.read(fileInputStreamReader);//magic ImageIO.read () !!!!
Likewise does not work for audio:
InputStream fileInputStreamReader =(jar.getInputStream(jar.getEntry(path)));
byte[] byteArray = new byte[fileInputStreamReader.available()];
fileInputStreamReader.read(byteArray);
I guess I didn’t learn Java well =)
So I solved the problem now like this:
InputStream fileInputStreamReader =(jar.getInputStream(jar.getEntry(path)));
byte[] byteArray = new byte[fileInputStreamReader.available()];
int i = 0;
int byteRead;
while ((byteRead = fileInputStreamReader.read()) != -1) {
byteArray[i] = (byte) byteRead;
i++;
}
InputStream newInputStreamFromArray = new BufferedInputStream(new ByteArrayInputStream(byteArray));
Long and short audios are now read from the JAR normally.
If someone knows how to do it better - I will be glad to hear the answer.
Related
I need some help with my Java application. Its purpose is to read a certain website, so I need to play many audio files in a row. The JAR is compiled using Java 8. I tested my application with Windows 11 and Java 16.0.1, everything works fine. Then I used the latest Ubuntu Linux and Java 11.0.13 as well as Java 8: It plays some audio, but not every file.
I wrote a test class and the result was, that - no matter in which order I play the audio - only the first (exactly!) 62 files are played. Every next file (even the ones, that were successfully played at first) produces the exception my code throws at this position:
if (mixerSelected != null) {
audioClip0 = AudioSystem.getClip(mixerSelected);
} else {
throw new IllegalArgumentException("File is not compatible: '" + audioFilePath + "'.");
}
I ensured that every audio file is .WAV with
8k sample rate,
16k Bytes per second in average,
16 Bits, and
pcm_s16le codec.
My application is built as JAR-file including my audio files in the resources directory.
This is my code:
public class PlayAudio {
/**
* plays an audio file
*
* #param audioFilePath String: path to the audio file
* #param speed double: speed applied to the audios
*/
public boolean singleFile(String audioFilePath, double speed) {
//audioFilePath = "audio" + File.separator + audioFilePath;
audioFilePath = "audio" + "/" + audioFilePath;
AudioInputStream audioStream0;
//create new file using path to the audio
try {
//load files from resources folder as stream
ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(audioFilePath);
InputStream bufferedInputStream = new BufferedInputStream(inputStream);
if (bufferedInputStream == null) {
throw new IllegalArgumentException("File not found: '" + audioFilePath + "'.");
} else {
//create new AudioStream
audioStream0 = AudioSystem.getAudioInputStream(bufferedInputStream);
}
} catch (IllegalArgumentException e) {
//handle
return false;
} catch (IOException e) {
//handle
return false;
} catch (UnsupportedAudioFileException e) {
//handle
return false;
}
try {
//create new AudioFormat
AudioFormat audioFormat0 = audioStream0.getFormat();
//create new Info
DataLine.Info info0 = new DataLine.Info(Clip.class, audioFormat0);
//initialize new Mixer
Mixer.Info mixerSelected = null;
for (Mixer.Info mixerInfo : AudioSystem.getMixerInfo()) {
Mixer mixer = AudioSystem.getMixer(mixerInfo);
if (mixer.isLineSupported(info0)) {
mixerSelected = mixerInfo;
break;
}
}
//create new Clip
Clip audioClip0;
if (mixerSelected != null) {
audioClip0 = AudioSystem.getClip(mixerSelected);
} else {
//THIS EXCEPTION GETS THROWN!!!
throw new IllegalArgumentException("File is not compatible: '" + audioFilePath + "'.");
}
//open created Clips via created AudioStream
audioClip0.open(audioStream0);
//start the play of audio file
audioClip0.start();
//wait until play completed
double waitTime = (double)((((double)audioClip0.getMicrosecondLength()/1000.0)/speed + 50.0) * 0.8);
Thread.sleep((long)waitTime);
return true;
//handle exceptions
} catch (LineUnavailableException e) {
//handle
return false;
} catch (IOException e) {
//handle
return false;
} catch (InterruptedException e) {
//handle
return false;
} catch (IllegalArgumentException e) {
//THIS EXCEPTION GETS THROWN!!!
//handle invalid audio clips
System.out.println(e);
e.printStackTrace();
return false;
}
}
/**
* plays multiple audio files in the order they are stored in an ArrayList
*
* #param fileNames ArrayList<String>: list with filenames of audio files to play
* #param speaker String: speaker to use for playing the audios (can be 'm' or 'w')
* #param speed double: speed applied to the audios
* #return boolean: true if playing audios completed successfully, otherwise false
*/
public static boolean multiFiles(ArrayList<String> fileNames, String speaker, double speed) {
PlayAudio player = new PlayAudio();
//play every audio file in the array of file names
for (int i = 0; (i < fileNames.toArray().length); i ++) {
//generate file names
String fullFileName = speaker + "_" + fileNames.toArray()[i];
//play audio
player.singleFile(fullFileName, speed);
}
return true;
}
}
What did I already try?
I tried it on another computer that runs Ubuntu Linux as well.
I created a new instance of PlayAudio() everytime a new audio is played.
I used audioClip0.stop(); after every audio.
I increased the milliseconds of sleep after every audio to length of the audio plus 1 second.
I rebuilt the projects ... nearly 1k times.
How can I reproduce the error?
I simply need to play more than 62 audio files running my JAR-file under Linux Ubuntu.
How can you help me?
I don't know how to handle this issue. What is the problem playing .WAV-files with Linux?
Is there a common way to fix this?
(I am not allowed to use any library except OracleJDK and OpenJDK.)
The #1 suggestion is by Mark Rotteveel. The AudioInputStream class needs closing. This is often a surprise for people, because Java is well known for managing garbage collection. But for AudioInputStream there are resources that need to be released. The API doesn't do an adequate job of pointing this out, imho, but the need for handling can be inferred from the description for the AudioInputStream.close() method:
Closes this audio input stream and releases any system resources
associated with the stream.
The #2 suggestion is from both Andrew Thompson and Hendrik may be more a helpful hint than a direct solution, but it is still a very good idea, and it seems plausible to me that the inefficiency of all the additional, unneeded infrastructure (ClassLoader, InputStream, BufferedInputStream) might be contributing to the issue. But I really don't have a good enough understanding of the underlying code to know how pertinent that is.
However, I think you can do even better. Don't use Clip. You current use of Clip goes against the concept of its design. Clips are meant for short duration sounds that are to be held in memory and played multiple times, not files that are repeatedly reloaded before each playback. The proper class for this sort of use (load and play) is the SourceDataLine.
An example of playback using a SourceDataLine can be found in the javasound wiki. This example also illustrates the use of URL for obtaining the necessary AudioInputStream. I will quote it here verbatim.
public class ExampleSourceDataLine {
public static void main(String[] args) throws Exception {
// Name string uses relative addressing, assumes the resource is
// located in "audio" child folder of folder holding this class.
URL url = ExampleSourceDataLine.class.getResource("audio/371535__robinhood76__06934-distant-ship-horn.wav");
// The wav file named above was obtained from https://freesound.org/people/Robinhood76/sounds/371535/
// and matches the audioFormat.
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);
AudioFormat audioFormat = new AudioFormat(
Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, false);
Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
SourceDataLine sourceDataLine = (SourceDataLine)AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
int bytesRead = 0;
byte[] buffer = new byte[1024];
sourceDataLine.start();
while((bytesRead = audioInputStream.read(buffer)) != -1)
{
// It is possible at this point manipulate the data in buffer[].
// The write operation blocks while the system plays the sound.
sourceDataLine.write(buffer, 0, bytesRead);
}
sourceDataLine.drain();
// release resources
sourceDataLine.close();
audioInputStream.close();
}
}
You will have to do some editing, as the example was set up to run via a main method. Also, you'll be using your audio format, and that the names of the audio files with their folder locations will have to match the relative or absolute location specified in the argument you use in getResource() method. Also, a larger size for the buffer array might be preferred. (I often use 8192).
But most importantly, notice that in this example, we close both the SourceDataLine and the AudioInputStream. The alternate suggestion to use try-with-resources is a good one and will also release the resources.
If there are difficulties altering the above to fit into your program, I'm sure if you show us what you try, we can help with making it work.
After applying the answer from #Phil Freihofner this worked for me:
/**
* plays an audio file
*
* #param audioFilePath String: path to the audio file
* #param speed double: speed applied to the audios
*/
public boolean singleFile(String audioFilePath) {
//get class
ClassLoader classLoader = getClass().getClassLoader();
//use try-with-resources
//load files from resources folder as stream
try (
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(
new BufferedInputStream(
Objects.requireNonNull(classLoader.getResourceAsStream(audioFilePath))))
) {
if (audioInputStream == null) {
throw new IllegalArgumentException("File not found: '" + audioFilePath + "'.");
}
//create new AudioFormat
AudioFormat audioFormat = audioInputStream.getFormat();
//create new Info
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
//create new SourceDataLine and open it
SourceDataLine sourceDataLine = (SourceDataLine)AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
//start the play of the audio file
int bytesRead;
byte[] buffer = new byte[8192];
sourceDataLine.start();
while ((bytesRead = audioInputStream.read(buffer)) != -1) {
sourceDataLine.write(buffer, 0, bytesRead);
}
sourceDataLine.drain();
sourceDataLine.close();
audioInputStream.close();
//return true, because play finished
return true;
} catch (Exception e) {
//ignore exceptions
return false;
}
}
Thak you all for contributing to my solution.
I have 2 disks in the Linux system, say /dev/dsk1 and /dev/dsk2, and I'm trying to read the raw data from dsk1 in bytes and write them into dsk2, in order to make dsk2 an exact copy of dsk1. I tried to do that in the following way (executed with sudo):
import...
public class Main {
public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
Path src = new File("/dev/dsk1").toPath();
Path dst = new File("/dev/dsk2").toPath();
FileChannel r = FileChannel.open(src, StandardOpenOption.READ, StandardOpenOption.WRITE);
FileChannel w = FileChannel.open(dst, StandardOpenOption.READ, StandardOpenOption.WRITE);
long size = r.size();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
for (int offset = 0; offset < size; offset+=1024) {
r.position(offset);
w.position(offset);
r.read(byteBuffer);
byteBuffer.flip();
w.write(byteBuffer);
byteBuffer.clear();
}
r.close();
w.close();
}
}
but after writing all the bytes in dsk1 to dsk2, dsk2's filesystem seems to be corrupted. No files can be found in it and if I try to mkdir it will say "structure needs cleaning".
I've tested the above code on regular files, like a text1.txt containing a few characters as src and an empty text2.txt as dst, and it worked fine.
Did I miss something there when reading & writing raw data on block device?
You never check if read method read all 1024 bytes, or if write method wrote them all. Most likely you're leaving gaps in the copy.
There's no magic involved reading from and writing to devices. The first thing I would try is this:
try (FileInputStream src = new FileInputStream("/dev/dsk1");
FileOutputStream dst = new FileOutputStream("/dev/dsk2")) {
src.transferTo(dst);
}
I'm desperate,...I have tried and search a lot, no luck. Please help
Bit of a Background:
Using a raspberry Pi 3, I develop a webcam streaming server as I don't want the ones available. With raspistill the fps is very low (4fps), that is why I look into v4l2 option for streaming the webcam. For this I output the mjpeg video into a pipe.
Reading from this pipe, the first jpeg image is shown, but consecutive reads return null.
To investigate this further I made a small demo program - same result.
Here the code I use:
Iterating 20 times reading from bufferedinputstream
private void standardRead()
{
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(new FileInputStream(new File(image_path)));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
System.out.println("Is mark supported? "+bis.markSupported());
try {
for(int i=0;i<20;i++)
{
readingImage(bis,i);
TimeUnit.MILLISECONDS.sleep(250);
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
Read method (enhanced with some System.out)
private void readingImage(BufferedInputStream bis,int iteration) throws IOException
{
System.out.println("Available bytes to read:"+bis.available());
System.out.println("Reading image"+iteration);
BufferedImage read = ImageIO.read(bis);
if(read!=null){
System.out.println(read.getRGB(25, 25)+" h:"+read.getHeight());System.out.println();
}else
{
System.out.println("image is null");
}
read = null;
}
What I have already tried:
- Creating a new BufferedInputStream for each iteration
- Closing and creating a new BufferedInputStream
- Tried using mark and reset (no luck)
- Reading from the stream using read instead of ImageIO (reads for ever obviously with about 20fps)
When I execute the program, v4l2 informs that frames are consumed, therefore the pipe is being emptied/read by the java program so new frames can be fed into it.
Only the first image and only during the first execution of the program gives me one image back. A second execution of the program gives null for the first image too.
Here an example output:
Is mark supported? true
Available bytes to read:65536
Reading image0
image is null
Available bytes to read:73720
Reading image1
image is null
Available bytes to read:73712
Reading image2
image is null
Available bytes to read:73704
Reading image3
image is null
Available bytes to read:73696
Reading image4
image is null
Available bytes to read:73688
Reading image5
image is null
One note, if any helpful. For the ImageIO.read(InputStream) function, Java doc states something strange which I can't understand:
(...) The InputStream is wrapped in an ImageInputStream. If no
registered ImageReader claims to be able to read the resulting stream,
null is returned (...)
Thanks in advance for your help and advice.
One sleepless night later, I got something working.
Eureka: I stream 1000 frames using v4l2 library into a linux pipe and can read all 1000 frames. With saving each file to a directory it takes about 103 seconds aka 10fps. No single frame skipped.
Here is how:
private void ReadImages(File path)
{
BufferedInputStream bis = null;
int index = 0;
ImageReader reader = null;
try {
bis = new BufferedInputStream(new FileInputStream(path));
ImageInputStream stream = ImageIO.createImageInputStream(bis);
while(bis.available()>0)
{
if(gotReader(stream))
{
reader = ImageIO.getImageReaders(stream).next();
reader.setInput(stream);
BufferedImage read = reader.read(index);
System.out.println("Image height"+read.getHeight() +" image width:"+read.getWidth()) ;
stream.flush();
index = 0;
}
}
} catch (IOException e) {
System.err.println(e.getMessage());
//e.printStackTrace();
}
}
Tip: flush the stream frequently and reset the index. Without flushing the growing memory breaks the performance dramatically.
Tip: Standard ImageIO does not read BGR3, RGB3,YU12,YUYV,YV12,YVYU but H264 and MJPEG
Tip: Reader is tested with
if(ImageIO.getImageReaders(stream).hasNext())
I've created an app that records audio, saves the sample to the sd card then plays it back, using the record and play buttons. I need to reverse this sample. I can do all this and the the reversed sample is saved on the SD card under a different name. The original sample is test.wav and the same sample reversed is save as revFile.wav. when i try play revFile.wav android says it can't play this format.
I've litterally put the sample in an array then reversed the contents, something is telling me that there could be header info at the start of the sample that needs striping first, any ideas. thanks.
Here's what i have so far.
public class recorder extends Activity {
MediaRecorder myRecorder = null;
DataInputStream dis = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void onClickPlay(View v){
Log.v("onClickplay", "play clicked");
try{
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(Environment.getExternalStorageDirectory().getPath() + "/test.wav");
mp.prepare();
mp.start();
} catch(Exception e3) {
e3.printStackTrace();
}
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("playing");
}
public void onClickRecord(View v){
Log.v("onClickRecord", "record clicked");
File path = Environment.getExternalStorageDirectory();
Log.v("file path", ""+path.getAbsolutePath());
File file = new File(path, "test.wav");
if(file.exists()){
file.delete();
}
path.mkdirs();
Log.v("file path", ""+file.getAbsolutePath());
myRecorder = new MediaRecorder();
myRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
myRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
myRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
myRecorder.setOutputFile(file.getAbsolutePath());
Log.i("myrecorder", "about to prepare recording");
try{
myRecorder.prepare();
} catch(Exception e) {
e.printStackTrace();
}
Log.i("myrecorder", "prepared");
myRecorder.start(); // Recording is now started
Log.i("myrecorder", "recording");
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("recording");
}
public void onClickStop(View v){
Log.v("onClickStop", "stop clicked");
try{
myRecorder.stop();
myRecorder.reset(); // You can reuse the object by going back to setAudioSource() step
myRecorder.release(); // Now the object cannot be reused
}catch(Exception e){}
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("recording stopped");
}
public void onClickReverse(View v){
Log.v("onClickReverse", "reverse clicked");
File f = Environment.getExternalStorageDirectory();
String path = f.getAbsolutePath();
path = path + "/test.wav";
Log.v("path = ", ""+path);
Log.v("dir = ", ""+f.getAbsolutePath());
Log.v("test file exists? = ", ""+f.getAbsolutePath()+"/test.wav");
File f2 = new File(path);
Log.v("f2 = ", ""+f2.getAbsolutePath());
try {
InputStream is = new FileInputStream(f2);
BufferedInputStream bis = new BufferedInputStream(is);
dis = new DataInputStream(bis);
} catch (Exception e) {
e.printStackTrace();
}
int fileLength = (int)f2.length();
byte[] buffer = new byte[fileLength];
/*File reversedFile = Environment.getExternalStorageDirectory();
File revFile = new File(reversedFile, "reversedFile.wav");
Log.v("reversedfile path", ""+ revFile.getAbsolutePath());
if(revFile.exists()){
revFile.delete();
}
reversedFile.mkdirs();
*/
byte[] byteArray = new byte[fileLength +1];
Log.v("bytearray size = ", ""+byteArray.length);
try {
while(dis.read(buffer) != -1 ) {
dis.read(buffer);
Log.v("about to read buffer", "buffer");
byteArray = buffer;
}
Log.v(" buffer size = ", ""+ buffer.length);
} catch (IOException e) {
e.printStackTrace();
}
byte[] tempArray = new byte[fileLength];
int j=0;
for (int i=byteArray.length-1; i >=0; i--) {
tempArray[ j++ ] = byteArray[i];
}
File revPath = Environment.getExternalStorageDirectory();
Log.v("revpath path", ""+revPath.getAbsolutePath());
File revFile = new File(revPath, "revFile.wav");
Log.v("revfile path ", ""+revFile.getAbsolutePath());
if(revFile.exists()){
revFile.delete();
}
revPath.mkdirs();
try {
OutputStream os = new FileOutputStream(revFile);
BufferedOutputStream bos = new BufferedOutputStream(os);
DataOutputStream dos = new DataOutputStream(bos);
Log.v("temparray size = ", ""+ tempArray.length);
dos.write(tempArray);
dos.flush();
dos.close();
} catch (Exception e) {
e.printStackTrace();
}
try{
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(Environment.getExternalStorageDirectory().getPath()
+"/revFile.wav");
mp.prepare();
mp.start();
} catch(Exception e3) {
e3.printStackTrace();
}
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("playing reversed file");
}
}// end of onclickrev
The WAV file format includes a 44-byte header chunk. Most WAV files consist of this 44 byte header, followed by the actual sample data. So, to reverse a WAV file, you should first copy the 44 byte header from the original file, and then copy the inverted sample data from the original after the header. If you just reverse the byte order of the original entire file, it definitely won't work. It also won't work if you copy the header and then reverse the byte order of the remainder of the file (actually it will sort of work, except what you get will be just noise). You actually need to reverse the frames, where the frame size is dependent on the bytes-per-sample and whether the file is stereo or mono (for example, if the file is stereo and 2 bytes per sample, then each frame is 4 bytes).
Note that not all WAV files are "canonical" like this. WAV files are actually a variant of RIFF files, so technically you need much more complicated code to find the various parts of the header and sample data within the original file. However, most WAV files are just the header followed by the samples (and this will certainly be true if you're recording the audio yourself), in which case you can save yourself a lot of work.
Joe Cullity's link is a good description of the WAV file format.
There is a good description of the .WAV header at link text
Also note that all data in the file is stored as Little Endian order (low order byte of 1 multi byte number is stored at the lowest address....) so you can't just reverse the bytes. you need to see how many bytes wide each sample is, (usually 16, but check the header) and reverse them in chunks of that size
I'm pretty sure that you're right and that the problem is that you can't just reverse the bytes in the file to reverse the waveform because you're destroying header information. You should try to see if there's a good library out there to do this, since I have little experience working with .wav files.
My goal is to play an mp3 file from Java. With every approach that I took, it always fails with a LineUnavailableException.
AudioInputStream inputStream = AudioSystem.getAudioInputStream(new URL("http://localhost:8080/agriserver/facebook/sound/test6.mp3"));
Clip clip = AudioSystem.getClip(info);
clip.open(inputStream);
clip.start();
Failed attempts to fix it:
Use Sun's mp3 plugin.
Use Jlayer 3rd party library
Use Tritonus 3rd party library
Re-encode the mp3 with Sony Sound Forge, Adobe Sound Booth, all no luck
Re-encode the mp3 with different encode rates and sampling rates
Try to use JMF
Use random mp3 from the Internet that plays fine in other applications
Read postings with the same error. None of the postings have an answer that helped resolve the issue.
Here is the exception:
Exception in thread "main" javax.sound.sampled.LineUnavailableException: line with format MPEG1L3 48000.0 Hz, unknown bits per sample, stereo, unknown frame size, 41.666668 frames/second, not supported.
at com.sun.media.sound.DirectAudioDevice$DirectDL.implOpen(DirectAudioDevice.java:494)
at com.sun.media.sound.DirectAudioDevice$DirectClip.implOpen(DirectAudioDevice.java:1280)
at com.sun.media.sound.AbstractDataLine.open(AbstractDataLine.java:107)
at com.sun.media.sound.DirectAudioDevice$DirectClip.open(DirectAudioDevice.java:1061)
at com.sun.media.sound.DirectAudioDevice$DirectClip.open(DirectAudioDevice.java:1151)
at Demo.playMp3(Demo.java:83)
Apparently, the mp3 has to be read into one stream. That stream has to be read into a second stream to decode it. The below code worked:
// read the file
AudioInputStream rawInput = AudioSystem.getAudioInputStream(new ByteArrayInputStream(data));
// decode mp3
AudioFormat baseFormat = rawInput.getFormat();
AudioFormat decodedFormat = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED, // Encoding to use
baseFormat.getSampleRate(), // sample rate (same as base format)
16, // sample size in bits (thx to Javazoom)
baseFormat.getChannels(), // # of Channels
baseFormat.getChannels()*2, // Frame Size
baseFormat.getSampleRate(), // Frame Rate
false // Big Endian
);
AudioInputStream decodedInput = AudioSystem.getAudioInputStream(decodedFormat, rawInput);
OK - Let's start by ruling out your MP3 files and your code.
Pick an MP3 file that you have and
play it with any MP3 player.
Download
http://www.javazoom.net/javalayer/sources/jlayer1.0.1.zip
Extract jl1.0.1.jar from zip file
and put in your classpath
Cut and Paste the code at the end of this answer into your dev environment.
compile and run making sure your mp3
file in step 1 is the parameter to
the file. (In my case I had this "C:\\Users\\romain\\Music\\Al DiMeola\\Elegant Gypsy\\01 Flight over Rio Al DiMeola.mp3")
I tested this and it works fine.
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import javazoom.jl.player.Player;
public class MP3 {
private String filename;
private Player player;
// 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);
}
// run in new thread to play in background
new Thread() {
public void run() {
try { player.play(); }
catch (Exception e) { System.out.println(e); }
}
}.start();
}
// test client
public static void main(String[] args) {
String filename = args[0];
MP3 mp3 = new MP3(filename);
mp3.play();
}
}