음악, 삶, 개발
DAW 의 PPQ 그리기 (Transport) 본문
아래 포스팅한 오디오 thread 와 메세지 thread 에 대한 글들을 숙지하였다면,
이제는 먼가를 그려볼 시간이다.
PPQ 의 값을 사용하여야한다.
< juce::AudioPlayHead::CurrentPositionInfo 클래스 >
ppq 값을 가지고있는 클래스로써,
AudioProcessor 클래스의 함수 getPlayHead() 함수를 이용하여,
오디오 thread, 즉 processBlock() 안에서 가져와야한다.
아래는 AudioProcessor 클래스의 getPlayHead() 에 대한 설명이다.
반드시 processBlock() 에서만 호출하라고 되어있다.
getPlayHead() 는 AudioPlayHead 객체의 포인터를 return 한다.
이 포인터로 juce::AudioPlayHead::CurrentPositionInfo 객체를 얻는다.
void processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiBuffer) override {
juce::AudioPlayHead::CurrentPositionInfo transport;
getPlayHead()->getCurrentPosition(transport);
ppq.store(transport.ppqPosition);
}
getCurrentPosition() 함수의 인자는 output 파라미터이다.
3번째 줄은 아래에서 설명하겠다.
< Juce 의 PPQ 값은 어떻게 출력되는가? >
일단 double 타입이다.
한마디가 0. ~ 3. 으로 표현된다.
1 마디는 4 Beats 인데, 1 Beat 에 1. 씩 올라간다고 보면 된다.
두번째 마디가 되면 4.0 으로 시작하여 7.999999 까지 세번째 마디 직전까지 가는것이다.
시퀀서로 VST3 로 표현하기위해서는 적절히 % 계산을 하여야한다.
소숫점까지 계산해야하므로 % 대신 <cmath> 에 있는 std::fmod 를 사용해야한다.
아래 코드 부분에서 확인할것이다.
< 순살 >
< 코드 >
#include <atomic>
#include <cmath>
class Plugin : public juce::AudioProcessor {
public:
void processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiBuffer) override {
juce::AudioPlayHead::CurrentPositionInfo transport;
getPlayHead()->getCurrentPosition(transport);
ppq.store(transport.ppqPosition);
}
double getPPQ() {
return ppq.load();
}
private:
std::atomic<double> ppq {0.0};
};
class PluginWindow : public juce::AudioProcessorEditor, juce::Timer {
public:
PluginWindow(Plugin& p)
: AudioProcessorEditor(&p), plugin(p)
{
startTimer(10);
setSize(1000, 1000);
}
~PluginWindow() override {
stopTimer();
}
void paint(juce::Graphics& g) override {
g.setColour(juce::Colours::white);
float x = ppq * 1000.0f;
g.drawLine(x, 0.0f, x, 1000.0f, 2.0f);
}
void timerCallback() override {
ppq = (float)((std::fmod(plugin.getPPQ(), 4.0)) / 4.0);
repaint();
}
private:
Plugin& plugin;
float ppq {0.0f};
};
위의 코드에서 PPQ 의 값을 scale 하기위해, std::fmod 를 사용하였다.
std::fmod 는 % 연산자와 같은데, % 연산자는 int 만을 연산할수있다.
따라서 float 이나 double 을 연산하기위해서는 std::fmod 를 사용해야한다.
std::fmod(float f1, float f2) 같이 2개의 인자를 받는다는것을 기억하라.
오버로드되어있는 함수이므로, std::fmod(double d1, double d2) 또한 가능하다.
< 정리 >
message thread (gui thread) 가 audio thread 에 존재하는 값을 가져올때 원칙을 정리해보자.
1. 공유할 변수는 반드시 std::atomic<Type> 이어야한다.
2. GUI 클래스에서 Timer 클래스를 상속받아, 주기적으로 std::atomic 값을 get 해온다.