음악, 삶, 개발

DAW 의 PPQ 그리기 (Transport) 본문

개발 공부/Juce 공부방

DAW 의 PPQ 그리기 (Transport)

Lee_____ 2020. 10. 10. 23:23

아래 포스팅한 오디오 thread 와 메세지 thread 에 대한 글들을 숙지하였다면,

이제는 먼가를 그려볼 시간이다. 

PPQ 의 값을 사용하여야한다.


juce::AudioPlayHead::CurrentPositionInfo 클래스 >

공식문서 

 

ppq 값을 가지고있는 클래스로써,

AudioProcessor 클래스의 함수 getPlayHead() 함수를 이용하여,

오디오 thread, 즉 processBlock() 안에서 가져와야한다.     

 

아래는 AudioProcessor 클래스의 getPlayHead() 에 대한 설명이다.

반드시 processBlock() 에서만 호출하라고 되어있다.

 

AudioProcessor 클래스의 getPlayHead() 함수

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 해온다.