====== 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로 저장하여 다른 도구에서 더 편하게 시각화 필요하다면 이러한 확장 기능을 추가한 버전도 제작할 수 있습니다.