Merge/Mix two .wav files into one

Hello! I’m trying to merge two .wav into the same one.

Could someone please help with this in terms of code ? I’m not exactly sure on how to begin or what juce functions to use. I’ve read through juce::File and juce::AndroidDocument but found nothing related to this. I’ve also found the juce::AudioFormatWriter a bit tricky to understand.

At least for my use case here are some particularities:

• First and the most important of them all: This app is for Android.

• All of the audio files have the same sample rate (I hope so)

• The length of the audio files will yes be different but they will always start at the same time. The user chooses a backing track and then records on top of it, so the end of the recording will probably be different.

• I need to be able to change the volume of each .wav file independently using a slider. This is an issue I’m facing right now: I’m using two different AudioFormatReaderSource mixed into a MixerAudioSource and I actually lost the volume control that I had when using just a single one to play a single sound. In my app the user should be able to change the volume of both the files independently before saving it into the final file.

• All the files will be stereo, but if there’s mono I’ll probably implement something to convert to stereo.

Thanks a lot!

you can read the AudioFormatRead with an AudioBuffer/AudioSampleBuffer which can be modified with getArrayOfWritePointers. When finished add the buffers and copy them to an AudioFormatWriter to write to the output file.

Although the matter of playing and interact with the volume is not that easy, I don’t know if it can be used with MixerAudioSource, otherwise here is an example to play AudioSampleBuffers.
https://docs.juce.com/master/tutorial_looping_audio_sample_buffer.html

I suppose that you should generate the block by adding each buffer, that previously each one of them has been multiplied by the gain. (since the blocks are few bytes you don’t need to modify all the audio while the user adjusts it, just what is playing)

Thanks a lot for your answer but I’m very dumb and all this and I can’t understand what you mean by:

you can read the AudioFormatRead with an AudioBuffer/AudioSampleBuffer which can be modified with getArrayOfWritePointers. When finished add the buffers and copy them to an AudioFormatWriter to write to the output file.

I tried reading and understanding that example you’ve showed but I can’t find what I need inside all of that =(

I’ve tried using chatGPT and here’s the (not working) result, maybe this can help you help me (?):

void mergeWavFiles(const juce::File& file1, const juce::File& file2, const juce::File& outputFile)
{
    auto formatManager = std::make_unique<juce::AudioFormatManager>();
    formatManager->registerBasicFormats();

    auto reader1 = formatManager->createReaderFor(voiceTransportSource);
    auto reader2 = formatManager->createReaderFor(file2);

    auto sampleRate = reader1->sampleRate;
    auto numChannels = reader1->numChannels;
    auto lengthInSamples = reader1->lengthInSamples;

    juce::AudioSampleBuffer buffer1(numChannels, lengthInSamples);
    juce::AudioSampleBuffer buffer2(numChannels, lengthInSamples);

    reader1->read(&buffer1, 0, lengthInSamples, 0, true, true);
    reader2->read(&buffer2, 0, lengthInSamples, 0, true, true);

    // Merge the two buffers
    buffer1.addFrom(0, 0, buffer2, 0, 0, buffer2.getNumSamples());

    // Create an AudioFormatWriter
    juce::AudioFormatWriter writer = formatManager->createReaderFor(outputFile);
    if (writer != nullptr)
    {
        writer->writeFromAudioSampleBuffer(buffer1, 0, buffer1.getNumSamples());
        delete writer;
    }
}

I never used an AudioFormatWriter, but… shouldn’t it be created with formatManager->createWriterFor?

By the way, I said before that you could modify the content by getting a pointer, but I saw that there is a function to apply the gain, so I think you would only need to include it just before the merge.

https://docs.juce.com/master/classAudioBuffer.html#a9ffc61d339e455d4bddc7cf055a63ee3