====== drum_sim_matrix.py 사용 설명서 ======
2‑bar 드럼 패턴(MIDI 파일)들 사이의 **유사도(similarity)**를 계산하고,
두 가지 방법으로 얻은 **전체 NxN 유사도 행렬**을 화면에 출력하는 스크립트입니다.
- 비교 대상: GM 드럼 채널(CH10, = MIDI 채널 번호 9)의 2‑bar 패턴
- 내부 표현: 12개의 드럼 슬롯 × ''cols''개의 시간 스텝(기본 32)로 이루어진 **이진 그리드**
- 유사도 지표:
1. **Hamming similarity** (해밍 유사도)
2. **Cosine similarity** (코사인 유사도)
각 유사도 값은 모두 **1.0에 가까울수록 더 비슷한 패턴**을 의미합니다.
----
===== 1. 요구 사항 및 실행 환경 =====
- Python 3.8 이상 권장
- 필수 라이브러리
- ''mido'' (MIDI 파일 파싱)
- 선택 사항
- 이 스크립트 자체는 추가적인 외부 라이브러리가 필요 없지만,
비교할 MIDI 파일들은 **모두 2‑bar 드럼 패턴**이라고 가정합니다
(예: ''split_drums_2bar_save.py''로 잘라낸 결과물).
설치 예시:
pip install mido
----
===== 2. 기본 아이디어 =====
이 스크립트의 핵심 아이디어는 **드럼 패턴을 하나의 고정 길이 이진 벡터로 바꾼 뒤,**
벡터 간의 거리를 재는 것입니다.
1. 각 MIDI 파일(패턴)을 다음과 같이 변환합니다.
- 채널 10(CH10, 드럼 채널)의 note_on 이벤트만 사용
- 시간 축: 2마디(2 bars) 구간을 **''cols''개의 균일한 스텝**으로 분할 (기본값 32 스텝)
- 음 높이: GM 드럼 노트를 **12개의 슬롯**으로 매핑 (킥, 스네어, 하이햇, 탐, 심벌, 퍼커션 등)
2. 그 결과, 각 패턴은 ''12 × cols'' 크기의 **이진 그리드(0/1)**가 됩니다.
- 특정 슬롯/스텝에 **노트가 하나라도 있으면 1**, 없으면 0
- 이 2차원 그리드를 **길이 ''12×cols''인 1차원 벡터**로 평탄화(flatten)
3. 이렇게 얻은 두 벡터를 이용해:
- Hamming similarity
- Cosine similarity
를 계산하고, 모든 파일 쌍에 대해 NxN 행렬을 만듭니다.
----
===== 3. 드럼 슬롯 정의 (12‑slot 매핑) =====
GM 드럼 노트 번호(35–81)를 **12개의 슬롯**으로 묶어서 표현합니다.
슬롯 인덱스와 라벨은 다음과 같습니다.
| 슬롯 | 라벨 | 설명 (대략) |
| 0 | BD | Kick (Bass Drum) |
| 1 | SD | Snare Drum (주로 Acoustic/Electric) |
| 2 | RS | Rim Shot / Side Stick |
| 3 | CP | Hand Clap |
| 4 | CH | Closed Hi‑Hat |
| 5 | PH | Pedal Hi‑Hat |
| 6 | OH | Open Hi‑Hat |
| 7 | LT | Low/Low‑mid toms |
| 8 | HT | Mid/High toms |
| 9 | CR | Crash 계열 cymbal |
| 10 | RD | Ride 계열 cymbal & Bell |
| 11 | PER| Cowbell 및 Latin/FX 퍼커션 묶음 |
예를 들어, 킥(35, 36)은 항상 슬롯 0(BD),
스네어(38, 40)는 슬롯 1(SD)에 매핑됩니다.
----
===== 4. 스크립트 사용법 =====
==== 4.1. 기본 호출 형태 ====
python drum_sim_matrix.py PATTERN1.MID PATTERN2.MID PATTERN3.MID ...
- 인자로 넘긴 모든 MIDI 파일에 대해 **좌표가 정해진 벡터**를 만들고,
- Hamming / Cosine 유사도를 각각 계산하여
- 두 개의 **NxN 유사도 행렬**을 출력합니다.
==== 4.2. 와일드카드(glob) 사용 ====
운영체제/쉘 종류에 따라 와일드카드 동작이 다릅니다.
#### (1) Linux / macOS (bash, zsh 등)
쉘이 직접 ''*.MID''를 확장하므로, 다음처럼 쓰면 됩니다.
python drum_sim_matrix.py RCK_P*.MID
#### (2) Windows PowerShell
PowerShell은 기본적으로 와일드카드를 Python에 그대로 넘기므로,
다음처럼 해도 확장이 되지 않습니다.
python drum_sim_matrix.py RCK_P*.MID # (X) 스크립트 안에서 '*'가 그대로 보임
PowerShell에서는 다음처럼 ''Get-ChildItem''을 이용해 확장할 수 있습니다.
python drum_sim_matrix.py (Get-ChildItem RCK_P*.MID).Name
====== 또는 ======
python drum_sim_matrix.py (gci RCK_P*.MID).Name
#### (3) Windows cmd.exe
고전 명령 프롬프트(cmd.exe)는 와일드카드를 자동으로 확장해 줍니다.
C:\> python drum_sim_matrix.py RCK_P*.MID
==== 4.3. 옵션: --cols ====
python drum_sim_matrix.py RCK_P*.MID --cols 32 # 기본값 32
- ''cols''는 2‑bar 구간을 나누는 **시간 슬롯 개수**입니다.
- 기본값 32는 **한 마디당 16 스텝**(2마디 = 32 스텝)을 의미합니다.
- 만약 더 촘촘한 해상도가 필요하면 48, 64 등으로 늘릴 수 있습니다.
- 단, 벡터 길이는 ''12×cols''로 늘어나며, 계산 비용도 올라갑니다.
----
===== 5. 내부 동작: 벡터 생성 과정 =====
각 MIDI 파일에 대해 다음과 같은 순서로 벡터를 만듭니다.
1. MIDI 파일 로드 (''mido.MidiFile'')
2. Type 0이면 첫 번째 트랙, Type 1이면 모든 트랙을 merge
3. delta time을 절대 시간으로 바꾼 ''abs_msgs'' 생성
4. 파일의 첫 시점에서의 **박자표 (time signature)**를 얻고,
- 예: 4/4 → 한 마디의 tick 수 = ''ticks_per_beat × 4''
5. **2마디 길이** = ''2 × bar_ticks'' 또는 그보다 짧으면 가능한 범위 내
6. 이 구간을 ''cols''개의 균일한 시간 구간으로 나누고,
7. 해당 구간에서 CH10(채널 9)의 note_on 메시지를 조사하여,
- 해당 시점의 tick 위치를 적절한 스텝 index(''0..cols-1'')로 매핑
- 노트 번호를 12‑slot 중 하나로 매핑
- 해당 슬롯/스텝에 노트가 있음을 표시 → 1
8. 이 12×cols 2차원 배열을 평탄화하여 1차원 벡터로 만듭니다.
----
===== 6. Hamming similarity (해밍 유사도) =====
==== 6.1. Hamming distance란? ====
**Hamming distance**는 두 개의 **같은 길이의 이진 벡터**가 있을 때,
**서로 다른 비트의 개수**를 세는 거리입니다.
예:
- A = 1 0 1 1 0
- B = 1 1 0 1 0
위 두 벡터를 자리별로 비교하면, 서로 다른 위치는 2개입니다.
따라서 Hamming distance d(A, B) = 2입니다.
일반적으로:
- ''d(A, B) = 서로 다른 위치의 수''
- 가능한 값의 범위: ''0'' (완전히 동일) ~ ''N'' (완전히 반대 패턴)
==== 6.2. Hamming similarity 정의 ====
이 스크립트에서는 보다 직관적인 해석을 위해
**유사도(similarity)**를 다음과 같이 정의합니다.
- 벡터 길이 N (여기서는 ''12×cols'')
- Hamming distance = d
Hamming similarity = 1 - d / N
- 완전히 동일한 패턴 → d = 0 → similarity = 1.0
- 절반 정도만 다름 → d ≈ N/2 → similarity ≈ 0.5
- 완전히 반대(이론적으로) → d ≈ N → similarity ≈ 0.0
==== 6.3. 드럼 패턴에서의 의미 ====
두 드럼 패턴의 **각 시간 슬롯/슬롯 위치마다 hit 유무(0/1)**을 비교하여,
- 같은 위치에서 같은 값을 가지면 “일치”
- 다르면 “불일치”로 보고 카운트합니다.
#### 장점
- 구현과 해석이 매우 단순합니다.
- **2‑bar 그리드**가 이미 준비되어 있는 Ardule 환경과 잘 맞습니다.
- 패턴을 구체적으로 어느 위치에서 얼마나 다르게 치는지를 정밀하게 반영합니다.
#### 단점 / 주의점
- **한 스텝 정도의 지연/앞당김**도 모두 “불일치”로 취급합니다.
- 예: 스네어가 한 칸 오른쪽으로 밀려 있으면, 사실상 비슷한 그루브인데도 Hamming distance는 크게 나옵니다.
- 약간의 swing, shuffle, humanize 등에 대해 둔감하지 못합니다.
> 따라서 이 값은 **“정확히 같은 grid 위에 올려놓았을 때의 모양이 얼마나 같은가”**를 보는 척도로 이해하는 것이 좋습니다.
----
===== 7. Cosine similarity (코사인 유사도) =====
==== 7.1. 정의 ====
**Cosine similarity**는 두 벡터 사이의 **각도(angle)**를 이용한 유사도입니다.
벡터 v₁, v₂에 대해:
cosine_similarity(v1, v2) = (v1 · v2) / (||v1|| * ||v2||)
여기서,
- ''v1 · v2''는 **내적(dot product)**
- ''||v1||'', ''||v2||''는 각 벡터의 **길이(노름)**입니다.
값의 범위:
- 이진 벡터의 경우 대체로 ''0.0 ~ 1.0'' 사이
- 1.0이면 **완전히 같은 방향** (패턴이 매우 유사)
- 0.0이면 **서로 전혀 겹치지 않는 방향** (공통된 hit 위치가 거의 없음)
==== 7.2. 이진 벡터에서의 직관 ====
패턴을 0/1 벡터로 두었을 때:
- ''v1 · v2''는 **두 패턴에서 모두 1인 위치의 개수**입니다.
- ''||v1||''는 **v1에서 1의 개수의 제곱근** (√(hit 수))
- ''||v2||''도 마찬가지
따라서 코사인 유사도는 **공통 hit 수**를
각 패턴의 전체 hit 수로 정규화한 값에 가깝게 해석할 수 있습니다.
#### 예시 (개념적)
- 패턴 A: 킥과 스네어를 자주 치는 상대적으로 **빽빽한** 패턴
- 패턴 B: A와 매우 비슷하지만 몇 개의 hit만 빠져 있는 패턴
- 패턴 C: 킥만 몇 번 치는 **드문드문한** 패턴
A와 B는 공통 hit가 많고 전체 hit 수도 비슷하므로,
Cosine similarity가 **매우 높은 값**(예: 0.95 이상)이 나오기 쉽습니다.
A와 C는 공통 hit가 적고, 한 쪽은 빽빽하고 다른 쪽은 희박하기 때문에,
Cosine similarity는 **상대적으로 낮은 값**이 나옵니다.
==== 7.3. Hamming과의 차이 ====
- Hamming은 **각 위치별로 동일/다름만 본다**
→ 작은 오차도 모두 불일치로 들어갑니다.
- Cosine은 **패턴의 전반적인 hit 분포**와 **공통된 히트의 비율**을 강조합니다.
- 예: 두 패턴이 모두 킥 8번, 스네어 4번, 하이햇 16번 정도의 밀도로 들어있는 경우,
비슷한 스타일로 인식되기 쉽습니다.
#### 간단 비교
| 항목 | Hamming similarity | Cosine similarity |
|------|--------------------|-------------------|
| 기본 개념 | 서로 다른 비트 수 | 벡터 사이의 각도 |
| 1.0 의미 | 완전히 동일한 패턴 | 방향이 동일(정규화 관점에서 거의 동일) |
| 민감도 | 위치가 한 칸만 어긋나도 크게 감소 | 전체 패턴의 분포와 공통 hit에 더 민감 |
| 해석 난이도 | 매우 직관적 | 약간 수학적이지만 널리 쓰이는 척도 |
두 척도를 함께 보면서:
- Hamming은 **정밀한 구조 차이**,
- Cosine은 **전체적인 스타일/밀도 차이**를 본다고 이해하면 좋습니다.
----
===== 8. 출력 형식 해석 =====
스크립트를 실행하면, 예를 들어 패턴 4개를 비교했을 때 다음과 같이 출력됩니다.
================================================================================
Hamming similarity matrix (1.000 = identical)
================================================================================
0 1 2 3
0: 1.000 0.945 0.910 0.850
1: 0.945 1.000 0.900 0.840
2: 0.910 0.900 1.000 0.830
3: 0.850 0.840 0.830 1.000
Index → Filename:
0: RCK_P001.MID
1: RCK_P002.MID
2: RCK_P003.MID
3: RCK_P010.MID
================================================================================
Cosine similarity matrix (1.000 = identical)
================================================================================
0 1 2 3
0: 1.000 0.972 0.960 0.900
1: 0.972 1.000 0.955 0.890
2: 0.960 0.955 1.000 0.885
3: 0.900 0.890 0.885 1.000
Index → Filename:
0: RCK_P001.MID
1: RCK_P002.MID
2: RCK_P003.MID
3: RCK_P010.MID
- 위쪽 행렬: Hamming similarity
- 아래쪽 행렬: Cosine similarity
- 왼쪽의 인덱스(0, 1, 2, 3…)는 아래의 ''Index → Filename'' 목록과 매칭됩니다.
- 대각선(각 i,i)은 항상 1.000 (자기 자신과의 유사도)입니다.
- (i,j)와 (j,i)는 동일 값입니다 (대칭 행렬).
----
===== 9. 이론적인 참고 정보 (간단 레퍼런스) =====
- **Hamming distance**
- Richard W. Hamming이 1950년대에 정립한 개념으로,
오류 검출·수정 코드, 정보 이론에서 널리 사용됩니다.
- 여기서는 오류 코드 대신, **드럼 패턴의 이진 표현**에 적용하여
“얼마나 많은 위치에서 다른가?”를 측정하는 용도로 사용합니다.
- **Cosine similarity**
- 정보 검색(Information Retrieval), 자연어 처리(NLP)에서
문서 벡터 간 유사도를 잴 때 가장 널리 쓰이는 척도 중 하나입니다.
- 이진 또는 실수 벡터 모두에 사용 가능하며,
“벡터가 가리키는 방향이 얼마나 비슷한가”를 의미합니다.
드럼 패턴 연구/리듬 분석 분야에서도 이 두 가지 계열의 아이디어는 흔히 사용되며,
보다 복잡한 연구에서는 **Dynamic Time Warping(DTW)**,
**편집 거리(edit distance)**, **회전 불변(cyclic/invariant) 거리** 등으로 확장되기도 합니다.
----
===== 10. 확장 아이디어 =====
현재 스크립트는 **고정된 grid 위에서의 단순 비교**를 수행합니다.
이후 다음과 같은 확장도 고려할 수 있습니다.
1. **회전 불변(cyclic shift‑invariant) Hamming distance**
- 패턴을 한 스텝씩 회전시키며 최소 Hamming distance를 사용
- “시작 위치가 달라서 생기는 차이”를 제거
2. **슬롯 가중치 부여**
- 예: 킥/스네어 차이는 크게, 심벌·퍼커션 차이는 조금만 반영
3. **부분 패턴 비교**
- 특정 구간(예: backbeat 스네어 위치, 하이햇 패턴 등)만 따로 비교
4. **결과를 CSV/엑셀로 export**
- 유사도 행렬을 CSV로 저장하여 다른 도구에서 더 편하게 시각화
필요하다면 이러한 확장 기능을 추가한 버전도 제작할 수 있습니다.