음악, 삶, 개발
GUI thread vs Audio thread 본문
이 주제에 관해 이야기하기 앞서,
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 포럼에도 가끔 보았는데, 일단 이런것이 있다 정도로만 알고있도록한다.