Convert audio stream to WAV byte array in Java without temp file - java

Given an InputStream called in which contains audio data in a compressed format (such as MP3 or OGG), I wish to create a byte array containing a WAV conversion of the input data. Unfortunately, if you try to do this, JavaSound hands you the following error:
java.io.IOException: stream length not specified
I managed to get it to work by writing the wav to a temporary file, then reading it back in, as shown below:
AudioInputStream source = AudioSystem.getAudioInputStream(new BufferedInputStream(in, 1024));
AudioInputStream pcm = AudioSystem.getAudioInputStream(AudioFormat.Encoding.PCM_SIGNED, source);
AudioInputStream ulaw = AudioSystem.getAudioInputStream(AudioFormat.Encoding.ULAW, pcm);
File tempFile = File.createTempFile("wav", "tmp");
AudioSystem.write(ulaw, AudioFileFormat.Type.WAVE, tempFile);
// The fileToByteArray() method reads the file
// into a byte array; omitted for brevity
byte[] bytes = fileToByteArray(tempFile);
tempFile.delete();
return bytes;
This is obviously less desirable. Is there a better way?

The problem is that the most AudioFileWriters need to know the file size in advance if writing to an OutputStream. Because you can't provide this, it always fails. Unfortunatly, the default Java sound API implementation doesn't have any alternatives.
But you can try using the AudioOutputStream architecture from the Tritonus plugins (Tritonus is an open source implementation of the Java sound API): http://tritonus.org/plugins.html

I notice this one was asked very long time ago. In case any new person (using Java 7 and above) found this thread, note there is a better new way doing it via Files.readAllBytes API. See:
How to convert .wav file into byte array?

Too late, I know, but I was needed this, so this is my two cents on the topic.
public void UploadFiles(String fileName, byte[] bFile)
{
String uploadedFileLocation = "c:\\";
AudioInputStream source;
AudioInputStream pcm;
InputStream b_in = new ByteArrayInputStream(bFile);
source = AudioSystem.getAudioInputStream(new BufferedInputStream(b_in));
pcm = AudioSystem.getAudioInputStream(AudioFormat.Encoding.PCM_SIGNED, source);
File newFile = new File(uploadedFileLocation + fileName);
AudioSystem.write(pcm, Type.WAVE, newFile);
source.close();
pcm.close();
}

The issue is easy to solve if you prepare class which will create correct header for you. In my example Example how to read audio input in wav buffer data goes in some buffer, after that I create header and have wav file in the buffer. No need in additional libraries. Just copy the code from my example.
Example how to use class which creates correct header in the buffer array:
public void run() {
try {
writer = new NewWaveWriter(44100);
byte[]buffer = new byte[256];
int res = 0;
while((res = m_audioInputStream.read(buffer)) > 0) {
writer.write(buffer, 0, res);
}
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
public byte[]getResult() throws IOException {
return writer.getByteBuffer();
}
And class NewWaveWriter you can find under my link.

This is very simple...
File f = new File(exportFileName+".tmp");
File f2 = new File(exportFileName);
long l = f.length();
FileInputStream fi = new FileInputStream(f);
AudioInputStream ai = new AudioInputStream(fi,mainFormat,l/4);
AudioSystem.write(ai, Type.WAVE, f2);
fi.close();
f.delete();
The .tmp file is a RAW audio file, the result is a WAV file with header.

Related

How to determine the compression method of a zip file

From a third party I am retrieving .zip files. I want to unzip these to another folder. To this end I found a method that does exactly that, see code below. It iterates through all files and unzips them to another folder. However, when observing the corresponding compression method I found out that this changes for some files. And for some files it states: "invalid compression method", after which it aborts further unzipping of the zip file.
As the compression method seems to change, I suspect I need to set the compression method to the correct one (however that might be a wrong assumption). So rises my question: how to determine the compression method needed?
The code I am using:
public void unZipIt(String zipFile, String outputFolder){
//create output directory is not exists
File folder = new File(OUTPUT_FOLDER);
if(!folder.exists()){
folder.mkdir();
}
FileInputStream fis = null;
ZipInputStream zipIs = null;
ZipEntry zEntry = null;
try
{
fis = new FileInputStream(zipFile);
zipIs = new ZipInputStream(new BufferedInputStream(fis));
while((zEntry = zipIs.getNextEntry()) != null){
System.out.println(zEntry.getMethod());
try{
byte[] tmp = new byte[4*1024];
FileOutputStream fos = null;
String opFilePath = OUTPUT_FOLDER + "\\" + zEntry.getName();
System.out.println("Extracting file to "+opFilePath);
fos = new FileOutputStream(opFilePath);
int size = 0;
while((size = zipIs.read(tmp)) != -1){
fos.write(tmp, 0 , size);
}
fos.flush();
fos.close();
} catch(IOException e){
System.out.println(e.getMessage());
}
}
zipIs.close();
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
}
catch(IOException ex){
System.out.println(ex.getMessage());
}
}
Currently I am retrieving the following output:
8
Extracting file to C:\Users\nlmeibe2\Documents\Projects\Output_test\SOPHIS_cptyrisk_tradedata_1192_20140616.csv
8
Extracting file to C:\Users\nlmeibe2\Documents\Projects\Output_test\SOPHIS_cptyrisk_underlying_1192_20140616.csv
0
Extracting file to C:\Users\nlmeibe2\Documents\Projects\Output_test\10052013/
12
Extracting file to C:\Users\nlmeibe2\Documents\Projects\Output_test\MRM_Daily_Position_Report_Package_Level_Underlying_View_EQB_v2_COBDATE_2014-06-16_RUNDATETIME_2014-06-17-04h15.csv
invalid compression method
invalid compression method
Since you only print the exception message and not the stack trace (with line numbers), it is impossible to know exactly where the exception is thrown, but I suppose it is not thrown until you actually try to read from the ZipEntry.
If the numbers in your output is the ZIP method, the last entry you encounter is compressed with method 12 (bzip2), which is not supported by the Java ZIP implementation. PKWare (the maintainers of the ZIP format) regularly add new compression methods to the ZIP specification and there are currently some 12-15 (not sure about the exact number) compression methods specified. Java only supports the methods 0 (stored) and 8 (deflated) and will throw an exception with the message "invalid compression method" if you try to decompress a ZIP file using an unsupported compression method.
Both WinZip and the ZIP functions in Windows may use compression methods not supported by the Java API.
Use zEntry.getMethod() to get the compression method
Returns the compression method of the entry, or -1 if not specified.
It will return an int which will be
public static final int STORED
public static final int DEFLATED
or -1 if it don't know the method.
Docs.

When writing .wav file from AudioInputStream, Windows Media Player can't play it

I'm trying to write a file from an AudioInputStream. The stream can read the original file just fine (as far as I can tell). But when it tries to write, it writes what should be a 15 KB file in 44 bytes. Windows Media Player gives the error "either the file type is unsupported, or WMP doesn't recognize the codec used to convert the file."
I got most of this code from http://docs.oracle.com/javase/tutorial/sound/converters.html. I've looked all over StackOverflow but nothing seemed to pertain to this problem.
I've tried getting the format of the AudioInputStream and it's definitely a .wav. As far as I can tell, a codec is software that converts analog to digital data, and both the wav file and the AudioInputStream are digital already.
EDIT: It looks like the stream is writing the WAVE file header to my new file, but nothing else. That's where my 44 byte file is coming from.
Here's the code. I think the problem is in writeFile() but I included the rest just in case:
public static void getFile()
{
try
{
File test = new File("C:\\Users\\Audrey\\Steganography\\correctamundo.wav"); //file to be written from
AudioInputStream stream = AudioSystem.getAudioInputStream(test);
AudioFileFormat format = AudioSystem.getAudioFileFormat(test);
int bytesPerFrame = stream.getFormat().getFrameSize();
byte[] b = new byte[30000]; //array of arbitrary size to hold the bytes in stream
if (bytesPerFrame == AudioSystem.NOT_SPECIFIED) //not sure why this is necessary
{
bytesPerFrame = 1;
}
int i = 0;
while(stream.available() > 0)
{
byte currentByte = (byte)stream.read();
b[i] = currentByte; //read bytes to array
System.out.println(b[i] + " " +(i+1)); //test statement
i++;
}
writeFile(format, stream);
} catch (IOException | UnsupportedAudioFileException e)
{
e.printStackTrace();
}
}
public static void writeFile (AudioFileFormat format, AudioInputStream stream) //format and stream created in getFile() under the same names
{
try
{
File fileOut = new File("C:\\Users\\Audrey\\Steganography\\testFile2.wav");
AudioFileFormat.Type fileType = format.getType(); //type of file the AudioInputStream can write to, since format refers to stream
if (AudioSystem.isFileTypeSupported(fileType, stream))
{
AudioSystem.write(stream, fileType, fileOut);
System.out.println(stream.getFrameLength()); //test statement
}
System.out.println(fileType); //test statement
}
catch (IOException e)
{
e.printStackTrace();
}
}

Writing Zip Files to GAE Blobstore

I'm using the Java API for reading and writing to the Google App Engine Blobstore.
I need to zip files directly into the Blobstore, meaning I have String objects which I want to be stored in the Blobstore when zipped.
My problem is that standard zipping methods are using OutputStream to write, while it seems that GAE doesn't provide one for writing to the Blobstore.
Is there a way to combine those APIs, or are there different APIs I can use (I haven't found such)?
If I am not wrong, you can try to use the Blobstore low level API. It offers a Java Channel (FileWriteChannel), so you could probably convert it to an OutputStream:
Channels.newOutputStream(channel)
And use that output stream with the java.util.zip.* classes you are currently using (here you have a related example that uses Java NIO to zip something to a Channel/OutputStream)
I have not tried it.
Here is one example to write content file and zip it and store it into blobstore:
AppEngineFile file = fileService.createNewBlobFile("application/zip","fileName.zip");
try {
FileWriteChannel writeChannel = fileService.openWriteChannel(file, lock);
//convert as outputstream
OutputStream blobOutputStream = Channels.newOutputStream(writeChannel);
ZipOutputStream zip = new ZipOutputStream(blobOutputStream);
zip.putNextEntry(new ZipEntry("fileNameTozip.txt"));
//read the content from your file or any context you want to get
final byte data[] = IOUtils.toByteArray(file1InputStream);
//write byte data[] to zip
zip.write(bytes);
zip.closeEntry();
zip.close();
// Now finalize
writeChannel.closeFinally();
} catch (IOException e) {
throw new RuntimeException(" Writing file into blobStore", e);
}
The other answer is using BlobStore api, but currently the recommended way is to use App Engine GCS client.
Here is what I use to zip multiple files in GCS :
public static void zipFiles(final GcsFilename targetZipFile,
Collection<GcsFilename> filesToZip) throws IOException {
final GcsFileOptions options = new GcsFileOptions.Builder()
.mimeType(MediaType.ZIP.toString()).build();
try (GcsOutputChannel outputChannel = gcsService.createOrReplace(targetZipFile, options);
OutputStream out = Channels.newOutputStream(outputChannel);
ZipOutputStream zip = new ZipOutputStream(out)) {
for (GcsFilename file : filesToZip) {
try (GcsInputChannel readChannel = gcsService.openPrefetchingReadChannel(file, 0, MB);
InputStream is = Channels.newInputStream(readChannel)) {
final GcsFileMetadata meta = gcsService.getMetadata(file);
if (meta == null) {
log.warn("{} NOT FOUND. Skipping.", file.toString());
continue;
}
final ZipEntry entry = new ZipEntry(file.getObjectName());
zip.putNextEntry(entry);
ByteStreams.copy(is, zip);
zip.closeEntry();
}
zip.flush();
}
}

java gzip can't keep original file's extension name

I'm using GZIPOutputStream to gzip one xml file to gz file, but after zipping I find the extension name of the xml file (.xml) is missing in the gz file hierarchy. I need to keep the extension name because the zipped gz file will be used by third party system which expects getting a .xml file after unzipping gz file. Are there any solutions for this? My test code is:
public static void main(String[] args) {
compress("D://test.xml", "D://test.gz");
}
private static boolean compress(String inputFileName, String targetFileName){
boolean compressResult=true;
int BUFFER = 1024*4;
byte[] B_ARRAY = new byte[BUFFER];
FileInputStream fins=null;
FileOutputStream fout=null;
GZIPOutputStream zout=null;
try{
File srcFile=new File(inputFileName);
fins=new FileInputStream (srcFile);
File tatgetFile=new File(targetFileName);
fout = new FileOutputStream(tatgetFile);
zout = new GZIPOutputStream(fout);
int number = 0;
while((number = fins.read(B_ARRAY, 0, BUFFER)) != -1){
zout.write(B_ARRAY, 0, number);
}
}catch(Exception e){
e.printStackTrace();
compressResult=false;
}finally{
try {
zout.close();
fout.close();
fins.close();
} catch (IOException e) {
e.printStackTrace();
compressResult=false;
}
}
return compressResult;
}
Maybe I'm missing something, but when I've gzipped files in the past, say test.xml, the output I get would be test.xml.gz. Perhaps if you changed the output filename to test.xml.tz you would still preserve your original file extension.
Not sure what the problem is here, you are calling your own compress function
private static boolean compress(String inputFileName, String targetFileName)
with the following arguments
compress("D://test.xml", "D://test.gz");
Quite obviously you are going to lose the .xml portion of the filename, you never pass it into your method.
Your code is perfectly fine. give the output file names as "D://test.xml.gz" you missed the file extension(.xml).
Ex: compress("D://test.xml", "D://test.xml.gz");
You can also use an ArchiveOutput stream (like Tar) before GZipping it.
Use the ZipOutputStream with ZipEntry instead of GZipOutputStream. so that it will keep the original file extension.
Sample code as below..
ZipOutputStream zipOutStream = new ZipOutputStream(new FileOutputStream(zipFile));
FileInputStream inStream = new FileInputStream(file); // Stream to read file
ZipEntry entry = new ZipEntry(file.getPath()); // Make a ZipEntry
zipOutStream.putNextEntry(entry); // Store entry
I created a copy of GZIPOutputStream and changed the code to allow for a different filename "in the gzip":
private final byte[] header = {
(byte) GZIP_MAGIC, // Magic number (short)
(byte)(GZIP_MAGIC >> 8), // Magic number (short)
Deflater.DEFLATED, // Compression method (CM)
8, // Flags (FLG)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Extra flags (XFLG)
0 // Operating system (OS)
};
private void writeHeader() throws IOException {
out.write(header);
out.write("myinternalfilename".getBytes());
out.write(new byte[] {0});
}
Info about gzip format: http://www.gzip.org/zlib/rfc-gzip.html#specification
I also had the same issue, I found that (apache) commons-compress has a similar class - GzipCompressorOutputStream that can be configured with parameters.
final File compressedFile = new File("test-outer.xml.gz");
final GzipParameters gzipParameters = new GzipParameters();
gzipParameters.setFilename("test-inner.xml");
final GzipCompressorOutputStream gzipOutputStream = new GzipCompressorOutputStream(new FileOutputStream(compressedFile), gzipParameters);
Dependency:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.8</version>
</dependency>

How to use java.util.zip to archive/deflate string in java for use in Google Earth?

Use Case
I need to package up our kml which is in a String into a kmz response for a network link in Google Earth. I would like to also wrap up icons and such while I'm at it.
Problem
Using the implementation below I receive errors from both WinZip and Google Earth that the archive is corrupted or that the file cannot be opened respectively. The part that deviates from other examples I'd built this from are the lines where the string is added:
ZipEntry kmlZipEntry = new ZipEntry("doc.kml");
out.putNextEntry(kmlZipEntry);
out.write(kml.getBytes("UTF-8"));
Please point me in the right direction to correctly write the string so that it is in doc.xml in the resulting kmz file. I know how to write the string to a temporary file, but I would very much like to keep the operation in memory for understandability and efficiency.
private static final int BUFFER = 2048;
private static void kmz(OutputStream os, String kml)
{
try{
BufferedInputStream origin = null;
ZipOutputStream out = new ZipOutputStream(os);
out.setMethod(ZipOutputStream.DEFLATED);
byte data[] = new byte[BUFFER];
File f = new File("./icons"); //folder containing icons and such
String files[] = f.list();
if(files != null)
{
for (String file: files) {
LOGGER.info("Adding to KMZ: "+ file);
FileInputStream fi = new FileInputStream(file);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(file);
out.putNextEntry(entry);
int count;
while((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
}
}
ZipEntry kmlZipEntry = new ZipEntry("doc.kml");
out.putNextEntry(kmlZipEntry);
out.write(kml.getBytes("UTF-8"));
}
catch(Exception e)
{
LOGGER.error("Problem creating kmz file", e);
}
}
Bonus points for showing me how to put the supplementary files from the icons folder into a similar folder within the archive as opposed to at the same layer as the doc.kml.
Update Even when saving the string to a temp file the errors occur. Ugh.
Use Case Note The use case is for use in a web app, but the code to get the list of files won't work there. For details see how-to-access-local-files-on-server-in-jboss-application
You forgot to call close() on ZipOutputStream. Best place to call it is the finally block of the try block where it's been created.
Update: To create a folder, just prepend its name in the entry name.
ZipEntry entry = new ZipEntry("icons/" + file);

Categories