
8-Step Sequencer 만들기
Tresillo 리듬 패턴을 8-Step Sequencer로 구현. metro + counter + zl nth + makenote를 결합해 MIDI로 출력하는 전체 과정을 다룹니다.
이 에피소드에서 배우는 것
- Tresillo 리듬 패턴의 구조와 음악적 의미
- Step Sequencer의 핵심 구성 요소: 클럭, 인덱스, 배열
- BPM을 밀리초로 변환하는 공식
- Max에서 8-Step Sequencer를 처음부터 구현하는 전체 과정
사전 지식
Tresillo 리듬이란
Tresillo(트레시요)는 라틴 음악의 기본 리듬 패턴으로, 3+3+2 구조를 가진다. 8개의 16분음표를 3, 3, 2로 그룹 지어 강세를 주는 패턴이다.
스텝: 1 2 3 4 5 6 7 8
Gate: [1] 0 0 [1] 0 0 [1] 0
● ● ●
|--3--| |--3--| |-2-|
1번, 4번, 7번 스텝에서 음이 발생하고 나머지는 쉰다. 8박자 안에 3번의 어택이 있는데, 간격이 고르지 않아서(3, 3, 2) 독특한 그루브가 만들어진다. 이 패턴은 아프로-쿠반 음악에서 유래하여 레게, 힙합, 일렉트로닉 음악까지 광범위하게 사용된다.
숫자로 표현하면 [1, 0, 0, 1, 0, 0, 1, 0]이다. 1은 음을 내라(Gate On), 0은 쉬어라(Gate Off)는 의미이다. 이것이 바로 시퀀서의 Gate 배열이다.
Step Sequencer의 구조
Step Sequencer는 세 가지 핵심 요소로 구성된다.
1) 클럭 (Clock): 일정한 간격으로 “다음 스텝으로”라는 신호를 보낸다. 음악에서 메트로놈에 해당한다. Max에서는 [metro]가 이 역할을 한다.
2) 인덱스 (Index): 현재 몇 번째 스텝인지를 추적하는 카운터이다. 클럭이 올 때마다 1씩 증가하고, 마지막 스텝에 도달하면 처음으로 돌아간다. Max에서는 [counter]가 이 역할을 한다.
3) 배열 (Array): 각 스텝에 저장된 데이터이다. Gate 패턴(음을 낼지 말지), Pitch(어떤 음을), Velocity(얼마나 세게) 등을 담는다. 이번 에피소드에서는 Gate 배열만 사용하고, 확장 아이디어에서 Pitch 배열을 다룬다.
전체 동작을 한 문장으로 요약하면 이렇다. 클럭이 뛸 때마다 인덱스가 1 증가하고, 해당 인덱스의 배열 값을 읽어서, 1이면 음을 내고 0이면 쉰다.
BPM → 밀리초 변환
metro에 넣을 시간 간격을 계산해야 한다. BPM(Beats Per Minute)에서 16분음표 하나의 길이(밀리초)를 구하는 공식은 다음과 같다.
16분음표 ms = 60000 / BPM / 4
왜 4로 나누는가? 1 Beat(4분음표)는 16분음표 4개로 이루어져 있기 때문이다.
| BPM | 4분음표 (ms) | 16분음표 (ms) |
|---|---|---|
| 60 | 1000 | 250 |
| 90 | 666.7 | 166.7 |
| 120 | 500 | 125 |
| 140 | 428.6 | 107.1 |
BPM 120에서 16분음표 하나는 125ms이다. 이 값을 metro에 넣으면 된다.
Max에서 이 계산을 구현하면 다음과 같다.
[number box] ← BPM 입력 (예: 120)
|
[/ 4] ← BPM을 4로 나눔 (사실상 16분음표 BPM)
|
[tempo] ← BPM을 ms로 변환하는 전용 오브젝트
|
[metro]
또는 수식으로 직접 계산할 수도 있다.
[number box] ← BPM 입력
|
[expr 60000. / $f1 / 4.] ← 16분음표 ms 계산
|
[metro]
counter: 순환하는 인덱스
[counter]는 bang을 받을 때마다 숫자를 1씩 증가시키는 카운터이다.
[counter 0 7]
- 첫 번째 인자
0: 시작값 (최솟값) - 두 번째 인자
7: 끝값 (최댓값)
bang을 보낼 때마다 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, … 순으로 순환한다. 8개의 스텝(0~7)을 무한히 반복하는 인덱스가 된다.
counter에는 세 개의 Outlet이 있다.
- Out1 (왼쪽): 현재 카운트 값
- Out2 (중앙): 최솟값에 도달했을 때 bang (underflow)
- Out3 (오른쪽): 최댓값에 도달했을 때 bang (carry/overflow)
구현: Tresillo 8-Step Sequencer
모든 재료가 준비되었다. 이제 조립해보자.
Step 1: Gate 배열 정의
Tresillo 패턴을 메시지로 정의한다.
[message: 1 0 0 1 0 0 1 0]
이 메시지를 bang하면 8개의 숫자로 된 리스트가 출력된다.
Step 2: 클럭 + 카운터
[toggle] ← 시퀀서 On/Off
|
[metro 125] ← BPM 120의 16분음표
|
[counter 0 7] ← 0~7 순환
toggle을 켜면 metro가 125ms마다 bang을 보내고, counter가 0부터 7까지 순환한다.
Step 3: 현재 스텝의 Gate 값 읽기
counter의 출력(현재 인덱스)으로 Gate 배열에서 해당 위치의 값을 꺼낸다. 여기서 [zl nth]를 사용한다.
[message: 1 0 0 1 0 0 1 0]
|
[zl nth] ← 오른쪽 Inlet에 인덱스(counter 출력 + 1)
|
(현재 스텝의 Gate 값: 0 또는 1)
주의: zl nth의 인덱스는 1부터 시작하지만 counter는 0부터 시작한다. 따라서 counter의 출력에 1을 더해야 한다.
[counter 0 7]
|
[+ 1] ← 0~7을 1~8로 변환
|
[zl nth]의 오른쪽 Inlet
하지만 여기서 실행 순서 문제가 발생한다. zl nth가 올바르게 동작하려면, 인덱스(오른쪽 Inlet)가 먼저 설정된 뒤 리스트(왼쪽 Inlet)가 들어와야 한다. 이를 위해 [trigger]를 사용한다.
[counter 0 7]
|
[+ 1]
|
[t i b]
| |
| [message: 1 0 0 1 0 0 1 0] (bang으로 리스트 출력)
| |
→ [zl nth] ← 오른쪽 Inlet에 인덱스, 왼쪽 Inlet에 리스트
|
(Gate 값)
[t i b]는 오른쪽부터 실행하므로:
- 먼저 오른쪽의
i가 인덱스 값을 zl nth의 오른쪽 Inlet에 보낸다. - 그 다음 왼쪽의
b가 message를 bang하여 리스트를 zl nth의 왼쪽 Inlet에 보낸다.
이 순서가 보장되어야 올바른 스텝의 값이 나온다.
Step 4: Gate로 Note 제어
zl nth에서 나오는 값은 0 또는 1이다. 1일 때만 음을 내면 된다. [select 1](줄여서 [sel 1])을 사용한다.
[zl nth]
|
[sel 1] ← 값이 1이면 왼쪽 Outlet으로 bang, 아니면 오른쪽 Outlet
|
(bang)
|
[60] ← 고정 Pitch (C4)
|
[makenote 100 100] ← Velocity 100, Duration 100ms
| |
[noteout] ← MIDI 출력
select 1은 입력값이 1일 때만 왼쪽 Outlet에서 bang을 출력한다. 이 bang이 고정된 Pitch 값(여기서는 60, Middle C)을 트리거하고, makenote가 Note On/Off 쌍을 만들어 noteout으로 보낸다.
전체 패치 흐름 요약
[toggle]
|
[metro 125]
|
[counter 0 7]
|
[+ 1]
|
[t i b]
| \
| [message: 1 0 0 1 0 0 1 0]
| |
+--→ [zl nth]
|
[sel 1]
|
[60]
|
[makenote 100 100]
| |
[noteout]
toggle을 켜면 Tresillo 리듬이 반복 재생된다. 간단하지만 이것이 모든 Step Sequencer의 기본 골격이다.
Step Sequencer 확장 아이디어
기본 구조를 이해했다면, 다음과 같이 확장할 수 있다.
1) 스텝별 Pitch 추가
Gate 배열 외에 Pitch 배열을 하나 더 만든다.
Gate: [1, 0, 0, 1, 0, 0, 1, 0]
Pitch: [60, 0, 0, 64, 0, 0, 67, 0]
동일한 인덱스로 두 배열에서 동시에 값을 읽어, Gate가 1인 스텝에서 해당 Pitch를 연주한다.
2) 스텝별 Velocity 추가
Velocity 배열을 추가하면 강세 표현이 가능해진다.
Gate: [1, 0, 0, 1, 0, 0, 1, 0]
Pitch: [60, 0, 0, 64, 0, 0, 67, 0]
Velocity: [120, 0, 0, 90, 0, 0, 100, 0]
3) 패턴 길이 변경
counter의 최댓값과 배열 길이를 바꾸면 다양한 박자를 만들 수 있다. 예를 들어 7스텝으로 만들면 7/8 박자 패턴이 된다.
4) 여러 트랙 동시 운용
동일한 metro를 여러 counter와 공유하면, 드럼의 킥/스네어/하이햇을 각각 독립적인 패턴으로 동시에 재생할 수 있다.
핵심 오브젝트 정리
| 오브젝트 | 역할 | 비고 |
|---|---|---|
[metro] | 일정 간격으로 bang 출력 | ms 단위 |
[counter] | 순환하는 카운터 | 시작값, 끝값 지정 가능 |
[zl nth] | 리스트에서 n번째 값 추출 | 인덱스 1부터 시작 |
[select] / [sel] | 특정 값 매칭 시 bang 출력 | 라우팅에 핵심 |
[makenote] | Note On/Off 쌍 생성 | Velocity, Duration 지정 |
[noteout] | MIDI Note 출력 | - |
[trigger] / [t] | 실행 순서 보장 | 오른쪽 → 왼쪽 |
[toggle] | On/Off 스위치 | metro 제어용 |
직접 해보기
- 기본 Tresillo 패턴
[1, 0, 0, 1, 0, 0, 1, 0]을 구현하고, BPM을 80, 120, 160으로 바꿔보면서 느낌의 차이를 체감해보자. - Gate 배열을
[1, 0, 1, 0, 1, 0, 1, 0](8비트),[1, 0, 0, 0, 1, 0, 0, 0](하프타임) 등으로 바꿔보자. 리듬이 어떻게 변하는가? - (도전) 스텝별 Pitch 배열을 추가하여, C Major 아르페지오(60, 64, 67)가 Tresillo 리듬으로 연주되는 시퀀서를 완성해보자.
다음 에피소드 예고
다음 에피소드에서는 MIDI를 넘어 오디오 시그널 프로세싱의 세계로 들어간다. 필터, 엔벨로프, 그리고 소리를 조각하는 방법을 배운다.
자주 묻는 질문
Tresillo 리듬은 어떤 음악에 쓰이나요?
Tresillo는 쿠바 음악에서 시작된 3+3+2 리듬 패턴으로 "clave 5-3"의 앞부분이기도 합니다. 살사·맘보 같은 라틴 음악의 기본 리듬이며, 자메이카로 건너가 레게의 "one drop" 패턴, 미국에서는 New Orleans 재즈와 R&B, 1990~2010년대 힙합과 R&B의 트랩 비트, 그리고 Calvin Harris의 "Summer", Drake의 여러 곡, K-pop·아프로비트 등 현대 일렉트로닉 음악에까지 광범위하게 쓰입니다. 8박자 안에서 강세가 고르지 않게 분포해 "굴러가는 그루브"가 만들어지는 것이 핵심입니다.
BPM 120의 16분음표 ms는 어떻게 계산하나요?
공식은 16분음표 ms = 60000 / BPM / 4 입니다. BPM 120이면 60000 / 120 / 4 = 125ms. 4로 나누는 이유는 1 Beat(4분음표)가 16분음표 4개로 이루어지기 때문입니다. 같은 방식으로 BPM 90 → 166.7ms, BPM 140 → 107.1ms입니다. Max에서는 [tempo] 오브젝트가 BPM ↔ ms 변환을 담당하지만, [expr 60000./$f1/4.]로 직접 계산하는 패턴도 흔히 쓰입니다.
각 스텝마다 다른 음(Pitch)을 내려면 어떻게 확장하나요?
Gate 배열 외에 Pitch 배열을 하나 더 만들고 같은 인덱스로 동시에 읽으면 됩니다. 예: Gate = [1 0 0 1 0 0 1 0], Pitch = [60 0 0 64 0 0 67 0]. counter의 출력을 [+ 1]을 거쳐 두 개의 [zl nth]에 동시 분기하고, Gate가 1인 스텝에서만 해당 인덱스의 Pitch 값을 [makenote]에 보냅니다. 더 나아가 Velocity 배열, Duration 배열까지 추가하면 강세와 노트 길이가 다른 풀 시퀀서가 되고, 같은 metro를 여러 트랙(킥/스네어/하이햇)이 공유하면 폴리리듬 드럼 머신이 됩니다.
왜 이 패치에 [trigger]가 꼭 필요한가요?
[zl nth]는 "오른쪽 Inlet에 인덱스를 먼저 설정하고, 왼쪽 Inlet에 리스트가 들어오는 순간" 그 인덱스의 값을 출력합니다. trigger 없이 counter의 출력을 두 곳(인덱스, 리스트 트리거)에 분기하면 화면 위치에 따라 "리스트가 먼저 발사되고 인덱스는 나중에 갱신"되는 경합 상황이 생겨 한 스텝씩 어긋난 값이 출력될 수 있습니다. [t i b]는 항상 오른쪽(b: 리스트 bang)부터 실행되지만, 우리가 원하는 것은 "인덱스를 먼저, 리스트를 나중에" 보내는 것이라 패치코드 연결을 그렇게 맞춰야 합니다(왼쪽 i가 zl nth의 오른쪽 Inlet으로). 실행 순서 버그를 막는 가장 안전한 보험입니다.
이 에피소드가 도움이 됐다면 눌러주세요.
실습 패치 다운로드
Max 8 이상에서 열어주세요. 파일을 Max로 드래그하거나 더블클릭하면 실행됩니다.
공식 문서 참조
YouTube
채널에서 더 많은 Max/MSP 예제를 이어서 보세요
튜토리얼의 흐름을 끊지 않고, 실제 영상 데모와 채널 콘텐츠를 연속해서 확인할 수 있습니다.