Hey, yall! I was sorta recreating the sample arpeggiator (but making it based on sixteenth notes (and prolly diff notes later), etc) but was running into an issue where the first note starts very inconsistently. Sometimes it’s very responsive, other times it’s very clearly delayed. I had found a similar thread where they had fixed this by moving the time’ variable setting further up in the processBlock, but this didn’t seem to work for me.
I’ve tried troubleshooting by sorta simplifying how the ‘time’ variable is set, but it didn’t seem to make a difference.
Any clues as to what might be causing this inconsistency? Thanks!
Here is a code snippet (I APOLOGIZE for all those comments, I’m a beginner and this won’t ever be published, so those comments are just to help me work through the code better ):
void MidiArpeggiatorAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
rate = static_cast<float> (sampleRate); // Get samplerate
notes.clear();
currentNote = 0;
lastNoteValue = -1;
time = 0.0;
sampleNoteDivision = 0.25;
}
void MidiArpeggiatorAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
//jassert(buffer.getNumChannels() == 0);
buffer.clear();
// Gets the bpm (If non provided 120) of the DAW.
double bpm{ 120 };
if (auto bpmFromHost = *getPlayHead()->getPosition()->getBpm())
bpm = bpmFromHost;
auto bps = bpm / 60; // Samples per second
auto samplesPerBeat = rate / bps; // Samples per beat (or quarternote)
auto numSamples = buffer.getNumSamples();
auto noteDuration = static_cast<int> (std::ceil(samplesPerBeat * sampleNoteDivision));
time += numSamples; // Proceed to the next time frame.
// Checks for user midi input, to see if we add/remove notes from the set.
for (const auto meta : midiMessages)
{
const auto currentMessage = meta.getMessage();
auto samplePos = meta.samplePosition;
if (currentMessage.isNoteOn()) notes.add(currentMessage.getNoteNumber());
else if (currentMessage.isNoteOff()) notes.removeValue(currentMessage.getNoteNumber());
}
midiMessages.clear();
// Within this block, we have received all the MIDI data we care about.
// Now, we are going to process the notes in the sortedset we have gathered.
// Working through all the notes in the notes sortedset.
if (time >= noteDuration) // If the note ends within this block.
{
time -= noteDuration;
auto offset = juce::jmax(0, juce::jmin((int)(noteDuration - time), numSamples - 1)); // Represents the number of samples from the start of the note.
if (lastNoteValue > 0) // If there has been a previous note played.
{
midiMessages.addEvent(juce::MidiMessage::noteOff(1, lastNoteValue), offset); // Stop playing that note.
lastNoteValue = -1;
}
if (notes.size() > 0) // If there's still notes to be played...
{
currentNote = (currentNote + 1) % notes.size(); // Goes to the next note, and loops over to the start once we get to end of sequence.
lastNoteValue = notes[currentNote]; // Gets the note...
midiMessages.addEvent(juce::MidiMessage::noteOn(1, lastNoteValue, (juce::uint8)127), offset); // Plays the note.
}
}
}