
대부분의 아날로그 악기들은 위의 Envelope 커브를 따르는 구조로 되어있고, 신디사이저는 아날로그 악기의 사운드 특성을 구현하기 위해서 ASDR이라는 커브를 활용하여 음의 걍약을 조절함
Attack
Attack은 사운드가 발생한시점에서 얼마나 빨리 피크값에 도달 하는지 결정하는 파라미터임. 어택이 짧은 악기의 예시는 피아노라고 생각하면됨. 피아노 건반을 누르면 해머가 바로 선을 치게되고 바로 피크값으로 도달하기 때문에 매우 짧은시간에 피크값에 도달 하게 됨
하지만 반대로 관악기의 경우에는 사용자의 의지에 따라서 완만하게 피크 까지 도달하게 되기때문에 어택이 길다고 할 수 있음.
Decay
peak에 도달한 사운드가 얼마나 빨리 sustain 레벨로 도달하는지 결정하는 파라미터임. 사운드가 감소되는 시간에 따라서 사운드의 성격을 결정 지을수 있는데, 짧게할 경우에는 타악기와 같은 뉘앙스고 길게하면 현악기와 같이 부드러운 연결감을 낼 수 있음.
Sustain
Sustain은 Decay이후 음을 종료할때까지 어느정도의 음의 크기로 유지하고있을지 설정하는 값임, sustain레벨이 작을 경우에는 타악기 처럼 바로 사라지는 특성이 있고, 반대로 높을경우에는 음이 계속이어지는 것과 같은 특성이 있음
Release
Relaase는 음이 종료되고 얼마나 빨리 감쇄될지 결정되는 값임, 해당값이 길수록 잔음감을 낼 수 있지만, 너무 길어버리면 사운드가 지저분 할 수 있음.
Juce에서 구현 할 경우에는 Oscilator와 Filter와 다르게 Midi신호도 같이 입력을 받아서 처리를 해야된다.
대략적인 순서는 아래와같이 구현됨
//1. Envelope 선언
juce::ADSR Envelope;
/*2. Envelope 제어를 위한 flag 선언
** noteOnMode는 note가 눌려진 상태 여부를 체크 하는 flag
** noteOffRelase는 note가 떼어진 상태를 점검하여 Release를 진행여부를 점검 하믄 flag
** releaseThreshold는 Release를 off시키기 위한 임계값 지정
*/
bool noteOnMode = false;
bool noteOffRelease = true;
const float releaseThreshold = 0.000001f
//3. Envelope Sampling rate 설정
void envelope::setSamplerate(double samplerate){
//set default Sampling Rate
sampleRate = samplerate;
Envelope.setSampleRate(sampleRate);
}
//4. Envelope 값 설정
void envelope::setParameters(juce::ADSR::Parameters input_para, envCurve input_curv){
Env_params.attack = input_para.attack; // 0.1초
Env_params.decay = input_para.decay; // 0.3초
Env_params.sustain = input_para.sustain; // 50% 레벨
Env_params.release = input_para.decay; // 1초
Envelope.setParameters(Env_params);
custom_curv = input_curv;
}
//5. Envelope 신호처리
void envelope::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midi) {
auto midiIterator = midi.cbegin();
auto midiEnd = midi.cend();
for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
{
// 매 샘플마다 엔벨로프 값 계산
while (midiIterator != midiEnd &&
(*midiIterator).samplePosition == sample)
{
auto message = (*midiIterator).getMessage();
if (message.isNoteOn())
{
Envelope.noteOn();
noteOnMode = true;
noteOffRelease = false;
}
else if (message.isNoteOff())
{
Envelope.noteOff();
noteOffRelease = true;
}
++midiIterator; // 다음 MIDI 이벤트로 이동
}
float envelopeValue = Envelope.getNextSample();
if (noteOffRelease && (envelopeValue < releaseThreshold)) {
envelopeValue = 0.0f; // release 상태일 때는 0으로 처리
noteOnMode = false; // noteOn 상태가 아니므로 noteOnMode를 false로 설정
Envelope.reset();
}
// 오디오에 적용
for (int channel = 0; channel < buffer.getNumChannels(); ++channel) {
float currentSample = buffer.getSample(channel, sample);
if (std::abs(currentSample) < releaseThreshold) {
currentSample = 0.0f; // releaseThreshold 이하의 값은 0으로 처리
noteOffRelease = false; // release 상태가 아니므로 noteOffRelease를 false로 설정
}
buffer.setSample(channel, sample, currentSample * envelopeValue);
}
}
}