找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 303|回复: 13
打印 上一主题 下一主题
收起左侧

想做一个世界上最小的八音盒

[复制链接]
跳转到指定楼层
楼主
机械和电子相结合可编程八音盒
想了七八年了,还是想不出来用什么结构,不知道有没有人能实现。
或者能提供想法,我来实现。
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:592807 发表于 2026-4-11 11:37 | 只看该作者
去找语音芯片的厂家。他们教你怎么做MP3.
回复

使用道具 举报

板凳
ID:342822 发表于 2026-4-11 12:50 | 只看该作者
$regfile = "m8def.dat"              ' 设置使用的AVR型号为ATmega8
$crystal = 8000000                  ' 设置AVR系统时钟频率为8MHz

' -------------------- 硬件引脚定义 --------------------
Su_port Alias Pinb.0                 ' 将PB0引脚命名为声音输出端口

' -------------------- 端口初始化 --------------------
Config Su_port = Output              ' 将声音输出端口设置为输出模式
Portb = &B11111110                   ' 端口B初始化: PB0为输出(0),PB1-PB7启用内部上拉电阻(1)

' ==================== 变量定义 ====================
' 音乐控制变量
Dim Mu_tempo As Word                 ' 速度值,决定每个音符的基础时长(毫秒)
Dim Mu_octave As Byte                ' 当前八度值(范围1-7)
Dim Mu_octdiv As Word                ' 八度分频值,用于计算不同八度的频率
Dim Mu_pldata As String * 40         ' 音乐数据行缓冲区,每行最多40个字符
Dim Mu_dtlen As Byte                 ' 当前数据行的剩余字符长度
Dim Mu_dtposi As Byte                ' 当前数据行的读取位置
Dim Mu_str As String * 1             ' 从数据行读取的单个字符
Dim Mu_freq As Word                  ' 计算出的音符频率值

' 通用临时变量
Dim Mu_temp1 As Byte      ' 临时变量Byte型 No.1 - 常用于存储ASCII码或中间结果
Dim Mu_temp2 As Byte      ' 临时变量Byte型 No.2 - 常用于存储音符索引
Dim Mu_temp3 As Byte      ' 临时变量Byte型 No.3 - 常用于存储音符时值等级
Dim Mu_temp4 As Byte      ' 临时变量Byte型 No.4 - 常用于存储音符代码
Dim mu_h As Byte          ' 高八度标志
Dim mu_L As Byte          ' 低八度标志
Dim mu_Z As Byte          ' 当前八度标志
Dim Mu_tempw1 As Word     ' 临时变量Word型 No.1 - 常用于存储音符时长
Dim Mu_tempw2 As Word     ' 临时变量Word型 No.2 - 常用于存储频率计算值
Dim Mu_templ1 As Long     ' 临时变量Long型 No.1 - 用于高精度计算

' ==================== I2C及OLED显示屏初始化 ====================
Config Scl = Portc.5                 ' 设置I2C时钟引脚(SCL)为PC5
Config Sda = Portc.4                 ' 设置I2C数据引脚(SDA)为PC4
Config Twi = 400000                  ' 设置I2C总线速度为400kHz

I2cinit                              ' 初始化I2C硬件

$lib "i2c_twi.lbx"                   ' 使用TWI硬件I2C库(非软件模拟)
$lib "glcdSSD1306-I2C.lib"           ' 使用SSD1306 OLED屏幕专用库

' 图形LCD配置: 128列 x 64行,使用SSD1306控制器
Config Graphlcd = Custom , Cols = 128 , Rows = 32 , Lcdname = "SSD1306"
Cls                                  ' 清屏
Setfont font8x8TT                    ' 选择8x8字体

' ==================== 主程序循环 ====================
Do                                   ' 无限循环开始
    ' 播放第一首乐曲:莫扎特 G大调弦乐小夜曲 第一乐章
    Restore Mu_data                  ' 将数据指针指向音乐数据表1的起始位置
    Gosub Mu_play                    ' 调用音乐播放子程序
cls
    Wait 2                           ' 播放完成后等待2秒钟

    ' 播放第二首乐曲:苏联国歌《牢不可破的联盟》
    Restore Mu_data1                 ' 将数据指针指向音乐数据表2的起始位置
    Gosub Mu_play                    ' 调用音乐播放子程序
cls
    Wait 2                           ' 播放完成后等待2秒钟

Loop                                 ' 重复循环,实现音乐连续播放

End                                  ' 程序结束(实际不会执行到这里,因为循环无限)

' ==================== 音乐播放子程序 ====================
' 功能: 主音乐播放流程控制
' 调用关系: 主程序 → Mu_play → Mu_playini → Mu_play1 → Mu_playsub → (Mu_datcnv/Mu_noterd)
Mu_play:
    Gosub Mu_playini                 ' ① 初始化:读取速度、设置初始八度
Mu_play1:
    Gosub Mu_playsub                 ' ② 解析并执行一个音乐代码(音符/八度/休止符)
    If Mu_temp1 = 0 Then Goto Mu_play1  ' ③ 如果未结束(Mu_temp1=0),继续处理下一个音符
Return                               ' ④ 当前曲目播放完毕,返回主程序

' -------------------- 初始化子程序 --------------------
' 功能: 初始化音乐播放参数,读取速度值
Mu_playini:
    Mu_octave = 4                    ' 将八度初始值设为4 (中央C所在的八度)
    Mu_octdiv = 80                   ' 设置默认八度分频值(O4对应的值)
    Read Mu_temp1                    ' 读取第一个数据(速度值,例如120或85)
    Mu_tempo = 60000 / Mu_temp1      ' 将速度转换为毫秒:60000ms/速度值 = 每四分音符毫秒数
    Mu_tempo = Mu_tempo * 4          ' 计算全音符的时长:四分音符时长 × 4
    Mu_dtlen = 0                     ' 初始化数据行长度为0,表示需要读取新行
Return

' -------------------- 音乐代码处理子程序 --------------------
' 功能: 解析并执行一个音乐指令(音符、八度变化、休止符等)
Mu_playsub:
    ' === 第1步:获取下一个字符 ===
    If Mu_dtlen = 0 Then
        Read Mu_pldata               ' 从DATA语句读取一行音乐数据
        Mu_dtlen = Len(mu_pldata)    ' 获取这行数据的字符长度
        Mu_dtposi = 1                ' 设置读取位置为第1个字符
    End If

    Gosub Mu_datcnv                  ' 从数据行读取一个字符到Mu_temp1
    Mu_temp1 = Mu_temp1 And &HDF     ' 将ASCII码转换为大写(清除第6位,即小写转大写)

    ' === 第2步:OLED显示处理(调试用)===
    If Mu_temp1 = &H41 Then Lcdat 3 , 60 , "6"   ' A -> 6
    If Mu_temp1 = &H42 Then Lcdat 3 , 60 , "7"   ' B -> 7
    If Mu_temp1 = &H43 Then Lcdat 3 , 60 , "1"   ' C -> 1
    If Mu_temp1 = &H44 Then Lcdat 3 , 60 , "2"   ' D -> 2
    If Mu_temp1 = &H45 Then Lcdat 3 , 60 , "3"   ' E -> 3
    If Mu_temp1 = &H46 Then Lcdat 3 , 60 , "4"   ' F -> 4
    If Mu_temp1 = &H47 Then Lcdat 3 , 60 , "5"   ' G -> 5

    ' === 第3步:判断指令类型 ===
    If Mu_temp1 = &H4F Then Goto Mu_plays2    ' ASCII 4F = "O" - 八度改变指令
    If Mu_temp1 = &H52 Then Goto Mu_plays3    ' ASCII 52 = "R" - 休止符指令
    If Mu_temp1 > &H47 Then Return            ' ASCII 47 = "G"以上,可能是"Z"结束代码

    ' === 第4步:处理音符指令(A-G) ===
    Mu_temp1 = Mu_temp1 - &H40                ' 将A~G (ASCII 41-47) 转换为数字1~7
    Mu_temp2 = Mu_temp1 + Mu_temp1            ' 乘以2,为查表做准备(表中每项占2字节)

    Gosub Mu_datcnv                           ' 读取下一个字符(检查是否有升降音)

    ' 检查是否为升音(#)或降音($)
    If Mu_temp1 < &H31 Then                   ' ASCII小于"1"的字符可能是#或$
        If Mu_temp1 = &H23 Then               ' ASCII 23 = "#" 升半音
            Mu_temp2 = Mu_temp2 + 1           ' 索引加1,对应升半音的频率
        Else                                  ' 否则是降音"$"
            Mu_temp2 = Mu_temp2 - 1           ' 索引减1,对应降半音的频率
        End If
        Gosub Mu_datcnv                       ' 再读取下一个字符(音符时值)
    End If

    Gosub Mu_noterd1                          ' 解析音符时值(全分、二分、四分等)

    ' === 第5步:计算音符频率 ===
    Mu_temp2 = Mu_temp2 - 1                   ' 将1-7转换为0-6的索引
    Mu_freq = Lookup(mu_temp2 , Mu_scale)     ' 查表获取基础频率
    Mu_freq = Mu_freq / Mu_octdiv             ' 根据当前八度调整频率
    Mu_tempw2 = Mu_freq * 12                  ' 计算SOUND指令需要的频率参数
    Mu_templ1 = _xtal / Mu_tempw2             ' 根据晶振频率计算分频值
    Mu_tempw2 = Mu_templ1                     ' 保存分频值

    ' === 第6步:计算音符时长 ===
    Mu_templ1 = Mu_tempw1 * Mu_freq           ' 时长 × 频率
    Mu_templ1 = Mu_templ1 / 1000              ' 转换为毫秒
    Mu_tempw1 = Mu_templ1                     ' 保存时长参数

    ' === 第7步:OLED显示高低音符号 ===
    IF Mu_octave = mu_h THEN Lcdat 1 , 60 , chr(46)   ' 高音显示点在上方
    IF Mu_octave = mu_L THEN Lcdat 4 , 60 , chr(46)   ' 低音显示点在下方
    IF Mu_octave = mu_Z THEN
        Lcdat 1 , 60 , chr(32)                        ' 中音清除上方点
        Lcdat 4 , 60 , chr(32)                        ' 中音清除下方点
    ENDIF

    ' === 第8步:输出声音 ===
    Sound Su_port , Mu_tempw1 , Mu_tempw2      ' 在PB0产生指定频率和时长的方波

Mu_plays4:
    Mu_temp1 = 0                               ' 设置标志,表示指令处理完成
Return

' -------------------- 八度处理子程序 --------------------
' 功能: 处理"O"指令,改变当前八度
Mu_plays2:
    Gosub Mu_datcnv                            ' 读取八度参数
    If Mu_temp1 < &H31 Then                    ' 如果参数是+或-(ASCII小于"1")
        If Mu_temp1 = &H2B Then                ' ASCII 2B = "+"
            Mu_octave = Mu_octave + 1          ' 八度增加
        Else                                   ' 否则是"-"
            Mu_octave = Mu_octave - 1          ' 八度降低
        End If
    Else                                       ' 否则是数字1-7
        Mu_octave = Mu_temp1 - &H30            ' 将ASCII数字转换为数值
        ' 指定中、低、高音频段分频值
        mu_h = Mu_octave + 1                   ' 高八度阈值(当前八度+1)
        mu_L = Mu_octave - 1                   ' 低八度阈值(当前八度-1)
        mu_Z = Mu_octave                       ' 当前八度
    End If

    Mu_temp1 = Mu_octave - 1                   ' 转换为0-6的索引
    Mu_octdiv = Lookup(mu_temp1 , Mu_ocdiv)    ' 查表获取对应的分频值
Goto Mu_plays4

' -------------------- 休止符处理子程序 --------------------
' 功能: 处理"R"指令,静音指定时长
Mu_plays3:
    Gosub Mu_noterd                            ' 解析休止符时长
    Waitms Mu_tempw1                           ' 等待指定毫秒(静音)
Goto Mu_plays4

' -------------------- 字符读取子程序 --------------------
' 功能: 从当前数据行读取一个字符并转换为ASCII码
Mu_datcnv:
    Mu_str = Mid(mu_pldata , Mu_dtposi , 1)    ' 从缓冲区提取一个字符
    Mu_dtposi = Mu_dtposi + 1                  ' 读取位置后移
    Mu_dtlen = Mu_dtlen - 1                    ' 剩余长度减1
    Mu_temp1 = Asc(mu_str)                     ' 将字符转换为ASCII码
Return

' -------------------- 音符/休止符时长解析子程序 --------------------
' 功能: 从数据中提取音符时值并计算实际时长
Mu_noterd:
    Gosub Mu_datcnv                            ' 读取时值参数
Mu_noterd1:
    Mu_temp4 = Mu_temp1                        ' 保存时值代码
    Select Case Mu_temp4
        Case &H36 : ' ASCII "6" - 64分音符
            Mu_temp3 = 6                       ' 右移6位(除以64)
            Mu_dtposi = Mu_dtposi + 1          ' 跳过下一个字符
            Mu_dtlen = Mu_dtlen - 1

        Case &H33 : ' ASCII "3" - 32分音符
            Mu_temp3 = 5                       ' 右移5位(除以32)
            Mu_dtposi = Mu_dtposi + 1
            Mu_dtlen = Mu_dtlen - 1

        Case &H31 : ' ASCII "1" - 可能是全音符或16分音符
            Mu_temp3 = 0                       ' 先假设是全音符
            If Mu_dtlen <> 0 Then              ' 如果还有字符
                Gosub Mu_datcnv                ' 再读一个字符
                If Mu_temp1 = &H36 Then        ' 如果是"6"(16分音符)
                    Mu_temp3 = 4               ' 右移4位(除以16)
                Else                           ' 否则是真正的全音符
                    Mu_dtposi = Mu_dtposi - 1  ' 回退指针
                    Mu_dtlen = Mu_dtlen + 1
                End If
            End If

        Case &H38 : ' ASCII "8" - 8分音符
            Mu_temp3 = 3                       ' 右移3位(除以8)

        Case &H32 : ' ASCII "2" - 2分音符
            Mu_temp3 = 1                       ' 右移1位(除以2)

        Case Else : ' 其他情况(默认) - 4分音符
            Mu_temp3 = 2                       ' 右移2位(除以4)
    End Select

    Mu_tempw1 = Mu_tempo                       ' 取全音符时长
    If Mu_temp3 <> 0 Then                      ' 如果不是全音符
        Shift Mu_tempw1 , Right , Mu_temp3     ' 右移计算实际时长
    End If
Return

' ==================== 数据表 ====================

' 音阶频率数据表
' 格式:每个音阶对应的频率值(以字WORD类型存储)
' 排列顺序:Ab, A, A#=Bb, B, (占位), C, C#=Db, D, D#=Eb, E, (占位), F, F#=Gb, G, G#
Mu_scale:
    Data 33228% , 35200% , 37296% , 39512% , 39512% , 20930% , 22176% , 23496%
    Data 24892% , 26372% , 26372% , 27936% , 29600% , 31360% , 33228%

' 八度分频数据表
' 对应八度1-7的分频系数,用于将基础频率调整到正确的八度
' 值越小,频率越高(高八度)
Mu_ocdiv:
    Data 640% , 320% , 160% , 80% , 40% , 20% , 10%

' ==================== 音乐数据表 ====================
' 音乐代码语法说明:
'   Ox    : 改变八度 [1~7] (O4A对应440Hz标准音)
'   + / - : 基于当前八度值增减(例如O+表示升一个八度)
'   C D E F G A B : 分别对应Do Re Mi Fa Sol La Si
'   #     : 升高半音
'   $     : 降低半音
'   1 2 4 8 16 32 64 : 音符时值(全音符、二分、四分、八分等)
'   Rx    : 休止符(长度与音符相同,例如R4表示四分休止)
'   Z     : 结束代码,标记音乐数据结束

' 曲目1:莫扎特 G大调弦乐小夜曲 第一乐章
Mu_data:
   Data 120                                                 ' TEMPO 速度值(每分钟120拍)
  Data "o7"
  Data "d4r8o-a8o+d4r8o-a8o+d8o-a8o+d8f#8a4r4"
   Data "g4r8e8g4r8e8g8e8c#8e8o-a4o+r4"
   Data "d4d4d8f#8e8d8d8c#8c#4c#8e8g8c#8"
   Data "e8d8d4d8f#8e8d8d8c#8c#4c#8e8g8c#8"
   Data "d8d8d16c#16o-b16o+c#16d8d8f#16e16d16e16"
   Data "f#8f#8a16g16f#16g16a4r4z"

' 曲目2:苏联国歌《牢不可破的联盟》- 带歌词注释
Mu_data1:
   Data 85    ' TEMPO 庄严的进行曲速度(每分钟85拍)
   Data "o4"
   Data "r8g8o+c4o-g8a8b4e8e8a4g8f8g4c8c8"
   Data "d4d8e8f4f8g8a4b8o+c8d4o-g4"
   Data "o+e4d8c8d4o-g8g8o+c4o-b8a8b4e8e8"
   Data "a4g8f8g4c8c8o+c4o-b8a8g4g4"
   Data "o+e2d8c8o-b8o+c8d4o-g4g2"
   Data "o+c4c4o-b8a8g8a8b4e4e4r4"
   Data "o+c4o-a8b8o+c4o-a8b8"
   Data "o+c4o-a4o+c8f4r8"
   Data "f2e8d8c8d8"
   Data "e4c8c2"
   Data "d2c8o-b8a8b8"
   Data "o+c4o-a8a2"
   Data "o+c4o-b8a8g4c8c8"
   Data "o+c4o-b8a8g4r8g8"
   Data "g2a4b4"
   Data "o+c1z"        ' Z标记音乐结束   

' ==================== 程序结束 ====================
$include "../font8x8TT.font"    ' 包含8x8字体文件 屏幕录制 2.zip (1.01 MB, 下载次数: 0)

回复

使用道具 举报

地板
ID:1064915 发表于 2026-4-11 14:12 | 只看该作者
买生日蛋糕会送你一个
回复

使用道具 举报

5#
ID:584814 发表于 2026-4-11 16:41 | 只看该作者
某一个宝上语音芯片成本几块钱简单到只需外接喇叭和电池且有可自己定义内容的
回复

使用道具 举报

6#
ID:462768 发表于 2026-4-11 19:09 | 只看该作者
黄youhui 发表于 2026-4-11 11:37
去找语音芯片的厂家。他们教你怎么做MP3.

要机械式的,电子的声音始终没有机械味
回复

使用道具 举报

7#
ID:462768 发表于 2026-4-11 19:09 | 只看该作者
joyb 发表于 2026-4-11 14:12
买生日蛋糕会送你一个

那种没有创新
回复

使用道具 举报

8#
ID:462768 发表于 2026-4-11 19:10 | 只看该作者
man1234567 发表于 2026-4-11 16:41
某一个宝上语音芯片成本几块钱简单到只需外接喇叭和电池且有可自己定义内容的

要机械式的,然后芯片控制一些 能实现可编程的,
回复

使用道具 举报

9#
ID:1168486 发表于 2026-4-13 07:30 | 只看该作者
我有个主意,楼主付费吗?
回复

使用道具 举报

10#
ID:857072 发表于 2026-4-13 08:15 来自触屏版 | 只看该作者
用光驱的滑台带动一个触针移动来确定音阶。主轴电机带动一个发声片按固定速度扫过触针。单片机只需要控制两个电机。

tb_image_share_1776039117147.png (515.39 KB, 下载次数: 0)

tb_image_share_1776039117147.png
回复

使用道具 举报

11#
ID:857072 发表于 2026-4-13 08:53 来自触屏版 | 只看该作者
怕光驱电机的力度不够。可以换成这种更小的滑台加减速步进电机比较有劲,但是它速度很慢。你的触针必须做成齿轮状。这样体积可以做的更小。

tb_image_share_1776041394226.png (458.83 KB, 下载次数: 0)

tb_image_share_1776041394226.png

tb_image_share_1776041373974.png (563.35 KB, 下载次数: 0)

tb_image_share_1776041373974.png
回复

使用道具 举报

12#
ID:462768 发表于 2026-4-13 14:17 | 只看该作者
a185980800 发表于 2026-4-13 08:53
怕光驱电机的力度不够。可以换成这种更小的滑台加减速步进电机比较有劲,但是它速度很慢。你的触针必须做成 ...

这种想过,但是小型化就感觉难以做到了,也想多每个音节都配一个机械手指的功能,也没想出具体好方案,现在的八音盒小的可以拿在手里,在加上这一套装置,空间还不能加大,太难了,而且我还想做到更小
回复

使用道具 举报

13#
ID:844772 发表于 2026-4-13 15:43 | 只看该作者
不要想着做全音阶或复杂节奏,就容易了。比如用簧片做好一两个音程,用一个一排旋转的小锤定时敲击,然后,敲击点压上绒布,需要响时打孔,就像编程纸带一样,用电机带动绒布转,就会有简单的旋律。
回复

使用道具 举报

14#
ID:69038 发表于 2026-4-13 17:13 | 只看该作者
m2006410 发表于 2026-4-13 14:17
这种想过,但是小型化就感觉难以做到了,也想多每个音节都配一个机械手指的功能,也没想出具体好方案,现 ...

30年多前,业界出现一款芯片,叫做电子和弦音IC,有8和弦、16和弦、32和弦。。就是单片机发数据给它,合成音输出。。主要用在无绳电话、BP机等产品上,那音质确实不是普通单音(BEEP、TONE)能媲美的,刚出来那会,很是惊艳。。。
说不定现在还能找到一些

评分

参与人数 1黑币 +5 收起 理由
bluemaster + 5

查看全部评分

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表