client.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. # A simple client that generates sine waves via python-pyaudio
  2. import signal
  3. import pyaudio
  4. import sys
  5. import socket
  6. import time
  7. import math
  8. import struct
  9. import socket
  10. import optparse
  11. from packet import Packet, CMD, stoi
  12. parser = optparse.OptionParser()
  13. parser.add_option('-t', '--test', dest='test', action='store_true', help='Play a test sequence (440,<rest>,880,440), then exit')
  14. parser.add_option('-g', '--generator', dest='generator', default='math.sin', help='Set the generator (to a Python expression)')
  15. parser.add_option('-u', '--uid', dest='uid', default='', help='Set the UID (identifier) of this client in the network')
  16. parser.add_option('-p', '--port', dest='port', type='int', default=13676, help='Set the port to listen on')
  17. parser.add_option('-r', '--rate', dest='rate', type='int', default=44100, help='Set the sample rate of the audio device')
  18. options, args = parser.parse_args()
  19. PORT = options.port
  20. STREAMS = 1
  21. IDENT = 'TONE'
  22. UID = options.uid
  23. LAST_SAMP = 0
  24. FREQ = 0
  25. PHASE = 0
  26. RATE = options.rate
  27. FPB = 64
  28. Z_SAMP = '\x00\x00\x00\x00'
  29. MAX = 0x7fffffff
  30. AMP = MAX
  31. MIN = -0x80000000
  32. def lin_interp(frm, to, p):
  33. return p*to + (1-p)*frm
  34. # Generator functions--should be cyclic within [0, 2*math.pi) and return [-1, 1]
  35. def tri_wave(theta):
  36. if theta < math.pi/2:
  37. return lin_interp(0, 1, theta/(math.pi/2))
  38. elif theta < 3*math.pi/2:
  39. return lin_interp(1, -1, (theta-math.pi/2)/math.pi)
  40. else:
  41. return lin_interp(-1, 0, (theta-3*math.pi/2)/(math.pi/2))
  42. def square_wave(theta):
  43. if theta < math.pi:
  44. return 1
  45. else:
  46. return -1
  47. #generator = math.sin
  48. #generator = tri_wave
  49. #generator = square_wave
  50. generator = eval(options.generator)
  51. def sigalrm(sig, frm):
  52. global FREQ
  53. FREQ = 0
  54. def lin_seq(frm, to, cnt):
  55. step = (to-frm)/float(cnt)
  56. samps = [0]*cnt
  57. for i in xrange(cnt):
  58. p = i / float(cnt-1)
  59. samps[i] = int(lin_interp(frm, to, p))
  60. return samps
  61. def samps(freq, phase, cnt):
  62. global RATE, AMP
  63. samps = [0]*cnt
  64. for i in xrange(cnt):
  65. samps[i] = int(AMP * generator((phase + 2 * math.pi * freq * i / RATE) % (2*math.pi)))
  66. return samps, (phase + 2 * math.pi * freq * cnt / RATE) % (2*math.pi)
  67. def to_data(samps):
  68. return struct.pack('i'*len(samps), *samps)
  69. def gen_data(data, frames, time, status):
  70. global FREQ, PHASE, Z_SAMP, LAST_SAMP
  71. if FREQ == 0:
  72. PHASE = 0
  73. if LAST_SAMP == 0:
  74. return (Z_SAMP*frames, pyaudio.paContinue)
  75. fdata = lin_seq(LAST_SAMP, 0, frames)
  76. LAST_SAMP = fdata[-1]
  77. return (to_data(fdata), pyaudio.paContinue)
  78. fdata, PHASE = samps(FREQ, PHASE, frames)
  79. LAST_SAMP = fdata[-1]
  80. return (to_data(fdata), pyaudio.paContinue)
  81. pa = pyaudio.PyAudio()
  82. stream = pa.open(rate=RATE, channels=1, format=pyaudio.paInt32, output=True, frames_per_buffer=FPB, stream_callback=gen_data)
  83. if options.test:
  84. FREQ = 440
  85. time.sleep(1)
  86. FREQ = 0
  87. time.sleep(1)
  88. FREQ = 880
  89. time.sleep(1)
  90. FREQ = 440
  91. time.sleep(2)
  92. exit()
  93. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  94. sock.bind(('', PORT))
  95. signal.signal(signal.SIGALRM, sigalrm)
  96. while True:
  97. data = ''
  98. while not data:
  99. try:
  100. data, cli = sock.recvfrom(4096)
  101. except socket.error:
  102. pass
  103. pkt = Packet.FromStr(data)
  104. print 'From', cli, 'command', pkt.cmd
  105. if pkt.cmd == CMD.KA:
  106. pass
  107. elif pkt.cmd == CMD.PING:
  108. sock.sendto(data, cli)
  109. elif pkt.cmd == CMD.QUIT:
  110. break
  111. elif pkt.cmd == CMD.PLAY:
  112. dur = pkt.data[0]+pkt.data[1]/1000000.0
  113. FREQ = pkt.data[2]
  114. AMP = MAX * (pkt.data[3]/255.0)
  115. signal.setitimer(signal.ITIMER_REAL, dur)
  116. elif pkt.cmd == CMD.CAPS:
  117. data = [0] * 8
  118. data[0] = STREAMS
  119. data[1] = stoi(IDENT)
  120. for i in xrange(len(UID)/4):
  121. data[i+2] = stoi(UID[4*i:4*(i+1)])
  122. sock.sendto(str(Packet(CMD.CAPS, *data)), cli)
  123. else:
  124. print 'Unknown cmd', pkt.cmd