Is it possible to join two audio files in one on android?

Asked

Viewed 1,569 times

3

Example: Audio A + Audio B = Audio C. I need the sounds to come together even, not concatenate.

The format may be WAV, but I would prefer MP3.

I found some examples on the net, but none worked.

protected Void myMethod() {
    List<String> selection = new ArrayList<String>(); 

    selection.add("a.wav");
    selection.add("b.wav");

    isProcessingOn=true;
    int RECORDER_SAMPLERATE = 0;
    try {
        DataOutputStream amplifyOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(Environment.getExternalStorageDirectory() + "/ssmultitrackPlus/project1/NOVO.wav")));
        DataInputStream[] mergeFilesStream = new DataInputStream[selection.size()];
        long[] sizes=new long[selection.size()];
        for(int i=0; i<selection.size(); i++) {
            File file = new File(Environment.getExternalStorageDirectory() + "/ssmultitrackPlus/project1/" +selection.get(i));
         sizes[i] = (file.length()-44)/2;
        }
        for(int i =0; i<selection.size(); i++) {
            mergeFilesStream[i] =new DataInputStream(new BufferedInputStream(new FileInputStream(Environment.getExternalStorageDirectory() + "/ssmultitrackPlus/project1/" +selection.get(i))));


           // if(i == selection.size()-1) {
                mergeFilesStream[i].skip(24);
                byte[] sampleRt = new byte[4];
                mergeFilesStream[i].read(sampleRt);
                ByteBuffer bbInt = ByteBuffer.wrap(sampleRt).order(ByteOrder.LITTLE_ENDIAN);
                RECORDER_SAMPLERATE = bbInt.getInt();
                mergeFilesStream[i].skip(16);
          //  }
            //    else {
               //     mergeFilesStream[i].skip(44);
           //     }

        }

        for(int b=0; b<selection.size(); b++) {
        for(int i=0; i<(int)sizes[b]; i++) {
             byte[] dataBytes = new byte[2];
             try {
             dataBytes[0] = mergeFilesStream[b].readByte();
             dataBytes[1] = mergeFilesStream[b].readByte();
             }
             catch (EOFException e) {
                amplifyOutputStream.close();
             }
             short dataInShort = ByteBuffer.wrap(dataBytes).order(ByteOrder.LITTLE_ENDIAN).getShort();
             float dataInFloat= (float) dataInShort/37268.0f;


            short outputSample = (short)(dataInFloat * 37268.0f);
            byte[] dataFin = new byte[2];
           dataFin[0] = (byte) (outputSample & 0xff);
           dataFin[1] = (byte)((outputSample >> 8) & 0xff);        
          amplifyOutputStream.write(dataFin, 0 , 2);

        }
        }
        amplifyOutputStream.close();
        for(int i=0; i<selection.size(); i++) {
            mergeFilesStream[i].close();
        }

    } catch (FileNotFoundException e) {

        e.printStackTrace();
    } catch (IOException e) {

        e.printStackTrace();
    }
    long size =0;
    try {
        FileInputStream fileSize = new FileInputStream(Environment.getExternalStorageDirectory() + "/ssmultitrackPlus/project1/NOVO.wav");
        size = fileSize.getChannel().size();
        fileSize.close();
    } catch (FileNotFoundException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }


    final int RECORDER_BPP = 16;

    long datasize=size+36;
    long byteRate = (RECORDER_BPP * RECORDER_SAMPLERATE)/8;
    long longSampleRate = RECORDER_SAMPLERATE;
    byte[] header = new byte[44];




     header[0] = 'R';  // RIFF/WAVE header 
     header[1] = 'I';
     header[2] = 'F';
     header[3] = 'F';
     header[4] = (byte) (datasize & 0xff);
     header[5] = (byte) ((datasize >> 8) & 0xff);
     header[6] = (byte) ((datasize >> 16) & 0xff);
     header[7] = (byte) ((datasize >> 24) & 0xff);
     header[8] = 'W';
     header[9] = 'A';
     header[10] = 'V';
     header[11] = 'E';
     header[12] = 'f';  // 'fmt ' chunk
     header[13] = 'm';
     header[14] = 't';
     header[15] = ' ';
     header[16] = 16;  // 4 bytes: size of 'fmt ' chunk
     header[17] = 0;
     header[18] = 0; 
     header[19] = 0;
     header[20] = 1;  // format = 1
     header[21] = 0;
     header[22] = (byte) 1;
     header[23] = 0;
     header[24] = (byte) (longSampleRate & 0xff);
     header[25] = (byte) ((longSampleRate >> 8) & 0xff);
     header[26] = (byte) ((longSampleRate >> 16) & 0xff);
     header[27] = (byte) ((longSampleRate >> 24) & 0xff);
     header[28] = (byte) (byteRate & 0xff);
     header[29] = (byte) ((byteRate >> 8) & 0xff);
     header[30] = (byte) ((byteRate >> 16) & 0xff);
     header[31] = (byte) ((byteRate >> 24) & 0xff);
     header[32] = (byte) ((RECORDER_BPP) / 8);  // block align
     header[33] = 0;
     header[34] = RECORDER_BPP;  // bits per sample
     header[35] = 0;
     header[36] = 'd';
     header[37] = 'a';
     header[38] = 't';
     header[39] = 'a';
     header[40] = (byte) (size & 0xff);
     header[41] = (byte) ((size >> 8) & 0xff);
     header[42] = (byte) ((size >> 16) & 0xff);
     header[43] = (byte) ((size >> 24) & 0xff);
   // out.write(header, 0, 44); 

    try {
         RandomAccessFile rFile = new RandomAccessFile(Environment.getExternalStorageDirectory() + "/ssmultitrackPlus/project1/NOVO.Wav", "rw");
        rFile.seek(0);
        rFile.write(header);
        rFile.close();
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }


    return null;
}

The code above concatenates the tracks, I would like you to join the two or more sounds at the same time.

1 answer

5


Your logic is correct, unfortunately you have not shown how you are trying to do on Android, you may need to know a little theory to understand what is happening with your code, any kind of sound can be defined as vibrations propagating through the air until they reach your ears:

inserir a descrição da imagem aqui

Have you ever wondered how you can hear a vast number of sounds happening simultaneously?

A: Without you knowing everything is being mixed, all vibrations occurring in the environment come together and are interpreted by your hearing system!

Computationally we are simulating the auditory system, if we want different sound sources to come together we just add the vibrational pulses.

How to do this ?

A: You will need the raw information to be able to manipulate your audio, this applies to any kind of manipulation needed ex: change the audio volume, change the frequency, slow or faster, etc.

When I said "informação crua" i mean that are the data of your audio before it undergoes any encoding in mp3, wav, ogg, etc., and to achieve this in recorded and encoded audios you need to decode, for each audio format you will find a different algorithm, start with samples in . wav are simpler to decode/encode, I believe there should be a vast alternative in Android for this step!

OK with the decoded data you can have a vector (if the audio is mono), with the manipulated values, I advise that the decoded values stay in the format float point 32bit, the values of your vector will be between -1 e 1.

With this data you can simply add the two decoded vectors in float format, to prevent any eventual overflow in the values with amplitude near the end (prevent noise) you can multiply the two vectors by 0.5 and then add, finally you can normalize the output, a pseudo code with this step would be:

resultado = (vetor1 * 0.5) + (vetor2 * 0.5)

To normalize find the maximum absolute value of the result above and multiply by the result

Resultado_final_normalizado = (1 / max(abs(resultado))) * resultado 

Ps: The two audio you want to join need to have the same sample rate, another point to be observed is if the two vectors after decoding will have the same size, if they are different concatene the smaller vector with zeros to stay the same size!

EDIT:

Code used to join three audio files:

private void mixFiles(){
                try {
                        InputStream is1 = getResources().openRawResource(R.raw.test1);
                        List<Short> music1 = createMusicArray(is1);

                        InputStream is2 = getResources().openRawResource(R.raw.test2);
                        List<Short> music2 = createMusicArray(is2);

                        InputStream is3 = getResources().openRawResource(R.raw.test3);
                        List<Short> music3 = createMusicArray(is3);

                        completeStreams(music1,music2,music3);
                        short[] music1Array = buildShortArray(music1);
                        short[] music2Array = buildShortArray(music2);
                        short[] music3Array = buildShortArray(music3);

                        short[] output = new short[music1Array.length];
                        for(int i=0; i < output.length; i++){

                                float samplef1 = music1Array[i] / 32768.0f;
                                float samplef2 = music2Array[i] / 32768.0f;
                                float samplef3 = music3Array[i] / 32768.0f;

                                float mixed = samplef1 + samplef2 + samplef3;
                                // reduce the volume a bit:
                                mixed *= 0.8;
                                // hard clipping
                                if (mixed > 1.0f) mixed = 1.0f;
                                if (mixed < -1.0f) mixed = -1.0f;
                                short outputSample = (short)(mixed * 32768.0f);


                                output[i] = outputSample;
                        }
                        CHAME_AQUI_SUA_FUNCAO_PARA_SALVAR_EM_WAVE
                } catch (NotFoundException e) {
                        e.printStackTrace();
                } catch (IOException e) {
                        e.printStackTrace();
                }
        }

Note the variable output it will contain the mixed raw files, call some function that saves the result in wave. The code shown by you seems to have a function that writes the file header + date to .wave, you can reuse this piece of code.

The above code is a little different from the one proposed by me, in this case it just checks whether the signal amplitude is greater than 1 or -1 if yes the amplitude is forced to stay in their extremes.

  • The normalization of the data is wrong: as the PCM audios are among [-1,1], and their proportion in the result is 50% each, the final result will also be in the range [-1,1]. Thus the expression max(abs(resultado)) will always be in the interval [0,1] that when operated on the vector resultado, will reduce the sample values by reducing the sound volume. The correct one would then be: Resultado_final_normalizado = (1.0 / max(abs(resultado))) * resultado

  • Well remembered @Viníciusgobboa.deOliveira, I’m editing the answer :-)

  • So that I am only a beginner, at this part, ta missing a lot in this source, I will have to study a lot to be able to join these tracks, but thanks for the help... ...

  • For pity the code for me is clear as a sunny morning, if you really want to write codes for analysis, processing and audio manipulation build a good mathematical/algebraic base, read articles and books about digital processing. Understand the basics as concepts of sample, frame, sampling taixa, etc. I consideto Mix sounds the way you want one of the simplest operations in the time domain.

  • After a lot of studying and researching, I managed to mix the wavs, but I’m still using these explanations as a basis, now my problem is another is to optimize the mixing...but this is a subject for another topic ^^

  • very good my congratulations, you just do one of the most basic operations in time domain :-), about optimization open a new question for me or someone else to assist you.

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.