### 步骤概述
1. **定义音符频率**:
- 使用 MIDI 频率表来定义常见的音符频率。
2. **定义旋律**:
- 创建一个详细的旋律序列,模仿歌曲的部分旋律。
3. **生成音频数据**:
- 使用 Karplus-Strong 算法生成每个音符的音频数据。
- 将这些音符按顺序组合成完整的旋律。
4. **播放和保存**:
- 播放合并后的音频数据。
- 绘制音频信号图表。
- 保存音频文件。
通过这些步骤,应该能够成功生成并播放一个简化的旋律,并查看生成的音频信号图表。如果需要更复杂的旋律或调整音符和节奏,请进一步修改 `melody_notes` 和 `durations` 列表。
import argparse
import numpy as np
import matplotlib.pyplot as plt
from scipy.io.wavfile import write
import sounddevice as sd
import time
# MIDI 频率表
MIDI_FREQUENCIES = {
'C4': 261.63, 'D4': 293.66, 'E4': 329.63, 'F4': 349.23, 'G4': 392.00, 'A4': 440.00, 'B4': 493.88,
'C5': 523.25, 'D5': 587.33, 'E5': 659.26, 'F5': 698.46, 'G5': 783.99, 'A5': 880.00, 'B5': 987.77,
}
def karplus_strong_synthesis(frequency, duration, decay, sample_rate):
# 计算波长
wavelength = int(sample_rate / frequency)
# 初始化弦的状态
string_buffer = np.random.randn(wavelength).astype(np.float32)
string_buffer /= max(string_buffer.max(), abs(string_buffer.min()))
# 生成音频数据
num_samples = int(duration * sample_rate)
audio_data = np.zeros(num_samples)
buffer_index = 0
for i in range(num_samples):
audio_data[i] = string_buffer[buffer_index]
next_val = (string_buffer[buffer_index] + string_buffer[(buffer_index + 1) % wavelength]) / 2 * decay
string_buffer[buffer_index] = next_val
buffer_index = (buffer_index + 1) % wavelength
return audio_data
def plot_audio(audio_data, sample_rate):
time_axis = np.linspace(0, len(audio_data) / sample_rate, num=len(audio_data))
plt.figure(figsize=(10, 4))
plt.plot(time_axis, audio_data)
plt.title("Generated Audio Signal")
plt.xlabel("Time [seconds]")
plt.ylabel("Amplitude")
plt.grid(True)
plt.show()
def generate_melody(melody_notes, durations, decay, sample_rate):
total_duration = sum(durations)
audio_data = np.zeros(int(total_duration * sample_rate))
start_time = 0
for note, duration in zip(melody_notes, durations):
if note is not None:
freq = MIDI_FREQUENCIES[note]
note_audio = karplus_strong_synthesis(freq, duration, decay, sample_rate)
end_time = start_time + len(note_audio)
audio_data[start_time:end_time] += note_audio[:end_time-start_time]
start_time += int(duration * sample_rate)
# 归一化音频数据
audio_data /= max(audio_data.max(), abs(audio_data.min()))
return audio_data
def main():
parser = argparse.ArgumentParser(description="""Generating sounds with the Karplus-Strong algorithm.
This script allows you to generate and play a melody similar to the National Anthem using the Karplus-Strong string synthesis method.""")
parser.add_argument('--decay', type=float, default=0.996,
help='Decay factor for the string simulation (default: 0.996)')
parser.add_argument('--sample_rate', type=int, default=44100,
help='Sample rate for audio output (default: 44100)')
args = parser.parse_args()
# 定义《歌曲》的部分旋律(详细版)
melody_notes = [
'D5', 'D5', 'D5', 'F5', 'D5', 'G5', 'F5', 'D5', 'A5', 'G5',
'D5', 'D5', 'D5', 'F5', 'D5', 'G5', 'F5', 'D5', 'E5', 'D5',
'C5', 'C5', 'C5', 'E5', 'G5', 'F5', 'D5', 'D5', 'D5', 'F5',
'D5', 'G5', 'F5', 'D5', 'A5', 'G5', 'D5', 'D5', 'D5', 'F5',
'D5', 'G5', 'F5', 'D5', 'E5', 'D5', 'C5', 'C5', 'C5', 'E5',
'G5', 'F5', 'D5', 'D5', 'D5', 'F5', 'D5', 'G5', 'F5', 'D5',
'A5', 'G5', 'D5', 'D5', 'D5', 'F5', 'D5', 'G5', 'F5', 'D5',
'E5', 'D5', 'C5', 'C5', 'C5', 'E5', 'G5', 'F5', 'D5', 'D5',
'D5', 'F5', 'D5', 'G5', 'F5', 'D5', 'A5', 'G5', 'D5', 'D5',
'D5', 'F5', 'D5', 'G5', 'F5', 'D5', 'E5', 'D5', 'C5', 'C5'
]
# 定义每个音符的持续时间(秒)
durations = [
0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 1.0,
0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 1.0,
0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 1.0,
0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 1.0,
0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 1.0,
0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 1.0,
0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 1.0,
0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 1.0,
0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 1.0,
0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 1.0
]
# 生成旋律
combined_audio_data = generate_melody(melody_notes, durations, args.decay, args.sample_rate)
# 播放声音
sd.play(combined_audio_data, samplerate=args.sample_rate)
sd.wait() # 等待播放完成
# 绘制声音信号
plot_audio(combined_audio_data, args.sample_rate)
# 保存声音文件
timestamp = time.strftime("%Y%m%d-%H%M%S")
filename = f"national_anthem_{timestamp}.wav"
write(filename, args.sample_rate, (combined_audio_data * 32767).astype(np.int16))
print(f"Melody saved to {filename}")
if __name__ == "__main__":
main()