일단 간단하게라도 피아노 VSTI를 만들어볼려고 한다.
위 파형을 통해 소리가 나는 피아노 가상악기를 제젝할 것이다.
그리고 제작했다.
핵심코드는 다음과 같다.
class MainComponent : public juce::AudioAppComponent
{
public:
//==============================================================================
MainComponent()
{
// Make sure you set the size of the component after
// you add any child components.
setSize (800, 600);
// Some platforms require permissions to open input channels so request that here
if (juce::RuntimePermissions::isRequired (juce::RuntimePermissions::recordAudio)
&& ! juce::RuntimePermissions::isGranted (juce::RuntimePermissions::recordAudio))
{
juce::RuntimePermissions::request (juce::RuntimePermissions::recordAudio,
[&] (bool granted) { setAudioChannels (granted ? 2 : 0, 2); });
}
else
{
// Specify the number of input and output channels that we want to open
setAudioChannels (2, 2);
}
}
struct SineWaveSound : public juce::SynthesiserSound
{
SineWaveSound() {}
bool appliesToNote(int) override {return true;}
bool appliesToChannel(int) override {return true;}
};
struct SineWaveVoice : public juce::SynthesiserVoice
{
SineWaveVoice() {}
bool canPlaySound (juce::SynthesiserSound* sound) override
{
return dynamic_cast<SineWaveSound*> (sound) != nullptr;
}
void startNote (int midiNoteNumber, float velocity,
juce::SynthesiserSound*, int /*currentPitchWheelPosition*/) override
{
currentAngle = 0.0;
level = velocity * 0.15;
tailOff = 0.0;
auto cyclesPerSecond = juce::MidiMessage::getMidiNoteInHertz (midiNoteNumber);
auto cyclesPerSample = cyclesPerSecond / getSampleRate();
angleDelta = cyclesPerSample * 2.0 * juce::MathConstants<double>::pi;
}
void stopNote (float /*velocity*/, bool allowTailOff) override
{
if (allowTailOff)
{
if (tailOff == 0.0)
tailOff = 1.0;
}
else
{
clearCurrentNote();
angleDelta = 0.0;
}
}
void pitchWheelMoved (int) override {}
void controllerMoved (int, int) override {}
void renderNextBlock (juce::AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override
{
if (angleDelta != 0.0)
{
if (tailOff > 0.0) // [7]
{
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; // [8]
if (tailOff <= 0.005)
{
clearCurrentNote(); // [9]
angleDelta = 0.0;
break;
}
}
}
else
{
while (--numSamples >= 0) // [6]
{
auto currentSample = (float) (std::sin (currentAngle) * level);
for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, currentSample);
currentAngle += angleDelta;
++startSample;
}
}
}
}
private:
double currentAngle = 0.0, angleDelta = 0.0, level = 0.0, tailOff = 0.0;
};