파이썬으로 음계도 만둘고 학교종히 땡땡땡도 연주하고 뭐 이런게 가능할까요?
당근이죠...
Python 이 계산하고 그래프만 그린다고 생각했다면 오산입니다. 다음 MP3를 play하면 파이썬으로 만든 도레미파솔라시도를 들을 수 있어요.
먼저 피타고라스 음계와 관련된 음계의 기본 개념을 정리해드리겠습니다. 또한, 왜 "도레미파솔라시도" 대신 "CDEFGABC"라는 표기가 사용되는지도 설명하겠습니다.
피타고라스 음계는 고대 그리스의 수학자 피타고라스가 제안한 음계 체계로, 음정(interval)을 단순한 정수 비율로 정의한 것이 특징입니다. 이 음계는 주로 5도의 음정(완전 5도)을 기반으로 만들어졌습니다.
완전 5도 (Perfect Fifth): 두 음의 주파수 비율이 3/2인 음정입니다. 예를 들어, 한 음의 주파수가 300Hz라면, 그보다 완전 5도 높은 음의 주파수는 300×(3/2)=450Hz입니다.
음계 확장: 피타고라스는 이 3/2의 비율을 반복적으로 적용하여 음계를 확장했습니다. 예를 들어: - 기준음(1:1)에서 3/2를 곱하면 완전 5도 위의 음이 생성됩니다. - 다시 (3/2)^2=9/4를 계산하고 옥타브(2배 주파수)를 고려하여 (9/4)÷2=9/8로 조정하면 장2도가 됩니다. - 이런 방식으로 음계를 계속 확장합니다.
- 피타고라스 음계는 5도 간격으로 음을 쌓아가기 때문에 원래의 옥타브와 약간의 차이가 발생합니다. 이를 피타고라스 콤마(Pythagorean Comma)라고 부릅니다
- 이로 인해 현대 서양 음악에서는 피타고라스 음계를 보완한 **평균율(12평균율)**을 사용합니다.
"도레미파솔라시도"는 이탈리아어로 된 음계의 계이름입니다. 이 표기법은 주로 고정도법(Fixed Do)을 사용하는 국가들에서 사용됩니다. 예를 들어, 한국, 이탈리아, 스페인 등에서 익숙한 방식입니다.
"CDEFGABC"는 영어권에서 사용하는 음계의 음이름입니다. 이는 음의 절대적인 높이를 나타내는 표기법으로, 이동도법(Movable Do)을 사용하는 국가들(영미권 등)에서 주로 사용됩니다.
"도레미파솔라시도"는 음계의 기능적 역할을 나타내며, 특정 음을 기준으로 다른 음의 관계를 표현합니다.
"CDEFGABC"는 음의 이름 자체를 나타냅니다. 예를 들어, "C"는 항상 동일한 음(C)을 의미합니다.
음정은 두 음 사이의 높이 차이를 나타내는 음악적 거리입니다. 이 거리는 두 음 사이의 음표 간격으로 측정되며, 음정은 기본 간격(숫자)과 질적 특성(장, 단, 완전 등)으로 구분됩니다.
기본 간격(숫자): 두 음 사이의 음표 개수를 나타냅니다. 예: 도(C)에서 솔(G)까지는 5도 (도, 레, 미, 파, 솔 → 5개의 음표) 도(C)에서 레(D)까지는 2도 (도, 레 → 2개의 음표)
질적 특성: 음정의 정확한 크기를 결정하며, 완전, 장, 단, 증, 감 등의 용어로 표현됩니다. 예: 도(C)에서 미(E)까지는 장3도 (C에서 E는 4반음) 도(C)에서 레(D)까지는 장2도 (C에서 D는 2반음)
5도는 두 음 사이의 음정이 5개의 음표로 이루어진 경우를 말합니다.
예: 도(C)에서 솔(G)까지 도 → 레 → 미 → 파 → 솔 (5개의 음) 이 음정은 완전 5도로 불리며, 주파수 비율은 3223입니다.
장2도는 두 음 사이의 음정이 2개의 음표로 이루어지며, 두 음 사이의 간격이 2반음(전음)인 경우를 말합니다.
예: 도(C)에서 레(D)까지 도 → 레 (2개의 음) 이 음정은 장2도로 불리며, 주파수 비율은 9/8입니다
사실 오늘은 음악시간이 아니라 이 정도만 해설을 할게요.
위에서 소개한 도래미파솔라시도의 코드는 다음과 같아요... 좀 길기는 한데 그래도 글자를 타이핑하는데 음게가 나온다는 것이 재미있자나~~~ 파일은 밑에 첨부했어요.
코드를 이해하려고 하지말고, 이런게 있구나, 소리가 나는구나 하면 되요. 로마는 결코 하루아침에 만들어 지지 않았거든요. 새해에는 좀 더 고급 연주자가 되어보자구요.
import numpy as np
from scipy.io import wavfile
# Base frequency for C4 (Do) in Pythagorean tuning
base_freq = 256.0 # Hz
# Pythagorean ratios for C major scale (Do-Re-Mi-Fa-Sol-La-Ti-Do)
ratios = [
1.0, # C (Do): 1
9/8, # D (Re): 9/8
81/64, # E (Mi): (9/8)^2 = 81/64
4/3, # F (Fa): 4/3
3/2, # G (Sol): 3/2
27/16, # A (La): (3/2)^3 / 2 = 27/16
243/128, # B (Ti): (3/2)^4 / 2 = 243/128 (or 81/64 * 3/2)
2.0 # C (Do): 2 (octave up)
]
# Calculate actual frequencies
frequencies = [base_freq * ratio for ratio in ratios]
# Audio parameters
sample_rate = 44100 # Hz (standard audio sample rate)
note_duration = 0.5 # seconds per note
silence_duration = 0.1 # seconds of silence between notes
def generate_sine_wave(freq, duration, sample_rate):
"""Generate a sine wave for a given frequency and duration."""
t = np.linspace(0, duration, int(sample_rate * duration), False)
wave = np.sin(2 * np.pi * freq * t)
# Normalize to 16-bit range and convert to int16
wave = (wave * 32767).astype(np.int16)
return wave
# Generate audio for all notes with silences
full_audio = []
for i, freq in enumerate(frequencies):
note_name = ['Do (C)', 'Re (D)', 'Mi (E)', 'Fa (F)', 'Sol (G)', 'La (A)', 'Ti (B)', 'Do (C)'][i]
print(f"Generating {note_name} at {freq:.2f} Hz")
# Generate note
note_wave = generate_sine_wave(freq, note_duration, sample_rate)
full_audio.append(note_wave)
# Add silence (except after the last note)
if i < len(frequencies) - 1:
silence_wave = np.zeros(int(sample_rate * silence_duration), dtype=np.int16)
full_audio.append(silence_wave)
# Combine all parts into one array
combined_audio = np.concatenate(full_audio)
# Save to WAV file
output_filename = 'pythagorean_scale.wav'
wavfile.write(output_filename, sample_rate, combined_audio)
print(f"Scale saved to '{output_filename}'! You can play it in any media player.")