findMinMax may have a bug?

I wrote this code to calculate the magnitude of samples in buffer.

Range<float> range;
for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
    for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
    {
        range = buffer.findMinMax(channel, 0, buffer.getNumSamples());
    }
    float magnitude = range.getLength() / 2.f;
}

I wrote that cuz I didn’t know there’s a function “getMagnitude”

sampleMaxValue = buffer.getMagnitude (0, buffer.getNumSamples());

So I believe this works the same as the first one and it is easier to use. But I found the first method “findMinMax” sometimes returns the magnitude = 0 even the buffer has non-zero values. I haven’t tested if it is a findMinMax() bug or getLength() bug, or maybe I did something wrong with my code.

I use MacOS Catalina 10.15.5 and Xcode 11.6. I tested this in Fl Studio.

Whilst it’s certainly possible that findMinMax has a bug, the internals of it haven’t changed for a very long time and we haven’t had any other big reports.

Would it be possible to get the contents of a buffer where it’s not doing what you expect it to?

Could it be that the isClear flag is set for those buffers? From another thread, doing something like the following (getting the write pointer before calling clear()), causes the isClear flag to remain set, even after changing data in the buffer:

auto buf = buffer.getWritePointer (0);
buffer.clear();
buf[0] = 1.f;

When that flag is set, findMinMax will return the range 0…0.

I took some screenshots yesterday.
The input was a continuous sin wave.
This was the code I wrote originally


And these were buffer values I printed when the magnitude returned 0.

And these were magnitude (sampleMaxValue) I printed for each buffer. They were normal (around 0.23) most time, but sometimes (maybe 1%) returned 0.

By the way, after I changed to getMagnitude instead of findMinMax, it always returned non-zero values, which was correct.

Would it be possible to narrow it down a little further? When sampleMaxType is zero could you print the number of channels in the butter, the number of samples in each channel, and the value of all the samples in each channel?

Ideally we would have a case where we could feed a single, prefilled, buffer to findMinMax and see it produce a zero.

Also you can paste code and output snippets into this forum if you surround the snippets with three backticks and it’ll be formatted nicely, so you don’t need to screenshot. This makes it searchable if anyone else if having the same problem.

```
Paste your code here
```

The isClear flag is no concern here, since it is checked in findMinMax and 0 is returned if it is true.

The difference is, because two definitions of magnitude are mixed here.
In JUCE magnitude is Us but the OP uses Uss/2 (Peak vs. Peak to peak halved)

Huh? I was responding to this:

But I found the first method “findMinMax” sometimes returns the magnitude = 0 even the buffer has non-zero values.

The code referenced (his first example, using findMinMax) gets the range from findMinMax, which would be 0…0 if isClear is set. This would cause his call to get the magnitude to return 0. How can that not be relevant? It’s exactly what he said was happening!

Apologies, somehow I read the problem the other way round.

1 Like

Nevertheless the more likely explanation that I pointed out is, that in the OP’s formula a buffer with a DC signal would yield 0, while the juce implementation will yield the value of the DC offset. Especially for buffers of the length for 1 sample, this will always return 0.

Reason for that is a wrong definition of magnitude. (I wonder which one is the correct?)

As Daniel has pointed out the definition of “magnitude” as determined by the algorithm in the original post is not the same as the one returned by getMagnitude from the JUCE AudioBuffer. JUCE returns the “highest absolute sample value”. The original post returns half of the range between the highest and lowest sample values.

Take the last two values printed in the example buffer values after start=====. They are both -0.197144, the range of these is 0, divided by 2 is still 0, whereas getMagnitude will return 0.197144. Maybe this is the result of buffers with very few values? (DAWs sometimes do this to improve automation accuracy) although @HowardAntares makes a good point too however it’s not clear from the examples why this would only happen occasionally.

As a side note the original post calls buffer.findMinMax for the entire buffer, for every sample? the sample variable is actually unused in the example (unless I’m missing something?).

No, the loop is over the number of channels, not over every sample. We don’t see the rest of the code for actually processing the samples in the post.

But yeah, if the buffer is short enough, that code will just grab a few adjacent values, whose range is not likely to cover the whole range of the audio over a larger time span, and could indeed lead to 0 as the range.

This is the code from the original post, it loops over the channels then inside that it loops over the samples but never does anything with the samples but instead calls findMinMax on the entire buffer (for each sample). It doesn’t really change anything in terms of outcome it just means the calculation is being performed for every sample when it only needs to be performed once.

Oh, I guess I didn’t see that code.

Hi, thanks for all your replies! Sorry, I was traveling for a week so I didn’t have time to test. This is the code I tested:

for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
    auto* channelData = buffer.getWritePointer(channel);
    for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
    {
        DBG(channelData[sample]);
    }
    Range<float> range = buffer.findMinMax(channel, 0, buffer.getNumSamples());
    float min = range.getStart();
    float max = range.getEnd();
    if (min!=0) {
        DBG("===min max====");
        DBG(min);
        DBG(max);
        DBG("===loop start====");
    }   
}

This is the output: (This is only a section of the output data that has problem)

...
===loop start====
-0.622248
-0.60635
-0.58998
-0.573027
-0.555385
-0.537037
-0.517889
-0.498088
-0.477648
-0.456605
-0.435
-0.412778
-0.390139
-0.367115
-0.343757
-0.320116
-0.296135
-0.272042
-0.247869
-0.223669
-0.199496
-0.175286
-0.151284
-0.127517
-0.104031
-0.080877
-0.0579862
-0.0356034
-0.0137434
0.00755922
0.0282652
0.048454
0.0678927
0.0865863
0.104522
0.121681
0.138155
0.153737
0.168459
0.182336
0.195375
0.20768
0.219081
0.229636
0.239387
0.248364
0.256681
0.264201
0.271
0.277141
0.282666
0.28768
0.292081
0.295946
0.299339
0.302306
0.30493
0.307135
0.308985
0.310536
0.311824
0.312904
0.313723
0.314327
0.314754
0.315026
0.315168
0.315153
0.315005
0.31474
0.314366
0.313879
0.313282
0.312573
0.311748
0.310802
0.309702
0.308483
0.307118
0.305584
0.303863
0.301897
0.29975
0.297374
0.29473
0.29179
0.288474
0.28487
0.280919
0.276568
0.271786
0.266473
0.260744
0.254532
0.247783
0.240468
0.232474
0.223943
0.214811
0.205031
0.194585
0.183355
0.171513
0.159003
0.145798
0.131892
0.117176
0.101843
0.0858595
0.0692168
0.0519275
0.033892
0.0153262
-0.00378603
-0.023431
-0.0435804
-0.0641991
-0.0853688
-0.10686
-0.128668
-0.150758
-0.17309
-0.195732
-0.218456
-0.241246
-0.264057
-0.286843
-0.30966
-0.332291
-0.354712
-0.376877
-0.398737
-0.420336
-0.441481
-0.462143
-0.482278
-0.501843
-0.520868
-0.539191
-0.556785
-0.573613
-0.589636
-0.604874
-0.619203
-0.632601
-0.645039
-0.656488
-0.666955
-0.676361
-0.684689
-0.691922
-0.698042
-0.703045
-0.706897
-0.709592
-0.711121
-0.711482
-0.710657
-0.708662
-0.705499
-0.701173
-0.695692
-0.68903
-0.68125
-0.672362
-0.662384
-0.651338
-0.639188
-0.626041
-0.611918
-0.596847
-0.580862
-0.563919
-0.546167
-0.527634
-0.50836
-0.488385
-0.467662
-0.446377
-0.42456
-0.402261
-0.379527
-0.356306
-0.332812
-0.309079
-0.285159
-0.261106
-0.236859
-0.212656
-0.188527
-0.164522
-0.140695
-0.11698
-0.0936228
-0.070645
-0.0480887
-0.026
-0.004305
0.0167556
0.0371757
0.0569307
0.0759897
0.0944385
0.112056
0.128858
0.144846
0.160014
0.174459
0.187992
0.200657
0.212484
0.223491
===min max====
-0.711482
0.315168
===loop start====
0.233558
0.242771
0.251462
0.25945
0.266698
0.27332
0.27922
0.284487
0.28919
0.293373
0.297129
0.300368
0.303163
0.305574
0.307643
0.30944
===min max====
0.233558
0.30944
===loop start====
0.233697
0.243115
0.251769
0.259687
0.266897
0.27351
0.279411
0.284678
0.289378
0.293554
0.297303
0.300535
0.303323
0.305728
0.30779
0.309581
===min max====
0.233697
0.309581
===loop start====
0.311329
0.312493
0.313265
0.313882
0.31443
0.314824
0.315048
0.315113
0.31504
0.31485
0.314536
0.31411
0.313576
0.312932
0.312162
0.311282
0.310281
0.309145
0.30786
0.306384
0.304763
0.302962
0.300949
0.298699
0.296146
0.293363
0.290297
0.286902
0.283146
0.27894
0.274386
0.269418
0.263983
0.258049
0.251509
0.244492
0.236929
0.228772
0.219992
0.210475
0.200378
0.189639
0.178221
0.166109
0.153191
0.139648
0.125436
0.110535
0.0949499
0.0785737
0.0616132
0.0440419
0.0258632
0.00709764
-0.0123481
-0.0322512
-0.0526184
-0.0734276
-0.0946438
-0.116346
-0.138305
-0.160512
-0.18293
-0.205514
-0.228331
-0.251155
-0.273966
-0.296719
-0.319367
-0.341962
-0.364292
-0.386333
-0.408038
-0.42936
-0.450249
-0.470739
-0.490652
-0.509959
-0.528619
-0.546589
-0.563896
-0.580394
-0.596059
-0.610856
-0.624754
-0.637764
-0.649784
-0.660795
-0.670772
-0.679693
-0.687559
-0.694311
-0.69994
-0.704431
-0.707775
-0.70996
-0.710976
-0.710822
-0.709495
-0.706998
-0.70331
-0.698468
-0.69248
-0.685356
-0.677111
-0.667715
-0.657252
-0.645737
-0.633194
-0.61965
-0.605065
-0.589569
-0.573183
-0.555944
-0.537888
-0.51897
-0.499357
-0.479079
-0.458179
-0.436705
-0.414603
-0.392076
-0.369156
-0.345894
-0.322342
-0.298443
-0.274423
-0.250316
-0.226173
-0.202048
-0.17788
-0.153911
-0.130166
-0.106695
-0.0835477
-0.0606546
-0.0382608
-0.0163813
0.00494916
0.0256909
0.0459231
0.0654127
0.0841643
0.102165
0.119394
0.135943
0.151607
0.166414
0.180379
0.193509
0.205908
0.217404
0.228054
0.2379
0.246972
0.255382
0.262994
0.269882
0.276109
0.281716
0.28681
0.291286
0.295222
0.298682
0.301711
0.304394
0.306653
0.308552
0.31015
0.311479
0.312597
0.313451
0.314086
0.314542
0.314839
0.315004
0.31501
0.314881
0.314633
0.314275
0.313803
0.31322
0.312524
0.311713
0.31078
0.309695
0.30849
0.30714
0.305623
0.30392
0.301975
0.29985
0.297499
0.294882
0.291973
0.28869
===min max====
-0.710976
0.315113
...

So from that output I think findMinMax returns the correct output. However, there are two kinds of output from that result. -0.710976 and 0.315113 are the correct values for min and max. But in another situation, the loop only has around 20 values for both left and right channels, not 512. And it returns 0.233697 and 0.309581. I don’t know why @t0m

Yes, exactly! I posted the test result just now and found that sometimes the buffer doesn’t have 512 samples, only around 20 samples. Does every DAW do this? I tested this in fl studio, what about Ableton Live?
@anthony-nicholls

Yeah, I realized that should only be performed once. Thanks for pointing out that mistake cuz I am a newbie lol.

That’s to be expected. DAWs are free to pass fewer samples than the maximum that you specified. They can even pass 0 samples (for example, to simply send parameter changes but not audio);

Off the top of my head I don’t know what DAWs do and don’t do this, however you’ll have to deal with it either way.

If you want to make sure you always have at least n samples then you’ll need to make a buffer. Set the size of the buffer in prepareToPlay() and create an int to keep track of the number of samples you have added to the buffer, once you have filled the buffer perform the calculation on the buffer and set the number of processed samples back to 0 and repeat.