tabextract.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. from lxml import etree
  4. import sys
  5. import getopt
  6. NOTE_TYPE= ["whole", "half", "quarter", "eighth", "16th", "32nd", "64th"]
  7. NOTE_TYPE_MULT=[ 4, 2, 1, 0.5, 0.25, 0.125, 0.0625]
  8. def noteTypeToIndex(s):
  9. i=0
  10. for st in NOTE_TYPE:
  11. if s==st:
  12. return i
  13. i=i+1
  14. return -1
  15. def fretToMidi(string, fret):
  16. tab=[0,40,45,50,55,59,64]
  17. tab=[0,64,59,55,50,45,40]
  18. return tab[string]+fret
  19. def xpathExists(obj, path):
  20. for x in obj.xpath(path):
  21. return True
  22. return False
  23. def xpathFirst(d, path):
  24. for x in d.xpath(path):
  25. return x
  26. return None
  27. class Note:
  28. def __init__(self, data):
  29. self.octave=0
  30. self.note="R"
  31. self.corde=0
  32. self.fret=0
  33. self.midiNote=0
  34. if xpathExists(data,"rest"):
  35. self.rest=True
  36. else:
  37. self.rest=False
  38. self.octave=int(xpathFirst(data,"pitch/octave").text)
  39. self.note=xpathFirst(data,"pitch/step").text
  40. self.corde=int(xpathFirst(data,"notations/technical/string").text)
  41. self.fret=int(xpathFirst(data,"notations/technical/fret").text)
  42. self.midiNote=fretToMidi(self.corde, self.fret)
  43. self.type=noteTypeToIndex(xpathFirst(data,"type").text)
  44. self.duree=int(xpathFirst(data,"duration").text)
  45. self.voix=int(xpathFirst(data,"voice").text)
  46. self.dot=xpathExists(data, "dot")
  47. self.beatmult=NOTE_TYPE_MULT[self.type]
  48. if self.dot:
  49. self.beatmult=1.5*self.beatmult
  50. def getMidi(self):
  51. return self.midiNote
  52. def getTime(self):
  53. return self.beatmult
  54. def show(self):
  55. print(str(self.getMidi())+": \""+str(self.type)+"\" "+str(self.dot)+" "+str(self.rest))
  56. class Measure:
  57. def __init__(self, data):
  58. self.notes=[]
  59. try:
  60. self.beats=int(xpathFirst(data, "attributes/time/beats").text)
  61. self.beatType=int(xpathFirst(data, "attributes/time/beat-type").text)
  62. except:
  63. self.notes=[]
  64. def addNote(self, note):
  65. self.notes.append(note)
  66. def noteToString(self):
  67. s=""
  68. i=0
  69. for x in self.notes:
  70. if i>0:
  71. s+=", "
  72. s+=str(self.notes[i].getTime())
  73. i+=1
  74. return s
  75. def timeToString(self):
  76. s=""
  77. i=0
  78. for x in self.notes:
  79. if i>0:
  80. s+=", "
  81. s+=str(self.notes[i].getTime())
  82. i+=1
  83. return s
  84. class Part:
  85. def __init__(self, data):
  86. self.id=data.get("id")
  87. self.notes=[]
  88. self.beat=0
  89. self.beatType=0
  90. path="/score-partwise/part-list/score-part[@id='"+self.id+"']/"
  91. self.name=xpathFirst(data, path+"part-name").text
  92. self.instrument=xpathFirst(data, path+"score-instrument/instrument-name").text
  93. self.midiChannel=xpathFirst(data, path+"midi-instrument/midi-channel").text
  94. self.midiProgram=xpathFirst(data, path+"midi-instrument/midi-program").text
  95. self.measures=[]
  96. for d in data.xpath("measure"):
  97. m=Measure(d)
  98. self.measures.append(m)
  99. for n in d.xpath("note"):
  100. note=Note(n)
  101. self.notes.append(note)
  102. m.addNote(note)
  103. def length(self):
  104. tot=0
  105. for x in self.notes:
  106. tot+=x.getTime()
  107. return tot
  108. def toString(self):
  109. return self.name+" : (\""+self.instrument+"\", "+str(self.length())+" beats, "+str(len(self.notes))+" notes)"
  110. def noteRangeToString(self, start, end):
  111. i=start
  112. s=""
  113. while i<=end:
  114. for x in self.measures[i].notes:
  115. s+= ("" if s=="" else ", ")+str(x.getMidi())
  116. i+=1
  117. return s
  118. def timeRangeToString(self, start, end):
  119. i=start
  120. s=""
  121. while i<=end:
  122. for x in self.measures[i].notes:
  123. s+= ("" if s=="" else ", ")+str(x.getTime())
  124. i+=1
  125. return s
  126. class System:
  127. def __init__(self, path):
  128. self.path=path
  129. self.partition=[]
  130. self.tree = etree.parse(self.path)
  131. self.parts=[]
  132. for data in self.tree.xpath("/score-partwise/part"):
  133. self.parts.append(Part(data))
  134. self.bufferNote=""
  135. self.bufferTime=""
  136. def noteRange(self, i, start, end):
  137. x=self.parts[i-1].noteRangeToString(start-1, end-1)
  138. self.bufferNote+= ("" if self.bufferNote=="" else ", ")+x
  139. return x;
  140. def timeRange(self, i, start, end):
  141. x=self.parts[i-1].timeRangeToString(start-1, end-1)
  142. self.bufferTime+= ("" if self.bufferTime=="" else ", ")+x
  143. return x;
  144. def note(self, i):
  145. return self.noteRange(i, 1, len(self.parts[i-1].measures));
  146. def time(self, i):
  147. return self.timeRange(i, 1, len(self.parts[i-1].measures));
  148. def range(self, i,start, end):
  149. self.timeRange(i, start, end)
  150. self.noteRange(i, start, end)
  151. def track(self, i):
  152. self.time(i)
  153. self.note(i)
  154. def before(self, i, n):
  155. self.timeRange(i, 1, n)
  156. self.noteRange(i, 1, n)
  157. def after(self, i, n):
  158. self.timeRange(i, n, len(self.parts[i-1].measures))
  159. self.noteRange(i, n, len(self.parts[i-1].measures))
  160. def new(self):
  161. self.bufferNote=""
  162. self.bufferTime=""
  163. def write(self, f):
  164. fd = open(f, "w")
  165. fd.write("notes=(ring "+self.bufferNote+")")
  166. fd.write("times=(ring "+self.bufferTime+")")
  167. fd.close()
  168. def print(self):
  169. print("notes=(ring "+self.bufferNote+")")
  170. print("times=(ring "+self.bufferTime+")")
  171. def list(self):
  172. i=0
  173. for x in self.parts:
  174. print("["+str(i)+"] : "+x.toString())
  175. i+=1
  176. if __name__ == '__main__':
  177. def usage():
  178. print("Usage:")
  179. print(sys.argv[0]+" PATH_TO_XML [COMMANDS]")
  180. print("ou " +sys.argv[0]+" -h | --help : Affiche cette aide")
  181. print("ou " +sys.argv[0]+" -l | --list : Liste les pistes")
  182. print("COMMANDS:")
  183. print("\t-n | --new : Vide le buffer courant")
  184. print("\t-r | --range TRACK START END : Ajoute les mesure de la piste TRACK entre les mesure START et END")
  185. print("\t-b | --before TRACK INDEX : Ajoute les mesure de la piste TRACK jusqu'a la mesure INDEX")
  186. print("\t-a | --after TRACK INDEX : Ajoute les mesure de la piste TRACK à partir de la mesure INDEX")
  187. print("\t-t | --track TRACK : Ajoute toutes la piste TRACK au buffer")
  188. print("\t-p | --print : Affiche le buffer courant")
  189. print("\t-w | --write FILE : Ecrit le buffer dans le fichier FILE")
  190. song=None
  191. path="/home/fanch/Documents/tabs/Johann Pachelbel - Canon In D (ver 6 by Ezechiel).xml"
  192. i=1
  193. op=False
  194. if len(sys.argv)==1:
  195. usage()
  196. sys.exit(-1)
  197. while i<len(sys.argv):
  198. if i==1:
  199. path=sys.argv[i]
  200. i+=1
  201. song=System(path)
  202. elif sys.argv[i]=="-n" or sys.argv[i]=="--new":
  203. song.new()
  204. elif sys.argv[i]=="-r" or sys.argv[i]=="--range":
  205. song.range(int(sys.argv[i+1]), int(sys.argv[i+2], sys.argv[i+3]))
  206. op=True
  207. i+=3
  208. elif sys.argv[i]=="-b" or sys.argv[i]=="--before":
  209. song.before(int(sys.argv[i+1]), int(sys.argv[i+2]))
  210. op=True
  211. i+=2
  212. elif sys.argv[i]=="-a" or sys.argv[i]=="--after":
  213. song.after(int(sys.argv[i+1]), int(sys.argv[i+2]))
  214. op=True
  215. i+=2
  216. elif sys.argv[i]=="-t" or sys.argv[i]=="--track":
  217. op=True
  218. song.track(int(sys.argv[i+1]))
  219. i+=1
  220. elif sys.argv[i]=="-p" or sys.argv[i]=="--print":
  221. song.print()
  222. elif sys.argv[i]=="-w" or sys.argv[i]=="--write":
  223. song.part(int(sys.argv[i+1]))
  224. i+=1
  225. elif sys.argv[i]=="-h" or sys.argv[i]=="--help":
  226. usage()
  227. sys.exit(-1)
  228. elif sys.argv[i]=="-l" or sys.argv[i]=="--list":
  229. usage()
  230. sys.exit(-1)
  231. else:
  232. usage()
  233. sys.exit(-1)
  234. i+=1
  235. if not op:
  236. song.track(1)
  237. song.print()