首页 > 汇编 > (怀旧向)第三弹:蜂鸣器播放音乐

(怀旧向)第三弹:蜂鸣器播放音乐

2012年7月25日 发表评论 阅读评论

要数我写过的最让我自己震惊,恐惧的代码,估计就是这一个了。。完全不敢想象我大二上学期学完汇编,然后居然会这么蛋疼去写这么“庞大”的一个程序。倒不是写这个东西蛋疼,而是调试。

好吧,先说这个代码起源,当年的某一天在豆瓣上看到这个活动,就是个PC版钢琴,用键盘当按键,不过令人蛋疼的是,它的按键规则是。。A-Z对应钢琴键从左到右的顺序,,尼马设计者不知道电脑键盘是乱序啊!!那个活动下方就有很多“谱子”,你只要凭着最基本的音感,按着节奏按下键盘那些按键就可以了。像下面这种:

北京欢迎你
MOMLMLM
MLIKML
LKIKLMOL
MPOHLK
LKIKLMOL
MPOOM
LMLKOPM
IMMLLK
MOROP
OPOMMOO
MOPRSROMLOMM
MOROPRSROMORP
MLMPTSSRR
MOROPRSROMORP
MLMPTSSSSRRR

但是我还是不爽,一气之下,决定下一个代码可以根据那些键盘的谱子自动把音乐播出来。

但是考虑到我当时编程只是个入门级别的,用C++写播放音乐的东西完全不知应该从何入手(话说我现在有空可以用MFC写一个诶。。),突然灵光一闪,我知道怎么样用汇编让电脑那个喇叭播出一定频率的声音!!(就是那个蜂鸣器)我勒个去,我真是个天才,汇编,哼哼,去我们班的音乐天才指挥家QQ同学家里向他请教一些简单的乐理,比如高音低音,休止符,节拍什么的(笑吧笑吧,我就是个音痴不给啊)。。就这样。。。。不堪回首的编程经历。。我花了3天没日没夜的调这个程序,把家里人都给吵死了。。

虽然觉得人生可以再来一次的话,打死我都不会再写这个程序了,至少不再用汇编写。。

程序功能是这样的,设定一定的文件规则来表示一首歌:我当时写的规则文档如下:

地址格式:e:/music.txt
休止符:~   例如:+1 ~ 2 ~|3 ~ ~ ~|
双休止符:=(占二分之一拍)   例如: +1 = ~|3 ~ =|
低音:-1
中音:1
高音:+1  当音高于+7后为 :+8 +9 +: +; +< ASCII码递推
输入结束:Q/q
高半音:#          例如:1 2# 2# 4#|2 3 4# 5|
每输入完四分之一拍后按空格即可
四分之一拍内连音:.   例如  3.2 4.5 5.5 5.4|1 2 3 4
不间断连音:@     例如: 3@ 2 1|1@ 2@
文件结尾必须加‘$’

综合实例:1 2.3 2.4# ~|1 2@ ~|2# = -1|= 2.3# -2#.5|

应该不可以有:2.3@这种格式出现。 

一开始可以选择从电脑内读入符合格式的txt文件(按1),或现场输入音乐(按2),这是会自动把输入的音乐放到e:/mysic.txt里面。

若按1,将要你输入文件路径,然后再输入播放速度(多少个四分之一拍每分钟)然后按任意键可以开始播放。

若按2,先输入音乐是几几拍,然后也是播放速度后可以播放。

音乐播放时会同步播放乐谱。

音乐转换程序先输入地址,txt文件的音乐格式为:
可以留空格,但不能太多,因为空格转换后为休止符,换行可有可无。
文件尾加‘$’

(这个文件上面的编码规则我现在看起来都觉得好震惊,当年我是怎么用汇编写出这个识别的代码来的。。)

然后程序开始时可以选择读取先前的文件,自动播放,或者临时写个文件,然后在播放,播放时还可以选择播放速度。

然后可以播放后我就想到了另一个问题,豆瓣上那个上面给出的键盘乐谱和我这个程序的文件规则不一样,我想听豆瓣上的乐谱里面的音乐,所以我就有很蛋疼的,用汇编写了一个转换程序,直接在一个文件里面复制上豆瓣给的乐谱,转换程序会把它转换成我这个主程序可以识别的那种文件,再在主程序里直接读取就可以了~(真的是我写的程序么??) 

哦,突然想起来了,以前记得好像这个程序只能在叉P下跑,不知道为什么。。 

主程序:

;=========================================音符宏定义(谈音乐 大调 )
.286
LDO EQU 4571*2
LDOU EQU 4307*2
LRE EQU 4072*2
LREU EQU 3836*2
LMI EQU 3626*2
LFA EQU 3418*2
LFAU EQU 3233*2
LSO EQU 3043*2
LSOU EQU 2875*2
LLA EQU 2711*2
LLAU EQU 2560*2
LXI EQU 2420*2
DO EQU 4571
DOU EQU 4307
RE EQU 4072
REU EQU 3836
MI EQU 3626
FA EQU 3418
FAU EQU 3233
SO EQU 3043
SOU EQU 2875
LA EQU 2711
LAU EQU 2560
XI EQU 2420
HDO EQU 4571/2
HDOU EQU 4307/2
HRE EQU 4072/2
HREU EQU 3836/2
HMI EQU 3626/2
HFA EQU 3418/2
HFAU EQU 3233/2
HSO EQU 3043/2
HSOU EQU 2875/2
HLA EQU 2711/2
HLAU EQU 2560/2
HXI EQU 2420/2
HHDO EQU 4571/4
HHDOU EQU 4307/4
HHRE EQU 4072/4
HHREU EQU 3836/4
HHMI EQU 3626/4
HHFA EQU 3418/4
HHFAU EQU 3233/4
HHSO EQU 3043/4
HHSOU EQU 2875/4
HHLA EQU 2711/4
HHLAU EQU 2560/4
HHXI EQU 2420/4
;=============================================数据段
DATA SEGMENT

MESS1 DB "CHOOSE THE OPERATION:",0DH,0AH,"      1)READ FROM THE TXT",0DH,0AH,"      2)INPUT THE MUSIC NOW",0DH,0AH,'$'
MESS2 DB "PLEASE INPUT THE NAME OF THE PATH:",'$'
MESS3 DB "PLEASE INPUT THE SPEED OF PLAYING MUSIC:",'$'
MESS4 DB "PLEASE INPUT THE TAP:",'$'
MESS5 DB "PLEASE INPUT THE DIAOZI:",'$';(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~)
MESS6 DB "PLEASE INPUT THE MUSIC(Q IS THE END):",0DH,0AH,'$'
MESS7 DB "PRESS ANY KEY TO PLAY MUSIC:",'$'
ERRORME DB "FILE READ ERROR!!!!",'$'
FNI DB 30,?
FN DB 30 DUP(?),'$'
FN2 DB 'E:\MUSIC.TXT',0
FILHAND1 DW ?
PLAY_SPEED DW 0
TAP DB ?,?
MUSIC   DW LDO,LDOU,LRE,LREU,LMI,LFA,LFA,LFAU,LSO,LSOU,LLA,LLAU,LXI,LXI;
      DW DO,DOU,RE,REU,MI,FA,FA,FAU,SO,SOU,LA,LAU,XI,XI;28
      DW HDO,HDOU,HRE,HREU,HMI,HFA,HFA,HFAU,HSO,HSOU,HLA,HLAU,HXI,HXI;
       DW HHDO,HHDOU,HHRE,HHREU,HHMI,HHFA,HHFA,HHFAU,HHSO,HHSOU,HHLA,HHLAU,HHXI,HHXI
TE DB 2000 DUP(' '),0
FREQ DB 2000 DUP(?)
TIME DB 2000 DUP(?)
JIANPU DB 80
DATA ENDS

;============================================段,蜂鸣器初始化
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:
MOV AX,DATA
MOV DS,AX
;===========================================清屏
MOV AH,00H
MOV AL,03H
INT 10H
;=========================================初始提示信息(选择操作)
LEA DX,MESS1
MOV AH,09H
INT 21H
;=========================================对操作作出判断(添加弹奏音乐的选项,提示信息要修改)
MOV AH,01H
INT 21H

CMP AL,31H
JE READ_TXT
CMP AL,32H
JE MUSIC_INPUT
JMP START
;=========================================输入文件名FN,读文件,FN所指文件送到TE
READ_TXT:
CALL NEW_LINE

LEA DX,MESS2
MOV AH,09H
INT 21H

LEA DX,FNI
MOV AH,0AH
INT 21H

MOV BL,FNI+1
MOV BH,0
MOV FN[BX],'$'

MOV AH,3DH
MOV AL,0
LEA DX,FN
INT 21H
JC ERROR
MOV FILHAND1,AX

MOV AH,3FH
MOV BX,FILHAND1
MOV CX,2000
LEA DX,TE
INT 21H
CALL NEW_LINE
JMP PLAY_MUSIC

ERROR:
CALL NEW_LINE
LEA DX,ERRORME
MOV AH,09
INT 21H
JMP READ_TXT
;=======================================乐谱输入(TE中为ASICC码,输出时再转换)
MUSIC_INPUT:
CALL NEW_LINE
;=======================================输入拍子
LEA DX,MESS4
MOV AH,09H
INT 21H
CALL NEW_LINE
I:MOV AH,01H
INT 21H
CMP AL,30H
JB I
CMP AL,39H
JA I
SUB AL,30H
MOV TAP,AL
MOV DL,32

CALL PRINT

J:MOV AH,01H
INT 21H
CMP AL,30H
JB J
CMP AL,39H
JA J
SUB AL,30H
MOV TAP+1,AL
LEA BX,TE

CALL NEW_LINE
;====================================输入到TE
LEA DX,MESS6
MOV AH,09
INT 21H

MOV DL,TAP+1
MOV CX,0
MOV MESS1,DL
SUB MESS1,1

INPUT:MOV AH,01H
INT 21H
CMP AL,0DH
JE NL
CMP AL,32
JE SPACE
CMP AL,'q';==================大小写不可变
JE INPUT_END
CMP AL,'Q'
JE INPUT_END
CMP AL,'='
JE COUNTER2
CMP AL,08H
JE BACK
CMP AL,'@'
JE COUNTER2
DEAL:MOV [BX],AL
INC BX
JMP INPUT

BACK:DEC BX
CMP AL,' '
JE DISA
CMP AL,'='
JE DISA
B:
JMP INPUT

DISA:DEC CX
JMP B

NL:CALL NEW_LINE
JMP INPUT

SPACE:CMP CL,MESS1
JE ADD1
INC CX
JMP DEAL

ADD1:MOV [BX],BYTE PTR '|'
INC BX
PUSHA
MOV DL,08H
CALL PRINT
MOV DL,'|'
CALL PRINT
POPA
MOV CX,0
JMP INPUT

BACK2:PUSHA
MOV DL,08H
CALL PRINT
POPA
JMP INPUT

COUNTER2:
CMP CL,MESS1
JE BACK2
INC CX
JMP DEAL
;==================================把TE的内容转到FN2所指TXT里
INPUT_END:
CALL NEW_LINE
MOV [BX],BYTE PTR '$'
MOV DI,BX
PUSHA
MOV DX,OFFSET FN2
MOV AH,3CH
XOR CX,CX
INT 21H
MOV BX,AX
MOV AH,40H
MOV DX,OFFSET TE
MOV CX,DI
INT 21H
MOV AH,3EH
INT 21H
POPA
JMP PLAY_MUSIC
;=======================================准备输出音乐
PLAY_MUSIC:
;=======================================播放速度输入
LEA DX,MESS3
MOV AH,09H
INT 21H
SPEED_INPUT:MOV AH,01H
INT 21H
CMP AL,0DH
JE PLAY
SUB AL,30H
MOV BX,PLAY_SPEED
MOV CH,0
MOV CL,AL
MOV AX,10
MUL BX
ADD AX,CX
MOV PLAY_SPEED,AX
JMP SPEED_INPUT

;=====================================音乐转换
PLAY:

MOV DX,0
MOV AX,1091
MOV BX,PLAY_SPEED
DIV BX
MOV PLAY_SPEED,AX

CALL NEW_LINE
LEA DX,MESS7
MOV AH,09H
INT 21H
MOV AH,01H
INT 21H
MOV AH,00H
MOV AL,03H
INT 10H
CALL NEW_LINE

LEA DI,TE-1
LEA BX,FREQ
LEA SI,TIME
MOV CX,1
MOV DX,PLAY_SPEED
MOV MESS2,BYTE PTR 1

CHANGE:
INC DI
MOV AL,[DI]
CMP AL,'$'
JE B_PLAY1
CMP AL,'~'
JE XIUZHI
CMP AL,'='
JE SXIUZHI
CMP AL,'.'
JE JJ
CMP AL,'|'
JE NEXT
CMP AL,32
JE NEXT
CMP AL,'+'
JE HT
CMP AL,'-'
JE LLT
CMP AL,'#'
JE JING
CMP AL,'@'
JE YC
SUB AL,42
ADD AL,AL
MOV [BX],AL
INC BX
JMP CHANGE

XIUZHI:
MOV [SI],DX
INC SI
INC DI
MOV [BX],BYTE PTR 80
INC BX
JMP CHANGE

SXIUZHI:
MOV [SI],DX
ADD [SI],DX
INC SI
INC DI
MOV [BX],BYTE PTR 80
INC BX
JMP CHANGE

B_PLAY1:JMP B_PLAY

JJ:INC CX
JMP CHANGE

HT:INC DI
MOV AL,[DI]
SUB AL,35
ADD AL,AL
MOV [BX],AL
INC BX
JMP CHANGE

LLT:INC DI
MOV AL,[DI]
SUB AL,49
ADD AL,AL
MOV [BX],AL
INC BX
JMP CHANGE

NEXT:MOV AX,DX
DIV CL
A1:CMP MESS2,1
JE POINT
SHL AX,1
SHR MESS2,1
JMP A1
POINT:MOV [SI],AL
INC SI
LOOP POINT
MOV CX,1
JMP CHANGE

JING:DEC BX
ADD [BX],BYTE PTR 1
INC BX
JMP CHANGE

YC:SHL MESS2,1
JMP CHANGE
;===========================
B_PLAY:

MOV AX,DX
DIV CL
POINT2:MOV [SI],AL
INC SI
LOOP POINT2
MOV CX,1

MOV [BX],BYTE PTR 100

;======================================MUSIC
MOV DI,PLAY_SPEED
MOV CX,0
CALL BEEP

LEA BX,FREQ
LEA SI,TIME
SOUND:MOV AL,[BX]
MOV JIANPU,AL
CMP AL,100
JE EXIT
LEA DI,MUSIC
MOV AH,0
ADD AL,AL
ADD DI,AX
MOV CX,[DI]
MOV DL,[SI]
MOV DH,0
MOV DI,DX
CMP AL,160
JE D
CALL BEEP
EEE:INC BX
INC SI
JMP SOUND

D:CALL DELAY
JMP EEE
;=========================EXIT
EXIT:
MOV AH,4CH
INT 21H
;===================================输出PRINT(入口DL)
PRINT:
PUSHA
MOV AH,02H
INT 21H
POPA
RET
;====================================换行
NEW_LINE:
MOV DL,0DH
CALL PRINT
MOV DL,0AH
CALL PRINT
RET
;=================================================音频+延时  BX频率  DI时间()
BEEP:
PUSHA
MOV AL,0B6H
OUT 43H,AL
MOV AX,CX
OUT 42H,AL
MOV AL,AH
OUT 42H,AL
IN AL,61H
OR AL,3
OUT 61H,AL
POPA

CALL DELAY
IN AL,61H
AND AL,0FCH
OUT 61H,AL

RET
;===========================延时模块;DI IS THE INPUT MSEC
DELAY:PUSHA
MOV AH,0
INT 1AH
ADD DX,DI
MOV BX,DX

DRAW:PUSHA
MOV AL,JIANPU
CMP AL,80
JE STT
CMP AL,14
JB DY
CMP AL,27
JA ZY
SUB AL,12
JMP YYY

QQQ:MOV DL,' '
CALL PRINT
POPA

DELAY_D:INT 1AH
CMP DX,BX
JA O
JNE DELAY_D

O:
POPA
RET

DY:MOV DL,'-'
CALL PRINT
MOV AH,0
MOV BL,2
DIV BL
MOV CH,AH
ADD AL,31H
MOV DL,AL
CALL PRINT
CMP CH,0
JE QQQ
MOV DL,'#'
CALL PRINT
JMP QQQ

ZY:MOV DL,'+'
CALL PRINT
SUB AL,26
YYY:MOV AH,0
MOV BL,2
DIV BL
MOV CH,AH
ADD AL,30H
MOV DL,AL
CALL PRINT
CMP CH,0
JE QQQ
MOV DL,'#'
CALL PRINT
JMP QQQ

STT:MOV DL,'~'
CALL PRINT
MOV DL,' '
CALL PRINT
JMP QQQ
;==========================结束
CODE ENDS
END START
转换程序:

data segment
te db 2000 dup(?)
zz db 2000 dup(?)
MESS2 DB "PLEASE INPUT THE NAME OF THE PATH:",'$'
ERRORME DB "FILE READ ERROR!!!!",'$'
FNI DB 30,?
FN DB 30 DUP(?),'$'
FILHAND1 DW ?
stander db 'A'
FN2 DB 'E:\MUSIC.TXT',0
data ends

CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:MOV AX,DATA
MOV DS,AX
;===================================================read
READ_TXT:

LEA DX,MESS2
MOV AH,09H
INT 21H

LEA DX,FNI
MOV AH,0AH
INT 21H

MOV BL,FNI+1
MOV BH,0
MOV FN[BX],'$'

MOV AH,3DH
MOV AL,0
LEA DX,FN
INT 21H
JC ERROR
MOV FILHAND1,AX

MOV AH,3FH
MOV BX,FILHAND1
MOV CX,2000
LEA DX,TE
INT 21H
jmp c
;=============================
ERROR:
LEA DX,ERRORME
MOV AH,09
INT 21H
JMP READ_TXT
;================================
C:lea si,te-1
lea di,zz

change:
inc si
mov al,[si]
cmp al,'$'
je outt
cmp al,0dh
je hh
cmp al,' '
je xiuzhi
CMP AL,'('
JE JI0
CMP AL,')'
JE JIO2
cmp al,'a'
jae xiaoxie
jmp w
xiaoxie:mov stander,byte ptr 'a'
w:sub al,stander
CMP AL,7
JB DY
CMP AL,13
JA GY

CALL JJ
ADD AL,42
MOV [DI],AL
INC DI
mov [di],byte ptr ' '

inc di
JMP CHANGE

hh:inc si
jmp change

DY:CALL JJ
MOV [DI],BYTE PTR '-'
INC DI
ADD AL,49
MOV [DI],AL
INC DI
mov [di],byte ptr ' '
inc di
JMP CHANGE

GY:CALL JJ
MOV [DI],BYTE PTR '+'
INC DI
ADD AL,35
MOV [DI],AL
INC DI
mov [di],byte ptr ' '
inc di
JMP CHANGE

JI0:MOV DX,1
JMP CHANGE

JIO2:MOV DX,0
JMP CHANGE

JJ:CMP DX,1
JNE CC
DEC DI
MOV [DI],BYTE PTR '.'
INC DI
CC:RET

xiuzhi:
mov [di],byte ptr '~'
inc di
mov [di],byte ptr ' '
inc di
jmp change

outt:mov [di],byte ptr '$'

MOV DX,OFFSET FN2
MOV AH,3CH
XOR CX,CX
INT 21H
MOV BX,AX
MOV AH,40H
MOV DX,OFFSET ZZ
MOV CX,2000
INT 21H
MOV AH,3EH
INT 21H

mov ah,4ch
int 21h
CODE ENDS
END START

【完】

本文内容遵从CC版权协议,转载请注明出自http://www.kylen314.com

分类: 汇编 标签: ,