음악, 삶, 개발

DAW 의 파라미터에 대한 모든것 : 파트1 본문

개발 공부/Juce 공부방

DAW 의 파라미터에 대한 모든것 : 파트1

Lee_____ 2020. 10. 12. 19:42

< Daw 의 파라미터 >

 

VST 를 Ableton Live 에서 열었을때.

위의 사진을 보면, 내가 노브를 돌렸을때 하단의 값이 변하고,

하단의 값을 움직이면 위의 노브가 변한다.

우리는 결국 이와 같은것을 하고싶다.

저렇게 VST 에 내가 원하는 이름의 파라미터를 추가하고싶다.

그뿐만 아니라, 내가 만든 GUI 로 저 파라미터를 조절하고싶다.


< 생각보다 거대한 문제 >

사용자의 느낌으로 얼핏보았을때는, 흠 쉬운데? 할수있지만

개발자의 입장에서는 불행히도 그렇지않다...

음식도 먹는게 쉽고, 만드는게 훨씬 어렵지않은가..

여러가지 스탭들이 합해야 위의 그림같은 상황을 연출할수있다.

내가 앞선 포스트들에서 audio thread 와 message thread 를 지속적으로 이야기한 이유가 결국 여기에 있다.

gui thread (즉, message thread) 가 파라미터 값을 업데이트할것이고,

이 값을 audio thread 에서 읽기때문이다.


< 그래서 무엇을 고민 해야하는가 ? >

DAW 의 나의 파라미터를 추가한다는것은, 

파라미터를 저장할 공간이 필요하다.

음식도 저장할려면 냉장고가 있어야한다. 

냉장고에 집어넣지앞고 음식을 저장한다라는것은 애초에 성립되지않는다.

또한 내가 저장한 파라미터는 추후 변경 할수있어야하며, 

현재 파라미터값은 VST 의 출력에 당연히 영향을 미쳐야한다.

이외에도 여러가지 고민 사항들이 필요하다.

이 고민들을 정리해보면 아래와 같다.

 

1. 어디에 나의 파라미터를 추가할것인지? 

2. 어떻게 나의 파라미터를 추가할수있는지? 

3. 어떻게 나의 VST 출력은 이 파라미터 값을 반영할수있는지? 

4. 어떻게 나의 파라미터를 GUI 에 연결할수있는지? 

5. 어떻게 나의 파라미터 값을 이 GUI 로 변경할수있는지?


1. 어디에 나의 파라미터를 추가할것인지? 

juce::AudioProcessorValueTreeState 클래스를 사용한다.

이름이 너무 길어서 줄여서 APVTS 라고 사람들이 부른다.

이 APVTS 가 VST 안에서 필요한 모든 파라미터들을 저장하고 관리하는 냉장고라고 보면 된다.

Max 에서 [dict] 가 하는 역할이라고 보면 된다.

APVTS 객체를 AudioProcessor 의 서브 클래스의 private member 로 생성한다.

class VSTPlugin  : public juce::AudioProcessor {

    public :


    private :

        juce::AudioProcessorValueTreeState apvts;

}

이 APVTS 객체는 반드시 AudioProcessor 의 서브클래스안에서 생성해야하며,

2개여서도 안된다.

그냥 여기에 단 하나만 있어야한다.

APVTS 객체가 AudioProcessor 클래스안에 들어가는것은 매우 당연하다.

APVTS 는 AudioProcessor 와 동일한 life-time (수명) 을 가져야한다.

GUI 클래스안에 들어가면, GUI 객체가 소멸되었을때, 같이 소멸되기때문이다.

우리의 VST 플러그인의 창이 꺼져있어도, 나의 플러그인은 계속해서 프로세싱을 해야한다.

사용자들은 창을 껐다 켰다 하지않는가.

창이 꺼도 나의 플러그인은 돌아가야한다.

 


2. 어떻게 나의 파라미터를 추가할수있는지? 

APVTS 객체를 생성했다면, 파라미터를 추가하는 코드를 이제부터 작성한다.

이 파라미터 추가는 VST 플러그인이 로딩되었을때,

즉 AudioProcessor 서브 클래스의 생성자에 의해 진행된다.

이 생성자로 APVTS 객체를 초기화 해주어야한다.

APVTS 초기화하기위해 문서를 살펴보았다.

AudioProcessorValueTreeState	(	

AudioProcessor & 	processorToConnectTo,
UndoManager * 	undoManagerToUse,
const Identifier & 	valueTreeType,
ParameterLayout 	parameterLayout 

)	

위의 생성자는 4개의 인자를 받고있는다.

 

1) AudioProcessor& proceesorToConnectTo : 이 프로세서임으로, *this 를 넘긴다.

2) UndoManager* : UndoManager 는 아직 배우지않았음으로 nullptr 를 넘긴다.

3) const Identifier& : 내부 valueTree 의 이름이다, 따라서 juce::Identifier("원하는이름") 을 넘기면 된다.

4) ParameterLayout 

 

ParamterLayout 클래스가 실질적으로 파라미터를 등록하는것이다.

결국 "어떻게 나의 파라미터를 추가할수있는지?" 라는 질문은 어떻게 ParameterLayout 객체를 만드는지? 일수있다.

 

ParameterLayout  만들기

juce::AudioProcessorValueTreeState::ParameterLayout createParams() {

          juce::AudioProcessorValueTreeState::ParameterLayout layout;

          layout.add(std::make_unique<juce::AudioParameterInt>("test", "Test", 0, 10, 0));

          return layout;

}

위의 코드는 외우는것이 좋다.

위의 코드는 int 값을 가진 파라미터 즉, AudioParameterInt 클래스의 객체를 추가하고있는데,

"test" 라는 파라미터 id 이고, "Test" 는 DAW 에 표시될 이름, 최소값 0, 최대값 10, 초기값 0 이다.

하지만 파라미터가 늘어날 경우, 앞에 type 명이 너무 길어 지져분해질것이다.

그래서 using 을 사용한다.

using Params = juce::AudioProcessorValueTreeState::ParameterLayout;
using ParamInt = juce::AudioParameterInt;

Params createParams() {

      Params params;

      params.add(std::make_unique<ParamInt>("Test", "Test", 0, 10, 0));

      return params;

}

Juce 가 제공하는 파라미터 클래스의 종류는 4가지가 있는데,

Bool, Choice, Float, Int 4가지가 존재한다.

아직까지는 파라미터를 추가하는 함수를 작성한것이지, 실제로 추가된것이아니다

마지막으로 해야할일은 apvts 객체를 이 함수를 사용하여 초기화하는것이다.

최종적으로 아래와 같은 코드가 된다.

class VSTPlugin  : public juce::AudioProcessor {

    public :

        VSTPlugin() : apvts(*this, nullptr, juce::Identifier("APVTS"), createParams()) // apvts 초기화!
        
        {



        }

        using Params = juce::AudioProcessorValueTreeState::ParameterLayout;
        using ParamInt = juce::AudioParameterInt;

        Params createParams() {

            Params params;

            params.add(std::make_unique<ParamInt>("Test", "Test", 0, 10, 0)); // Id, Display Name, Min, Max, Default

            return params;

        }


    private :

        juce::AudioProcessorValueTreeState apvts; // apvts 객체 생성!

}

 

< 참고 자료 >

 

Tutorial: Saving and loading your plug-in state

AudioProcessorValueTreeState 클래스