Read WAV data as a byte array in Android Java

Content of this article

If you just want to play the sound of WAV files on Android, you can use MediaPlayer class etc. You may want to read the WAV data "in advance" and mess with the data.

"In advance" means "before playing the sound". Because you can get the raw data and FFT with Visualizer.setDataCaptureListener using the MediaPlayer class, Since this is the sound being played, it will be processed in real time.

External link: Display equalizers and waveforms using VISUALIZER

Therefore, in this article, I will introduce how to read WAV data as a byte array in advance and handle it while paying attention to headers and so on.

Read WAV data

If the WAV file (example: music.wav) is stored in the res / raw folder, you can get the byte array with InputStream.

        try {
            InputStream is = getResources().openRawResource(R.raw.music);
            wavData = new byte[is.available()];
            String readBytes = String.format(Locale.US, "read bytes = %d", is.read(wavData));
            Log.e(TAG, readBytes);
            is.close();
        } catch (Exception e){
            e.printStackTrace();
        }

Reference external link: Play WAV audio files on AudioTrack

Header analysis

The WAV file has a header of about 44 bytes, followed by data. However, it seems that this 44 bytes may be added in various ways, so it seems better to acquire the data while checking the contents of the header properly.

Reference external link: About the data structure of the sound file (extension: WAV file)

The type of header is called chunk, and it would be nice to know'fmt'and'data'. (For details, please see the external link above.) Therefore, check these'fmt'== 0x6d7420 and'data'== 0x64617461 and save their respective indexes.

        int fmtIdx = 0;
        for(int i = 0; i < wavData.length - 4; i ++){
            if(wavData[i] == 0x66 && wavData[i + 1] == 0x6d
                    && wavData[i + 2] == 0x74 && wavData[i + 3] == 0x20){ // 'fmt ' chunk
                fmtIdx = i;
                Log.i("Test", "fmtIdx:" + fmtIdx);
                break;
            }
        }
        if(fmtIdx == 0){
            Log.e(TAG, "No fmt chunk");
        }

        int dataIdx = 0;
        for(int i = 0; i < wavData.length - 4; i ++){
            if(wavData[i] == 0x64 && wavData[i + 1] == 0x61
                    && wavData[i + 2] == 0x74 && wavData[i + 3] == 0x61){ // 'data' chunk
                dataIdx = i;
                Log.i("Test", "dataIdx:" + dataIdx);
                break;
            }
        }
        if(dataIdx == 0){
            Log.e(TAG, "No data chunk");
        }

Next, let's get the number of channels, sampling rate, number of bits (bytes in this case), and data size. The data size is basically the part excluding the header, but it seems that you can freely add a footer to the WAV format, so get it. The sound source I actually tried contained a fair amount of footer.

        int wavChannel = (int)(wavData[fmtIdx + 10]);
        Log.i("Test", "wavChannel:" + wavChannel);

        //int wavSamplingRate = ((int)(wavData[fmtIdx + 15]) << 24) + ((int)(wavData[fmtIdx + 14]) << 16)
        //        + ((int)(wavData[fmtIdx + 13]) << 8) + (int)(wavData[fmtIdx + 12]);
        //↑ It seems that the writing style is not good, so correct it
        byte[] bytes1 = {wavData[fmtIdx + 15], wavData[fmtIdx + 14], wavData[fmtIdx + 13], wavData[fmtIdx + 12]};
        int wavSamplingRate = ByteBuffer.wrap(bytes1).getInt();

        Log.i("Test", "wavSamplingRate:" + wavSamplingRate);

        int wavByte = (int)(wavData[fmtIdx + 22]) / 8;
        Log.i("Test", "wavByte:" + wavByte);

        //int wavDataSize = ((int)(wavData[dataIdx + 7]) << 24) + ((int)(wavData[dataIdx + 6]) << 16)
        //        + ((int)(wavData[dataIdx + 5]) << 8) + (int)(wavData[dataIdx + 4]);
        byte[] bytes2 = {wavData[dataIdx + 7], wavData[dataIdx + 6], wavData[dataIdx + 5], wavData[dataIdx + 4]};
        int wavDataSize = ByteBuffer.wrap(bytes2).getInt();

        Log.i("Test", "wavDataSize:" + wavDataSize);

        int wavHeaderSize = dataIdx + 8;

        int[] musicDataRight = new int[wavDataSize / wavByte / wavChannel];
        int[] musicDataLeft = new int[wavDataSize / wavByte / wavChannel];

Where it is stored in 4 bytes, it is set to int by ~~ shift operation ~~ ByteBuffer. Also, the header size is calculated for later use. When the channel is 2, the left and right sound sources can be acquired, so put them in musicDataRight and musicDataLeft.

        byte[] bytes_temp = {0, 0, 0, 0}; //If it is not 4 bytes, BufferUnderflowException will occur.
        if(wavByte == 1 && wavChannel == 1){
            for(int i = 0, j = wavHeaderSize; i < musicDataRight.length; i ++, j ++){
                musicDataRight[i] = (int)wavData[j];
                musicDataLeft[i]  = (int)wavData[j];
            }
        } else if(wavByte == 1 && wavChannel == 2){
            for(int i = 0, j = wavHeaderSize; i < musicDataRight.length; i ++, j += 2){
                musicDataRight[i] = (int)wavData[j];
                musicDataLeft[i]  = (int)wavData[j + 1];
            }
        } else if(wavByte == 2 && wavChannel == 1){
            for(int i = 0, j = wavHeaderSize; i < musicDataRight.length; i ++, j += 2){
                //musicDataRight[i] = ((int)wavData[j + 1] << 8) + (int)wavData[j];
                //musicDataLeft[i]  = ((int)wavData[j + 1] << 8) + (int)wavData[j];
                bytes_temp[2] = wavData[j + 1];
                bytes_temp[3] = wavData[j];
                musicDataRight[i] = ByteBuffer.wrap(bytes_temp).getInt();
                musicDataLeft[i] = ByteBuffer.wrap(bytes_temp).getInt();

            }
        } else if(wavByte == 2 && wavChannel == 2){
            for(int i = 0, j = wavHeaderSize; i < musicDataRight.length; i ++, j += 4){
                //musicDataRight[i] = ((int)wavData[j + 1] << 8) + (int)wavData[j];
                //musicDataLeft[i]  = ((int)wavData[j + 3] << 8) + (int)wavData[j + 2];
                bytes_temp[2] = wavData[j + 1];
                bytes_temp[3] = wavData[j];
                musicDataRight[i] = ByteBuffer.wrap(bytes_temp).getInt();
                bytes_temp[2] = wavData[j + 3];
                bytes_temp[3] = wavData[j + 2];
                musicDataLeft[i] = ByteBuffer.wrap(bytes_temp).getInt();

            }
        }

Data is stored by the number of channels and the number of bytes. If I wanted to do it, I could put together the formulas (I actually tried it), but I wrote them separately because they would reduce readability. When the number of channels is 1, the same sound is put on the left and right. Also, if the number of channels or bytes is 3 or more, it is necessary to add them separately.

This is the end of the procedure for reading WAV data as a byte array.

in conclusion

This makes it possible to display the read waveform as a graph and process the data. However, as I mentioned at the beginning, please tell me if there is an easier way.

Recommended Posts

Read WAV data as a byte array in Android Java
How to convert a file to a byte array in Java
Convert a Java byte array to a string in hexadecimal notation
When seeking multiple in a Java array
[Android / Java] Operate a local database in Room
How to ZIP a JAVA CSV file and manage it in a Byte array
Read JSON in Java
How to create a data URI (base64) in Java
Work as a team! Face Null head-on in Java
Java11: Run Java code in a single file as is
Gzip-compress byte array in Java and output to file
[Java] I want to convert a byte array to a hexadecimal number
Read standard input in Java
Multiplication in a Ruby array
Find a subset in Java
Import Excel data in Java
Read binary files in Java 2
[Java] [Android] Read ini file
Importing Excel data in Java 3
Sorting a list with an int array as an element (Java) (Comparator)
Source to display character array with numberPicker in Android studio (Java)
Cast an array of Strings to a List of Integers in Java
Read items containing commas in a CSV file without splitting (Java)
Determining if a custom keyboard is enabled in Android Studio (Java)
Sorting hashes in a Ruby array
Try implementing Android Hilt in Java
Easily read text files in Java (Java 11 & Java 7)
3 Implement a simple interpreter in Java
Read Java properties file in C #
I created a PDF in Java.
Display Firestore data in RecyclerView [Java]
Read CSV in Java (Super CSV Annotation)
A simple sample callback in Java
Get stuck in a Java primer
How to make a Java array
I want to ForEach an array with a Lambda expression in Java
[Java] [For beginners] How to insert elements directly in a 2D array
About returning a reference in a Java Getter
What is a class in Java language (3 /?)
Create variable length binary data in Java
[Creating] A memorandum about coding in Java
Output Notes document as XML document in Java
Organized memo in the head (Java --Array)
Java creates a table in a Word document
Java creates a pie chart in Excel
What is a class in Java language (1 /?)
What is a class in Java language (2 /?)
Create a TODO app in Java 7 Create Header
Read Felica using RC-S380 (PaSoRi) in Java
Play RAW, WAV, MP3 files in Java
Try making a calculator app in Java
Read xlsx file in Java with Selenium
Implement something like a stack in Java
Split a string with ". (Dot)" in Java
Creating a matrix class in Java Part 1
Java to C and C to Java in Android Studio
A way for Java / Scala developers to advance their careers as data engineers.