用51实现的真正和弦音乐
一直想用单片机来产生和弦音乐,不用太复杂,有和弦效果就行,但网上100%的单片机产生音乐都是假和弦,基本都是单调的7个调加上时间控制,难听。直到我听到这个:
这个是真的和弦,效果非常好,无奈联系不上作者,只有HEX文件,用的STC12C5A60S2单片机。我自行下载测试也是和录音的相同效果。
STC12C5A系统自行反编译明显出错,只好花钱网上找人反编译,反编译出的汇编代码编译出来的和原来的HEX文件效果一致,到这一步已经有希望了。
但问题来了....我已经忘记汇编了,寄存器一多就乱,只好花了些时间把数组提取出来,放上论坛,有精力的莫友可以一起帮把汇率转成C的。
汇率代码不多,就只有100行左右寄存器多的我看不过来。
能转成C语言也是一件好事,毕竟这是目前的最好效果了。
测试电路也简单
这个厉害,值得借鉴。 听了下,确实牛叉。现在产品中还是用到滴滴的蜂鸣器,要是能改成和弦提示音,就有点儿高大上了。 听了听效果不错{:smile:} 刚玩51时这个东西就是我的梦想啊。 看了程序,等于波表了。 对于51单片机这个效果非常好了 lans0625 发表于 2015-6-24 15:18
刚玩51时这个东西就是我的梦想啊。
梦想就在眼前啊{:titter:} 关注一下,看看怎么实现,以前只会单调声音 amazing030 发表于 2015-6-24 15:27
梦想就在眼前啊
好多年前的了,现在都不玩单片机了。 本帖最后由 sbk100 于 2015-6-24 15:42 编辑
牛逼啊 顶一个!里面的汇编是源程序还是嵌入的汇编呢? sbk100 发表于 2015-6-24 15:41
牛逼啊 顶一个!里面的汇编是源程序还是嵌入的汇编呢?
汇编是源程序,别外的C是我自己建的没添加进工程 里面的数组怎么用音乐转换出来呢? 这功能不错,MARK先 哇靠,这个实在太 NB 了! 这个真是牛! 就是用PWM实现DA的方法。
声音数据其实就是单声道的WAV
只要有足够的ROM存WAV数据,就可以播放音乐 yy625 发表于 2015-6-24 16:55
就是用PWM实现DA的方法。
声音数据其实就是单声道的WAV
只要有足够的ROM存WAV数据,就可以播放音乐 ...
你想简单了 用PWM播放WAV反而简单 只好花钱网上找人反编译,
----------------
这个你就破费了{:titter:} {:lol:} wkman 发表于 2015-6-24 17:40
只好花钱网上找人反编译,
----------------
这个你就破费了...
这个只能编译标准的51系列吧,像这些1T的外设多了的会出错 能用Proteus仿真出效果吗? 可惜我不知道真和弦和假和弦本质区别是什么,一个单片机同时产生两个假和弦楼主会不?然后叠加应该就是你要的真和弦了。你看到的都是软件延时做的假和弦,所以觉得这个很牛。 关键字 DTMF 双音多频 听效果不是简单的双音频啊,有些长音结尾音量是逐渐衰减的,这个不是普通的PWM能做出来的,猜测可能用了SPWM这样的PWM调制,肯定函数做幅度衰减运算控制 这个人真的很牛~~~~~~~~~~~ 嘿嘿,mark!! 收藏,期待C的程序出来 顶你.加油,一定要搞定啊.大家一起研究下. 同时调PWM的周期和占空比,应该能做出这种效果,关键是码表 很多年以前,咋坛子里有一个类似的帖子,是有c语言源码的。电路比这还简单,输出直接pwm无滤波。我还修改到avr上玩过。
你搜搜“和弦”,可能还能搜到。
本帖最后由 xiaoxu191 于 2015-6-24 22:19 编辑
AVR PWM 和弦产生器的来源是这个:
255-Voice PCM Sound Generator
下载学习与研究一下,谢谢Lz 和这个使用STC12C5A60S2 相同之处在于,STC这款通过PCA实现的PWM,也有较高的频率,因此声音会好听一些。一般的AVR时钟频率不够高,所以音质差一些。
还有这个:
Simple SD Audio Player with an 8-pin IC
当然AVR 的官方文档也说了:
AVR131: Using the AVR’s High-speed PWM xiaoxu191 发表于 2015-6-24 22:16
和这个使用STC12C5A60S2 相同之处在于,STC这款通过PCA实现的PWM,也有较高的频率,因此声音会好听一些。一 ...
不,不是PWM播放WAV方式的,你看数据大小就知道了,播放WAV的话你存储空间够吗? 8K的采样率单片机那种存储你能播放多久? 关注中。。。。 算法的核心应该是N(n<8)个不同幅度不同频率的正弦波实时查表计算和叠加。和弦不难,难的是音色。以51的速度,难在用有限的谐波合成好听的音色,如果能用3,5个谐波就能弄出好音色,剩下的事情就比较好办了。我估计旋律音是有音色的,而伴奏和弦音是没有音色的。不知道哪里有乐器音色的谐波成分表,可以简化一下谐波试试,看能不能弄出个也还好听的音色来。 学习谢谢!!! 来学习学习。。。。。。。。 洗衣机上的铃声就是这个吧,哪位高手能用C语言搞一下就太好了 amazing030 发表于 2015-6-24 22:27
不,不是PWM播放WAV方式的,你看数据大小就知道了,播放WAV的话你存储空间够吗? 8K的采样率单片机那种存 ...
是的,完全同意,此处不是用PWM播放WAV。但是发声的原理是一样的,就是用PWM对已经量化的声音数据进行还原,理论依据是香农采样定理(又称奈奎斯特采样定理):为了不失真地恢复模拟信号,采样频率应该不小于模拟信号频谱中最高频率的2倍。 我注意到你的硬件电路中使用了22MHz晶振,而STC12C5A60的PCA生成PWM的频率和其它80512芯片比较高,所以认为和那个AVR(使用Tiny85,Tiny861,内部有高频振荡器供PWM)有相似之处,就是采样(还原)频率较高,音质会比较好。根据使用的GPIO引脚功能和晶振,判断是PWM无疑。
这不是重点。
我听了你帖子里的音频,感觉和玩具(使用语音芯片的和弦功能)比较,并没有更好。只能说,实现多音频的和弦,比单音好听。
值得进一步分析的是:如何编辑出好听的音色?
39楼 teddeng 的观点有启发。
标记一下 有意思,抽空试试 我把数组中的数据按16位绘制成了波形,从波形上看,ADDR_02E0与ADDR_0630较乱,可能是用于产生音色,
其它数组可能是与音源相乘,产生调幅信号.
这个要关注,多媒体了 怎么听?用当真软件可以吗 真有这么好听么,有空试试 LZ真有耐心, 还原反汇编的代码..... 为毛,这几天下载速度这么揪心呢?下载文件也是坏的 看了反汇编的程序,代码不长,转C应该问题不大 MARK ,有时间研究下,下次不用滴滴滴了。只要实现最简单的 替换 滴滴 ,呵呵 里面哪个hex是原始hex?
用keil v2编译STC12C5A60.asm,得到的hex和下载的hex比较有点不一样。 如果是弄成和弦音,真是高大上。关注中 这绝对是一技术贴,期待高手出现。 关注 俺也就是做到单音频加强度 这还是比较值得关注的.... 关注,期待大牛神作。。。 这是PWM播放,网上很多例程吧,我也做出来了,效果还不错。 这个感觉高大上的样子,期待高手 明确几点:
1. 不是PWM播放WAV的方式,看程序里数组大小就知道了,只用了几K可以播放几分钟。还要教我怎么播放WAV的话看我这个帖子:
http://www.amobbs.com/thread-4743480-1-1.html
算是单片机播放WAV的一个极致应用了,而且,是开源的。
2.觉得用定时器代替软件延时就让我觉得NB的请自行听一下录音,并看下程序,没有外挂FLASH。
3.真假和弦,个人说法而已,大家也听的出来这个已经有了多种音乐效果。
4.楼上有网友切到谐波也就是码表的重点了,关键是,码表已经在程序里了,也就是说可以做出来一样音色的其它歌曲。
5.这个程序真的不复杂,要分析的不多,熟悉汇编的都能搞定 而且这个效果比低端的牛屎和弦芯片效果要好,还是很有意义的 继续关注中 amazing030 发表于 2015-6-26 17:35
而且这个效果比低端的牛屎和弦芯片效果要好,还是很有意义的
有做过SPI flash芯片做字库吗?能否指点一下呢? 收藏了,回家详细看 感觉仅仅懂单片机搞不出这样的效果 amazing030 发表于 2015-6-26 17:29
明确几点:
1. 不是PWM播放WAV的方式,看程序里数组大小就知道了,只用了几K可以播放几分钟。还要教我怎么 ...
能否用C语言实现一个呢??
这个音质,有些带音乐的计算器可以发出来。具体原理不知道,用示波器直接看的话,像是PWM控制音量。好久以前的事情了,记不太清了。后来也没再研究。 amazing030 发表于 2015-6-26 17:29
明确几点:
1. 不是PWM播放WAV的方式,看程序里数组大小就知道了,只用了几K可以播放几分钟。还要教我怎么 ...
大师我错了
原来你是玩WAV长大的。
期待你的成功 http://v.youku.com/v_show/id_XNTY0MzkxMTUy.html
给大家看看一个程序不NB但做出来很NB的东东,不是我做的,想做没那么多精力和余钱啊 原帖的地址就不贴出来了,大家可以度娘一下。
原作者说的原理是“
1.波形合成,产生基准音高,事先生成一个正弦波形表,查表实现
2.谐波混频,产生几个奇次谐波适量混入基频,改变音色,也可在波形表里直接使用含各次谐波的波形数据,而省略此步骤,但音色效果便不能灵活改变
3.包络调制,产生弹奏型或吹奏型或打击型效果,事先生成各种包络数据,查表,
4.多通道混合,以上123用多个通道分别实现主旋律\伴奏\等,只要单片机速度允许.
5.pwm做da输出,
第1步决定音调,第2和第3步决定音色效果,比如混入适量方波,使用吹奏型包络调制,可产生类似长笛小号萨克斯效果,使用弹奏型包络则有吉他或电吉他效果,如音调较高 则有风铃或八音盒效果.单纯的正弦波声音暗哑无光,必须在正弦波里混入一些谐波或制造一点失真,产生的音色才会丰满,明亮
波形数据在进行运算时要按有符号数处理,最后进行直流偏置,如果按无符号数处理,不做直流偏置,则音符和音符之间有咔哒声” 我们坛里高手就是牛,有你们才精彩,赞一个 谁能做出来可太厉害了 牛人啊 不知道你们要的是不是这个.这是以前收集到的.主要精髓在以下代码里.附件是原程序
/**************************************************************************
SOUND PLAY FOR 51MCU
COPYRIGHT (c) 2005 BY JJJ.
--ALL RIGHTS RESERVED--
File Name: SoundPlay.h
Author: Jiang Jian Jun
Created: 2005/5/16
Modified: NO
Revision: 1.0
*******************************************************************************/
/*说明**************************************************************************
曲谱存贮格式 unsigned char code MusicName{音高,音长,音高,音长...., 0,0}; 末尾:0,0 表示结束(Important)
音高由三位数字组成:
个位是表示 1~7 这七个音符
十位是表示音符所在的音区:1-低音,2-中音,3-高音;
百位表示这个音符是否要升半音: 0-不升,1-升半音。
音长最多由三位数字组成:
个位表示音符的时值,其对应关系是:
|数值(n):|0 |1 |2 |3 | 4 | 5 | 6
|几分音符: |1 |2 |4 |8 |16 |32 |64 音符=2^n
十位表示音符的演奏效果(0-2):0-普通,1-连音,2-顿音
百位是符点位: 0-无符点,1-有符点
调用演奏子程序的格式
Play(乐曲名,调号,升降八度,演奏速度);
|乐曲名 : 要播放的乐曲指针,结尾以(0,0)结束;
|调号(0-11) : 是指乐曲升多少个半音演奏;
|升降八度(1-3) : 1:降八度, 2:不升不降, 3:升八度;
|演奏速度(1-12000): 值越大速度越快;
***************************************************************************/
#ifndef __SOUNDPLAY_H_REVISION_FIRST__
#define __SOUNDPLAY_H_REVISION_FIRST__
//**************************************************************************
#define SYSTEM_OSC 12000000 //定义晶振频率12000000HZ
#define SOUND_SPACE 4/5 //定义普通音符演奏的长度分率,//每4分音符间隔
sbit BeepIO = P3^7; //定义输出管脚
unsigned intcode FreTab= { 262,277,294,311,330,349,369,392,415,440,466,494 }; //原始频率表
unsigned char code SignTab= { 0,2,4,5,7,9,11 }; //1~7在频率表中的位置
unsigned char code LengthTab= { 1,2,4,8,16,32,64 };
unsigned char Sound_Temp_TH0,Sound_Temp_TL0; //音符定时器初值暂存
unsigned char Sound_Temp_TH1,Sound_Temp_TL1; //音长定时器初值暂存
//**************************************************************************
void InitialSound(void)
{
BeepIO = 0;
Sound_Temp_TH1 = (65535-(1/1200)*SYSTEM_OSC)/256; // 计算TL1应装入的初值 (10ms的初装值)
Sound_Temp_TL1 = (65535-(1/1200)*SYSTEM_OSC)%256; // 计算TH1应装入的初值
TH1 = Sound_Temp_TH1;
TL1 = Sound_Temp_TL1;
TMOD|= 0x11;
ET0 = 1;
ET1 = 0;
TR0 = 0;
TR1 = 0;
EA = 1;
}
void BeepTimer0(void) interrupt 1 //音符发生中断
{
BeepIO = !BeepIO;
TH0 = Sound_Temp_TH0;
TL0 = Sound_Temp_TL0;
}
//**************************************************************************
void Play(unsigned char *Sound,unsigned char Signature,unsigned Octachord,unsigned int Speed)
{
unsigned int NewFreTab; //新的频率表
unsigned char i,j;
unsigned int Point,LDiv,LDiv0,LDiv1,LDiv2,LDiv4,CurrentFre,Temp_T,SoundLength;
unsigned char Tone,Length,SL,SH,SM,SLen,XG,FD;
for(i=0;i<12;i++) // 根据调号及升降八度来生成新的频率表
{
j = i + Signature;
if(j > 11)
{
j = j-12;
NewFreTab = FreTab*2;
}
else
NewFreTab = FreTab;
if(Octachord == 1)
NewFreTab>>=2;
else if(Octachord == 3)
NewFreTab<<=2;
}
SoundLength = 0;
while(Sound != 0x00) //计算歌曲长度
{
SoundLength+=2;
}
Point = 0;
Tone = Sound;
Length = Sound; // 读出第一个音符和它时时值
LDiv0 = 12000/Speed; // 算出1分音符的长度(几个10ms)
LDiv4 = LDiv0/4; // 算出4分音符的长度
LDiv4 = LDiv4-LDiv4*SOUND_SPACE; // 普通音最长间隔标准
TR0 = 0;
TR1 = 1;
while(Point < SoundLength)
{
SL=Tone%10; //计算出音符
SM=Tone/10%10; //计算出高低音
SH=Tone/100; //计算出是否升半
CurrentFre = NewFreTab+SH]; //查出对应音符的频率
if(SL!=0)
{
if (SM==1) CurrentFre >>= 2; //低音
if (SM==3) CurrentFre <<= 2; //高音
Temp_T = 65536-(50000/CurrentFre)*10/(12000000/SYSTEM_OSC);//计算计数器初值
Sound_Temp_TH0 = Temp_T/256;
Sound_Temp_TL0 = Temp_T%256;
TH0 = Sound_Temp_TH0;
TL0 = Sound_Temp_TL0 + 12; //加12是对中断延时的补偿
}
SLen=LengthTab; //算出是几分音符
XG=Length/10%10; //算出音符类型(0普通1连音2顿音)
FD=Length/100;
LDiv=LDiv0/SLen; //算出连音音符演奏的长度(多少个10ms)
if (FD==1)
LDiv=LDiv+LDiv/2;
if(XG!=1)
if(XG==0) //算出普通音符的演奏长度
if (SLen<=4)
LDiv1=LDiv-LDiv4;
else
LDiv1=LDiv*SOUND_SPACE;
else
LDiv1=LDiv/2; //算出顿音的演奏长度
else
LDiv1=LDiv;
if(SL==0) LDiv1=0;
LDiv2=LDiv-LDiv1; //算出不发音的长度
if (SL!=0)
{
TR0=1;
for(i=LDiv1;i>0;i--) //发规定长度的音
{
while(TF1==0);
TH1 = Sound_Temp_TH1;
TL1 = Sound_Temp_TL1;
TF1=0;
}
}
if(LDiv2!=0)
{
TR0=0; BeepIO=0;
for(i=LDiv2;i>0;i--) //音符间的间隔
{
while(TF1==0);
TH1 = Sound_Temp_TH1;
TL1 = Sound_Temp_TL1;
TF1=0;
}
}
Point+=2;
Tone=Sound;
Length=Sound;
}
BeepIO = 0;
}
//**************************************************************************
#endif 这个音质有点不行哦,楼主这样也能接受?我觉得以前坛里曾有的一个贴子里的音质比这个要好些,http://www.amobbs.com/thread-3614256-1-1.html ,里面有C语言源程序和各种讨论。 果然厉害,下来听一下~ hwbrat 发表于 2015-6-29 09:10
这个音质有点不行哦,楼主这样也能接受?我觉得以前坛里曾有的一个贴子里的音质比这个要好些,http://www ...
哈哈,多谢了,是很不错啊 这个效果挺不错,利害啊! 持续关注中…… hwbrat 发表于 2015-6-29 09:10
这个音质有点不行哦,楼主这样也能接受?我觉得以前坛里曾有的一个贴子里的音质比这个要好些,http://www ...
的确,这个效果更好啊,但程序里关键的一段还是汇编的,还是用的AVR,我就更头疼了....有没有熟悉汇编的网友帮忙转一下啊 要转的只有100行不到吧,应该有人收空做一下吧{:dizzy:} 不懂音乐 看看 大牛真是多啊 不懂音乐,听了下,确实不错!
有个想法,把这两首歌的乐谱拿出来对比下是不是能发现什么。。。 以前的收集的。 oktek 发表于 2015-7-1 09:53
以前的收集的。
没有码表一看就是不行的,只是简单的频率发音 {:sweat:}唉,就没个人熟悉汇编吗,要转C的语句都不多 amazing030 发表于 2015-7-1 10:02
唉,就没个人熟悉汇编吗,要转C的语句都不多
一楼的汇编资料我已经转了一部分了,剩下的还在慢慢啃 zouzhichao 发表于 2015-7-1 10:14
一楼的汇编资料我已经转了一部分了,剩下的还在慢慢啃
哈哈,不错。79楼提到的这个效果好像更好,你可以听一下,只是中断部分是汇编,用的AVR,汇编部分有注释的
http://www.amobbs.com/thread-3614256-1-1.html 这个只能用STC12C5A60S2试听吗?手上只有STC的别的MCU. amazing030 发表于 2015-7-1 10:18
哈哈,不错。79楼提到的这个效果好像更好,你可以听一下,只是中断部分是汇编,用的AVR,汇编部分有注释 ...
谢谢提供资料 amazing030 发表于 2015-7-1 10:18
哈哈,不错。79楼提到的这个效果好像更好,你可以听一下,只是中断部分是汇编,用的AVR,汇编部分有注释 ...
中午我把分析的资料整理一下发上来交流一下 wintelboy 发表于 2015-7-1 10:19
这个只能用STC12C5A60S2试听吗?手上只有STC的别的MCU.
你要看下那个个寄存器是不是一样的 这是79楼地址里的效果
http://d1.amobbs.com/bbs_upload782111/files_19/ourdev_487499.txt amazing030 发表于 2015-7-1 10:02
唉,就没个人熟悉汇编吗,要转C的语句都不多
程序流程估计你已经弄出来了,我就写写小小的细节吧。
程序中多次用到LCALL Q0DF9,想必这个Q0DF9是个数学计算函数,那就先把它读明白。
Q0DF9:
MOV A,R3 ;0DF9 EB
MOV B,R7 ;0DFA 8F F0
MUL AB ;0DFC A4
;BA = R3*R7
;乘法指令,结果B为高8位,A为低8位
MOV R4,B ;0DFD AC F0
MOV R5,A ;0DFF FD
;R4R5 = R3*R7
;将B和A挪至R4R5
MOV A,R3 ;0E00 EB
MOV B,R6 ;0E01 8E F0
MUL AB ;0E03 A4
;BA = R3*R6
;乘法指令,结果B为高8位,A为低8位
ADD A,R4 ;0E04 2C
MOV R4,A ;0E05 FC
;R4 = R4 + A
;注意这个时候可能有进位
CLR A ;0E06 E4
ADDCA,B ;0E07 35 F0
;B = B + 进位,实质上这是8位单片机做16/32位整数加法的常用代码
MOV R3,A ;0E09 FB
;将A挪至R3
;R3R4R5 = R3*R7 + (R3*R6 << 8)
;这一步一定要琢磨明白,下面的代码跟这个类似
MOV A,R2 ;0E0A EA
MOV B,R7 ;0E0B 8F F0
MUL AB ;0E0D A4
;BA = R2*R7
;这个就不啰嗦了,同上分析
ADD A,R4 ;0E0E 2C
MOV R4,A ;0E0F FC
MOV A,R3 ;0E10 EB
ADDCA,B ;0E11 35 F0
MOV R3,A ;0E13 FB
;R3R4 = R3R4 + BA
;R3R4R5 = R3*R7 + ((R3*R6 + R2*R7) << 8)
;这个就不啰嗦了,同上分析
CLR A ;0E14 E4
RLC A ;0E15 33左移一位
;A = CY
;这两行代码就是为了取出上次加法运算的进位码,为后面更高位的运算做铺垫
XCH A,R2 ;0E16 CA
;交换A和R2,相当于几个MOV
MOV B,R6 ;0E17 8E F0
MUL AB ;0E19 A4
;R2R3R4R5 = R3*R7 + ((R3*R6 + R2*R7) << 8)
;BA = R2*R6
;这个就不啰嗦了,同上分析
ADD A,R3 ;0E1A 2B
MOV R3,A ;0E1B FB
MOV A,R2 ;0E1C EA
ADDCA,B ;0E1D 35 F0
MOV R2,A ;0E1F FA
;R2R3 = R2R3 + BA
;R2R3R4R5 = R3*R7 + ((R3*R6 + R2*R7) << 8) + (R2*R6 << 16);
;这个就不啰嗦了,同上分析
;至此,整个函数的功能很明白了,至于用C重写,那简单得很了
RET ;0E20 22 amazing030 发表于 2015-7-1 10:02
唉,就没个人熟悉汇编吗,要转C的语句都不多
Q0DE4和Q0175都是初始化定时器的代码,这个更简单,转换成C也分分钟的事,就不写了 很不错啊,上次做了一个PWM输出时,杂音很大。 用51实现的真正和弦音乐!
页:
[1]
2