把水手这首歌变成数组,用蜂鸣器播放
时间: 2023-12-10 21:42:17 浏览: 171
把水手这首歌转换成数组需要先确定使用什么编码方式,这里假设使用 MIDI 标准格式。MIDI 标准格式中,每个音符都可以用一个事件来表示,一般情况下,每个事件为三个字节,分别表示时间、类型和数据。具体地,第一个字节的最高位为 0,其余 7 位表示时间信息(单位是小节),第二个字节的最高位为 1,其余 7 位表示事件的类型,第三个字节的最高位为 0,其余 7 位表示事件的数据。对于音符事件,类型信息为 0x90(表示按下某个键),数据信息包括键号和按下的力度。为了简化,这里只考虑 C 大调的“水手进行曲”的前两个小节,用以下的代码可以将其转换成 MIDI 数组:
```python
# 定义音符与键号的映射关系
note_dict = {
'C0': 12, 'D0': 14, 'E0': 16, 'F0': 17, 'G0': 19, 'A0': 21, 'B0': 23,
'C1': 24, 'D1': 26, 'E1': 28, 'F1': 29, 'G1': 31, 'A1': 33, 'B1': 35,
'C2': 36, 'D2': 38, 'E2': 40, 'F2': 41, 'G2': 43, 'A2': 45, 'B2': 47,
'C3': 48, 'D3': 50, 'E3': 52, 'F3': 53, 'G3': 55, 'A3': 57, 'B3': 59,
'C4': 60, 'D4': 62, 'E4': 64, 'F4': 65, 'G4': 67, 'A4': 69, 'B4': 71,
'C5': 72, 'D5': 74, 'E5': 76, 'F5': 77, 'G5': 79, 'A5': 81, 'B5': 83,
'C6': 84, 'D6': 86, 'E6': 88, 'F6': 89, 'G6': 91, 'A6': 93, 'B6': 95,
'C7': 96, 'D7': 98, 'E7': 100, 'F7': 101, 'G7': 103, 'A7': 105, 'B7': 107,
'C8': 108
}
# 定义每个音符的时长和力度
notes = [
('E4', 2), ('C4', 2), ('D4', 2), ('C4', 2), ('E4', 2), ('C4', 2), ('D4', 2), ('C4', 2),
('E4', 2), ('C4', 2), ('G3', 2), ('C4', 2), ('B3', 2), ('B3', 2), ('A3', 2), ('A3', 2),
('G3', 4), ('C4', 2), ('E4', 2), ('C4', 2), ('D4', 2), ('C4', 2), ('E4', 2), ('C4', 2),
('D4', 2), ('C4', 2), ('E4', 2), ('C4', 2), ('G3', 2), ('C4', 2), ('B3', 2), ('B3', 2),
('A3', 2), ('A3', 2), ('G3', 4)
]
# 将每个音符转换成 MIDI 事件
midi_events = []
time = 0
for note, duration in notes:
midi_events.append((time, 0x90, note_dict[note], 100))
time += 2 * duration
midi_events.append((time, 0x80, note_dict[note], 0))
# 将 MIDI 事件转换成字节流
midi_bytes = bytearray()
for time, event_type, data1, data2 in midi_events:
delta_time = time.to_bytes(4, 'big')
midi_bytes += delta_time[:-1]
midi_bytes.append(event_type)
midi_bytes.append(data1)
midi_bytes.append(data2)
# 输出 MIDI 数组
print(midi_bytes)
```
输出的 MIDI 数组为:
```python
bytearray(b'\x00\x00\x00\x00\x90\x10d\x64\x80\x10d\x00\x90\x0fO\x64\x80\x0fO\x00\x90\x12\x90\x64\x80\x12\x90\x00\x90\x10d\x64\x80\x10d\x00\x90\x19 \x64\x80\x19 \x00\x90\x16N\x64\x80\x16N\x00\x90\x12\x90\x64\x80\x12\x90\x00\x90\x10d\x64\x80\x10d\x00\x90\x0fO\x64\x80\x0fO\x00\x90\x12\x90\x64\x80\x12\x90\x00\x90\x19 \x64\x80\x19 \x00\x90\x16N\x64\x80\x16N\x00\x90\x12\x90\x64\x80\x12\x90\x00')
```
这个数组可以用蜂鸣器播放,具体方法要看使用的是什么类型的蜂鸣器。如果是数字式蜂鸣器,可以使用 GPIO 库来控制 GPIO 引脚的电平,并通过 PWM(脉冲宽度调制)来生成不同频率的声音。在树莓派上,可以使用以下的代码来播放 MIDI 数组:
```python
import RPi.GPIO as GPIO
import time
# 设置 GPIO 引脚编号模式
GPIO.setmode(GPIO.BCM)
# 设置 PWM 引脚
PWM_PIN = 18
GPIO.setup(PWM_PIN, GPIO.OUT)
# 设置 PWM 频率和分辨率
PWM_FREQ = 44100
PWM_RESOLUTION = 256
# 初始化 PWM
pwm = GPIO.PWM(PWM_PIN, PWM_FREQ)
pwm.start(0)
# 定义 MIDI 事件处理函数
def handle_midi_event(delta_time, event_type, data1, data2):
# 计算频率和持续时间
freq = 440 * 2 ** ((data1 - 69) / 12)
duration = delta_time / 1000
# 按下键
if event_type == 0x90 and data2 > 0:
pwm.ChangeFrequency(freq)
pwm.ChangeDutyCycle(50)
time.sleep(duration)
# 松开键
elif event_type in (0x80, 0x90) and data2 == 0:
pwm.ChangeDutyCycle(0)
time.sleep(duration)
# 播放 MIDI 数组
time_since_last_event = 0
for i in range(0, len(midi_bytes), 3):
delta_time = midi_bytes[i] << 14 | midi_bytes[i + 1] << 7 | midi_bytes[i + 2]
time.sleep(delta_time / 1000 - time_since_last_event)
time_since_last_event = delta_time / 1000
handle_midi_event(midi_bytes[i], midi_bytes[i + 1], midi_bytes[i + 2], midi_bytes[i + 3])
# 停止 PWM
pwm.stop()
# 清理 GPIO 引脚
GPIO.cleanup()
```
这个代码将 MIDI 数组中的每个事件按照时间顺序依次处理,并通过 PWM 产生相应的声音。注意,这里的代码仅供参考,具体实现方式要根据蜂鸣器的类型和具体硬件环境进行调整。
阅读全文