Player fadein fadeout

Hello everyone. I made a very convenient audio player for myself, but I can’t find an example of fadein, fadeout, or crossfade anywhere. This is my second year of programming and my brain is racing. I will be glad for any help.
Thank you in advance.

imagine a signal that just ramps up from 0 to 1, or down from 1 to 0. if you multiplied such a signal with a sound that would be fades

Yes, I understand that this is an automatic volume knob. But I can’t figure out how to implement this, through a timer callback, or some other way?
I implemented it like this:
void MainComponent::fadeOut()
{
startTimer (100);
}
void MainComponent::timerCallback()
{
//DBG (String (transportSource.getLengthInSeconds() - transportSource.getCurrentPosition()));
//if (transportSource.getCurrentPosition() > transportSource.getLengthInSeconds() - 252.0)
{
playerVolume = playerVolume - 0.01;
DBG (String(playerVolume));
if (playerVolume <= 0.01)
{
stopButtonClicked();
stopTimer();
playerVolume = 1.0;
}
}
}
But I have great doubts whether this is right.

you don’t need a timer. working with time in processBlock directly is as easy as counting samples. remember 1sec is sampleRate.

so if you had a variable called phase starting at 0.f

you could have one called inc = 1.f / sampleRate;

then for each sample in the block (in mono):
phase += inc;

and it takes exactly 1sec to reach the value 1

Thanks for the tip, but unfortunately I have never encountered a processBlock, so the question is still open for me.
By the way, if you use Thumbnail graphics to fade, you also need to use a processBlock, or in this case it is better to use a timerCallback?

You cannot use a Timer for this. ‘audio time’, which is measured in the number of samples, is not related to other time.

If you do not have a processBlock, where are you getting the audio data from the file and sending it to the audio device? This is where you would put your fade code. This is where you put all of your audio processing code. You will measure time by counting samples, as @Mrugalla said. You know the sample rate, such as 44100, which means there will be 44100 samples coming through for each second.

The reason I am not giving you the direct code you need, is because it is better if you come to understand this and do it yourself.

So, first write code that tracks time by the number of samples. Verify you understand that. Then use the time to control time related things, such as fade in/out.

Thank you. I’ll try.

Is your goal to avoid clicks when starting or stopping your player? Because in this case you are sorted, the AudioTransportSource you are using already implements it, and it is done in that way that I was just about to suggest:

But in general you need a stateful class that keeps track of how much samples you already faded in or out, because a nice fade out will span over multiple blocks, so you cannot use the approach you find in the sources I linked above.

Ideally you write your own helper class, either on eyou call inside your getNextAudioBlock or you create your own PositionableAudioSource you can plug in the signal flow.
It is a fun exercise, let us know if you get stuck.

Hi Daniel. After almost 5 months, I finally decided to exercise. Unfortunately, I really lack knowledge, because… I played the saxophone all my life, and in my old age I took up programming. I know I wrote the code completely wrong, but, strangely enough, it works, and without clicks!
Code:

void GUI::AudioPlayer::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
{
    transportSource.prepareToPlay (samplesPerBlockExpected, sampleRate);
    fadeInc = 1.0 / sampleRate;
}
void GUI::AudioPlayer::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill)
{
    if (readerSource.get() == nullptr)
        {
            bufferToFill.clearActiveBufferRegion();
            return;
        }
        
    transportSource.getNextAudioBlock(bufferToFill);
    
    double volume = sliderPanel.volumeSlider.getValue()
    if (fadeInOut > 0.0 && fadeInt == 1)    //STOP
    {
        fadeInOut -= fadeInc * 200.0;
        DBG ("AAA = " + String (fadeInOut));
        //volume = fadeInOut;
    }
    else if (fadeInOut > 0.6 && fadeInt == 2)
    {
        fadeInOut -= fadeInc * 200.0;
        DBG ("AAA = " + String (fadeInOut));
    }
    else if (fadeInOut < 1.0 && fadeInt == 3)
    {
        fadeInOut += fadeInc * 200.0;
        DBG ("AAA = " + String (fadeInOut));
    }
    else
    {
        fadeInt = 0;
        fadeInOut = sliderPanel.volumeSlider.getValue();
    }
    for (int channel = 0; channel < bufferToFill.buffer->getNumChannels(); channel++) 
    {		auto buffer = bufferToFill.buffer->getWritePointer(channel);

		for (int sample = 0; sample < bufferToFill.buffer->getNumSamples(); sample++) 
		{
			buffer[sample] *= volume + doubleVolume;
			setPeakLevel(channel, buffer[sample]);
		}
	}
}

This looks good.

You may want to use SmoothedValue too for these cases. JUCE: SmoothedValue< FloatType, SmoothingType > Class Template Reference