gautrais 5 年之前
父节点
当前提交
df337c8b70
共有 6 个文件被更改,包括 175 次插入21 次删除
  1. 87 0
      compose/midicompose.py
  2. 2 1
      main.py
  3. 0 1
      padrouter.py
  4. 8 1
      simplemidi/midimessage.py
  5. 72 15
      simplemidi/midiparser.py
  6. 6 3
      simplemidi/midiplayer.py

+ 87 - 0
compose/midicompose.py

@@ -0,0 +1,87 @@
+#!/usr/bin/python
+
+from simplemidi.midiparser import *
+from simplemidi.midiplayer import *
+import os
+"""
+md=MIDIHeaderChunk()
+md.length=6
+md.format=MIDIHeaderChunk.FORMAT_MULTIPLE_CHANNEL
+md.tracks=5
+md.division=960
+
+mt=MIDITrackChunk()
+mt.addNote(0.5, 60)
+mt.addNote(0.5, 63)
+mt.addNote(1, 63)
+mt.addNote(0.25, 63)
+
+f=open("test", "wb")
+print(f)
+md.write(f)
+mt.write(f)
+f.close()
+"""
+from simplemidi.alsaconnector import AlsaConnector, AlsaPort
+from random import randint
+os.sched_setscheduler(os.getpid(), os.SCHED_FIFO, os.sched_param(1))
+class Compose:
+    _DEFAULT_PARAM={
+        'keys' : [57, 59,60,62,64,65,67],
+        "times": [ [0.5,4], [1,4], [1.5, 1]]
+    }
+
+    def __init__(self, param=_DEFAULT_PARAM):
+        self.track = MIDITrackChunk()
+        keys=param["keys"] if "keys" in param else Compose._DEFAULT_PARAM["keys"]
+        self.keys=[]
+        for i in keys: self.keys.append((i, randint(0,10)))
+        self.times=param["times"] if "times" in param else Compose._DEFAULT_PARAM["times"]
+
+        temps=64
+        while temps>0:
+            x=self.randTime(temps)
+            k=self.randKey()
+            self.track.addNote(x, k)
+            temps-=x
+        print("Mesure: "+str(8-temps))
+
+    def randTime(self, max):
+        x=0
+        for i in self.times: x+=i[1] if len(i)>1 and i[1]<=max else 1
+        dice=randint(0,x-1)
+        for i in self.times:
+            if i[1]<=max:
+                dice-=i[1]
+            if dice<=0: return i[0]
+        return self.times[0][0]
+
+    def randKey(self):
+        x = 0
+        for i in self.keys: x += i[1] if len(i) > 1 else 1
+        dice = randint(0, x - 1)
+        for i in self.keys:
+            dice -= i[1]
+            if dice <= 0: return i[0]
+        return self.keys[0][0]
+
+
+    def play(self):
+        self.file=MidiFile()
+
+        self.file.tracks=[self.track.events]
+        mp=MidiPlayer(self.file)
+        #mp.setBpmRatio(2)
+        AlsaConnector.connect((133,0), (130,0))
+        #input("...")
+        mp.port.send(Stop())
+        mp.port.send(Continue())
+        mp.port.send(Start())
+        while True:
+            mp.play()
+            print("===========")
+
+
+
+c=Compose()
+c.play()

+ 2 - 1
main.py

@@ -13,6 +13,7 @@ import sys
 import rtmidi
 from simplemidi.options import *
 from simplemidi.midieventtrigger import *
+import compose.midicompose
 
 #path="/home/fanch/Documents/tabs/Misc Children - Twinkle Twinkle Little Star2.mid"
 #path="/home/fanch/Documents/tabs/Johann Pachelbel - Canon In D (ver 6 by Ezechiel).mid"
@@ -101,4 +102,4 @@ if __name__=="__main__":
 	if n==3: _test_midiPlayer()
 	if n==4: _test_paintPad()
 	if n==5: _test_serpent()
-	if n==5: _test_()
+	if n==5: _test_compose()

+ 0 - 1
padrouter.py

@@ -22,7 +22,6 @@ class PadRouter(Pad):
                  'client_name': 'MidiPlayer'
              },
              'pad_translate': 0,
-
          })
 
 

+ 8 - 1
simplemidi/midimessage.py

@@ -210,7 +210,9 @@ class NoteOn(MidiVoiceMessage):
 		if fd:
 			self.key=self.byte(fd)
 			self.velocity=self.byte(fd)
-			
+		else:
+			self.key = 0
+			self.velocity = 0
 	def bytes(self):
 		return [MidiType.NOTE_ON | (self.channel-1), self.key, self.velocity]
 	
@@ -236,6 +238,9 @@ class NoteOff(MidiVoiceMessage):
 		if fd:
 			self.key=self.byte(fd)
 			self.velocity=self.byte(fd)
+		else:
+			self.key = 0
+			self.velocity = 0
 			
 	def bytes(self):
 		return [MidiType.NOTE_OFF | (self.channel-1), self.key, self.velocity]
@@ -251,6 +256,8 @@ class NoteOff(MidiVoiceMessage):
 		self.key+=n
 
 
+	def __str__(self):
+		return "NOTEOFF("+str(self.key)+", "+ str(self.channel) +")"
 
 
 

+ 72 - 15
simplemidi/midiparser.py

@@ -29,7 +29,11 @@ class InputStreamWrapper:
 	def record(self, rec=True):
 		self.doRecord=rec
 
-		
+def littleEndian(x, n):
+	b=bytes()
+	for i in range(n):
+		b+=bytes([(x >> (8 * (n - 1 - i))) & 0xff])
+	return b
 
 class MIDIChunk:
 	def __init__(self, fd):
@@ -42,6 +46,9 @@ class MIDIChunk:
 	
 	def print(self):
 		pass
+
+	def write(self, fd):
+		pass
 	
 	def readMagick(self):
 		return self.fd.read(4).decode('ascii')
@@ -53,23 +60,26 @@ class MIDIChunk:
 			out+= (x[i]<< (8*(n-1-i)))
 		return out
 	
-	def intN(self, n):
-		return self.reverseMsb(self.fd.read(n))
+	def intN(self, n, x=None):
+		if x!=None:
+			for i in range(n):
+				self.fd.write(bytes([ (x>>(8*(n-1-i)) )& 0xff]))
+		else: return self.reverseMsb(self.fd.read(n))
 	
-	def int4(self):
-		return self.intN(4)
+	def int4(self, x=None):
+		self.intN(4,x)
 		
-	def int2(self):
-		return self.intN(2)
+	def int2(self, x=None):
+		return self.intN(2,x)
 		
-	def byte(self):
-		return self.intN(1)
+	def byte(self, x=None):
+		return self.intN(1,x)
 		
 	def skip(self, n=1):
 		self.fd.read(n)
 
 class MIDIHeaderChunk(MIDIChunk):
-	def __init__(self, fd):
+	def __init__(self, fd=None):
 		MIDIChunk.__init__(self,fd)
 		self.format=0
 		self.tracks=0
@@ -80,7 +90,17 @@ class MIDIHeaderChunk(MIDIChunk):
 		
 		self.variableTicks=False
 		self.ticksPerQuarter=0 # beat per quarter note
-	
+
+
+	def write(self, fd):
+		self.fd=fd
+		self.fd.write("MThd".encode())
+		self.int4(self.length)
+		self.int2(self.format)
+		self.int2(self.tracks)
+		self.int2(self.division)
+
+
 	def parse(self):
 		f=self.fd
 		self.magick=self.readMagick()
@@ -97,7 +117,7 @@ class MIDIHeaderChunk(MIDIChunk):
 			self.ticksPerQuarter= self.division & 0x7fff
 
 class MIDITrackChunk(MIDIChunk):
-	def __init__(self, fd, skipTrack=False):
+	def __init__(self, fd=None, skipTrack=False):
 		MIDIChunk.__init__(self,fd)
 		self.time=0
 		self.events=[]
@@ -118,7 +138,41 @@ class MIDITrackChunk(MIDIChunk):
 			
 		self.time+=ret
 		return ret
-	
+
+	def write(self, fd):
+		self.fd=fd
+		self.fd.write("MTrk".encode())
+		self.int4(self.length)
+		b=bytes()
+		count=0
+		for evt in self.events:
+			e=littleEndian(int(evt[0]*960),1)+bytes([0xff])+bytes(evt[1].bytes())
+			count+=len(e)
+			print(evt[1].bytes())
+			print(bytes(evt[1].bytes()))
+			count+=evt[0]
+			b+=e
+			print(b)
+		b+=bytes([0])+bytes(EndOfTrack().bytes())
+		print(b)
+		self.fd.write(b)
+
+	def addEvent(self, time, evt):
+		self.events.append((time, evt))
+
+
+	def addNote(self, time, note, channel=0):
+		non= NoteOn(channel)
+		non.key=note
+		non.velocity=0x7f
+		self.events.append((self.time,non))
+		nof= NoteOff(channel)
+		nof.key=note
+		self.events.append((self.time+time*960,nof))
+		self.time+=time*960
+		print(time*960)
+
+
 	def parse(self):
 		f=self.fd
 		self.magick=self.readMagick()
@@ -137,6 +191,7 @@ class MIDITrackChunk(MIDIChunk):
 				events.append((self.time, evt))
 				if evt.isEndTrack():
 					break
+		self.events=events
 		return events
 	
 
@@ -145,12 +200,14 @@ class MIDITrackChunk(MIDIChunk):
 		return MidiMessage.parse(self.fd)
 		
 class MidiFile:
-	def __init__(self, path):
-		self.path=path
+	def __init__(self, path=None):
+		if path:
+			self.path=path
 		self.tracksCount=0
 		self.header=None
 		self.tracks=[]
 		self.loaded=False
+		self.events=[]
 	
 	def parse(self):
 		self.fd=0

+ 6 - 3
simplemidi/midiplayer.py

@@ -109,7 +109,7 @@ class MidiPlayer:
 	}
 	
 	
-	def __init__(self, path, params):
+	def __init__(self, path, params=_DEFAULT_PARAMS):
 		if isinstance(path, str):
 			self.file=MidiFile(path)
 		else:
@@ -118,9 +118,10 @@ class MidiPlayer:
 		self.file.parse()
 		self.header=self.file.header
 		self.tracks=self.file.tracks
-		self.nSoundTracks=self.header.tracks
+		self.nSoundTracks=self.header.tracks if self.header else 1
+		self.div = self.file.header.division if self.header else 960
+
 		self.nTotalTracks=len(self.file.tracks)
-		self.div=self.file.header.division
 		self.trackToPlay=list(range(self.nSoundTracks))
 		
 		param=initParams(MidiPlayer._DEFAULT_PARAMS, params)
@@ -179,6 +180,7 @@ class MidiPlayer:
 		
 
 	def __initPlay(self):
+		self.events=[]
 		self.tick=0
 		self.startTime=time.time()
 		self._load_track_event()
@@ -193,6 +195,7 @@ class MidiPlayer:
 
 	def play(self):
 		self.__initPlay()
+
 		for evt in self.events:
 			evt=evt.transpose(self.transposeNote)
 			self.waitForEvent(evt)