음악, 삶, 개발
ValueTree 공부하기 파트2 : 연습 본문
앞서 이론을 배웠으니, 실제 ValueTree 에 요소들을 추가, 삭제하는등에 대해
연습문제를 풀면서 해보겠다.
< 연습 1 : ValueTree 만들기 >
문제
< Note />
정답
juce::Identifier treeName {"Note"};
juce::ValueTree tree {treeName};
설명
setProperty 와 헷깔리지말자.
setProperty 는 말그대로 이미 생성된 ValueTree 에 Property 를 추가 또는 변경하는것이다.
ValueTree 의 이름은 반드시 초기화할때 인자로 넘겨야한다.
또한 초기화후 해당 ValueTree 의 이름은 절대 변경될수없다.
< 연습 2: property 추가하기 >
문제
< Note pitch = 64 >
정답
const juce::Identifier treeName {"Note"};
const juce::Identifier pitch {"pitch"};
juce::ValueTree tree {treeName};
tree.setProperty(pitch, 64, nullptr);
< 연습 3 : 여러개의 property 추가하기 >
문제
< Note id = "ThisIsNoteId" pitch = 36 velocity = 64 start = 0.1 end = 0.5 active = true >
정답
const juce::Identifier treeName {"Note"};
const juce::Identifier id {"id"};
const juce::Identifier pitch {"pitch"};
const juce::Identifier velocity {"velocity"};
const juce::Identifier start {"start"};
const juce::Identifier end {"end"};
const juce::Identifier active {"active"};
juce::ValueTree tree {treeName};
tree.setProperty(id, "ThisIsNoteId", nullptr);
tree.setProperty(pitch, 36, nullptr);
tree.setProperty(velocity, 64, nullptr);
tree.setProperty(start, 0.1, nullptr);
tree.setProperty(end, 0.5, nullptr);
tree.setProperty(active, true, nullptr);
설명
이 예제가 중요한것은,
setProperty 의 2번째 인자가 var 객체인데,
어떠한 값이든 리터럴로 넘길수있다는것이다.
"ThisisNoteId" 는 juce::String 이다.
36 은 int 이다.
0.1 은 double 이다.
true 는 bool 이다.
< 연습 4 - property 를 추가하고, 수정하기 >
문제
< Note pitch = 36 > // property 추가
< Note pitch = 64 > // property 수정
정답
const juce::Identifier treeName {"Note"};
const juce::Identifier pitch {"pitch"};
juce::ValueTree tree {treeName};
tree.setProperty(pitch, 36, nullptr); // property 추가
tree.setProperty(pitch, 64, nullptr); // property 수정
설명
setProperty 는 property 를 추가, 수정하는데에 둘다 사용된다
만약 내가 제공하는 juce::Identifier 가 해당 tree 에 없다면,
property 는 추가되고, 이미 존재한다면 property 가 수정된다.
< 매우 중요!!!!!!!!!!!!!!!!!!!!!>
< 연습 4 - property 의 값을 가져오기 (var 에 대해 이해하기!) >
문제 : ValueTree 의 pitch property 를 출력하라!
정답
const juce::Identifier treeName {"Note"};
const juce::Identifier pitch {"pitch"};
juce::ValueTree tree {treeName};
tree.setProperty(pitch, 36, nullptr); // property 추가
// 가져오기
juce::var p1 = tree.getProperty(pitch);
int p2 = tree.getProperty(pitch); // also vaild!
DBG(p2); // 36
설명
getProperty(const juce::Identifier& id) 를 사용한다.
이 함수는 var 객체를 return 하는데, 이때 매우 중요한것이 있다.
해당 property 의 타입에 맞는 타입의 객체로 assignment 할수있다는것이다.
따라서 아래와 같은 코드가 valid 이다.
int p2 = tree.getProperty(pitch); // also vaild!
물론 var 객체에 이런 저런 type 을 역으로 집어넣는것도 가능하다.
juce::var v1 = 1; // int
juce::var v2 = 0.2f; // float
juce::var v3 = 0.5; // double
juce::var v4 = false; // bool
juce::var v5 = "Hello"; // juce::String
이런 유연한(?) 대입들이 가능한것은 var 클래스안에,
각 primitive type 들에 대한 operator= 가 정의되어있기때문이다.
var 는 굉장히 JavaScript 의 var 같은 녀석이다. type 의 구애를 받지않는것이다.
따라서 아래와 같은 것도 가능하다.
juce::Identifier treeName {"tree"};
juce::Identifier propertyAny {"any"};
juce::ValueTree tree {treeName};
int v1 = 1;
float v2 = 0.5f;
double v3 = 0.9;
bool v4 = false;
juce::String v5 = "Hello";
tree.setProperty(propertyAny, v1, nullptr); // Now property is int.
tree.setProperty(propertyAny, v2, nullptr); // Now property is float.
tree.setProperty(propertyAny, v3, nullptr); // Now property is double.
tree.setProperty(propertyAny, v4, nullptr); // Now property is bool.
tree.setProperty(propertyAny, v5, nullptr); // Now property is juce::String
property 가 기존에 bool 이었어도, 다시 setProperty 할때 int 또는 double 또는 juce::String 이 될수있는것이다.
이런 유연함이 좋을수도, 독이 될수도있다.
굉장히 C++ 스럽지않기때문이다.
유연함과 엄격함의 적절한 조화를 찾을지, C++ 의 엄격함을 유지해야할지 잘 선택해야할거같다.
< 연습 5 - ValueTree 안에 ValueTree 집어넣기 >
문제
<Parent>
<Child>
<GrandChild/>
</Child>
</Parent>
정답
juce::Identifier parentNodeName {"Parent"};
juce::Identifier childNodeName {"Child"};
juce::Identifier grandChildNodeName {"GrandChild"};
juce::ValueTree parentNode {parentNodeName};
juce::ValueTree childNode {childNodeName};
juce::ValueTree grandChildNode {grandChildNodeName};
parentNode .appendChild(childNode, nullptr);
childNode .appendChild(grandChildNode, nullptr);
설명
appendChild 를 실행하면 vector 의 push_back 처럼 부모 tree 의 자식 tree 들중 가장 뒤로 해당 tree 를 집어넣게된다.
만약 특정 위치에 넣고싶다면
void addChild (const ValueTree& child, int index, UndoManager *undoManager)
를 사용해야한다.
아래의 두 코드는 동일하다.
parentNode.appendChild(childNode, nullptr); // 뒤에 삽입
parentNode.addChild(childNode, -1, nullptr); // 뒤에 삽입 : -1 은 가장 마지막을 나타냄.
< 연습 6 - Sequncer 안에 미디노트를 표현해보기 >
코드
using Id = const juce::Identifier;
Id sequencerId {"sequencer"};
Id noteId {"note"};
juce::ValueTree sequencer {sequencerId};
juce::ValueTree note1 {noteId};
juce::ValueTree note2 {noteId};
juce::ValueTree note3 {noteId};
juce::ValueTree note4 {noteId};
sequencer.appendChild(note1, nullptr);
sequencer.appendChild(note2, nullptr);
sequencer.appendChild(note3, nullptr);
sequencer.appendChild(note4, nullptr);
출력