一起玩转树莓派(5)——让蜂鸣器播放音乐

简介: 前面博客中,我们尝试使用开关控制有源蜂鸣器的播放。有源蜂鸣器的一大特点是使用简单,无需复杂的程序控制即可发声,然而其缺陷也很明显,其发声的频率是一定的,我们无法通过频率控制器音调高低。本次实验,我们将尝试使用无源蜂鸣器来进行音乐的播放。

一起玩转树莓派(5)——让蜂鸣器播放音乐

前面博客中,我们尝试使用开关控制有源蜂鸣器的播放。有源蜂鸣器的一大特点是使用简单,无需复杂的程序控制即可发声,然而其缺陷也很明显,其发声的频率是一定的,我们无法通过频率控制器音调高低。本次实验,我们将尝试使用无源蜂鸣器来进行音乐的播放。

一、实验前的准备

在开始本实验前,我们先来对乐理只是和无源蜂鸣器的工作原理进行简单的学习。

1、关于音调的基础乐理知识

简单的物理学知识告诉我们,声音的声调高低是有声波的频率决定的,而声音的大小是有声波的振幅决定的。人耳可以听到的声音频率在20Hz-20000Hz之间。频率低于20Hz的声波被称为次声波,我们听不到。同样,频率高于20000Hz的声波称为超声波,我们也听不到。生活中,我们都喜欢听美妙的音乐,音乐是由很多不同的音调和参差的拍子组合而成的。小时候音乐课上常说的Do,Re,Mi,Fa,So,La,Xi就是最常见的音调。我们以C大调为例,其7个音阶与对应的频率如下表所示:

| Do | Re | Mi | Fa | So | La | Xi |
| 262 | 294 | 330 | 350 | 393 | 441 | 495 |

与上表对应,如果是高音音调,只需要将上面的频率乘以2,如果是低音音调,需要将上面的音频除以2。

要演奏出美妙的旋律,仅仅只控制音调是不够的,我们还需要把握每个音调演奏的节奏,即每个音调播放的时长。在乐曲中,节奏的把控是通过节拍来定义的,例如常见的四分之四节拍,指的是四分音符为一拍,每小节有四拍,这样,每遇到一个四分音符我们就播放这个音调一拍的单位时间,如果遇到八分音符,我们就播放二分之一拍的单位时间,如果遇到二分音符,我们就播放两拍的单位时间。

现在,你可以练习一下,将喜欢的歌曲翻译为编程符号,音调翻译成频率,对应的节拍翻译为毫秒时间。后面,我们将使用树莓派控制蜂鸣器来播放它。

2、无源蜂鸣器播放声音原理

之前,我们分析过有源蜂鸣器的工作原理,其很好理解,电压触发器内部的振荡源工作,震荡源的震动产生一定频率的声波,从而发出声音让我们听到。无源蜂鸣器内部没有震荡源,如果直接通过直流电,其无法产生周期性的声波,因此简单的接通电源是无法使无源蜂鸣器工作的。无源蜂鸣器内部没有震荡源,但却有震荡片,当通电时,无源蜂鸣器通过电磁感应现象来吸引震荡片,当失去电流时,震荡片位置还原。因此,我们只要周期性的给蜂鸣器通电,其就会产生周期性的振荡,从而产生声波,发出声音。

由于无源蜂鸣器的这种特点,我们可以非常容易的通过控制加压的频率来实现播放不同音调的声音。相比有源蜂鸣器而言,无源蜂鸣器成本更低,且可以控制音调高低,唯一的不足之处是程序要略微复杂一些。

本次实验,我们使用的无源蜂鸣器是低电平触发的,如下图所示:

二、使用树莓派制作电子琴

明白了无源蜂鸣器的工作原理,本实验对你来说应该非常简单,使用到的知识都是我们之前实验有涉及过的。当你听到视频频率来控制无缘蜂鸣器的发声时,你一定已经想到了,使用PWM脉冲宽度调制技术,其刚好可以产生指定频率的脉冲电压。

1.开始连线

连线本身非常简单,只是在开始之前,我们要先确定所使用的树莓派引脚。无缘蜂鸣器有3个引脚,其中VCC接3.3V电源,GMD接地,I/O控制引脚接一个树莓派的GPIO功能引脚,我们选择BCM编码为17的GPIO引脚,即物理引脚11。笔者这里使用的扩展板连接如下图所示:

这一步非常简单,下面我们来开始程序的编写。

2.开始动手编程

在本实验中,我们将编写这个一个程序,其类似一个简易的电子琴,有7个按键来发出不同音调的7种声音,同时我们内置一首示例乐曲。笔者这里选用周杰伦的《花海》前奏作为示例乐曲。完整的程序示例代码如下(我建议先自主动手实践,遇到问题再参考示例代码):

#coding:utf-8

# 导入UI模块
import tkinter as Tkinter
import RPi.GPIO as GPIO
import time

# 定义音调频率
# C调低音
CL = [0, 131, 147, 165, 175, 196, 211, 248] 
# C调中音
CM = [0, 262, 294, 330, 350, 393, 441, 495]   
# C调高音
CH = [0, 525, 589, 661, 700, 786, 882, 990]  

# 定义乐谱
# 音调 0表示休止符
songP = [
    CM[1],CM[2],CM[3],CM[5],CM[5],CM[0],CM[3],CM[2],CM[1],CM[2],CM[3],CM[0],
    CM[1],CM[2],CM[3],CM[7],CH[1],CH[1],CH[1],CM[7],CH[1],CM[7],CM[6],CM[5],CM[0],
    CM[1],CM[2],CM[3],CM[5],CM[5],CM[0],CM[3],CM[2],CM[1],CM[2],CM[1],CM[0],
    CM[1],CM[2],CM[3],CM[5],CM[1],CM[0],CM[1],CL[7],CL[6],CL[7],CM[1],CM[0]
]
# 音调对应的节拍
songT = [
    2,2,2,1,5,4,2,2,2,1,5,4,
    2,2,2,1,5,2,2,2,1,3,2,4,4,
    2,2,2,1,5,4,2,2,2,1,3,5,
    2,2,2,1,5,4,2,2,2,2,8,2
]
# 定义标准节拍时间
metre = 0.125

# 初始化要使用的引脚
io = 11
GPIO.setmode(GPIO.BOARD)
GPIO.setup(io, GPIO.OUT)
pwm = GPIO.PWM(io, 440)
# 开始设置占空比为100 则无缘蜂鸣器不会发声
pwm.start(100)

# 播放示例乐曲的方法
def playSong():
    # 修改占空比为50 此时频率生效
    pwm.ChangeDutyCycle(50)
    # 遍历所有音调
    for i in range(0, len(songP)):
        # 0表示休止 禁声
        if songP[i] == 0:
            pwm.ChangeDutyCycle(100)
        # 更改频率控制音调
        else:
            pwm.ChangeDutyCycle(50)
            pwm.ChangeFrequency(songP[i])
        # 通过节拍控制播放时间
        time.sleep(songT[i] * metre) 
    # 禁声
    pwm.ChangeDutyCycle(100)

# 分别播放各个音阶的方法
def playDo():
    pwm.ChangeDutyCycle(50)
    pwm.ChangeFrequency(CM[1])
    time.sleep(0.5)
    pwm.ChangeDutyCycle(100)

def playRe():
    pwm.ChangeDutyCycle(50)
    pwm.ChangeFrequency(CM[2])
    time.sleep(0.5)
    pwm.ChangeDutyCycle(100)

def playMi():
    pwm.ChangeDutyCycle(50)
    pwm.ChangeFrequency(CM[3])
    time.sleep(0.5)
    pwm.ChangeDutyCycle(100)

def playFa():
    pwm.ChangeDutyCycle(50)
    pwm.ChangeFrequency(CM[4])
    time.sleep(0.5)
    pwm.ChangeDutyCycle(100)

def playSo():
    pwm.ChangeDutyCycle(50)
    pwm.ChangeFrequency(CM[5])
    time.sleep(0.5)
    pwm.ChangeDutyCycle(100)

def playLa():
    pwm.ChangeDutyCycle(50)
    pwm.ChangeFrequency(CM[6])
    time.sleep(0.5)
    pwm.ChangeDutyCycle(100)

def playXi():
    pwm.ChangeDutyCycle(50)
    pwm.ChangeFrequency(CM[7])
    time.sleep(0.5)
    pwm.ChangeDutyCycle(100)


# UI相关设置
# 主页面设置
top = Tkinter.Tk()
top.geometry('360x300')
top.minsize(420, 300) 
top.maxsize(420, 300)
top.title("自制电子琴")
l = Tkinter.Label(top, text='自制电子琴', font=('Arial', 18), width=30, height=2)
l.pack()


# UI上的按钮布局
songBtn = Tkinter.Button(top, text="示例歌曲:花海", command=playSong)
songBtn.place(x=30,y=30,width=120,height=40)

doBtn = Tkinter.Button(top,bitmap="gray50", text="Do", compound=Tkinter.LEFT, command=playDo)
doBtn.place(x=0,y=105,width=60,height=200)

reBtn = Tkinter.Button(top,bitmap="gray50", text="Re", compound=Tkinter.LEFT, command=playRe)
reBtn.place(x=60,y=105,width=60,height=200)

miBtn = Tkinter.Button(top,bitmap="gray50", text="Mi", compound=Tkinter.LEFT, command=playMi)
miBtn.place(x=120,y=105,width=60,height=200)

faBtn = Tkinter.Button(top,bitmap="gray50", text="Fa", compound=Tkinter.LEFT, command=playFa)
faBtn.place(x=180,y=105,width=60,height=200)

soBtn = Tkinter.Button(top,bitmap="gray50", text="So", compound=Tkinter.LEFT, command=playSo)
soBtn.place(x=240,y=105,width=60,height=200)

laBtn = Tkinter.Button(top,bitmap="gray50", text="La", compound=Tkinter.LEFT, command=playLa)
laBtn.place(x=300,y=105,width=60,height=200)

xiBtn = Tkinter.Button(top,bitmap="gray50", text="Xi", compound=Tkinter.LEFT, command=playXi)
xiBtn.place(x=360,y=105,width=60,height=200)

stopButton = Tkinter.Button(top, text="关闭")
stopButton.place(x=340,y=30,width=60,height=40)


# 进入消息循环
top.mainloop()



上面代码有比较详尽的注释,程序运行的UI效果如下图所示:

现在,尽情的玩耍吧!

专注技术,懂的热爱,愿意分享,做个朋友
目录
相关文章
|
网络虚拟化 网络架构
计算机网络实验(华为eNSP模拟器)——第三章 配置IP地址和网关
计算机网络实验(华为eNSP模拟器)——第三章 配置IP地址和网关
计算机网络实验(华为eNSP模拟器)——第三章 配置IP地址和网关
|
编解码 物联网
【BLE】蓝牙5.2 新特性 - LE Audio
连接同步通道是基于蓝牙连接的,首先要先建立ble连接基于时间同步的音频传输机制,可以实现多个设备的数据同步一个master可以建立多个CIG每个CIG可以最多31个CIS每个CIS里面最多有31个subevent链路层有LL_CIS_REQ 和 LL_CIS_RSP来创建CIS无连接的单向的,无应答机制广播通道,对接收者的数量没有限制不仅可以广播数据包还可以广播控制包每个big里面最多可以包含31个bis。
3065 1
【BLE】蓝牙5.2 新特性 - LE Audio
|
人工智能 自然语言处理 机器人
谷歌AI Gemin怎么使用?Gemini国内使用指南!(2024.8.19)
从自然语言处理(NLP)到对话生成,AI语言模型已经成为科技界的一个重要组成部分。在众多杰出的AI语言模型中,Gemini凭借其卓越的性能和广泛的应用而脱颖而出。作为谷歌旗下的多模态AI巨头,Gemini融合了最先进的语言处理技术,为用户提供了无与伦比的语言理解和生成能力。
|
Linux Go
【Linux基础】 文件基本属性
Linux文件基本属性是指文件或目录在Linux系统中具有的一系列特性和信息。这些属性提供了关于文件或目录的详细信息,包括其类型、权限、大小、创建和修改时间等。本篇文章带你详细了解Linux属性概念,以及怎样更改文件属性。
268 0
【Linux基础】 文件基本属性
|
机器学习/深度学习 人工智能 算法
神经网络自适应PID控制及其应用
神经网络自适应PID控制及其应用
3434 0
神经网络自适应PID控制及其应用
|
机器学习/深度学习 计算机视觉
【YOLOv8改进】MSFN(Multi-Scale Feed-Forward Network):多尺度前馈网络
**HCANet: 高光谱图像去噪新方法**\n混合卷积与注意力网络(Hybrid Convolutional and Attention Network)是针对HSI去噪的创新模型,结合CNN和Transformer,强化全局与局部特征。它使用卷积注意力融合模块捕获长距离依赖和局部光谱相关性,多尺度前馈网络提升多尺度信息聚合。代码可在[GitHub](https://github.com/summitgao/HCANet)获取。
|
安全 网络安全 数据安全/隐私保护
阿里云服务器不能发邮件禁用25端口的三种解决方法
阿里云服务器不能发邮件禁用25端口的三种解决方法
1856 0
|
关系型数据库 API 数据库
基于Patroni的PostgreSQL高可用环境部署
在部署PostgreSQL到生产环境中时,选择适合的高可用方案是一项必不可少的工作。本文介绍基于Patroni的PostgreSQL高可用的部署方法,供大家参考。
7836 1
|
传感器 Python
一起玩转树莓派(4)——用开关控制蜂鸣器发声
在本系列的前几篇博客中,我们使用桌面软件控制过红绿灯,也编程点亮过炫彩的3色LED 灯。在这些实验的过程中,相信你对树莓派GPIO引脚如何输出高低电平,以及如何使用PWM技术来控制电压输出等都有了了解。本篇博客,我们将再探索一些树莓派编程更多新鲜好玩的领域,来尝试使用硬件来控制硬件。
1684 0
|
前端开发 算法 开发工具
工作两年,本地git分支达到了惊人的361个,该怎么快速清理呢?
工作两年,本地git分支达到了惊人的361个,该怎么快速清理呢?
881 0