python编写的midi制作软件
发布时间:2018-09-13 12:48:59
发布时间:2018-09-13 12:48:59
心之所向,所向披靡
用Python编写的Mid音频制作软件
源代码如下:
#Midi.py:
#This file can be used to simply turn MidiTxt file to Mid file.
#Author:loupen
#Date:2010-7-20
#If you want to use it, you must write the MidiTxt file by some rules.
#Read the document to learn more.
from struct import *
#Three global variables
TrackBegin=[]
TrackEnd=[]
ToneFore=1
#write the MidiHeader
def MidiHeader(buffer):
buffer.write(pack('ccccBBBB','M','T','h','d',0,0,0,6))
def msg(buffer,tracks,deltatime):
if deltatime>0xff:
buffer.write(pack('BBBBBB',0,1,0,tracks,deltatime>>8,deltatime&0xff))
else:
buffer.write(pack('BBBBBB',0,1,0,tracks,0,deltatime))
return buffer.tell()-3
#write the header and tail of tracks
def TrackHeader(buffer,list):
buffer.write(pack('cccc','M','T','r','k'))
list.append(buffer.tell())
buffer.write(pack('i',0))
def TrackTail(buffer,list):
buffer.write(pack('BBBB',0,0xff,0x2f,0))
list.append(buffer.tell())
#Set the pulse , instrument and controller
def SetSpeed(buffer,time):
time=60.0/int(time)
time=int(time*(10**6))
buffer.write(pack('BBBBBBB',0,0xff,0x51,3,time>>16,(time&0xff00)>>8,time&0xff))
return 1
#The words , FenZi and FenMu , are from Chinese . ~O~
def SetPulse(buffer,FenZi,FenMu):
FenZi=int(FenZi)
FenMu=int(FenMu)
if FenMu==2:
FenMu=1
elif FenMu==4:
FenMu=2
elif FenMu==8:
FenMu=3
else: return 0
buffer.write(pack('BBBBBBBB',0,0xff,0x58,4,int(FenZi),int(FenMu),0x18,0x08))
return 1
def SetTone(buffer):
buffer.write(pack('BBBBBB',0,0xff,0x59,2,0,0))
return 1
def SetNumOrder(buffer,time):
buffer.write(pack('BBBB',time>>24,(time&0xff0000)>>16,(time&0xff00)>>8,time&0xff))
def SetTrackNum(buffer,ad,n):
buffer.seek(ad)
buffer.write(pack('B',n+1))
def SetInstrument(buffer,trackNum,ITNum):
trackNum=int(trackNum)
trackNum+=0xc0
buffer.write(pack('BBB',0,trackNum,ITNum))
def SetController(buffer,trackNum,CtrNum):
trackNum=int(trackNum)
trackNum+=0xb0
buffer.write(pack('BBBB',0,trackNum,11,CtrNum))
buffer.write(pack('BBB',0,0xa,0x40))
def SetVolume(buffer,trackNum,S):
trackNum=int(trackNum)
trackNum+=0xb0
if S=='H':
buffer.write(pack('BBBB',0,trackNum,7,0x7f))
buffer.write(pack('BBB',0,0xa,0x40))
if S=='L':
buffer.write(pack('BBBB',0,trackNum,7,80))
buffer.write(pack('BBB',0,0xa,0x40))
def SetChanYin(buffer,trackNum):
trackNum=int(trackNum)
trackNum+=0xb0
buffer.write(pack('BBBB',0,trackNum,1,127))
#imitate the function of buffer in C
def readNext(buffer):
while True:
a=buffer.read(1)
if a in [' ','\n']:
pass
else: return a
#def seekFore(buffer,offset):
# buffer.seek(buffer.tell()-offset)
#Use the following two functions to deal with the events
def liftup(buffer,time,note):
time=int(time)
if time>=128:
buffer.write(pack('BBBB',0x80|(time>>7),0x7f&time,note,0))
else:
buffer.write(pack('BBB',time,note,0))
def pressdown(buffer,num,dict,mark,note,force):
global ToneFore
a=dict[note]+(5+mark)*12
if ToneFore==1:
num+=0x90
buffer.write(pack('BBBB',0,num,a,force))
else:
buffer.write(pack('BBB',0,a,force))
ToneFore=a
def main(lp):
track=1;deltatime=120
speed='';temp=0
begin=0;mark=0;time=deltatime
TrackNum=[0,1,2,3];ITNum=lp;CtrNum=0x7f
dict={'0':0,'1':0,'2':2,'3':4,'4':5,'5':7,'6':9,'7':11}
pTxt=open(r'.\1.txt')
s=".\\%d.mid" %lp[0]
pMid=open(s,'wb')
if pTxt.read(6)!="
print "This is not MidiTxt file!"
pMid.close()
pTxt.close()
return 0
while True:
if pTxt.read(1)=='|':
FenMu=pTxt.read(1)
pTxt.seek(pTxt.tell()-3)
FenZi=pTxt.read(1)
n=0
while n<8:
if readNext(pTxt)=='<':break
n+=1
if n==8:
print "This is not MidiTxt file!"
pMid.close()
pTxt.close()
return 0
break
n=0
a=readNext(pTxt)
while a!='>':
speed+=a
a=readNext(pTxt)
MidiHeader(pMid)
track=msg(pMid,0,deltatime)
TrackHeader(pMid,TrackBegin)
SetPulse(pMid,FenZi,FenMu)
SetTone(pMid)
SetSpeed(pMid,speed)
TrackTail(pMid,TrackEnd)
pMid.seek(TrackBegin[0])
SetNumOrder(pMid,TrackEnd[0]-TrackBegin[0]-4)
pMid.seek(TrackEnd[0])
a=readNext(pTxt)
while a=='[':
pTxt.readline()
TrackHeader(pMid,TrackBegin)
SetInstrument(pMid,TrackNum[n],ITNum[n])
SetController(pMid,TrackNum[n],CtrNum)
a=readNext(pTxt)
n+=1
while True:
if a=='|':
a=readNext(pTxt)
if a=='|':
a=readNext(pTxt)
if a in ['[','']:
global ToneFore
ToneFore=-1;break
elif a=='\'':
mark+=1
a=readNext(pTxt)
elif a=='.':
if begin==0: mark-=1
elif begin==1:
time*=3.0/2
liftup(pMid,time,ToneFore)
begin=0
elif begin==2:
a=pTxt.read(1)
if a in [' ','|']:
if a=='|':
print "\'|\' is too close to Number !\n",pTxt.tell(),pTxt.read(1)
time*=3.0/2
time+=temp
liftup(pMid,time,ToneFore)
begin=0;temp=0
mark=0
else:continue
a=readNext(pTxt)
elif a=='Y':
time=deltatime/2
a=pTxt.read(1)
if a in [' ','|']:
if a=='|':
print "\'|\' is too close to Number !\n",pTxt.tell(),pTxt.read(1)
if begin==2:time+=temp
liftup(pMid,time,ToneFore)
begin=0;temp=0
mark=0
a=readNext(pTxt)
elif a=='E':
time=deltatime/4
a=pTxt.read(1)
if a in [' ','|']:
if a=='|':
print "\'|\' is too close to Number !\n",pTxt.tell(),pTxt.read(1)
if begin==2:time+=temp
liftup(pMid,time,ToneFore)
begin=0;temp=0
mark=0
a=readNext(pTxt)
elif a=='S':
time=deltatime/8
a=pTxt.read(1)
if a in [' ','|']:
if a=='|':
print "\'|\' is too close to Number !\n",pTxt.tell(),pTxt.read(1)
if begin==2:time+=temp
liftup(pMid,time,ToneFore)
begin=0;temp=0
mark=0
a=readNext(pTxt)
elif '7'>=a>='0':
if begin==0:
if a=='0': force=0
else: force=100
pressdown(pMid,TrackNum[n-1],dict,mark,a,force)
time=deltatime
mark=0;begin=1
a=pTxt.read(1)
if a in [' ','|']:
if a=='|':
print "\'|\' is too close to Number !\n",pTxt.tell(),pTxt.read(1)
liftup(pMid,time,ToneFore)
begin=0
a=readNext(pTxt)
elif begin==2:
time=deltatime
a=pTxt.read(1)
if a in [' ','|']:
if a=='|':
print "\'|\' is too close to Number !\n",pTxt.tell(),pTxt.read(1)
time+=temp
liftup(pMid,time,ToneFore)
begin=0;temp=0
mark=0
a=readNext(pTxt)
elif a=='^':
begin=2
temp+=time
a=readNext(pTxt)
elif a=='-':
a=pTxt.read(1)
if a in [' ','|']:
if a=='|':
print "\'|\' is too close to Number !\n",pTxt.tell(),pTxt.read(1)
time+=temp
liftup(pMid,time,ToneFore)
begin=0;temp=0
mark=0
a=readNext(pTxt)
elif a=='*':
SetVolume(pMid,TrackNum[n-1],'H')
ToneFore=1
a=readNext(pTxt)
elif a==',':
SetVolume(pMid,TrackNum[n-1],'L')
ToneFore=1
a=readNext(pTxt)
elif a=='W':
SetChanYin(pMid,TrackNum[n-1])
ToneFore=1
a=readNext(pTxt)
else:
print "This is not MidiTxt file!"
pMid.close()
pTxt.close()
return 0
TrackTail(pMid,TrackEnd)
pMid.seek(TrackBegin[n])
SetNumOrder(pMid,TrackEnd[n]-TrackBegin[n]-4)
pMid.seek(TrackEnd[n])
SetTrackNum(pMid,track,n)
pMid.close()
pTxt.close()
print "\nOK!---LOUPEN"
以上文件存为midi.py。
#test.py:
import midi
i=1
z=[]
j=int(input("How many tracks?"))
while i<=j:
tmp=input("The Instrument number is:")
z.append(int(tmp))
i+=1
midi.main(z)
以上文件存为test.py。
以下为mid文本范例【赛马】:
<2|4> <120>
[1]
|| 6. 3E 5E | 6. 3E 5E | 6. 3E 5E | 6. 3E 5E | 6E 5E 3E 5E 6E 5E 3E 5E | 6E 5E 3E 5E 6E 5E 3E 5E | 6Y 5E 6E 6Y 5E 6E |
6Y 5E 6E 6Y 5E 6E | .6Y 3Y 1Y .6Y | 3Y 6Y 5Y 3Y | 2E 3E 2E 1E 2E 3E 2E 1E | 2E 3E 2E 1E 2E 3E 2E 1E | .6Y 3Y 1Y .6Y | 3Y 6Y 5Y 3Y |
2E 3E 2E 1E 2E 3E 2E 1E | 2E 3E 2E 1E 2E 3E 2E 1E | 2. .6E 1E | 2. .6E 1E | 2. .6E 1E | 2. .6E 1E | 2E 3E 2E 1E 2E 3E 2E 1E |
2E 3E 2E 1E 2E 3E 2E 1E | 2Y 1E 2E 2Y 1E 2E | 2Y 1E 2E 2Y 1E 2E | .6 6 | 5 3 | 2 5 | 3 1 |
.6 6 | 5 3 | 2 5 | 3 1 | .6. 1E 2E | .6. 1E 2E | .6. 1E 2E | .6. 1E 2E | .6E 2E 1E 2E .6E 2E 1E 2E | .6E 2E 1E 2E .6E 2E 1E 2E |
.6 .6Y .6Y | .6^ .6 | 3 6Y. '1E | 5. 3Y | 5Y 6Y '1Y. '3E | 6^ 6 | 3 6Y. '1E | 5 5Y 3Y | 2Y 3Y 6Y 5Y | 3^ 3 | 5 6Y. '1E |
1. .6Y | 2Y 3Y 6Y 5Y | 3 3Y 2Y | 1Y. 2E 3Y 5Y | 6 .6 | 2Y 3Y 1Y. 3E | .6^ .6 | 3Y 3E 3E 6Y '1Y | 5Y 5E 5E 5Y 3Y |
5Y 5E 6E '1Y '2E '1E | 6Y 6E 6E 6Y .6Y | 3Y 3E 3E 6Y '1Y | 5Y 5E 5E 5Y 3Y | 2Y 2E 3E 6Y 5Y | 3Y 3E 5E 3Y .6Y | 5Y 5E 5E 6Y '1Y |
1Y 1E 1E 1Y .6Y | 2Y 2E 3E 6Y 5Y | 3Y 3E 5E 3Y 2Y | 1E .6E 1E 2E 3E 2E 3E 5E | 6E 5E 6E '1E 5E 6E 5E 3E | 2E 3E 2E 1E 2E 1E .6E 1E | .6 6 |
0Y .6Y 1Y 3Y | 0Y .6Y 1Y 3Y | 0Y 2Y .7Y 2Y | .6Y 3Y 1Y 3Y | 0Y .6Y 1Y 3Y | 0Y .6Y 1Y 3Y | 0Y 2Y .7Y 2Y | .6Y 3Y 1Y 3Y | 0Y .6Y 1Y 3Y |
0Y .6Y 1Y 3Y | 0Y 2Y .7Y 2Y | .6Y 3Y 1Y 3Y | 0Y 5Y 3Y 2Y | 1Y 2Y 1Y .6 | 2Y 2Y 3Y 1Y | .6. 3E 5E | .6. 3E 5E | .6. 3E 5E |
.6. 3E 5E | .6. 1E 2E | 3E 2E 3E 5E 6E '1E 6E 5E | 3E 2E 3E 5E 6E '1E 6E 5E | 3E 5E 3E 2E 1Y 3Y | .6. 1E 2E | 3E 2E 3E 5E 6E '1E 6E 5E | 3E 2E 3E 5E 6E '1E 6E 5E |
3E 5E 3E 2E 1Y 3Y | .6. 3E 6E | '1Y 6Y 6Y '3Y | '1Y 6Y 6Y 3Y | '1Y 6Y 6Y '3Y | '1Y 6Y 6Y 3Y | 1Y .6E 1E 2Y 1E 2E | 3Y 2E 3E 5Y 3E 5E |
5Y 3E 5E 6Y 5E 6E | '1Y 6E '1E '2Y '1E '2E | '3E '2E '1E '2E '3E '2E '1E '2E | '3E '2E '1E '2E '3E '2E '1E '2E | '3E '2E '1E '2E '3E '2E '1E '2E | '3E '2E '1E '2E '3E '2E '3E '5E |
'6^ '6^ | '6^ '6^ | '6^ '6 | '6 0 | 6 6 | .6^ .6 ||
上述文件存为1.txt,和midi.py , test.py放在同一个文件夹下,运行test.py
Tracks为文本中[]的个数,instrument为乐器号取值范围为0-127.
注:后附mid文本编辑规范。
Midi文本需按以下格式书写。
<3|8> <120>#每分钟的节拍数
[1]
||...||
[2]
||...||
//////////////////////////////////////////////////////////////////////////////////////////////////////
||1. ''2. ..3. 4^ -^ - #5 b6 7Y. ''1Y^ -^ -||
#1。'表示升高一个八度。[''1]表示比1高两个八度。
#2。.在音符前表示降低一个八度。如[..6]表示比6低两个八度。
#3。.在音符后表示付点音符。如[6.]
#4。Y表示8分音符,即“一条横线”。如[6Y]
#5。E表示16分音符,即“二条横线”。如[6E]
#6。S表示32分音符,即“三条横线”。如[6S]
#7。#表示升高半音
#8。b表示降低半音
#9。-表示延长一个四分音符。如[6^ -]
#10。^表示两个相同的音相连。如6 -表示为6^ -
注:
1。对于表示升降的符号['],[.]后需紧接音符。
2。附点音符[.],[^]需紧接在音符之后。
[^]前后两个音必须相同,即[6^ 6]或[6^ -]
3。[Y],[E],[S]需紧接在音符之后。
4。空格的作用非常重要,慎用。
音符后接空格表示此音符已结束。
5。其他功能,像控制器之类,还有待完善!对于升降音因为很少用就忽略了。编辑后的文学习使人进步
本需以1.txt命名。
本人刚学python不足之处请高手指教。本文代码在wm5 sp手机上编辑和测试。