I'm writing a generalized utility for converting audio files to WAV. Works ok for WAV to WAV (I'm also changing some of the attributes), but I can't convert MP3 files. I have mp3spi in my classpath, so it seems to be able to read the MP3, but the WAV file that gets written doesn't seem to work.
In this example, I'm not trying to change any properties. Just reading the MP3 and writing to a WAV file
My code looks something like this
File inputFileObj = new File(input);
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(inputFileObj);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Input file format:");
System.out.println(AudioSystem.getAudioFileFormat(inputFileObj));
try {
AudioSystem.write(audioInputStream, outputType, new File(output));
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Output file format:");
System.out.println(AudioSystem.getAudioFileFormat(new File(output)));
Here's the output. As you can see, it appears to write the output file, but when I try to retrieve the format of the output file, it can't handle it. And if I try to play the output file, the player doesn't recognize it.
Input file: c:\testfiles\sample-b-converted.mp3
Output file: c:\testfiles\foo.wav
Output type: wav
Input file format:
MP3 (.mp3) file, byte length: 13227300, data format: MPEG2L3 16000.0 Hz, unknown bits per sample, mono, unknown frame size, 27.777779 frames/second, , frame length: 122475
Bytes written: 13227344
Output file format:
Exception in thread "main" javax.sound.sampled.UnsupportedAudioFileException: file is not a supported file type
at javax.sound.sampled.AudioSystem.getAudioFileFormat(AudioSystem.java:1078)
at org.torchai.AudioFileConvert01.main(AudioFileConvert01.java:60)
Is there something else I need to get this working?
Someone posted a comment referring me to mp3 to wav conversion in java. I had seen this issue, but didn't quite see the main aspect of the answer, since it wasn't really explained well.
An MP3 file apparently needs to go through a 2-step conversion. I don't fully understand why, but it seems you must first convert it to PCM_SIGNED with a sample size of 16 and a framesize of 2*# of channels. At that point, you can convert it again to the final format that you want.
Would still love to have a better explanation, but this at least gets me past my issue.
Related
My goal
I want to do the following in Java: record some sound made by Java itself, and then save it as a wav file.
I've searched for a lot of these kind of programs online, and I've found some good ones, but I get the same problem again and again (the current version can be found here). The problem is always something like this:
In the public class, the function start begins with
AudioFormat format = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line not supported");
System.exit(0);
}
, where getAudioFormat() returns AudioFormat(16000,8,2,true,true). If I run the file, it prints Line not supported, but why?
What I've tried:
I've searched for loads of different formats online, and tried each of them.
I've taken an existing wav file which Java can play, taken the format of that one, and used it as the format for the file I try to create.
Some more information:
System.out.println(AudioSystem.getTargetLineInfo(info));
System.out.println(AudioSystem.getAudioFileTypes());
System.out.println(AudioSystem.getMixerInfo());
System.out.println(AudioSystem.getSourceDataLine(format));
where format is the format of the existing wav file, and info is defined as in the first code snippet, prints:
[Ljavax.sound.sampled.Line$Info;#4eec7777
[Ljavax.sound.sampled.AudioFileFormat$Type;#41629346
[Ljavax.sound.sampled.Mixer$Info;#6d311334
com.sun.media.sound.DirectAudioDevice$DirectSDL#448139f0
I use Windows 7, 64 bits.
I'd like to add that if there's a better, entirely different way to achieve my goal, that's fine too.
DicomDroid.jar used to open a .dcm formated image in my Android application. I got the follwing exception when try to open it.
java.io.IOException: DICOM JPEG compression not yet supported
Adding my code below
try {
// Read the imagefile into a byte array (data[])
File imagefile = new File(path);
byte[] data = new byte[(int) imagefile.length()];
FileInputStream fis = new FileInputStream(imagefile);
fis.read(data);
fis.close();
// Create a DicomReader with the given data array (data[])
DicomReader DR = new DicomReader(data);
} catch (Exception ex) {
Log.e("ERROR", ex.toString());
}
What can be done to avoid this error?
Thanks in advance.
The cause is pretty obvious. That DICOM library doesn't support that particular kind of DICOM file.
There's not much you can do about it ... unless you are prepared to enhance the library yourself.
But I think you have probably made a mistake in setting up your instrument to generate DICOM files with JPEG compression. JPEG is lossy, and best practice is to capture and store images with the best resolution feasible. If you need to downgrade resolution to reduce bandwidth, it would be better to
save a high resolution DICOM,
convert the DICOM to a low resolution JPG, and
send the JPEG.
Another option is to get the Dicom file in an uncompressed format (ej: Explicit VR Little Endian). This is the simplest dicom file format and every dicom library has support for such format.
So, when you get your Dicom file from your PACS, force this transfer syntax. This way, your dicom library will be able to deal with the image file.
I am building a speech synthesizer, and everything works except the audio. I have a list of phonemes that are stored as .wav files, and I am calling them with AudioInputStreams, but they won't repeat. I have no idea what could be the issue, so any help would be appreciated.
The code that initializes a HashMap full of phones is
for(File phone : listOfFiles){
String path = phone.getPath();
if(path.startsWith(".")){continue;}
path = path.replace(".wav", "").replace("phones/", "");
AudioInputStream clip1 = AudioSystem.getAudioInputStream(phone);
phonemes.put(path,clip1);
}
and the code that combines and outputs the sound is
public void speak(String[] input){
AudioInputStream phrase = phonemes.get(input[0]);
AudioInputStream phone;
int x = input.length;
for(int i=1; i<input.length; i++){
phone = phonemes.get(input[i]);
phrase = new AudioInputStream(new SequenceInputStream(phrase, phone), phrase.getFormat(), phrase.getFrameLength() + phone.getFrameLength());
}
try {
Clip clip = AudioSystem.getClip();
clip.open(phrase);
clip.start();
} catch (Exception e) {
e.printStackTrace();
}
}
To replay a Clip, you have to stop it and reposition it, then start it. I don't think you can close and reopen a given Clip. But attempts to do that should have generated a LineUnavailable exception, and you say you got no exceptions.
To troubleshoot, I'd first verify that it is possible to play the .wav files prior to placing them in the hash table. Sometimes an unexpected format (e.g., 24-bit or 32-bit encoding, or big-endian rather than little-endian) can lead to .wav files not playing.
If you are trying to concatenate a series of clips or audio data into a single clip, that could also be problematic. I think that AudioInputStream expects a single set of "header" data from the .wav file, but the SequenceInputStream could in effect be sending multiple "headers", one for each source file. I've never seen concatenation attempted like that before.
You might need to make your own data storage for the raw audio for each phoneme, and then build your combined phonemes from that rather than directly from .wav files. Instead of loading to Clips, load the raw PCM from the AudioInputStream into byte arrays. To output the raw audio bytes, you can use a SourceDataLine.
I'm trying to create copy of a mp3 file.This I need to trim that mp3 file.So using input,output stream can be used to this I guess.Can the normal text file type of copying will be able to create a file that could be played .Someone with good knowledge of file handling in java help me in this.
MP3 file is binary file. So as long as you copy MP3 using binary file operations copy will succeed.
However to trim an MP3 file, you need to be aware of MP3 file structure. MP3 file consists of series of MP3 frames. Each frame starts with a MP3 header and followed by data. MP3 frame header contains information, using which you can find frame length.
More details on MP3 header at http://www.id3.org/mp3Frame
So as long as you copy at integral number of frames, you should be okay. Even otherwise, decoders will ignore incomplete frame.
Below code is a solution to copy an mp3 file or everything else. I have never experienced the trimming part. However, I think it is logically possible to trim your file just by specifying the amount of buffer you need.)
Actually dst is the name of the copied file in the directory.
private void copyFile(String src, String dst) {
FileInputStream inputStream; // create an input stream
FileOutputStream outputStream; // create an output stream
try {
inputStream = new FileInputStream(src); // create object
outputStream = openFileOutput(dst, Context.MODE_PRIVATE); // save your file in private mode, which makes it inaccessible by other applications
int bufferSize;
byte[] bufffer = new byte[512]; // I think logically here could be useful for trimming the file. I mean just copy an specified part of the file.
while ((bufferSize = inputStream.read(bufffer)) > 0) {
outputStream.write(bufffer, 0, bufferSize);
}
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
This is a suggestion about how to copy your files, which it will copy them in internal storage under your application package name. Moreover, I have not tested the trim part yet. So, I am not sure about that.
One more thing, you can get the path directory of your application by
getFilesDir();
I need to be able to create a WAV file using the mic in Android. Currently, I'm having a lot of trouble. So far, this is my situation. I'm using parts of the micDroid project code to record thus:
//read thread
int sampleRate = 44100;
int bufferSize = AudioRecord.getMinBufferSize(sampleRate,android.media.AudioFormat.CHANNEL_CONFIGURATION_MONO,android.media.AudioFormat.ENCODING_PCM_16BIT);
AudioRecord ar = new AudioRecord(AudioSource.MIC,sampleRate,android.media.AudioFormat.CHANNEL_CONFIGURATION_MONO,android.media.AudioFormat.ENCODING_PCM_16BIT,bufferSize);
short[] buffer = new short[bufferSize];
ar.startRecording();
while(isRunning){
try{
int numSamples = ar.read(buffer, 0, buffer.length);
queue.put(new Sample(buffer, numSamples));
} catch (InterruptedException e){
e.printStackTrace();
}
}
//write thread
int sampleRate = 44100;
WaveWriter writer = new WaveWriter("/sdcard","recOut.wav",sampleRate,android.media.AudioFormat.CHANNEL_CONFIGURATION_MONO,android.media.AudioFormat.ENCODING_PCM_16BIT);
try {
writer.createWaveFile();
} catch (IOException e) {
e.printStackTrace();
}
while(isRunning){
try {
Sample sample = queue.take();
writer.write(sample.buffer, sample.bufferSize);
} catch (IOException e) {
//snip
}
}
Which appears to work fine, however the end result recognizably contains whatever I said, but it is horribly distorted. Here's a screen cap of audacity (WMP refuses to play the file).
Any help would be greatly appreciated, let me know if you need more code / info.
Update
Following markus's suggestions, here is my updated code:
int sampleRate = 16000;
writer = new WaveWriter("/sdcard","recOut.wav",sampleRate,android.media.AudioFormat.CHANNEL_IN_MONO,android.media.AudioFormat.ENCODING_PCM_16BIT);
int bufferSize = AudioRecord.getMinBufferSize(sampleRate,android.media.AudioFormat.CHANNEL_IN_MONO,android.media.AudioFormat.ENCODING_PCM_16BIT);
AudioRecord ar = new AudioRecord(AudioSource.MIC,sampleRate,android.media.AudioFormat.CHANNEL_IN_MONO,android.media.AudioFormat.ENCODING_PCM_16BIT,bufferSize);
byte[] buffer = new byte[bufferSize]; //all references changed from short to byte type
...and another audacity screencap:
CHANNEL_CONFIGURATION_MONO is deprecated as far as i know, use CHANNEL_IN_MONO. consider looking at rehearsalassistant project, afaik they use AudioRecord class, too. samplerates should be chosen to fit the standard sample rates like explained in this wikipedia article.
You can set a buffer to be little-endian by using the following:
buffer.order(ByteOrder.LITTLE_ENDIAN);
I have a lot of theories:
if you had a signal saturated of noise and you cannot understand anything or you can understand something. It can be that you have a problem with big endian and little endian. When you record 16bit (2bytes) can be stored like AB or like BA (where A and B it's a byte). In java all is big endian (http://mindprod.com/jgloss/endian.html) so you are storing a sample of 16 bit as short in big endian, then you try to construct a wav, but a wav need to have data stored on little endian (https://ccrma.stanford.edu/courses/422/projects/WaveFormat/). Well the result is clear and you have an screenshot. The solution is to use byte always, you had done and it probably all it's ok now
if not check queue.put and queue.take I think you don't need them because you stored your audio in and array that's enough. In the first while I would replace queue.put by a function to store the buffer in a huge array of bytes with all the bytes. Then you can put the header of the wav.
Remember a wav has the header and the bytes of the audio, maybe you have forgot the header and the program you are using is inventing a header to play it.
The other option is: you have the problem on the wav header of 44 bytes. check your WaveWriter function. if you write bad code on it, you can heard a lot of different sounds of your voice.
note: I had seen the function WaveWriter or similar on the source code of android but it had not been ready to use three months ago. Some new about waveheader?
It's pretty clear, Audacity thinks that your file is floating point.
Either the WAV header is screwed up or else it's how you opened it.
Make a recording.
In Audacity, open it using Project / Import Raw Data
Set the parameters for how you think that you recorded it.
I'll bet it plays back ok.