호도법과 관련해서는 아래글을 참고
https://blog.naver.com/PostView.naver?blogId=welcomelady&logNo=222409978578&parentCategoryNo=&categoryNo=26&viewDate=&isShowPopularPosts=true&from=search
헷갈릴때는 이 비례만 기억하자
360 : 2 x pi x r = 1rad : r
np.sin에는 radian을 넣어주어야 한다. 더 정확히는 파동이 몇번의 주기를 지났는지 혹은 파동을 그리는 원형 운동이 몇 바퀴를 회전했는지를 넣어주어야 한다. 이를 period라고 부를 수 있다. period는 frequency * time이다.
frequency = 440 # 주파수를 Hz단위로 표현
amplitude = 0.5 # Digital audio signal을 표현할 때 최대 최소 값이 [-1, 1]
duration = 3 # 길이를 초로 표현(sec)
sample_rate = 16000 # (samples/sec)
# 3초에 대항하는 샘플의 수가 얼마인지를 계산
total_number_of_samples = duration * sample_rate
# 48000개의 샘플
time_frame = np.arange(total_number_of_samples)
# time_frame을 second 단위로 변환
time_frame_in_sec = time_frame / sample_rate
# sin파 = sin(각속도 * time)
sine_wave = np.sin(2 * np.pi * (frequency * time_frame_in_sec)) * amplitude
# Frequency (period / sec) * time (sec) = period
# 즉 주어진 시간 동안 몇번 주기를 반복했는지 (몇 바퀴 회전하였는지)
# 한바퀴 회전을 각(radian)으로 표현하면 2*pi
plt.plot(sine_wave[:100])
soundfile은 wave 파일로 만들어준 파형을 저장을 해준다. 이를 librosa를 통해 불러오면 해상도를 default Hz인 22050으로 다시 자르게 된다. 그렇게 되면 이미지처럼 음파도 열화가 되는걸까?
import soundfile as sf
sf.write('clipping.wav', data=sine_wave, samplerate=sample_rate)
sine_wave_load, sr_load = librosa.load('clipping.wav')
print(sine_wave_load.shape, sr_load)
# (66150,) 22050
ipd.display(ipd.Audio(sine_wave_load, rate=sr_load))
plt.plot(sine_wave_load[:100])
def make_sine_wave(freq, amp, dur, sr=16000):
'''
입력 : Frequency, Amplitude, Duration, sample rate
출력 : 1D array
'''
total_number_of_samples = dur * sr
time_frame = np.arange(total_number_of_samples)
time_frame_in_sec = time_frame / sr
sine_wave = np.sin(2 * np.pi * (freq * time_frame_in_sec)) * amp
return sine_wave
fund = make_sine_wave(220, 0.8, 3)
harmonic_1 = make_sine_wave(220 * 2, 0.5, 3)
harmonic_2 = make_sine_wave(220 * 4, 0.5, 3)
combine = fund + harmonic_1 + harmonic_2
plt.plot(combine[:200])
ipd.Audio(combine, rate=sample_rate)
import random
num_harmonics = 10
fund_frequency = 220
fund = make_sine_wave(fund_frequency, 1, 3)
for i in range(2,50):
harmonics = make_sine_wave(fund_frequency * i, random.random()/i, 3)
fund += harmonics
plt.plot(fund[:300])
ipd.Audio(fund, rate=sample_rate)
class Oscilator:
def __init__(self, frequency, amplitude=1, sample_rate=16000):
self.frequency = frequency
self.amplitude = amplitude
self.sample_rate = 16000
def generate(self, duration):
return make_sine_wave(self.frequency, self.amplitude, duration, sr=self.sample_rate)
def generate_with_harmonics(self, duration): # class method의 첫번째 인풋은 self
fund = make_sine_wave(self.frequency, self.amplitude, duration)
for i in range(2,50):
harmonics = make_sine_wave(self.frequency * i, self.calculate_harmonics_amplitude(i), duration)
fund += harmonics
return fund
def calculate_harmonics_amplitude(self, i):
return self.amplitude
oscil_a = Oscilator(440)
print(oscil_a.frequency)
output = oscil_a.generate_with_harmonics(5)
ipd.display(ipd.Audio(oscil_a.generate(5), rate=oscil_a.sample_rate))
plt.plot(output[:100])
class RandomOscil(Oscilator):
def __init__(self, freq, amp, sr):
# 부모 클래스의 init을 진행하는 코드
super().__init__(freq, amp, sr)
def calculate_harmonics_amplitude(self, i):
return random.random() * self.amplitude / i
rand_oscil = RandomOscil(440, 0.3, 16000)
print(rand_oscil.frequency)
output = rand_oscil.generate_with_harmonics(4)
plt.plot(output[:100])
ipd.Audio(output, rate=rand_oscil.sample_rate)
class SawOscil(Oscilator):
def __init__(self, freq, amp, sr):
super().__init__(freq, amp, sr)
def calculate_harmonics_amplitude(self, i):
return self.amplitude / i
saw_oscil = SawOscil(440, 0.3, 16000)
print(saw_oscil.frequency)
output = saw_oscil.generate_with_harmonics(4)
plt.plot(output[:100])
ipd.Audio(output, rate = saw_oscil.sample_rate)