Creating a Note Slide Effect

Hello,

First of all, I want to admit I’m rather new to audio programming.

I’m working with some people to create an 8-bit VST to make sounds similar to the NES. A common effect is note slide. I’m just having some trouble getting started here… here’s what I need:

  • bpm (120 )
  • the key pressed (C )
  • the target key (in half-steps) (3 half steps => D# )
  • the duration to slide (quarter-note )

…so that when C is pressed on the keyboard, the note slides up to D# for a duration of a quarter-note.

I figured out how to access the bpm of the DAW, and there is midi input implemented. The target note in half-steps and the speed to slide will be options for the user.

Given all that, what’s a good approach for a note slide? Do I do some linear interpolation on the frequency inside of SynthVoice::renderNextBlock? What kind of equation would be useful here to manipulate the frequency-- given bpm, key pressed, target key, and duration?

It depends on what kind of sound you want to reproduce, but being a total noob on DSP / Audio too, I would think that if you would just generate a sin wave (plenty of examples like that are in the audio section of the JUCE demo) for which you change the frequency progressively should do what you want?

For example if you oscillate between 440Hz and then up to 880Hz I would expect an octave range slide increase?

Now for the 8 bit resolution you could probably take each sample convert it to 32-bit integers rounded values then take the MSB byte and clear all others bit to 0, then convert back to float range and it should give you something rough and 8-bits ? Probably not optimum yet but quite intuitive.

Learning Audio programming here too, so I hope I’m not too far from reality!

Look at the AudioSynthetiserDemo.h file, and then go to SineWaveVoice struct then look at the renderNextBlock() code you could play with it to change the freq to start by changing the code that is for now no varying for a given note:

        auto cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
        auto cyclesPerSample = cyclesPerSecond / getSampleRate();
        angleDelta = cyclesPerSample * MathConstants<double>::twoPi;

If now we have (for a given constant midi note) angleDelta increasing in the while() loop below that code instead of being constant it should do the trick? then you need to keep it gradually increase between blocks so keep track of the last value you ended with and starts from it on the next block.

Yes I was thinking of something somewhat similar. But, where will angleDelta be used?

This is the code we have right now in SynthVoice’s render next block:

You can assume that I have the bpm, duration (quarter-note, eighth-note,… I don’t know how to convert that into something useful yet…), and the target note value in Hz.

    void renderNextBlock(juce::AudioBuffer<float>& outputBuffer, int startSample,
        int numSamples)
    {

    // change frequency with target note, duration, and bpm...

    for (int sample = 0; sample < numSamples; ++sample)
    {
		float theWave;
		switch (currentWaveFlag)
		{
		case SynthVoice::Saw:
			theWave = osc.saw(frequency); break;
		case SynthVoice::Noise:
			theWave = osc.noise(); break;
		case SynthVoice::Triangle:
			theWave = osc.triangle(frequency); break;
		case SynthVoice::Pulse25:
			theWave = osc.pulse(frequency, 0.25); break;
		case SynthVoice::Pulse50:
			theWave = osc.pulse(frequency, 0.5); break;
		case SynthVoice::Pulse75:
			theWave = osc.pulse(frequency, 0.75); break;
		default:
			theWave = osc.sinewave(frequency); break;
		}
        double theSound = env.adsr(theWave, env.trigger) * level;

        for (int channel = 0; channel < outputBuffer.getNumChannels(); ++channel)
        {
            outputBuffer.addSample(channel, startSample, theSound); 
        }
        ++startSample;
    }

You don’t seem to be looking at the right file, so again, have a look to:
AudioSynthesiserDemo.h (lines111-145 in the JUCE/examples/Audio subfolder)

AudioSynthetiserDemo.h:

    void renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override
    {
        if (angleDelta != 0.0)
        {
            if (tailOff > 0.0)
            {
                while (--numSamples >= 0)
                {
                    auto currentSample = (float) (std::sin (currentAngle) * level * tailOff);

                    for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
                        outputBuffer.addSample (i, startSample, currentSample);

                    currentAngle += angleDelta;
                    ++startSample;

                    tailOff *= 0.99;

                    if (tailOff <= 0.005)
                    {
                        clearCurrentNote();

                        angleDelta = 0.0;
                        break;
                    }
                }
            }
            else
            {
                while (--numSamples >= 0)
                {
                    auto currentSample = (float) (std::sin (currentAngle) * level);

                    for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
                        outputBuffer.addSample (i, startSample, currentSample);

                    currentAngle += angleDelta;
                    ++startSample;
                }
            }
        }
    }

A bit of a late reply, but I managed to get it working thanks to your help (and through looking up some music math).