0

I've got the following problem:

I need to convert audio bytes (byte[]) from

48kHz, 16 bit, stereo, PCM signed, BIG ENDIAN

to

48kHz, 16 bit, stereo, PCM signed, LITTLE ENDIAN

in java and save it as .wav file.

                      List<byte[]> orderedBytes = bytesFromVoice;

                            /*
                            Here i need to sort the bytes
                             */

                            int size = 0;
                            for (byte[] bs : orderedBytes) {
                                size += bs.length;
                            }
                            byte[] decodedData = new byte[size];
                            int i = 0;
                            for (byte[] bs : orderedBytes) {
                                for (int j = 0; j < bs.length; j++) {
                                    decodedData[i++] = bs[j];
                                }
                            }

                            //writing to file
                            try {
                                getWavFile(getNextFile(), decodedData);
                            } catch (IOException exception) {
                                exception.printStackTrace();
                            }
  • Do you understand the difference between big endian and little endian? It isn't clear from your question what problem you are having doing the conversion. – tgdavies Jul 18 '21 at 00:35
  • this may help https://stackoverflow.com/questions/14116916/do-i-need-to-care-about-big-endian-and-little-endian-when-i-read-data-through-au also checkout https://dsp.stackexchange.com/questions/66820/what-signed-16-bits-little-endian-mean-in-pcm-audio-buffer – Scott Stensland Jul 18 '21 at 01:10
  • I understand the difference, currently i am coding a bot for discord that should recognize the voice and uses the google api for speech to text. I am getting it with big endian but google needs little endian – Jes Müller Jul 18 '21 at 09:56

2 Answers2

1

Found a way ... it's pretty easy:

private void getWavFile(File outFile, byte[] decodedData) throws IOException {
    AudioFormat format = new AudioFormat(48000.0F, 16, 2, true, true);
    boolean convertable = AudioSystem.isConversionSupported(
                           new AudioFormat(48000, 16, 2, true, false), format);
    System.out.println("Can be converted: " + convertable);
    AudioSystem.write(new AudioInputStream(
        new ByteArrayInputStream(decodedData), format, decodedData.length), 
        AudioFileFormat.Type.WAVE, outFile);
    File converted = new File("converted.wav");
    try {
        AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(
            new AudioFormat(48000, 16, 2, true, false), 
            AudioSystem.getAudioInputStream(outFile));
        AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, converted);
    } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
    }
}
Phil Freihofner
  • 7,645
  • 1
  • 20
  • 41
0

When the data is 16-bit, each PCM value is stored as two bytes. All you have to do is iterate through the array, swapping the positions of every pair of bytes.

Phil Freihofner
  • 7,645
  • 1
  • 20
  • 41
  • I already tried this, but it doesn't worked. – Jes Müller Jul 18 '21 at 18:23
  • I suspect there is a problem in how you iterated through the file. It would require using `AudioInputStream` as the input (I should have specified this). But glad to see that you got it working, that the conversion you used is supported! I didn't know conversion was supported for sample rates beyond 44100 or I would have pointed you there. – Phil Freihofner Jul 18 '21 at 18:32
  • @pbierre Did you check the API for `AudioInputStream` before making your comment? An AIS consists of PCM encoded in bytes according to the AudioFormat. The .wav header is not included. Maybe I should have provided more details (I definitely should have mentioned the use of AudioInputStream, as noted earlier), but it seemed to me that swapping bytes is pretty elementary. – Phil Freihofner Jul 13 '22 at 03:01
  • I think there's an even easier solution, and that is to change the value of the isBigEndian boolean in the Format declaration. That would redirect the stream reader to swap the bytes. My problem is something entirely different. The Java 8 Mac vs. Win JREs are behaving differently. The Win JRE is throwing an IllegalArgument exception when trying to create a Clip. Changing the file to a Microsoft .wav file makes no difference -- I get the same error. Will post as a new issue – pbierre Jul 14 '22 at 16:43
  • Will look for it. When you post, mention the source of the JRE (e.g., Oracle vs OpenJDK vs whatever) might be helpful. I've found the OpenJDK for Java 8 on Linux to have some odd lapses (though related to JavaFX not audio). But it could be relevant. Also, when loading a `Clip`, use of `InputStream` can also cause odd discrepancies. I recommend you test with `URL` as your source when you present the question. – Phil Freihofner Jul 14 '22 at 16:54