picadae calibration programs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NoteTester.py 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import sys,os,types,json
  2. from random import randrange
  3. class NoteTester:
  4. def __init__( self, cfg, api ):
  5. self.cfg = cfg
  6. self.api = api
  7. r = types.SimpleNamespace(**cfg.NoteTester)
  8. self.durMsL = [ randrange(r.minNoteDurMs, r.maxNoteDurMs) for _ in range(r.noteCount) ]
  9. self.pauseMsL = [ randrange(r.minPauseDurMs, r.maxPauseDurMs) for _ in range(r.noteCount) ]
  10. self.eventL = []
  11. self.nextMs = 0 # next transition time
  12. self.eventIdx = 0 # next event to play
  13. self.noteOnFl = False # True if note is currently sounding
  14. self.pitch = r.pitch #
  15. self.filename = r.filename
  16. self.isStartedFl = False
  17. self.minAttackUsec = r.minAttackUsec
  18. self.maxAttackUsec = r.maxAttackUsec
  19. def start( self ):
  20. self.eventIdx = 0
  21. self.noteOnFl = False
  22. self.nextMs = 0
  23. self.isStartedFl = True
  24. def stop( self ):
  25. self.isStartedFl = False
  26. self.write()
  27. def tick( self, ms ):
  28. if self.isStartedFl and ms > self.nextMs:
  29. offsMs = 0
  30. if self.noteOnFl:
  31. self.noteOnFl = False
  32. self.api.note_off( self.pitch )
  33. offsMs = self.pauseMsL[ self.eventIdx ]
  34. self.eventIdx += 1
  35. print("off:%i ms" % (offsMs))
  36. else:
  37. usec = self.minAttackUsec + (int(self.eventIdx * 250) % int(self.maxAttackUsec - self.minAttackUsec))
  38. decay_level = self.api.calc_decay_level( usec )
  39. self.api.note_on_us( self.pitch, usec, decay_level )
  40. offsMs = self.durMsL[ self.eventIdx ]
  41. print("usec:%i %i dcy:%i" % (usec,offsMs, decay_level) )
  42. self.noteOnFl = True
  43. self.eventL.append( (ms, self.noteOnFl) )
  44. self.nextMs = ms + offsMs
  45. if self.eventIdx >= len(self.durMsL):
  46. self.write();
  47. self.isStartedFl = False
  48. print("done % i" % (len(self.eventL)))
  49. def write( self ):
  50. with open(self.filename,"w") as f:
  51. json.dump({ "eventL":self.eventL },f )
  52. def note_tester_compare( nt_fn, logica_fn ):
  53. eventL = []
  54. logicaL = []
  55. with open(nt_fn,"r") as f:
  56. r = json.load(f)
  57. eventL = r['eventL']
  58. eventL = [ (ms-eventL[0][0], level ) for ms,level in eventL ]
  59. with open(logica_fn,"r") as f:
  60. logicaL = [ ( d['count']/16e3,d['level']) for d in json.load(f) if d['signal'] == 0 ]
  61. logicaL = [ (ms-logicaL[0][0], level!=0 ) for ms,level in logicaL ]
  62. print(len(eventL))
  63. print(len(logicaL))
  64. #edL = [ eventL[i][0] - eventL[i-1][0] for i in range(2,len(eventL)) ]
  65. #ldL = [ logicaL[i][0] - logicaL[i-1][0] for i in range(2,len(logicaL)) ]
  66. #print(edL[:10])
  67. #print(ldL[:10])
  68. durMs = 0
  69. ms = 0
  70. for i,(t0,t1) in enumerate(zip(eventL,logicaL)):
  71. t = t0[0] # eventL[] time
  72. dt = int(t - t1[0]) # diff between eventL[] and logicaL[] time
  73. fl = ' ' if t0[1] == t1[1] else '*' # mark level mismatch with '*'
  74. print("%5i %7i %4i %i %s" % (i,durMs,dt,t0[1],fl))
  75. durMs = t-ms
  76. ms = t
  77. if __name__ == "__main__":
  78. nt_fn = "note_tester.json"
  79. logica_fn = sys.argv[1]
  80. if len(sys.argv) > 2:
  81. nt_fn = sys.argv[2]
  82. note_tester_compare( nt_fn, logica_fn)