Tutorial: Add distortion through waveshaping and convolution [problem with convolution]

Hi everyone,

I am about a week into learning JUCE and I am having a great time!

Today, I encountered something that I am having trouble figuring out. I am going through the “Add distortion through waveshaping and convolution” tutorial and I don’t hear anything different when implementing the convolution processor chain. The signal just gets quieter. I have commented out the dsp reverb within the AudioEngine class to hear the signal more clearly, and when I try the cassette_recorder.wav the same thing happens - no effect on the audio except for the signal getting quieter. I have even tried loading in some reverb wav IRs, but the same result. I haven’t really changed the code from the tutorial, so I am stuck on how I can load impulse responses.

Any thoughts or suggestion?

I ended up solving this problem by replacing the following tutorial code:

auto dir = File::getCurrentWorkingDirectory();

    int numTries = 0;

    while (!dir.getChildFile("Resources").exists() && numTries++ < 15)
        dir = dir.getParentDirectory();

    auto& convolution = processorChain.template get<convolutionIndex>(); // [5]
    convolution.loadImpulseResponse(dir.getChildFile("Resources").getChildFile("guitar_amp.wav"), true, false, 1024); // [6]

with the following:

auto impulsFile = File(“C:/Users/James/Documents/AudioEngineering/JamesGuitarAmp/Resources/P055 - Large Chamber.wav”);

    processorChain.template get<convolutionIndex>().loadImpulseResponse(impulsFile, false, true, 0);

Im getting the exact same problem with the tutorial code.
Is there maybe a better solution than using an absolute file path, or are we just implementing the tutorial code wrong @jules ?

I hope we can find a solid solution to this :slight_smile:

After looking around the forum for a bit longer I found this:

Import the file with the Projucer and convert it to Binary Data, then you can just access it with BinaryData:: :slight_smile: This also includes it in the release built as far as I know. Perfect! :+1:

1 Like

@jamand and @nicolezim if you haven’t already it would also be worth looking into loading impulse responses from an AudioSampleBuffer with the

copyAndLoadImpulseResponseFromBuffer

method from JUCE’s convolution class. There are also more methods for loading impulse responses you can find in the class reference.

Oh yes! I’ve seen that method but I wasnt sure how to pass the file into the AudioBuffer… Do you happen to know how?

This is a function inspired by @daniel I used for loading a bunch of impulse responses loaded in as binaries to an array of AudioSampleBuffers. If you’re just loading 1 impulse response obviously you won’t need any of the for() loop and indexing business. Let me know if you have any issues!

// Relevant declarations
#define HRIR_FILE_SIZE 1068
AudioFormatReader* reader;
AudioSampleBuffer bufferArray[BinaryData::namedResourceListSize];

void IRBank::build()
{
    // Iterate through the list of binary resources (i.e. the impulse responses)
    for (int i = 0; i < BinaryData::namedResourceListSize; ++i)
    {
        const char* binaryData = 0;
        int binaryDataSize = 0;

        // Extract impulse response from binary resource list
        binaryDataSize = HRIR_FILE_SIZE;
        binaryData = BinaryData::getNamedResource(BinaryData::namedResourceList[i], binaryDataSize);

        // Create a memory stream for the impulse response
        auto* inputStream = new MemoryInputStream (binaryData, binaryDataSize, false);

        // Create WAV format reader for this stream
        WavAudioFormat format;
        reader = format.createReaderFor (inputStream, true);  // takes ownership

        // If reader was successfully created, read stream into corresponding index of AudioSampleBuffer array
        if (reader)
        {
            int streamNumChannels = reader->numChannels;
            int streamNumSamples = (int)reader->lengthInSamples;

        bufferArray[i] = juce::AudioBuffer<float>(streamNumChannels, streamNumSamples);
        reader->read(&bufferArray[i], 0, streamNumSamples, 0, true, true);
        }
    }
}

To get the size of a binary if you don’t know it, invoke the BinaryData namespace and follow it with the name of the binary file followed immediately by “Size” like so (in this case file is called IR.wav):

BinaryData::IR_wavSize
1 Like

Thank you Jack! That’s a chuck of code that wouldve took me a long time to figure out…
Two questions though:

  1. Why did you define HRIR_FILE_SIZE to be 1068?
  2. What if there are other files stored in BinaryData, like images? Will this still work?
  1. In my case HRIR_FILE_SIZE was 1068 as all of my impulse responses I was using were 1068 bytes. If you want to load a bunch of impulse responses of differing sizes that will be a bit trickier, because as far as I can tell the BinaryData namespace allows you return individual items from a list of loaded binaries and and the number of items in the list (amongst other things: https://github.com/juce-framework/JUCE/blob/master/extras/AudioPluginHost/JuceLibraryCode/BinaryData.h), but you can’t return the size of individual items from the binary list. Aside from working out the size of each binary item and hardcoding them into a separate list, the only other way I can think of would be some kind of use of the “Size” thing I mentioned in my previous post.

  2. If this for() loop structure came across an image you would invariably be thrown an error. If you want to load both audio and image binaries your best bet would be to drag all your binaries into the Projucer first (say for argument’s sake you have 10), then your images. Then you’d have this for() loop iterate from 0 to 9. I’ve loaded and drawn binary images like so:

    Image cachedImage_Mattblack = ImageCache::getFromMemory (BinaryData::Mattblack_jpg, BinaryData::Mattblack_jpgSize);
    

    Then in your paint() function

    g.drawImage(cachedImage_Mattblack, 0, 60, 800, 740, 0, 0, 400, 400, false);

Thanks for explaining, Jack. I really appreciate it :slight_smile: will give this method a shot