[Audio] Python 활용해 조성 얻어내기

2025. 3. 17. 23:42Developers 공간 [Shorts]/Vision & Audio

728x90
반응형
<분류>
A. 수단
- OS/Platform/Tool : Linux, Kubernetes(k8s), Docker, AWS
- Package Manager : node.js, yarn, brew, 
- Compiler/Transpillar : React, Nvcc, gcc/g++, Babel, Flutter

- Module Bundler  : React, Webpack, Parcel

B. 언어
- C/C++, python, Javacsript, Typescript, Go-Lang, CUDA, Dart, HTML/CSS

C. 라이브러리 및 프레임워크 및 SDK
- OpenCV, OpenCL, FastAPI, PyTorch, Tensorflow, Nsight

 


1. What? (현상)

 

이번 글에서는 python에서 조성(조, Key)라고 불리는 장조/단조를 알아내는 좋은 소스가 있어 소개하려고 합니다.

 

먼저 조성이 무엇인지 살펴보겠습니다. 

 

장조와 단조의 가장 큰 차이는 아래와 같이 구분됩니다. 

  • 장조 : "온온반온온온반"으로 구성된 스케일로 구성된 조성. 밝고 안정적인 분위기를 가집니다.
  • 단조 : "온반온온반온온"으로 구성된 스케일로 구성된 조성. 어둡고 서정적인 분위기를 가집니다.
    ** 자연단음계를 기준으로 설명합니다.

아래와 같이 대표적인 장조는 C장조이며, 대표적인 단조는 A단조입니다. 이때, A 단조의 구성을 C단조에 대해 대입해보고 비교해보면 확실히 차이를 알 수 있습니다.

[대표적인 장조 단조]

 

 

5도권을 기준으로 아래와 같은 규칙으로 장조와 단조의 스케일을 구성하며, 이 관계에서 아래 그림과 같이 나란한조, 같은 으뜸음조, 이명동음조 등이 발생합니다.

** 주의. 아래 그림에서 같은 으뜸음조이명동음조로 인해 왼쪽과 오른쪽이 일치하지 않을 수도 있습니다.

[모든 조성]

 

위에서 특히 나란한조는 같은 음계를 가지는 장조와 단조가 어떻게 다른느낌을 제공할 수 있는지 궁금할 수 있습니다.

이에 대해 간단히만 언급하자면, 스케일은 같더라도 악보를 만들 때 토닉음(중심음, 으뜸음)이 다르며, 악보를 구성하는 코드 즉 화성이 각각의 위치에서의 역할이 바뀌기 때문에 악보에서 느낌을 다르게 나타납니다.

 

그럼 노래를 듣고 이 조성을 알아내려면 보통 어떻게 할까요?

사람마다 다른 방법을 사용하겠지만, 필자는 아래와 같은 순서로 조성을 알아냅니다.

  • 1. 곡의 전반적인 분위기나 정서를 바탕으로 메이저 키인지 마이너 키인지를 예측합니다.
  • 2. 마지막 끝나는 음, 혹은 시작하는 음은 으뜸음일 가능성이 높습니다.
  • 3. 주요 악절의 음을 청음하여 사용된 음계를 파악하고, 그 음계에 따라 어떤 조표(# 또는 ♭)가 해당 조성에 사용되는지를 유추합니다.
  • 4. 조표를 바탕으로 가능한 조성을 추정하고, 이를 통해 해당 곡의 스케일과 조성을 더블체크합니다.

 

위 설명을 보시면 아시겠지만, 익숙하지 않다면 과정이 오래걸리고 정확하지도 않을 수도 있습니다.

 

이번 글에서는 이런 조성을 알아내는 코드를 하나 소개하겠습니다.


2. Why? (원인)

  • X

3. How? (해결책)

 

소스 코드는 아래와 같은 key-finder를 활용하겠습니다. 아래는 Krumhansl-Schmuckler key-finding 알고리즘을 활용해 키를 찾아내는 코드입니다.

** https://github.com/jackmcarthur/musical-key-finder

 

그럼 이제 활용해보겠습니다. 먼저 소스를 아래와 같이 clone한 뒤에 해당 폴더에서 작업하겠습니다.

git clone https://github.com/jackmcarthur/musical-key-finder.git

 

keyfinder.py라는 파일을 import해서 사용하겠습니다. 코드는 아래와 같습니다. 

  1. 0sec~22sec에서 어느 pitch class가 가장 많이 보이는 지를 프린트합니다.
  2. 0sec~22sec에서 어떤 키가 가장 가능성이 높은지를 프린트합니다.
  3. 22sec~33sec에서 어떤 키가 가장 가능성이 높은지를 프린트합니다.
import librosa
from keyfinder import Tonal_Fragment

listm= ["test_a.mp3",
    "./test_b.mp3",
    "./test_c.mp3",
    "./test_d.mp3"]

for audio_path in listm:
    print("finding ... {}".format(audio_path))
    y, sr = librosa.load(audio_path)
    y_harmonic, y_percussive = librosa.effects.hpss(y)

    print("===============================")
    tonal_frag = Tonal_Fragment(y_harmonic, sr, tend=22)
    tonal_frag.print_chroma()
    
    print("===============================")
    tonal_frag.print_key()

    print("===============================")
    tonal_frag = Tonal_Fragment(y_harmonic, sr, tstart=22, tend=33)
    tonal_frag.print_key()

 

예시 결과는 아래와 같습니다

finding ... ./test_a.mp3

===============================
C  0.557
C#  0.492
D  0.463
D#  0.599
E  0.385
F  0.354
F#  0.496
G  0.579
G#  1.000
A  0.665
A#  0.947
B  0.974
===============================
likely key: G# minor, correlation: 0.667
===============================
likely key: G# minor, correlation: 0.752

 

728x90
반응형