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.
Related
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.
I am writing an app that takes Morse code, and plays it over the speakers.
Currently I am able to record audio over the microphone using this code:
public void startRecord() throws Exception{
if (record != null){
record.release();
}
File fileOut = new File(FILE);
if (fileOut != null){
fileOut.delete(); // delete any existing file at that location.
}
record = new MediaRecorder();
record.setAudioSource(MediaRecorder.AudioSource.MIC);
record.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
record.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
record.setOutputFile(FILE);
record.prepare();
record.start();
}
and i am able to generate morse code in a string formatted like this:
"-.... .---- -.... -.-. -.... ..... --... ---.."
I can iterate over this string using a for loop such as this:
char[] chars = message.toCharArray();
for (char ch : chars) {
//add to audio file
}
But I am not sure how to create a file out of strung together wav files. Ive seen some posts that mention setting the audio source as a file from the device, but Im not sure how to pick and choose which file and where to insert them, or how to compile it all into a single audio file.
Instead of creating a new sound file and playing that, it would probably be easier to just play each sound individually and when that sound finishes, you play the next sound, or you wait for a brief pause if it's a space.
I think you are trying to do this the harder way. What if you were to simply have the program read the first letter, play the appropriate sound, do the same for the next letter and so on throughout the text. I believe it is much simpler but if you are really set on trying to put it into one file you could have the program make an empty file but in the name it sets have the extension `.wav` or `.mp3` and do research into how they are encoded.
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();
To stream audio file I have implemented following code. But i am getting Exception:
javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input file
at javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1170)
Can Any one help me please......
try {
// From file
System.out.println("hhhhhhhhhhhhhhhh");
AudioInputStream stream = AudioSystem.getAudioInputStream(new File("C:\\track1.mp3"));
System.out.println("stream created");
AudioFormat format = stream.getFormat();
if (format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED) {
System.out.println("in if");
format = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
format.getSampleRate(),
format.getSampleSizeInBits()*2,
format.getChannels(),
format.getFrameSize()*2,
format.getFrameRate(),
true); // big endian
stream = AudioSystem.getAudioInputStream(format, stream);
}
// Create line
SourceDataLine.Info info = new DataLine.Info(
SourceDataLine.class, stream.getFormat(),
((int)stream.getFrameLength()*format.getFrameSize()));
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
line.open(stream.getFormat());
line.start();
// Continuously read and play chunks of audio
int numRead = 0;
byte[] buf = new byte[line.getBufferSize()];
while ((numRead = stream.read(buf, 0, buf.length)) >= 0) {
int offset = 0;
while (offset < numRead) {
offset += line.write(buf, offset, numRead-offset);
}
}
line.drain();
line.stop();
}
That you're doing this job in a servlet class gives me the impression that your intent is to play the mp3 file whenever someone visits your website and that the visitor should hear this mp3 file.
If true, I'm sorry to say, but you're approaching this entirely wrong. Java servlet code runs in webserver machine and not in webbrowser machine. Whenever someone visits your website, this way the mp3 file would only be played at the webserver machine. This is usually a physically completely different machine which runs at the other side of the network connection and the visitor ain't ever going to hear the music.
You want to send the mp3 file raw (unmodified byte by byte) from webserver to the webbrowser without massaging it by some Java Audio API and instruct the webbrowser to play this file. The easist way is to just drop the mp3 file in public webcontent (there where your HTML/JSP files also are) and use HTML <embed> tag to embed it in your HTML/JSP file. The below example assumes the MP3 file to be in the same folder as the HTML/JSP file:
<embed src="file.mp3" autostart="true"></embed>
That's all and this is supported in practically every browser and it will show a player as well.
If the MP3 file is by business requirement stored outside public webcontent, then you may indeed need a servlet for this, but the servlet should do absolutely nothing more than getting an InputStream of it in some way and write it unmodified to the OutputStream of the HttpServletResponse the usual Java IO way. You only need to set the HTTP Content-Type header to audio/mpeg beforehand and if possible also the HTTP Content-Length header. Then point the src to the servlet's URL instead.
<embed src="mp3servlet" autostart="true"></embed>
Default java AudioInputStream does not support mp3 files. You have to plug in MP3SPI to let it decode mp3.
ALso, what do you mean by streaming? This code will play the audio file, not stream it as in internet radio streaming.
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.