| 180 | #endif |
| 181 | |
| 182 | void GranularSynth::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) { |
| 183 | juce::ScopedNoDenormals noDenormals; |
| 184 | auto totalNumInputChannels = getTotalNumInputChannels(); |
| 185 | auto totalNumOutputChannels = getTotalNumOutputChannels(); |
| 186 | const int bufferNumSample = buffer.getNumSamples(); |
| 187 | |
| 188 | // Update mod source values once per block |
| 189 | mParameters.processModSources(); |
| 190 | |
| 191 | mKeyboardState.processNextMidiBuffer(midiMessages, 0, bufferNumSample, true); |
| 192 | for (const auto& messageMeta : midiMessages) { |
| 193 | juce::MidiMessage msg = messageMeta.getMessage(); |
| 194 | if (msg.isController() && msg.getControllerNumber() == 1) { |
| 195 | // Update macro 1 based on mod wheel input |
| 196 | ParamHelper::setParam(mParameters.global.macros[0].macro, msg.getControllerValue() / 127.0f); |
| 197 | } else if (msg.isPitchWheel()) { |
| 198 | // Update local pitch bend value |
| 199 | mCurPitchBendSemitones = ((msg.getPitchWheelValue() / 8192.0f) - 1.0f) * MAX_PITCH_BEND_SEMITONES; |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | // In case we have more outputs than inputs, this code clears any output |
| 204 | // channels that didn't contain input data, (because these aren't |
| 205 | // guaranteed to be empty - they may contain garbage). |
| 206 | // This is here to avoid people getting screaming feedback |
| 207 | // when they first compile a plugin, but obviously you don't need to keep |
| 208 | // this code if your algorithm always overwrites all the output channels. |
| 209 | for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i) { |
| 210 | buffer.clear(i, 0, bufferNumSample); |
| 211 | } |
| 212 | |
| 213 | // Reference tone |
| 214 | mReferenceTone.getNextAudioBlock(juce::AudioSourceChannelInfo(&buffer, 0, buffer.getNumSamples())); |
| 215 | |
| 216 | // Playback from trim selection panel |
| 217 | if (mParameters.ui.playingTrimSelection) { |
| 218 | int numPlaybackSamples = bufferNumSample; |
| 219 | if (mParameters.ui.trimPlaybackSample + bufferNumSample >= mInputBuffer.getNumSamples()) { |
| 220 | // Done playing, reset status |
| 221 | mParameters.ui.playingTrimSelection = false; |
| 222 | } else { |
| 223 | // if output buffer is stereo and the input in mono, duplicate into both channels |
| 224 | // if output buffer is mono and the input in stereo, just play one channel for simplicity |
| 225 | for (int ch = 0; ch < buffer.getNumChannels(); ++ch) { |
| 226 | const int inputChannel = juce::jmin(ch, mInputBuffer.getNumChannels() - 1); |
| 227 | buffer.copyFrom(ch, 0, mInputBuffer, inputChannel, mParameters.ui.trimPlaybackSample, numPlaybackSamples); |
| 228 | } |
| 229 | mParameters.ui.trimPlaybackSample += numPlaybackSamples; |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | // Add contributions from each note |
| 234 | auto bufferChannels = buffer.getArrayOfWritePointers(); |
| 235 | for (int i = 0; i < bufferNumSample; ++i) { |
| 236 | // Don't use a for(auto x : mActiveNotes) loop here as mActiveNotes can be added outside this function. If it is partially added |
| 237 | // it might to use it and the undefined data will cause a crash eventually |
| 238 | const int activeNoteSize = mActiveNotes.size(); |
| 239 | for (int noteIndex = 0; noteIndex < activeNoteSize; noteIndex++) { |
nothing calls this directly
no test coverage detected