<!DOCTYPE html><html lang="zh" data-theme="dark"><head><meta charset="utf-8"><meta name="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>树莓派 Pico 通过有源蜂鸣器播放音频 · Cytrogen 的个人博客</title><meta name="description" content="本文是一篇使用 MicroPython 在树莓派 Pico 上通过有源蜂鸣器播放音乐的详细教程。文章以《超级马里奥》主题曲为例,从零开始指导你如何将在线乐谱(来自 Online Sequencer)解析为程序可用的数据结构,并利用 MicroPython 的 PWM 功能精确控制蜂鸣器的频率和发声时长。内容不仅涵盖了代码实现,还为不熟悉乐理的读者科普了音符、节拍等基础知识,非常适合 Pico 入门玩家和对硬件音乐项目感兴趣的朋友。"><link rel="icon" href="../favicon.png"><link rel="canonical" href="https://cytrogen.icu/posts/ba6a.html"><link rel="webmention" href="https://webmention.io/cytrogen.icu/webmention"><link rel="me" href="https://m.otter.homes/@Cytrogen"><link rel="me" href="https://github.com/cytrogen"><meta name="fediverse:creator" content="@Cytrogen@m.otter.homes"><link rel="preload" href="../fonts/opensans-regular-latin.woff2" as="font" type="font/woff2" crossorigin="anonymous"><style>@font-face {
font-family: 'Open Sans';
src: url('../fonts/opensans-regular-latin.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
size-adjust: 107%;
ascent-override: 97%;
descent-override: 25%;
line-gap-override: 0%;
}
</style><script>(function() {
try {
// 优先级:用户选择 > 系统偏好 > 默认浅色
const saved = localStorage.getItem('theme');
const theme = saved ||
(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
document.documentElement.style.colorScheme = theme;
} catch (error) {
// 失败时使用默认主题,不阻塞渲染
document.documentElement.setAttribute('data-theme', 'light');
}
})();
</script><link rel="stylesheet" href="../css/ares.css"><script data-netlify-skip-bundle="true">(function() {
document.addEventListener('DOMContentLoaded', function() {
const theme = document.documentElement.getAttribute('data-theme');
const pageWrapper = document.getElementById('page-wrapper');
if (pageWrapper && theme) {
pageWrapper.setAttribute('data-theme', theme);
}
});
})();
</script><!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 8.1.1"><link rel="alternate" href="atom.xml" title="Cytrogen 的个人博客" type="application/atom+xml">
</head><body><div id="page-wrapper"><a class="skip-link" href="#main-content">跳到主要内容</a><div class="wrap"><header><a class="logo-link" href="../index.html"><img src="../favicon.png" alt="logo"></a><div class="h-card visually-hidden"><img class="u-photo" src="https://cytrogen.icu/favicon.png" alt="Cytrogen"><a class="p-name u-url u-uid" href="https://cytrogen.icu">Cytrogen</a><p class="p-note">Cytrogen 的个人博客,Cytrogen's Blog</p><a class="u-url" rel="me noopener" target="_blank" href="https://m.otter.homes/@Cytrogen">Mastodon</a><a class="u-url" rel="me noopener" target="_blank" href="https://github.com/cytrogen">GitHub</a></div><nav class="site-nav"><div class="nav-main"><div class="nav-primary"><ul class="nav-list hidden-mobile"><li class="nav-item"><a class="nav-link" href="../index.html">首页</a></li></ul><div class="nav-tools"><div class="language-menu"><button class="language-toggle" type="button"><svg class="icon icon-globe" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 1.855A7.97 7.97 0 0 0 5.145 4H7.5V1.077zM4.09 4a9.267 9.267 0 0 1 .64-1.539 6.7 6.7 0 0 1 .597-.933A7.025 7.025 0 0 0 2.255 4H4.09zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a6.958 6.958 0 0 0-.656 2.5h2.49zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5H4.847zM8.5 5v2.5h2.99a12.495 12.495 0 0 0-.337-2.5H8.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5H4.51zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5H8.5zM5.145 12c.138.386.295.744.468 1.068.552 1.035 1.218 1.65 1.887 1.855V12H5.145zm.182 2.472a6.696 6.696 0 0 1-.597-.933A9.268 9.268 0 0 1 4.09 12H2.255a7.024 7.024 0 0 0 3.072 2.472zM3.82 11a13.652 13.652 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5H3.82zm6.853 3.472A7.024 7.024 0 0 0 13.745 12H11.91a9.27 9.27 0 0 1-.64 1.539 6.688 6.688 0 0 1-.597.933zM8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855A7.97 7.97 0 0 0 10.855 12H8.5zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.65 13.65 0 0 1-.312 2.5zm2.802-3.5a6.959 6.959 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5h2.49zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7.024 7.024 0 0 0-3.072-2.472c.218.284.418.598.597.933zM10.855 4a7.966 7.966 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4h2.355z"></path></svg><span>中文</span></button><div class="language-dropdown"></div></div></div><div class="nav-controls"><div class="more-menu hidden-mobile"><button class="more-toggle" type="button"><span>更多</span><svg class="icon icon-chevron-down" width="12" height="12" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 8.825c-.2 0-.4-.1-.5-.2l-3.3-3.3c-.3-.3-.3-.8 0-1.1s.8-.3 1.1 0l2.7 2.7 2.7-2.7c.3-.3.8-.3 1.1 0s.3.8 0 1.1l-3.3 3.3c-.1.1-.3.2-.5.2z"></path></svg></button><div class="more-dropdown"><ul class="dropdown-list"><li class="dropdown-item"><a class="nav-link" href="../archives/index.html">归档</a></li><li class="dropdown-item"><a class="nav-link" href="../categories/index.html">分类</a></li><li class="dropdown-item"><a class="nav-link" href="../tags/index.html">标签</a></li><li class="dropdown-item"><a class="nav-link" href="../about/index.html">关于</a></li><li class="dropdown-item"><a class="nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></div><div class="theme-switcher"><button class="theme-toggle" type="button" role="switch" aria-pressed="false" aria-label="切换主题"><div class="theme-icon moon-icon"><svg class="icon icon-moon" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"></path></svg></div><div class="theme-icon sun-icon"><svg class="icon icon-sun" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"></path></svg></div></button></div><details class="mobile-menu-details hidden-desktop"><summary class="hamburger-menu" aria-label="nav.menu"><svg class="icon icon-bars" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"></path></svg><span class="menu-text">nav.menu</span></summary><div class="mobile-menu-dropdown"><ul class="mobile-nav-list"><li class="mobile-nav-item"><a class="mobile-nav-link" href="../index.html">首页</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../archives/index.html">归档</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../categories/index.html">分类</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../tags/index.html">标签</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../about/index.html">关于</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></details></div></div></div></nav></header><main class="container" id="main-content" tabindex="-1"><div class="post"><article class="post-block h-entry"><div class="post-meta p-author h-card visually-hidden"><img class="author-avatar u-photo" src="../favicon.png" alt="Cytrogen"><span class="p-name">Cytrogen</span><a class="u-url" href="https://cytrogen.icu">https://cytrogen.icu</a></div><a class="post-permalink u-url u-uid visually-hidden" href="https://cytrogen.icu/posts/ba6a.html">永久链接</a><div class="p-summary visually-hidden"><p>如何使用 MicroPython 在树莓派 Pico 上通过有源蜂鸣器播放音频。</p></div><div class="visually-hidden"><a class="p-category" href="../categories/%E7%BC%96%E7%A8%8B%E7%AC%94%E8%AE%B0/">编程笔记</a><a class="p-category" href="../tags/Python/">Python</a><a class="p-category" href="../tags/%E6%A0%91%E8%8E%93%E6%B4%BE/">树莓派</a></div><h1 class="post-title p-name">树莓派 Pico 通过有源蜂鸣器播放音频</h1><div class="post-info"><time class="post-date dt-published" datetime="2023-05-15T22:49:48.000Z">5/15/2023</time><time class="dt-updated visually-hidden" datetime="2026-02-09T17:16:55.401Z"></time></div><div class="post-content e-content"><html><head></head><body><p>如何使用 MicroPython 在树莓派 Pico 上通过有源蜂鸣器播放音频。</p>
<span id="more"></span>
<h1 id="目录"><a class="markdownIt-Anchor" href="#目录"></a> 目录</h1>
<ul>
<li><a href="#%E7%9B%AE%E5%BD%95">目录</a></li>
<li><a href="#%E5%89%8D%E8%A8%80">前言</a></li>
<li><a href="#%E5%87%86%E5%A4%87">准备</a>
<ul>
<li><a href="#%E5%AF%BC%E5%85%A5%E5%BA%93">导入库</a></li>
<li><a href="#%E9%9F%B3%E7%AC%A6%E4%B8%8E%E9%A2%91%E7%8E%87">音符与频率</a>
<ul>
<li><a href="#%E9%A2%98%E5%A4%96%E8%AF%9D">题外话</a></li>
</ul>
</li>
<li><a href="#%E8%AE%BE%E7%BD%AE%E8%9C%82%E9%B8%A3%E5%99%A8">设置蜂鸣器</a></li>
</ul>
</li>
<li><a href="#%E7%BC%96%E5%86%99%E4%BB%A3%E7%A0%81">编写代码</a>
<ul>
<li><a href="#%E8%8E%B7%E5%8F%96%E4%B9%90%E8%B0%B1">获取乐谱</a></li>
<li><a href="#%E6%92%AD%E6%94%BE%E9%9F%B3%E9%A2%91">播放音频</a></li>
<li><a href="#%E6%92%AD%E6%94%BE%E4%B9%90%E8%B0%B1">播放乐谱</a></li>
</ul>
</li>
<li><a href="#%E6%9C%80%E7%BB%88%E6%95%88%E6%9E%9C">最终效果</a></li>
</ul>
<center> ————————</center>
<h1 id="前言"><a class="markdownIt-Anchor" href="#前言"></a> 前言</h1>
<p>我的树莓派 Pico 早在过年时就到手了,但是一直没有时间玩,最近闲到开始生一种叫做 senioritis 的病,才开始捣鼓起来。</p>
<p>买的时候贪方便,直接购入了已焊接的板子,如下图所示:</p>
<img src="/posts/ba6a/1.jpeg" alt="创乐博MAKEROBO PicoBlock扩展板,已焊接树莓派Pico主板、面包板、LED灯、方向按键、有源蜂鸣器、传感器等套件">
<p>不过本文内容专注于 MicroPython,所以不会细讲硬件部分。如何配置树莓派 Pico 和安装 IDE 等基础内容也不会讲,可以自行搜索。</p>
<div class="danger">
<ul>
<li>本文使用的 IDE 为 Thonny</li>
<li>本文乐谱均来自于 <a target="_blank" rel="noopener" href="https://onlinesequencer.net/">Online Sequencer</a> 这个网站</li>
</ul>
</div>
<center>————————</center>
<h1 id="准备"><a class="markdownIt-Anchor" href="#准备"></a> 准备</h1>
<h2 id="导入库"><a class="markdownIt-Anchor" href="#导入库"></a> 导入库</h2>
<figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> machine <span class="keyword">import</span> PWM, Pin</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li><code>time</code> 库自不用多说,是用来延时的</li>
<li><code>machine</code> 库则包含了和特定电路板上的硬件有关的方法,例如我们刚刚导入的 <code>PWM</code> 和 <code>Pin</code>。详细的内容可以参考 <a target="_blank" rel="noopener" href="https://docs.micropython.org/en/latest/library/machine.html">MicroPython 官方文档</a></li>
</ul>
<h2 id="音符与频率"><a class="markdownIt-Anchor" href="#音符与频率"></a> 音符与频率</h2>
<figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">tones = {</span><br><span class="line"> <span class="string">'C0'</span>: <span class="number">16</span>, <span class="string">'C#0'</span>: <span class="number">17</span>, <span class="string">'D0'</span>: <span class="number">18</span>, <span class="string">'D#0'</span>: <span class="number">19</span>, <span class="string">'E0'</span>: <span class="number">21</span>, <span class="string">'F0'</span>: <span class="number">22</span>,</span><br><span class="line"> <span class="string">'F#0'</span>: <span class="number">23</span>, <span class="string">'G0'</span>: <span class="number">24</span>, <span class="string">'G#0'</span>: <span class="number">26</span>, <span class="string">'A0'</span>: <span class="number">28</span>, <span class="string">'A#0'</span>: <span class="number">29</span>, <span class="string">'B0'</span>: <span class="number">31</span>,</span><br><span class="line"> <span class="string">'C1'</span>: <span class="number">33</span>, <span class="string">'C#1'</span>: <span class="number">35</span>, <span class="string">'D1'</span>: <span class="number">37</span>, <span class="string">'D#1'</span>: <span class="number">39</span>, <span class="string">'E1'</span>: <span class="number">41</span>, <span class="string">'F1'</span>: <span class="number">44</span>,</span><br><span class="line"> <span class="string">'F#1'</span>: <span class="number">46</span>, <span class="string">'G1'</span>: <span class="number">49</span>, <span class="string">'G#1'</span>: <span class="number">52</span>, <span class="string">'A1'</span>: <span class="number">55</span>, <span class="string">'A#1'</span>: <span class="number">58</span>, <span class="string">'B1'</span>: <span class="number">62</span>,</span><br><span class="line"> <span class="string">'C2'</span>: <span class="number">65</span>, <span class="string">'C#2'</span>: <span class="number">69</span>, <span class="string">'D2'</span>: <span class="number">73</span>, <span class="string">'D#2'</span>: <span class="number">78</span>, <span class="string">'E2'</span>: <span class="number">82</span>, <span class="string">'F2'</span>: <span class="number">87</span>,</span><br><span class="line"> <span class="string">'F#2'</span>: <span class="number">92</span>, <span class="string">'G2'</span>: <span class="number">98</span>, <span class="string">'G#2'</span>: <span class="number">104</span>, <span class="string">'A2'</span>: <span class="number">110</span>, <span class="string">'A#2'</span>: <span class="number">117</span>, <span class="string">'B2'</span>: <span class="number">123</span>,</span><br><span class="line"> <span class="string">'C3'</span>: <span class="number">131</span>, <span class="string">'C#3'</span>: <span class="number">139</span>, <span class="string">'D3'</span>: <span class="number">147</span>, <span class="string">'D#3'</span>: <span class="number">156</span>, <span class="string">'E3'</span>: <span class="number">165</span>, <span class="string">'F3'</span>: <span class="number">175</span>,</span><br><span class="line"> <span class="string">'F#3'</span>: <span class="number">185</span>, <span class="string">'G3'</span>: <span class="number">196</span>, <span class="string">'G#3'</span>: <span class="number">208</span>, <span class="string">'A3'</span>: <span class="number">220</span>, <span class="string">'A#3'</span>: <span class="number">233</span>, <span class="string">'B3'</span>: <span class="number">247</span>,</span><br><span class="line"> <span class="string">'C4'</span>: <span class="number">262</span>, <span class="string">'C#4'</span>: <span class="number">277</span>, <span class="string">'D4'</span>: <span class="number">294</span>, <span class="string">'D#4'</span>: <span class="number">311</span>, <span class="string">'E4'</span>: <span class="number">330</span>, <span class="string">'F4'</span>: <span class="number">349</span>,</span><br><span class="line"> <span class="string">'F#4'</span>: <span class="number">370</span>, <span class="string">'G4'</span>: <span class="number">392</span>, <span class="string">'G#4'</span>: <span class="number">415</span>, <span class="string">'A4'</span>: <span class="number">440</span>, <span class="string">'A#4'</span>: <span class="number">466</span>, <span class="string">'B4'</span>: <span class="number">494</span>,</span><br><span class="line"> <span class="string">'C5'</span>: <span class="number">523</span>, <span class="string">'C#5'</span>: <span class="number">554</span>, <span class="string">'D5'</span>: <span class="number">587</span>, <span class="string">'D#5'</span>: <span class="number">622</span>, <span class="string">'E5'</span>: <span class="number">659</span>, <span class="string">'F5'</span>: <span class="number">698</span>,</span><br><span class="line"> <span class="string">'F#5'</span>: <span class="number">740</span>, <span class="string">'G5'</span>: <span class="number">784</span>, <span class="string">'G#5'</span>: <span class="number">831</span>, <span class="string">'A5'</span>: <span class="number">880</span>, <span class="string">'A#5'</span>: <span class="number">932</span>, <span class="string">'B5'</span>: <span class="number">988</span>,</span><br><span class="line"> <span class="string">'C6'</span>: <span class="number">1047</span>, <span class="string">'C#6'</span>: <span class="number">1109</span>, <span class="string">'D6'</span>: <span class="number">1175</span>, <span class="string">'D#6'</span>: <span class="number">1245</span>, <span class="string">'E6'</span>: <span class="number">1319</span>, <span class="string">'F6'</span>: <span class="number">1397</span>,</span><br><span class="line"> <span class="string">'F#6'</span>: <span class="number">1480</span>, <span class="string">'G6'</span>: <span class="number">1568</span>, <span class="string">'G#6'</span>: <span class="number">1661</span>, <span class="string">'A6'</span>: <span class="number">1760</span>, <span class="string">'A#6'</span>: <span class="number">1865</span>, <span class="string">'B6'</span>: <span class="number">1976</span>,</span><br><span class="line"> <span class="string">'C7'</span>: <span class="number">2093</span>, <span class="string">'C#7'</span>: <span class="number">2217</span>, <span class="string">'D7'</span>: <span class="number">2349</span>, <span class="string">'D#7'</span>: <span class="number">2489</span>, <span class="string">'E7'</span>: <span class="number">2637</span>, <span class="string">'F7'</span>: <span class="number">2794</span>,</span><br><span class="line"> <span class="string">'F#7'</span>: <span class="number">2960</span>, <span class="string">'G7'</span>: <span class="number">3136</span>, <span class="string">'G#7'</span>: <span class="number">3322</span>, <span class="string">'A7'</span>: <span class="number">3520</span>, <span class="string">'A#7'</span>: <span class="number">3729</span>, <span class="string">'B7'</span>: <span class="number">3951</span>,</span><br><span class="line"> <span class="string">'C8'</span>: <span class="number">4186</span>, <span class="string">'C#8'</span>: <span class="number">4435</span>, <span class="string">'D8'</span>: <span class="number">4699</span>, <span class="string">'D#8'</span>: <span class="number">4978</span>, <span class="string">'E8'</span>: <span class="number">5274</span>, <span class="string">'F8'</span>: <span class="number">5588</span>,</span><br><span class="line"> <span class="string">'F#8'</span>: <span class="number">5920</span>, <span class="string">'G8'</span>: <span class="number">6272</span>, <span class="string">'G#8'</span>: <span class="number">6645</span>, <span class="string">'A8'</span>: <span class="number">7040</span>, <span class="string">'A#8'</span>: <span class="number">7459</span>, <span class="string">'B8'</span>: <span class="number">7902</span>,</span><br><span class="line"> <span class="string">'C9'</span>: <span class="number">8372</span>, <span class="string">'C#9'</span>: <span class="number">8870</span>, <span class="string">'D9'</span>: <span class="number">9397</span>, <span class="string">'D#9'</span>: <span class="number">9956</span>, <span class="string">'E9'</span>: <span class="number">10548</span>, <span class="string">'F9'</span>: <span class="number">11175</span>,</span><br><span class="line"> <span class="string">'F#9'</span>: <span class="number">11840</span>, <span class="string">'G9'</span>: <span class="number">12544</span>, <span class="string">'G#9'</span>: <span class="number">13290</span>, <span class="string">'A9'</span>: <span class="number">14080</span>, <span class="string">'A#9'</span>: <span class="number">14917</span>, <span class="string">'B9'</span>: <span class="number">15804</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>这个字典包含了从 C0 到 B9 的所有音符的频率,单位为 Hz。这些频率之后会被用来设置 PWM 的频率。</p>
<h4 id="题外话"><a class="markdownIt-Anchor" href="#题外话"></a> 题外话</h4>
<p>如果不懂音符,但又想要照着现实中的乐谱来弹奏,那该怎么办?(当然,如果你懂的话,可以直接跳到 <a href="#%E8%AE%BE%E7%BD%AE%E8%9C%82%E9%B8%A3%E5%99%A8">这个位置</a>)</p>
<p>先来看看一个简单的乐谱长什么样子。有请我们的主角 ——《小星星》:</p>
<img src="/posts/ba6a/2.jpg" alt="小星星乐谱">
<p>首先,乐谱每一行都有数个小节,被竖线隔开。</p>
<p>每一行的最左边都有一个数字,代表了这一行的拍子。例如这个乐谱的拍子是 4 / 4 拍,也就是说每一小节有四拍,每一拍是一个四分音符。</p>
<p>那什么又是四分音符?这又要从音符的时值说起了。</p>
<p>音符的时值分为全音符、二分音符、四分音符、八分音符、十六分音符、三十二分音符等等。为了直接分辨,上图:</p>
<img src="/posts/ba6a/3.png" alt="圆圈,全音符,时值为4拍,简谱符号为1---;圆圈插一个天线,二分音符,时值为2拍,简谱符号为1-;黑圆圈插一个天线,四分音符,时值为1拍,简谱符号为1;黑圆圈插一个挂了旗子的天线,八分音符,时值为1/2拍,简谱符号为1.;黑圆圈插一个挂了俩旗子的天线,十六分音符,时值为1/4拍,简谱符号为1..;黑圆圈插一个挂了仨旗子的天线,三十二分音符,时值为1/8拍,简谱符号为1...;所有的.代表下划线">
<p>现在回到《小星星》的乐谱上,我们可以看到,第一小节正好有四个四分音符,而第二小节则有两个四分音符和一个二分音符:</p>
<img src="/posts/ba6a/4.jpg" alt="小星星简谱的第1小节和第2小节">
<p>那么在实际演奏中,这两个小节的区别是什么呢?</p>
<p>四个四分音符实际演奏:啊 啊 啊 啊;<br>
两个四分音符 & 一个二分音符实际演奏:啊 啊 啊 ——。</p>
<p>也就是说,在这个谱子里一个二分音符相当于把两个四分音符融合在一起,拉了个长音。</p>
<p>说完时值,现在来看五线谱中各个音符所处的位置。想必你也发现了,有些音符在线上、有些音符在线下、有些音符在中间、有些音符在上下两个线之间…… 这是为什么呢?</p>
<p>这又要说到音符的音高了。这里要注意一点,《小星星》的乐谱中标记了 1 = C,也就是 C 大调。C 大调由 C、D、E、F、G、A、B 七个音组成,况且还是唯一一个没有升降号的大调。</p>
<blockquote>
<p>钢琴上不是有白键和黑键吗?白键总体分为 C、D、E、F、G、A、B 七个音,而黑键总体分为 C#、D#、F#、G#、A# 五个音。<br>
# 符号代表了升号,也就是把这个音调提高半个音阶。比如 C# 是 C 的升半音,也是 C 和 D 之间的音。<br>
还有一个名为 b 的符号,代表了降号。和 #符号相反,b 符号是把这个音调降低半个音阶。Db 是 D 的降半音,是 C 和 D 之间的音,也是 C#。</p>
</blockquote>
<p>我们说了这么多 C、D、E、F、G、A、B,但是在《小星星》里,我们只看到了数字。这是因为在简谱中,用以表示音的高低的符号是阿拉伯数字。1 代表了 C,2 代表了 D,3 代表了 E,以此类推。</p>
<blockquote>
<p>而在现实生活中,我们最常使用的还是唱名,由 do、re、mi、fa、so、la、si 来表示音的高低。do 代表了 C,re 代表了 D,mi 代表了 E,以此类推。</p>
</blockquote>
<img src="/posts/ba6a/6.jpg" alt="阿拉伯数字、唱名、汉字的对照表">
<p>我们已经知道了如何用阿拉伯数字辨认音的高低,但是这仅能在简谱中使用。我们依旧还是要学会如何看五线谱。C 大调的音阶是这样的:</p>
<img src="/posts/ba6a/7.png" alt="C大调的音阶">
<p>来看《小星星》的第一小节。不需要看阿拉伯数字,这次也可以参照上一张图判断出这是 CCGG 了:</p>
<img src="/posts/ba6a/5.jpg" alt="小星星的第一小节,1155">
<p>但是在我们先前的 <a href="#%E9%9F%B3%E7%AC%A6%E4%B8%8E%E9%A2%91%E7%8E%87">音符对频率字典</a> 里,C、D、E、F、G、A、B 后面还跟了多个数字。比如 C4、C5、C6…… 这些数字代表了这个音的频率,也就是音的高低。C4 通常被认为是中央 C,C5 是高一个八度的 C,C6 是再高一个八度的 C,以此类推。</p>
<blockquote>
<p>八度指的是音的高低,在频率上是 2 倍的关系。比如 C4 的频率是 261.63Hz,C5 的频率是 523.25Hz,C6 的频率是 1046.50Hz,以此类推。<br>
不过我们不需要那么详细的小数,只要四舍五入到整数即可。</p>
</blockquote>
<p>为了方便,我们直接使用中央 C 作为基准,也就是 C4。这样,我们就可以把《小星星》的乐谱和音符结合起来了:</p>
<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">C4 C4 G4 G4 A4 A4 G4- F4 F4 E4 E4 D4 D4 C4- </span><br><span class="line">G4 G4 F4 F4 E4 E4 D4- G4 G4 F4 F4 E4 E4 D4- </span><br><span class="line">C4 C4 G4 G4 A4 A4 G4- F4 F4 E4 E4 D4 D4 C4-</span><br></pre></td></tr></tbody></table></figure>
<div class="danger">
这不是最终我们会使用的乐谱,只是为了方便理解而已。
</div>
<h2 id="设置蜂鸣器"><a class="markdownIt-Anchor" href="#设置蜂鸣器"></a> 设置蜂鸣器</h2>
<p>来设置蜂鸣器的引脚。我习惯使用 GP15(参考 <a href="#%E5%89%8D%E8%A8%80">先前的图片</a>,GP15 引脚已经和蜂鸣器连接好了)。你自然也可以根据自己的需要更改引脚:</p>
<figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">buzzer = PWM(Pin(<span class="number">15</span>))</span><br><span class="line">buzzer.freq(<span class="number">50000</span>)</span><br><span class="line">buzzer.duty_u16(<span class="built_in">int</span>(<span class="number">65536</span> * <span class="number">0.2</span>))</span><br></pre></td></tr></tbody></table></figure>
<p>我们先创建了一个 PWM 对象,然后设置了 PWM 周期的频率为 50000Hz(不发出任何声音),最后设置了占空比为 20%。</p>
<center>————————</center>
<h1 id="编写代码"><a class="markdownIt-Anchor" href="#编写代码"></a> 编写代码</h1>
<h2 id="获取乐谱"><a class="markdownIt-Anchor" href="#获取乐谱"></a> 获取乐谱</h2>
<p>我选择用超级马里奥的 <a target="_blank" rel="noopener" href="https://onlinesequencer.net/25966">经典曲子</a> 来演示。Online Sequencer 的乐谱可以直接复制粘贴,如下:</p>
<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Online Sequencer:616413:0 F#5 1 7;0 E6 1 7;2 F#5 1 7;2 E6 1 7;6 F#5 1 7;6 E6 1 7;10 F#5 1 7;10 C6 1 7;12 F#5 1 7;12 E6 1 7;16 G5 1 7;16 B5 1 7;16 G6 1 7;24 G5 1 7;32 E5 1 7;32 C6 1 7;38 C5 1 7;38 G5 1 7;44 G4 1 7;44 E5 1 7;50 C5 1 7;50 A5 1 7;54 D5 1 7;54 B5 1 7;58 C#5 1 7;58 A#5 1 7;60 C5 1 7;60 A5 1 7;64 C5 1 7;64 G5 1 7;66 G5 1 7;66 E6 1 7;69 B5 1 7;69 G6 1 7;72 C6 1 7;72 A6 1 7;76 A5 1 7;76 F6 1 7;78 B5 1 7;78 G6 1 7;82 A5 1 7;82 E6 1 7;86 E5 1 7;86 C6 1 7;88 F5 1 7;88 D6 1 7;90 D5 1 7;90 B5 1 7;96 E5 1 7;96 C6 1 7;102 C5 1 7;102 G5 1 7;108 G4 1 7;108 E5 1 7;114 C5 1 7;114 A5 1 7;118 D5 1 7;118 B5 1 7;122 C#5 1 7;122 A#5 1 7;124 C5 1 7;124 A5 1 7;128 C5 1 7;128 G5 1 7;130 G5 1 7;130 E6 1 7;133 B5 1 7;133 G6 1 7;136 C6 1 7;136 A6 1 7;140 A5 1 7;140 F6 1 7;142 B5 1 7;142 G6 1 7;146 A5 1 7;146 E6 1 7;150 E5 1 7;150 C6 1 7;152 F5 1 7;152 D6 1 7;154 D5 1 7;154 B5 1 7;164 E6 1 7;164 G6 1 7;166 D#6 1 7;166 F#6 1 7;168 D6 1 7;168 F6 1 7;170 B5 1 7;170 D#6 1 7;174 C6 1 7;174 E6 1 7;178 E5 1 7;178 G#5 1 7;180 F5 1 7;180 A5 1 7;182 G5 1 7;182 C6 1 7;186 C5 1 7;186 A5 1 7;188 E5 1 7;188 C6 1 7;190 F5 1 7;190 D6 1 7;196 E6 1 7;196 G6 1 7;198 D#6 1 7;198 F#6 1 7;200 D6 1 7;200 F6 1 7;202 B5 1 7;202 D#6 1 7;206 C6 1 7;206 E6 1 7;210 F6 1 7;210 G6 1 7;210 C7 1 7;214 F6 1 7;214 G6 1 7;214 C7 1 7;216 F6 1 7;216 G6 1 7;216 C7 1 7;228 E6 1 7;228 G6 1 7;230 D#6 1 7;230 F#6 1 7;232 D6 1 7;232 F6 1 7;234 B5 1 7;234 D#6 1 7;238 C6 1 7;238 E6 1 7;242 E5 1 7;242 G#5 1 7;244 F5 1 7;244 A5 1 7;246 G5 1 7;246 C6 1 7;250 C5 1 7;250 A5 1 7;252 E5 1 7;252 C6 1 7;254 F5 1 7;254 D6 1 7;260 G#5 1 7;260 D#6 1 7;266 F5 1 7;266 D6 1 7;272 C5 1 7;272 E5 1 7;272 C6 1 7;292 E6 1 7;292 G6 1 7;294 D#6 1 7;294 F#6 1 7;296 D6 1 7;296 F6 1 7;298 B5 1 7;298 D#6 1 7;302 C6 1 7;302 E6 1 7;306 E5 1 7;306 G#5 1 7;308 F5 1 7;308 A5 1 7;310 G5 1 7;310 C6 1 7;314 C5 1 7;314 A5 1 7;316 E5 1 7;316 C6 1 7;318 F5 1 7;318 D6 1 7;324 E6 1 7;324 G6 1 7;326 D#6 1 7;326 F#6 1 7;328 D6 1 7;328 F6 1 7;330 B5 1 7;330 D#6 1 7;334 C6 1 7;334 E6 1 7;338 F6 1 7;338 G6 1 7;338 C7 1 7;342 F6 1 7;342 G6 1 7;342 C7 1 7;344 F6 1 7;344 G6 1 7;344 C7 1 7;356 E6 1 7;356 G6 1 7;358 D#6 1 7;358 F#6 1 7;360 D6 1 7;360 F6 1 7;362 B5 1 7;362 D#6 1 7;366 C6 1 7;366 E6 1 7;370 E5 1 7;370 G#5 1 7;372 F5 1 7;372 A5 1 7;374 G5 1 7;374 C6 1 7;378 C5 1 7;378 A5 1 7;380 E5 1 7;380 C6 1 7;382 F5 1 7;382 D6 1 7;388 G#5 1 7;388 D#6 1 7;394 F5 1 7;394 D6 1 7;400 E5 1 7;400 C6 1 7;416 G#5 1 7;416 C6 1 7;418 G#5 1 7;418 C6 1 7;422 G#5 1 7;422 C6 1 7;426 G#5 1 7;426 C6 1 7;428 A#5 1 7;428 D6 1 7;432 G5 1 7;432 E6 1 7;434 E5 1 7;434 C6 1 7;438 E5 1 7;438 A5 1 7;440 C5 1 7;440 G5 1 7;448 G#5 1 7;448 C6 1 7;450 G#5 1 7;450 C6 1 7;454 G#5 1 7;454 C6 1 7;458 G#5 1 7;458 C6 1 7;460 A#5 1 7;460 D6 1 7;462 G5 1 7;462 E6 1 7;480 G#5 1 7;480 C6 1 7;482 G#5 1 7;482 C6 1 7;486 G#5 1 7;486 C6 1 7;490 G#5 1 7;490 C6 1 7;492 A#5 1 7;492 D6 1 7;496 G5 1 7;496 E6 1 7;498 E5 1 7;498 C6 1 7;502 E5 1 7;502 A5 1 7;504 C5 1 7;504 G5 1 7;512 F#5 1 7;512 E6 1 7;514 F#5 1 7;514 E6 1 7;518 F#5 1 7;518 E6 1 7;522 F#5 1 7;522 C6 1 7;524 F#5 1 7;524 E6 1 7;528 G5 1 7;528 B5 1 7;528 G6 1 7;536 G5 1 7;544 E5 1 7;544 C6 1 7;550 C5 1 7;550 G5 1 7;556 G4 1 7;556 E5 1 7;562 C5 1 7;562 A5 1 7;566 D5 1 7;566 B5 1 7;570 C#5 1 7;570 A#5 1 7;572 C5 1 7;572 A5 1 7;576 C5 1 7;576 G5 1 7;578 G5 1 7;578 E6 1 7;581 B5 1 7;581 G6 1 7;584 C6 1 7;584 A6 1 7;588 A5 1 7;588 F6 1 7;590 B5 1 7;590 G6 1 7;594 A5 1 7;594 E6 1 7;598 E5 1 7;598 C6 1 7;600 F5 1 7;600 D6 1 7;602 D5 1 7;602 B5 1 7;608 E5 1 7;608 C6 1 7;614 C5 1 7;614 G5 1 7;620 G4 1 7;620 E5 1 7;626 C5 1 7;626 A5 1 7;630 D5 1 7;630 B5 1 7;634 C#5 1 7;634 A#5 1 7;636 C5 1 7;636 A5 1 7;640 C5 1 7;640 G5 1 7;642 G5 1 7;642 E6 1 7;645 B5 1 7;645 G6 1 7;648 C6 1 7;648 A6 1 7;652 A5 1 7;652 F6 1 7;654 B5 1 7;654 G6 1 7;658 A5 1 7;658 E6 1 7;662 E5 1 7;662 C6 1 7;664 F5 1 7;664 D6 1 7;666 D5 1 7;666 B5 1 7;672 C6 1 7;672 E6 1 7;674 A5 1 7;674 C6 1 7;678 E5 1 7;678 G5 1 7;684 E5 1 7;684 G#5 1 7;688 F5 1 7;688 A5 1 7;690 C6 1 7;690 F6 1 7;694 C6 1 7;694 F6 1 7;696 F5 1 7;696 A5 1 7;704 G5 1 7;704 B5 1 7;706 F6 1 7;706 A6 1 7;709 F6 1 7;709 A6 1 7;712 F6 1 7;712 A6 1 7;714 E6 1 7;714 G6 1 7;717 D6 1 7;717 F6 1 7;720 C6 1 7;720 E6 1 7;722 A5 1 7;722 C6 1 7;726 F5 1 7;726 A5 1 7;728 E5 1 7;728 G5 1 7;736 C6 1 7;736 E6 1 7;738 A5 1 7;738 C6 1 7;742 E5 1 7;742 G5 1 7;748 E5 1 7;748 G#5 1 7;752 F5 1 7;752 A5 1 7;754 C6 1 7;754 F6 1 7;758 C6 1 7;758 F6 1 7;760 F5 1 7;760 A5 1 7;768 G5 1 7;768 B5 1 7;770 D6 1 7;770 F6 1 7;774 D6 1 7;774 F6 1 7;776 D6 1 7;776 F6 1 7;778 C6 1 7;778 E6 1 7;781 B5 1 7;781 D6 1 7;784 G5 1 7;784 C6 1 7;786 E5 1 7;790 E5 1 7;792 C5 1 7;800 C6 1 7;800 E6 1 7;802 A5 1 7;802 C6 1 7;806 E5 1 7;806 G5 1 7;812 E5 1 7;812 G#5 1 7;816 F5 1 7;816 A5 1 7;818 C6 1 7;818 F6 1 7;822 C6 1 7;822 F6 1 7;824 F5 1 7;824 A5 1 7;832 G5 1 7;832 B5 1 7;834 F6 1 7;834 A6 1 7;837 F6 1 7;837 A6 1 7;840 F6 1 7;840 A6 1 7;842 E6 1 7;842 G6 1 7;845 D6 1 7;845 F6 1 7;848 C6 1 7;848 E6 1 7;850 A5 1 7;850 C6 1 7;854 F5 1 7;854 A5 1 7;856 E5 1 7;856 G5 1 7;864 C6 1 7;864 E6 1 7;866 A5 1 7;866 C6 1 7;870 E5 1 7;870 G5 1 7;876 E5 1 7;876 G#5 1 7;880 F5 1 7;880 A5 1 7;882 C6 1 7;882 F6 1 7;886 C6 1 7;886 F6 1 7;888 F5 1 7;888 A5 1 7;896 G5 1 7;896 B5 1 7;898 D6 1 7;898 F6 1 7;902 D6 1 7;902 F6 1 7;904 D6 1 7;904 F6 1 7;906 C6 1 7;906 E6 1 7;909 B5 1 7;909 D6 1 7;912 G5 1 7;912 C6 1 7;914 E5 1 7;918 E5 1 7;920 C5 1 7;928 G#5 1 7;928 C6 1 7;930 G#5 1 7;930 C6 1 7;934 G#5 1 7;934 C6 1 7;938 G#5 1 7;938 C6 1 7;940 A#5 1 7;940 D6 1 7;944 G5 1 7;944 E6 1 7;946 E5 1 7;946 C6 1 7;950 E5 1 7;950 A5 1 7;952 C5 1 7;952 G5 1 7;960 G#5 1 7;960 C6 1 7;962 G#5 1 7;962 C6 1 7;966 G#5 1 7;966 C6 1 7;970 G#5 1 7;970 C6 1 7;972 A#5 1 7;972 D6 1 7;974 G5 1 7;974 E6 1 7;992 G#5 1 7;992 C6 1 7;994 G#5 1 7;994 C6 1 7;998 G#5 1 7;998 C6 1 7;1002 G#5 1 7;1002 C6 1 7;1004 A#5 1 7;1004 D6 1 7;1008 G5 1 7;1008 E6 1 7;1010 E5 1 7;1010 C6 1 7;1014 E5 1 7;1014 A5 1 7;1016 C5 1 7;1016 G5 1 7;1024 F#5 1 7;1024 E6 1 7;1026 F#5 1 7;1026 E6 1 7;1030 F#5 1 7;1030 E6 1 7;1034 F#5 1 7;1034 C6 1 7;1036 F#5 1 7;1036 E6 1 7;1040 G5 1 7;1040 B5 1 7;1040 G6 1 7;1048 G5 1 7;1056 C6 1 7;1056 E6 1 7;1058 A5 1 7;1058 C6 1 7;1062 E5 1 7;1062 G5 1 7;1068 E5 1 7;1068 G#5 1 7;1072 F5 1 7;1072 A5 1 7;1074 C6 1 7;1074 F6 1 7;1078 C6 1 7;1078 F6 1 7;1080 F5 1 7;1080 A5 1 7;1088 G5 1 7;1088 B5 1 7;1090 F6 1 7;1090 A6 1 7;1093 F6 1 7;1093 A6 1 7;1096 F6 1 7;1096 A6 1 7;1098 E6 1 7;1098 G6 1 7;1101 D6 1 7;1101 F6 1 7;1104 C6 1 7;1104 E6 1 7;1106 A5 1 7;1106 C6 1 7;1110 F5 1 7;1110 A5 1 7;1112 E5 1 7;1112 G5 1 7;1120 C6 1 7;1120 E6 1 7;1122 A5 1 7;1122 C6 1 7;1126 E5 1 7;1126 G5 1 7;1132 E5 1 7;1132 G#5 1 7;1136 F5 1 7;1136 A5 1 7;1138 C6 1 7;1138 F6 1 7;1142 C6 1 7;1142 F6 1 7;1144 F5 1 7;1144 A5 1 7;1152 G5 1 7;1152 B5 1 7;1154 D6 1 7;1154 F6 1 7;1158 D6 1 7;1158 F6 1 7;1160 D6 1 7;1160 F6 1 7;1162 C6 1 7;1162 E6 1 7;1165 B5 1 7;1165 D6 1 7;1168 G5 1 7;1168 C6 1 7;1170 E5 1 7;1174 E5 1 7;1176 C5 1 7;0 D4 1 7;2 D4 1 7;6 D4 1 7;10 D4 1 7;12 D4 1 7;24 G4 1 7;32 G4 1 7;38 E4 1 7;44 C4 1 7;50 F4 1 7;54 G4 1 7;58 F#4 1 7;60 F4 1 7;64 E4 1 7;66 C5 1 7;69 E5 1 7;72 F5 1 7;76 D5 1 7;78 E5 1 7;82 C5 1 7;86 A4 1 7;88 B4 1 7;90 G4 1 7;96 G4 1 7;102 E4 1 7;108 C4 1 7;114 F4 1 7;118 G4 1 7;122 F#4 1 7;124 F4 1 7;128 E4 1 7;130 C5 1 7;133 E5 1 7;136 F5 1 7;140 D5 1 7;142 E5 1 7;146 C5 1 7;150 A4 1 7;152 B4 1 7;154 G4 1 7;160 C4 1 7;166 G4 1 7;172 C5 1 7;176 F4 1 7;182 C5 1 7;184 C5 1 7;188 F4 1 7;192 C4 1 7;198 E4 1 7;204 G4 1 7;206 C5 1 7;220 G4 1 7;224 C4 1 7;230 G4 1 7;236 C5 1 7;240 F4 1 7;246 C5 1 7;248 C5 1 7;252 F4 1 7;256 C4 1 7;260 G#4 1 7;266 A#4 1 7;278 G4 1 7;280 G4 1 7;284 C4 1 7;288 C4 1 7;294 G4 1 7;300 C5 1 7;304 F4 1 7;310 C5 1 7;312 C5 1 7;316 F4 1 7;320 C4 1 7;326 E4 1 7;332 G4 1 7;334 C5 1 7;348 G4 1 7;352 C4 1 7;358 G4 1 7;364 C5 1 7;368 F4 1 7;374 C5 1 7;376 C5 1 7;380 F4 1 7;384 C4 1 7;388 G#4 1 7;394 A#4 1 7;400 C5 1 7;406 G4 1 7;408 G4 1 7;412 C4 1 7;416 G#3 1 7;422 D#4 1 7;428 G#4 1 7;432 G4 1 7;438 C4 1 7;444 G3 1 7;448 G#3 1 7;454 D#4 1 7;460 G#4 1 7;464 G4 1 7;470 C4 1 7;476 G3 1 7;480 G#3 1 7;486 D#4 1 7;492 G#4 1 7;496 G4 1 7;502 C4 1 7;508 G3 1 7;512 D4 1 7;514 D4 1 7;518 D4 1 7;522 D4 1 7;524 D4 1 7;536 G4 1 7;544 G4 1 7;550 E4 1 7;556 C4 1 7;562 F4 1 7;566 G4 1 7;570 F#4 1 7;572 F4 1 7;576 E4 1 7;578 C5 1 7;581 E5 1 7;584 F5 1 7;588 D5 1 7;590 E5 1 7;594 C5 1 7;598 A4 1 7;600 B4 1 7;602 G4 1 7;608 G4 1 7;614 E4 1 7;620 C4 1 7;626 F4 1 7;630 G4 1 7;634 F#4 1 7;636 F4 1 7;640 E4 1 7;642 C5 1 7;645 E5 1 7;648 F5 1 7;652 D5 1 7;654 E5 1 7;658 C5 1 7;662 A4 1 7;664 B4 1 7;666 G4 1 7;672 C4 1 7;678 F#4 1 7;680 G4 1 7;684 C5 1 7;688 F4 1 7;692 F4 1 7;696 C5 1 7;698 C5 1 7;700 F4 1 7;704 D4 1 7;710 F4 1 7;712 G4 1 7;716 B4 1 7;720 G4 1 7;724 G4 1 7;728 C5 1 7;730 C5 1 7;732 G4 1 7;736 C4 1 7;742 F#4 1 7;744 G4 1 7;748 C5 1 7;752 F4 1 7;756 F4 1 7;760 C5 1 7;762 C5 1 7;764 F4 1 7;768 G4 1 7;774 G4 1 7;776 G4 1 7;778 A4 1 7;781 B4 1 7;784 C5 1 7;788 G4 1 7;792 C4 1 7;800 C4 1 7;806 F#4 1 7;808 G4 1 7;812 C5 1 7;816 F4 1 7;820 F4 1 7;824 C5 1 7;826 C5 1 7;828 F4 1 7;832 D4 1 7;838 F4 1 7;840 G4 1 7;844 B4 1 7;848 G4 1 7;852 G4 1 7;856 C5 1 7;858 C5 1 7;860 G4 1 7;864 C4 1 7;870 F#4 1 7;872 G4 1 7;876 C5 1 7;880 F4 1 7;884 F4 1 7;888 C5 1 7;890 C5 1 7;892 F4 1 7;896 G4 1 7;902 G4 1 7;904 G4 1 7;906 A4 1 7;909 B4 1 7;912 C5 1 7;916 G4 1 7;920 C4 1 7;928 G#3 1 7;934 D#4 1 7;940 G#4 1 7;944 G4 1 7;950 C4 1 7;956 G3 1 7;960 G#3 1 7;966 D#4 1 7;972 G#4 1 7;976 G4 1 7;982 C4 1 7;988 G3 1 7;992 G#3 1 7;998 D#4 1 7;1004 G#4 1 7;1008 G4 1 7;1014 C4 1 7;1020 G3 1 7;1024 D4 1 7;1026 D4 1 7;1030 D4 1 7;1034 D4 1 7;1036 D4 1 7;1048 G4 1 7;1056 C4 1 7;1062 F#4 1 7;1064 G4 1 7;1068 C5 1 7;1072 F4 1 7;1076 F4 1 7;1080 C5 1 7;1082 C5 1 7;1084 F4 1 7;1088 D4 1 7;1094 F4 1 7;1096 G4 1 7;1100 B4 1 7;1104 G4 1 7;1108 G4 1 7;1112 C5 1 7;1114 C5 1 7;1116 G4 1 7;1120 C4 1 7;1126 F#4 1 7;1128 G4 1 7;1132 C5 1 7;1136 F4 1 7;1140 F4 1 7;1144 C5 1 7;1146 C5 1 7;1148 F4 1 7;1152 G4 1 7;1158 G4 1 7;1160 G4 1 7;1162 A4 1 7;1165 B4 1 7;1168 C5 1 7;1172 G4 1 7;1176 C4 1 7;:</span><br></pre></td></tr></tbody></table></figure>
<p>真是一大串呢~</p>
<p>稍微分析一下 Online Sequencer 乐谱的格式:</p>
<ol>
<li><code>Online Sequencer:616413:</code> 没有什么用,要被除掉</li>
<li><code>0 F#5 1 7;</code> 才是重点。<code>0</code> 是音符所处的时间位置,<code>F#5</code> 是音符的音高,<code>1</code> 是音符的持续时间,<code>7</code> 是播放用的乐器类型。Online Sequencer 乐谱中每个音符都是这样的格式,用分号隔开</li>
<li>还要注意最后有一个 <code>:</code>,这个也要除掉</li>
</ol>
<p>那么来编写一下将 Online Sequencer 乐谱转换为 Python 字典的代码吧。</p>
<p>先将 Online Sequencer 乐谱中需要被去除的部分去除掉,然后将乐谱中的音符部分根据 <code>;</code> 分割开来:</p>
<figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">song = song_file.split(<span class="string">":"</span>)[<span class="number">2</span>]</span><br><span class="line">notes_list = song.split(<span class="string">";"</span>)[:-<span class="number">1</span>]</span><br></pre></td></tr></tbody></table></figure>
<p>接着把音符的时间位置提取出来,设为字典的键,音符的音高、持续时间和乐器类型设为字典的值:</p>
<figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">notes_dict = {}</span><br><span class="line"><span class="keyword">for</span> note <span class="keyword">in</span> notes_list:</span><br><span class="line"> note_parts = note.split()</span><br><span class="line"> beat = <span class="built_in">float</span>(note_parts[<span class="number">0</span>])</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 如果该时间位置是初次被添加到字典当中</span></span><br><span class="line"> <span class="keyword">if</span> beat <span class="keyword">not</span> <span class="keyword">in</span> notes_dict:</span><br><span class="line"> <span class="comment"># 添加该音符的信息到字典里</span></span><br><span class="line"> notes_dict[beat] = note_parts[<span class="number">1</span>:]</span><br><span class="line"> <span class="comment"># 如果该时间位置并不是第一次被添加到字典当中</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="comment"># 如果该音符的音高比字典中已有的音符的音高更高</span></span><br><span class="line"> <span class="keyword">if</span> tones[note_parts[<span class="number">1</span>]] > tones[notes_dict[beat][<span class="number">0</span>]]:</span><br><span class="line"> <span class="comment"># 将该音符的信息覆盖到字典里</span></span><br><span class="line"> notes_dict[beat] = note_parts[<span class="number">1</span>:]</span><br></pre></td></tr></tbody></table></figure>
<p>由于是单个蜂鸣器播放,所以无法同时播放多个音符。当同个时间位置有多个音符时,我们只能选择音高最高的音符来播放。</p>
<p>至此,我们已经有了一个 Python 字典作为我们的乐谱。</p>
<h2 id="播放音频"><a class="markdownIt-Anchor" href="#播放音频"></a> 播放音频</h2>
<p>想要让蜂鸣器发出声音来很简单,代码如下:</p>
<figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">buzzer.freq(tones[tone])</span><br><span class="line">time.sleep(duration)</span><br></pre></td></tr></tbody></table></figure>
<p><code>buzzer.freq()</code> 会设置蜂鸣器的频率,<code>time.sleep()</code> 会让程序暂停一段时间(在我们这个例子中,就是用来拉长音的)。</p>
<h2 id="播放乐谱"><a class="markdownIt-Anchor" href="#播放乐谱"></a> 播放乐谱</h2>
<p>结合上面两个部分,我们可以让字典中的音符一个一个地播放出来。但在这之前,还需要设置乐谱的播放速度。</p>
<figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">bpm = <span class="number">199</span> <span class="comment"># 每分钟节拍数</span></span><br><span class="line">time_signature = [<span class="number">4</span>, <span class="number">4</span>] <span class="comment"># 拍号</span></span><br><span class="line">beat_duration = (<span class="number">60</span> / bpm) / time_signature[<span class="number">0</span>] <span class="comment"># 每拍的持续时间</span></span><br></pre></td></tr></tbody></table></figure>
<p>聊 <a href="#%E9%A2%98%E5%A4%96%E8%AF%9D">乐谱知识</a> 时说到过,拍号决定了每一小节有多少拍,什么音符的时值是一拍。从我们拿来马里奥曲子的 <a target="_blank" rel="noopener" href="https://onlinesequencer.net/25966">网站</a> 里能看到,马里奥曲子的 bpm 是 199、拍号是 4 / 4。</p>
<p>进行一小些计算,算出每一拍的持续时间为 <code>(60 / 199) / 4</code>。</p>
<p>继续编写播放乐谱的代码:</p>
<figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 从第0拍开始播放</span></span><br><span class="line">current_beat = <span class="built_in">float</span>(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 播放到乐谱中最后一个音符的时间位置</span></span><br><span class="line"><span class="keyword">while</span> current_beat <= <span class="built_in">max</span>(notes_dict.keys()): </span><br><span class="line"> <span class="comment"># 取出当前时间位置的音符的持续时间(持续了多少拍),乘以每拍的持续时间,得到音符的持续时间(持续了多少秒)</span></span><br><span class="line"> note_duration = <span class="built_in">float</span>(notes_dict[current_beat][<span class="number">1</span>]) * beat_duration</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 如果当前时间位置有音符,就播放</span></span><br><span class="line"> <span class="keyword">if</span> current_beat <span class="keyword">in</span> notes_dict:</span><br><span class="line"> note_name = notes_dict[current_beat][<span class="number">0</span>]</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"第"</span> + <span class="built_in">str</span>(current_beat) + <span class="string">"拍:播放音符 "</span> + note_name + <span class="string">" 中,持续时间为 "</span> + <span class="built_in">str</span>(note_duration) + <span class="string">" 秒。"</span>)</span><br><span class="line"> buzzer.freq(tones[note_name])</span><br><span class="line"> time.sleep(note_duration)</span><br><span class="line"></span><br><span class="line"> current_beat += <span class="built_in">float</span>(notes_dict[current_beat][<span class="number">1</span>])</span><br><span class="line"> buzzer.freq(<span class="number">50000</span>)</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 如果当前时间位置没有音符,就暂停</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"第"</span> + <span class="built_in">str</span>(current_beat) + <span class="string">"拍:暂停,持续时间为"</span> + <span class="built_in">str</span>(note_duration) + <span class="string">" 秒。"</span>)</span><br><span class="line"> buzzer.freq(<span class="number">50000</span>)</span><br><span class="line"> time.sleep(beat_duration)</span><br><span class="line"> current_beat += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"播放结束。"</span>)</span><br><span class="line">buzzer.freq(<span class="number">50000</span>)</span><br></pre></td></tr></tbody></table></figure>
<p>一个简单的播放乐谱的程序便完成了。</p>
<p>最终代码可以在 <a target="_blank" rel="noopener" href="https://github.com/Cytrogen/Music_Player_Pico">这个仓库</a> 里找到。</p>
<center>————————</center>
<h1 id="最终效果"><a class="markdownIt-Anchor" href="#最终效果"></a> 最终效果</h1>
<div class="danger">
该视频实际上并未是最终版本的效果,但没有差别多少。
</div>
<video id="video" controls="" height="420">
<source id="mp4" src="ba6a/8.mp4" type="video/mp4">
</video></body></html></div></article></div></main><footer><div class="paginator"><a class="prev" href="66e.html">上一篇</a><a class="next" href="cf92.html">下一篇</a></div><!-- Webmention 显示区域--><div class="webmention-section webmention-empty" data-page-url="posts/ba6a.html" data-full-url="https://cytrogen.icu/posts/ba6a.html" data-mode="static">
<h3 class="webmention-title">Webmentions (<span class="webmention-count">0</span>)</h3>
<div class="webmention-list"></div>
<span>暂无 Webmentions</span>
</div><div class="copyright"><p class="footer-links"><a href="../friends/index.html">友链</a><span class="footer-separator"> ·</span><a href="../links/index.html">邻邦</a><span class="footer-separator"> ·</span><a href="../contact/index.html">联络</a><span class="footer-separator"> ·</span><a href="../colophon/index.html">营造记</a><span class="footer-separator"> ·</span><a href="../atom.xml">RSS订阅</a></p><p>© 2025 - 2026 <a href="https://cytrogen.icu">Cytrogen</a>, powered by <a href="https://hexo.io/" target="_blank">Hexo</a> and <a href="https://github.com/cytrogen/hexo-theme-ares" target="_blank">hexo-theme-ares</a>.</p><p><a href="https://blogscn.fun" target="_blank" rel="noopener">BLOGS·CN</a></p></div></footer></div></div><a class="back-to-top" href="#top" aria-label="返回顶部"><svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M3.293 9.707a1 1 0 010-1.414L9.586 2a2 2 0 012.828 0l6.293 6.293a1 1 0 01-1.414 1.414L11 3.414V17a1 1 0 11-2 0V3.414L2.707 9.707a1 1 0 01-1.414 0z"></path></svg></a><script>document.addEventListener('DOMContentLoaded', function() {
const codeBlocks = document.querySelectorAll('figure.highlight');
codeBlocks.forEach(block => {
let caption = block.querySelector('figcaption');
if (!caption) {
caption = document.createElement('figcaption');
block.insertBefore(caption, block.firstChild);
}
const info = document.createElement('div');
info.className = 'info';
const filename = caption.querySelector('span');
if (filename) {
filename.className = 'filename';
info.appendChild(filename);
}
const lang = block.className.split(' ')[1];
if (lang) {
const langSpan = document.createElement('span');
langSpan.className = 'lang-name';
langSpan.textContent = lang;
info.appendChild(langSpan);
}
const sourceLink = caption.querySelector('a');
if (sourceLink) {
sourceLink.className = 'source-link';
info.appendChild(sourceLink);
}
const actions = document.createElement('div');
actions.className = 'actions';
const codeHeight = block.scrollHeight;
const threshold = 300;
if (codeHeight > threshold) {
block.classList.add('folded');
const toggleBtn = document.createElement('button');
toggleBtn.textContent = '展开';
toggleBtn.addEventListener('click', () => {
block.classList.toggle('folded');
toggleBtn.textContent = block.classList.contains('folded') ? '展开' : '折叠';
});
actions.appendChild(toggleBtn);
}
const copyBtn = document.createElement('button');
copyBtn.textContent = '复制';
copyBtn.addEventListener('click', async () => {
const codeLines = block.querySelectorAll('.code .line');
const code = Array.from(codeLines)
.map(line => line.textContent)
.join('\n')
.replace(/\n\n/g, '\n');
try {
await navigator.clipboard.writeText(code);
copyBtn.textContent = '已复制';
copyBtn.classList.add('copied');
setTimeout(() => {
copyBtn.textContent = '复制';
copyBtn.classList.remove('copied');
}, 3000);
} catch (err) {
console.error('复制失败:', err);
copyBtn.textContent = '复制失败';
setTimeout(() => {
copyBtn.textContent = '复制';
}, 3000);
}
});
actions.appendChild(copyBtn);
caption.innerHTML = '';
caption.appendChild(info);
caption.appendChild(actions);
const markedLines = block.getAttribute('data-marked-lines');
if (markedLines) {
const lines = markedLines.split(',');
lines.forEach(range => {
if (range.includes('-')) {
const [start, end] = range.split('-').map(Number);
for (let i = start; i <= end; i++) {
const line = block.querySelector(`.line-${i}`);
if (line) line.classList.add('marked');
}
} else {
const line = block.querySelector(`.line-${range}`);
if (line) line.classList.add('marked');
}
});
}
});
});</script><script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script"></script><script>(function() {
document.addEventListener('DOMContentLoaded', function() {
const themeToggle = document.querySelector('.theme-toggle');
if (!themeToggle) return;
const getCurrentTheme = () => {
return document.documentElement.getAttribute('data-theme') || 'light';
};
const updateUI = (theme) => {
const isDark = theme === 'dark';
themeToggle.setAttribute('aria-pressed', isDark.toString());
};
const setTheme = (theme) => {
document.documentElement.setAttribute('data-theme', theme);
document.documentElement.style.colorScheme = theme;
const pageWrapper = document.getElementById('page-wrapper');
if (pageWrapper) {
pageWrapper.setAttribute('data-theme', theme);
}
// Find and remove the temporary anti-flicker style tag if it exists.
// This ensures the main stylesheet takes full control after the initial load.
const antiFlickerStyle = document.getElementById('anti-flicker-style');
if (antiFlickerStyle) {
antiFlickerStyle.remove();
}
localStorage.setItem('theme', theme);
updateUI(theme);
};
const toggleTheme = () => {
const current = getCurrentTheme();
const newTheme = current === 'light' ? 'dark' : 'light';
setTheme(newTheme);
};
updateUI(getCurrentTheme());
themeToggle.addEventListener('click', toggleTheme);
if (window.matchMedia) {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', function(e) {
if (!localStorage.getItem('theme')) {
const theme = e.matches ? 'dark' : 'light';
setTheme(theme);
}
});
}
});
})();
</script><script src="../js/details-toggle.js" defer></script><script>(function() {
document.addEventListener('DOMContentLoaded', function() {
const backToTopBtn = document.querySelector('.back-to-top');
if (!backToTopBtn) return;
const toggleButtonVisibility = () => {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const shouldShow = scrollTop > 200;
if (shouldShow) {
backToTopBtn.classList.add('is-visible');
} else {
backToTopBtn.classList.remove('is-visible');
}
};
let ticking = false;
const handleScroll = () => {
if (!ticking) {
requestAnimationFrame(() => {
toggleButtonVisibility();
ticking = false;
});
ticking = true;
}
};
const scrollToTop = (event) => {
event.preventDefault();
window.scrollTo({
top: 0,
behavior: 'smooth'
});
};
window.addEventListener('scroll', handleScroll);
backToTopBtn.addEventListener('click', scrollToTop);
toggleButtonVisibility();
});
})();</script></body></html>