| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE examples.
 -    Copyright (c) 2022 - Raw Material Software Limited
 - 
 -    The code included in this file is provided under the terms of the ISC license
 -    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 -    To use, copy, modify, and/or distribute this software for any purpose with or
 -    without fee is hereby granted provided that the above copyright notice and
 -    this permission notice appear in all copies.
 - 
 -    THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 -    WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 -    PURPOSE, ARE DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - /*******************************************************************************
 -  The block below describes the properties of this PIP. A PIP is a short snippet
 -  of code that can be read by the Projucer and used to generate a JUCE project.
 - 
 -  BEGIN_JUCE_PIP_METADATA
 - 
 -  name:             PluckedStringsDemo
 -  version:          1.0.0
 -  vendor:           JUCE
 -  website:          http://juce.com
 -  description:      Simulation of a plucked string sound.
 - 
 -  dependencies:     juce_audio_basics, juce_audio_devices, juce_audio_formats,
 -                    juce_audio_processors, juce_audio_utils, juce_core,
 -                    juce_data_structures, juce_events, juce_graphics,
 -                    juce_gui_basics, juce_gui_extra
 -  exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 - 
 -  moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 - 
 -  type:             Component
 -  mainClass:        PluckedStringsDemo
 - 
 -  useLocalCopy:     1
 - 
 -  END_JUCE_PIP_METADATA
 - 
 - *******************************************************************************/
 - 
 - #pragma once
 - 
 - 
 - //==============================================================================
 - /**
 -     A very basic generator of a simulated plucked string sound, implementing
 -     the Karplus-Strong algorithm.
 - 
 -     Not performance-optimised!
 - */
 - class StringSynthesiser
 - {
 - public:
 -     //==============================================================================
 -     /** Constructor.
 - 
 -         @param sampleRate      The audio sample rate to use.
 -         @param frequencyInHz   The fundamental frequency of the simulated string in
 -                                Hertz.
 -     */
 -     StringSynthesiser (double sampleRate, double frequencyInHz)
 -     {
 -         doPluckForNextBuffer.set (false);
 -         prepareSynthesiserState (sampleRate, frequencyInHz);
 -     }
 - 
 -     //==============================================================================
 -     /** Excite the simulated string by plucking it at a given position.
 - 
 -         @param pluckPosition The position of the plucking, relative to the length
 -                              of the string. Must be between 0 and 1.
 -     */
 -     void stringPlucked (float pluckPosition)
 -     {
 -         jassert (pluckPosition >= 0.0 && pluckPosition <= 1.0);
 - 
 -         // we choose a very simple approach to communicate with the audio thread:
 -         // simply tell the synth to perform the plucking excitation at the beginning
 -         // of the next buffer (= when generateAndAddData is called the next time).
 - 
 -         if (doPluckForNextBuffer.compareAndSetBool (1, 0))
 -         {
 -             // plucking in the middle gives the largest amplitude;
 -             // plucking at the very ends will do nothing.
 -             amplitude = std::sin (MathConstants<float>::pi * pluckPosition);
 -         }
 -     }
 - 
 -     //==============================================================================
 -     /** Generate next chunk of mono audio output and add it into a buffer.
 - 
 -         @param outBuffer  Buffer to fill (one channel only). New sound will be
 -                           added to existing content of the buffer (instead of
 -                           replacing it).
 -         @param numSamples Number of samples to generate (make sure that outBuffer
 -                           has enough space).
 -     */
 -     void generateAndAddData (float* outBuffer, int numSamples)
 -     {
 -         if (doPluckForNextBuffer.compareAndSetBool (0, 1))
 -             exciteInternalBuffer();
 - 
 -         // cycle through the delay line and apply a simple averaging filter
 -         for (auto i = 0; i < numSamples; ++i)
 -         {
 -             auto nextPos = (pos + 1) % delayLine.size();
 - 
 -             delayLine[nextPos] = (float) (decay * 0.5 * (delayLine[nextPos] + delayLine[pos]));
 -             outBuffer[i] += delayLine[pos];
 - 
 -             pos = nextPos;
 -         }
 -     }
 - 
 - private:
 -     //==============================================================================
 -     void prepareSynthesiserState (double sampleRate, double frequencyInHz)
 -     {
 -         auto delayLineLength = (size_t) roundToInt (sampleRate / frequencyInHz);
 - 
 -         // we need a minimum delay line length to get a reasonable synthesis.
 -         // if you hit this assert, increase sample rate or decrease frequency!
 -         jassert (delayLineLength > 50);
 - 
 -         delayLine.resize (delayLineLength);
 -         std::fill (delayLine.begin(), delayLine.end(), 0.0f);
 - 
 -         excitationSample.resize (delayLineLength);
 - 
 -         // as the excitation sample we use random noise between -1 and 1
 -         // (as a simple approximation to a plucking excitation)
 - 
 -         std::generate (excitationSample.begin(),
 -                        excitationSample.end(),
 -                        [] { return (Random::getSystemRandom().nextFloat() * 2.0f) - 1.0f; } );
 -     }
 - 
 -     void exciteInternalBuffer()
 -     {
 -         // fill the buffer with the precomputed excitation sound (scaled with amplitude)
 - 
 -         jassert (delayLine.size() >= excitationSample.size());
 - 
 -         std::transform (excitationSample.begin(),
 -                         excitationSample.end(),
 -                         delayLine.begin(),
 -                         [this] (double sample) { return static_cast<float> (amplitude * sample); } );
 -     }
 - 
 -     //==============================================================================
 -     const double decay = 0.998;
 -     double amplitude = 0.0;
 - 
 -     Atomic<int> doPluckForNextBuffer;
 - 
 -     std::vector<float> excitationSample, delayLine;
 -     size_t pos = 0;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StringSynthesiser)
 - };
 - 
 - //==============================================================================
 - /*
 -     This component represents a horizontal vibrating musical string of fixed height
 -     and variable length. The string can be excited by calling stringPlucked().
 - */
 - class StringComponent   : public Component,
 -                           private Timer
 - {
 - public:
 -     StringComponent (int lengthInPixels, Colour stringColour)
 -         : length (lengthInPixels), colour (stringColour)
 -     {
 -         // ignore mouse-clicks so that our parent can get them instead.
 -         setInterceptsMouseClicks (false, false);
 -         setSize (length, height);
 -         startTimerHz (60);
 -     }
 - 
 -     //==============================================================================
 -     void stringPlucked (float pluckPositionRelative)
 -     {
 -         amplitude = maxAmplitude * std::sin (pluckPositionRelative * MathConstants<float>::pi);
 -         phase = MathConstants<float>::pi;
 -     }
 - 
 -     //==============================================================================
 -     void paint (Graphics& g) override
 -     {
 -         g.setColour (colour);
 -         g.strokePath (generateStringPath(), PathStrokeType (2.0f));
 -     }
 - 
 -     Path generateStringPath() const
 -     {
 -         auto y = (float) height / 2.0f;
 - 
 -         Path stringPath;
 -         stringPath.startNewSubPath (0, y);
 -         stringPath.quadraticTo ((float) length / 2.0f, y + (std::sin (phase) * amplitude), (float) length, y);
 -         return stringPath;
 -     }
 - 
 -     //==============================================================================
 -     void timerCallback() override
 -     {
 -         updateAmplitude();
 -         updatePhase();
 -         repaint();
 -     }
 - 
 -     void updateAmplitude()
 -     {
 -         // this determines the decay of the visible string vibration.
 -         amplitude *= 0.99f;
 -     }
 - 
 -     void updatePhase()
 -     {
 -         // this determines the visible vibration frequency.
 -         // just an arbitrary number chosen to look OK:
 -         auto phaseStep = 400.0f / (float) length;
 - 
 -         phase += phaseStep;
 - 
 -         if (phase >= MathConstants<float>::twoPi)
 -             phase -= MathConstants<float>::twoPi;
 -     }
 - 
 - private:
 -     //==============================================================================
 -     int length;
 -     Colour colour;
 - 
 -     int height = 20;
 -     float amplitude = 0.0f;
 -     const float maxAmplitude = 12.0f;
 -     float phase = 0.0f;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StringComponent)
 - };
 - 
 - //==============================================================================
 - class PluckedStringsDemo   : public AudioAppComponent
 - {
 - public:
 -     PluckedStringsDemo()
 -        #ifdef JUCE_DEMO_RUNNER
 -         : AudioAppComponent (getSharedAudioDeviceManager (0, 2))
 -        #endif
 -     {
 -         createStringComponents();
 -         setSize (800, 560);
 - 
 -         // specify the number of input and output channels that we want to open
 -         auto audioDevice = deviceManager.getCurrentAudioDevice();
 -         auto numInputChannels  = (audioDevice != nullptr ? audioDevice->getActiveInputChannels() .countNumberOfSetBits() : 0);
 -         auto numOutputChannels = jmax (audioDevice != nullptr ? audioDevice->getActiveOutputChannels().countNumberOfSetBits() : 2, 2);
 - 
 -         setAudioChannels (numInputChannels, numOutputChannels);
 -     }
 - 
 -     ~PluckedStringsDemo() override
 -     {
 -         shutdownAudio();
 -     }
 - 
 -     //==============================================================================
 -     void prepareToPlay (int /*samplesPerBlockExpected*/, double sampleRate) override
 -     {
 -         generateStringSynths (sampleRate);
 -     }
 - 
 -     void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
 -     {
 -         bufferToFill.clearActiveBufferRegion();
 - 
 -         for (auto channel = 0; channel < bufferToFill.buffer->getNumChannels(); ++channel)
 -         {
 -             auto* channelData = bufferToFill.buffer->getWritePointer (channel, bufferToFill.startSample);
 - 
 -             if (channel == 0)
 -             {
 -                 for (auto synth : stringSynths)
 -                     synth->generateAndAddData (channelData, bufferToFill.numSamples);
 -             }
 -             else
 -             {
 -                 memcpy (channelData,
 -                         bufferToFill.buffer->getReadPointer (0),
 -                         ((size_t) bufferToFill.numSamples) * sizeof (float));
 -             }
 -         }
 -     }
 - 
 -     void releaseResources() override
 -     {
 -         stringSynths.clear();
 -     }
 - 
 -     //==============================================================================
 -     void paint (Graphics&) override {}
 - 
 -     void resized() override
 -     {
 -         auto xPos = 20;
 -         auto yPos = 20;
 -         auto yDistance = 50;
 - 
 -         for (auto stringLine : stringLines)
 -         {
 -             stringLine->setTopLeftPosition (xPos, yPos);
 -             yPos += yDistance;
 -             addAndMakeVisible (stringLine);
 -         }
 -     }
 - 
 - private:
 -     void mouseDown (const MouseEvent& e) override
 -     {
 -         mouseDrag (e);
 -     }
 - 
 -     void mouseDrag (const MouseEvent& e) override
 -     {
 -         for (auto i = 0; i < stringLines.size(); ++i)
 -         {
 -             auto* stringLine = stringLines.getUnchecked (i);
 - 
 -             if (stringLine->getBounds().contains (e.getPosition()))
 -             {
 -                 auto position = (e.position.x - (float) stringLine->getX()) / (float) stringLine->getWidth();
 - 
 -                 stringLine->stringPlucked (position);
 -                 stringSynths.getUnchecked (i)->stringPlucked (position);
 -             }
 -         }
 -     }
 - 
 -     //==============================================================================
 -     struct StringParameters
 -     {
 -         StringParameters (int midiNote)
 -             : frequencyInHz (MidiMessage::getMidiNoteInHertz (midiNote)),
 -               lengthInPixels ((int) (760 / (frequencyInHz / MidiMessage::getMidiNoteInHertz (42))))
 -         {}
 - 
 -         double frequencyInHz;
 -         int lengthInPixels;
 -     };
 - 
 -     static Array<StringParameters> getDefaultStringParameters()
 -     {
 -         return Array<StringParameters> (42, 44, 46, 49, 51, 54, 56, 58, 61, 63, 66, 68, 70);
 -     }
 - 
 -     void createStringComponents()
 -     {
 -         for (auto stringParams : getDefaultStringParameters())
 -         {
 -             stringLines.add (new StringComponent (stringParams.lengthInPixels,
 -                                                   Colour::fromHSV (Random().nextFloat(), 0.6f, 0.9f, 1.0f)));
 -         }
 -     }
 - 
 -     void generateStringSynths (double sampleRate)
 -     {
 -         stringSynths.clear();
 - 
 -         for (auto stringParams : getDefaultStringParameters())
 -         {
 -             stringSynths.add (new StringSynthesiser (sampleRate, stringParams.frequencyInHz));
 -         }
 -     }
 - 
 -     //==============================================================================
 -     OwnedArray<StringComponent> stringLines;
 -     OwnedArray<StringSynthesiser> stringSynths;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluckedStringsDemo)
 - };
 
 
  |