음악, 삶, 개발

GUI thread vs Audio thread 본문

개발 공부/Juce 공부방

GUI thread vs Audio thread

Lee_____ 2020. 10. 8. 22:52

이 주제에 관해 이야기하기 앞서,

Max 에서 내가 했던것을 먼저 살펴보자.


< Max 에서 오디오 signal 을 그리기 >

 

나는 Max 에서 [phasor] 의 오디오 signal 을 메세지로 변환하여,

아래와 같이 그릴수있었다.

 

[snapshot~ 20] 을 통해,

20ms 마다 현재 오디오 signal 의 값을 체크하여 [multislider] 에 그리고있다.

[phasor~] 는 어딘가로 값을 보낼 뿐이고, [snapshot~] 이나 [multislider] 가 뭔지 모른다.


< GUI thread vs Audio thread >

 

Max 에서는 오디오 signal 을 그리는것이 그리어렵지않지만,

Juce 즉, C++ 환경에서는 thread 에 대한 이해가 있어야한다.

먼저 아래 사항을 기억해야한다.

 

1. processBlock() 함수는 Audio thread 이다.

2. Audio thread 는 절대 GUI 를 업데이트하거나, valueTree 를 읽기 또는 쓰기해서는 안된다.

3. 만약 이렇게 한다면, 사용자는 click 이나 pop 을 겪을것이다.

4. 한마디로 Audio thread 는 audio 프로세싱에만 집중할수있도록 해주어야한다.

5. Audio thread 는 GUI 를 모른다. (GUI 없이도 동작해야한다)

6. Max 에 [snapshot~] 이 있다면 Juce 에는 Timer 클래스가 있다.

7. GUI 가 Audio thread 의 상태를 Timer 로 체크해야한다. (반대여서는 안된다)


< Juce  >

 

나는 DAW 의 Transport 상태를 알고싶었고, 이 상태를 GUI 에서 그리고싶었다.

이 Transport 상태는 processBlock(); 안에서만 알수있으며, 즉 Audio thread 이다.

앞서 말했듯이, Audio thread 는 GUI 에 대해 전혀 몰라야하며, 먼가를 그리려 시도해서도 안된다.

그리기위한 정보를 알아내는것은 온전히 GUI 쓰레드의 몫이다.

이때 Timer 클래스를 사용하여, AudioProcessor 의 서브클래스 객체의 상태를 주기적으로 체크한다.

juce::Timer 를 추가적으로 상속받고, pure virtual 함수인 timerCallback() 함수를 정의해줘야한다.

 

class GUIthread : public juce::AudioProcessorEditor, juce::Timer {

    public :

        GUI(AudioThread& a) : audioThread(a) {

            startTimer(1000);

        }

        ~GUI() {

            stopTimer();

        }

        void paint(juce::Graphics& g) override {

            const auto& transportInfo {processor.getTransport()};

        }

        void timerCallback() override {

            repaint();

        }


    private :

        AudioThread& audioThread;


}

class AudioThread : public juce::AudioProcessor {

    public :

        void processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiBuffer) override {
                
            getPlayHead()->getCurrentPosition(transport);
                    
        }

        const juce::AudioPlayHead::CurrentPositionInfo& getTransport() {

            return transport;

        }

    prviate :

        juce::AudioPlayHead::CurrentPositionInfo transport;


}

< 결론 >

 

Audio thread 가 알려주는게 아니라,

GUI thread 가 알아내야한다. (이게 핵심!)

Audio thread 는 audio 나 midi 프로세싱만을 해야한다.

다른걸 절대 해서는 안된다.


< 추가적으로 공부할 내용 - std::atomic >

 

thread 간에 변수를 공유할때 thread safety 를 보장받기위해 사용하는 라이브러리라고한다.

나는 위의 코드에서 일반 변수를 사용했지만 std::atomic 을 사용하여 변수를 정의해야한다고한다.

Juce 포럼에도 가끔 보았는데, 일단 이런것이 있다 정도로만 알고있도록한다.