AudioFormatReader::searchForLevel for > stereo audio


#1

Any plans to change AudioFormatReader::searchForLevel() to handle multi-channel audio (more than stereo).

Also, shouldn’t this be a method in the AudioBuffer class?

Thanks,

Rail


#2

Agreed, there are a few methods in the AudioFormatReader class that need to be made a bit more flexible - not sure when we’ll get around to it though.

I don’t think it necessarily belongs in the AudioBuffer though, that class already has methods to get the magnitude, and the point of being in the format reader is that it avoids doing a time-consuming scan of an entire audio file if it detects the required level early on. That optimisation is less relevant in a buffer.


#3

Thanks Jules.

I have a custom AudioFormatReader… so I’ll create my own searchForLevel method for now.

Cheers,

Rail


#4

BTW, my thought was that instead of using a HeapBlock/TempBuffer in the AudioFormatReader method… use an AudioBuffer and then use the AudioBuffer method to do the search… so your AudioBuffer is only the size of the buffer you currently use (but for multiple channels).

Cheers,

Rail


#5

Yep, that could be a clean way of doing it. The code that’s in there’s pretty old!


#6

I’m sure you can clean it up… but this seems to work okay for floats…

int64 AudioFormatReader::searchForLevel (int64 startSample,
                                         int64 numSamplesToSearch,
                                         const double magnitudeRangeMinimum,
                                         const double magnitudeRangeMaximum,
                                         const int minimumConsecutiveSamples)
{
    if (numSamplesToSearch == 0)
        return -1;

    const int bufferSize = 4096;

    AudioSampleBuffer tempBuffer (numChannels, bufferSize);

    jassert (magnitudeRangeMaximum > magnitudeRangeMinimum);

    int64 bufferStart = startSample;

    while (numSamplesToSearch != 0)
    {
        const int numThisTime = (int) jmin (abs64 (numSamplesToSearch), (int64) bufferSize);

        bool bReverseSearch = false;

        if (numSamplesToSearch < 0)
            {
            bufferStart -= numThisTime;
            bReverseSearch = true;
            }

        if (bufferStart >= (int) lengthInSamples)
            break;

        tempBuffer.clear();

        read (&tempBuffer, 0, numThisTime, bufferStart, false, false);

        int64 iRet = -1;

        if (bReverseSearch)
            iRet = tempBuffer.searchForLevel (0, numThisTime * -1, magnitudeRangeMinimum, magnitudeRangeMaximum, minimumConsecutiveSamples);
        else
            iRet = tempBuffer.searchForLevel (0, numThisTime, magnitudeRangeMinimum, magnitudeRangeMaximum, minimumConsecutiveSamples);

        if (iRet != -1)
            return bufferStart + iRet;

        if (numSamplesToSearch > 0)
            numSamplesToSearch -= numThisTime;
        else
            numSamplesToSearch += numThisTime;
    }

    return -1;
}

and in AudioBuffer:

int64 searchForLevel (int64 startSample,
                      int64 numSamplesToSearch,
                      const double magnitudeRangeMinimum,
                      const double magnitudeRangeMaximum,
                      const int minimumConsecutiveSamples)
{
    if (numSamplesToSearch == 0)
        return -1;

    int consecutive = 0;
    int64 firstMatchPos = -1;

    jassert (magnitudeRangeMaximum > magnitudeRangeMinimum);

    int64 bufferStart = startSample;

    while (numSamplesToSearch != 0)
        {
        const int numThisTime = (int) jmin (abs64 (numSamplesToSearch), (int64) size);
    
        if (numSamplesToSearch < 0)
            bufferStart -= numThisTime;
    
        if (bufferStart >= size)
            break;
    
        int num = numThisTime;
    
        while (--num >= 0)
            {
            if (numSamplesToSearch < 0)
                --startSample;
        
            bool matches = false;
            const int index = (int) (startSample - bufferStart);
        
            for (int i = 0; i < numChannels; ++i)
                {
                const Type sample = std::abs (getSample (i, index));
            
                matches = (sample >= magnitudeRangeMinimum && sample <= magnitudeRangeMaximum);
            
                if (matches)
                    break;
                }
        
            if (matches)
                {
                if (firstMatchPos < 0)
                    firstMatchPos = index;
            
                if (++consecutive >= minimumConsecutiveSamples)
                    {
                    if (firstMatchPos < 0 || firstMatchPos >= size)
                        return -1;
                
                    return firstMatchPos;
                    }
                }
            else
                {
                consecutive = 0;
                firstMatchPos = -1;
                }
        
            if (numSamplesToSearch > 0)
                ++startSample;
            }
    
        if (numSamplesToSearch > 0)
            numSamplesToSearch -= numThisTime;
        else
            numSamplesToSearch += numThisTime;
        }

     return -1;
}

I’m sure you could use a Range for the Min/Max too.

Cheers,

Rail


#7

I’m going to bump this for the New Year… since I need to find the first location of the highest Magnitude sample in an AudioBuffer (getMagnitude() returns the sample value, but not the position).

Cheers,

Rail