audiodev.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. """Classes for manipulating audio devices (currently only for Sun and SGI)"""
  2. __all__ = ["error","AudioDev"]
  3. class error(Exception):
  4. pass
  5. class Play_Audio_sgi:
  6. # Private instance variables
  7. ## if 0: access frameratelist, nchannelslist, sampwidthlist, oldparams, \
  8. ## params, config, inited_outrate, inited_width, \
  9. ## inited_nchannels, port, converter, classinited: private
  10. classinited = 0
  11. frameratelist = nchannelslist = sampwidthlist = None
  12. def initclass(self):
  13. import AL
  14. self.frameratelist = [
  15. (48000, AL.RATE_48000),
  16. (44100, AL.RATE_44100),
  17. (32000, AL.RATE_32000),
  18. (22050, AL.RATE_22050),
  19. (16000, AL.RATE_16000),
  20. (11025, AL.RATE_11025),
  21. ( 8000, AL.RATE_8000),
  22. ]
  23. self.nchannelslist = [
  24. (1, AL.MONO),
  25. (2, AL.STEREO),
  26. (4, AL.QUADRO),
  27. ]
  28. self.sampwidthlist = [
  29. (1, AL.SAMPLE_8),
  30. (2, AL.SAMPLE_16),
  31. (3, AL.SAMPLE_24),
  32. ]
  33. self.classinited = 1
  34. def __init__(self):
  35. import al, AL
  36. if not self.classinited:
  37. self.initclass()
  38. self.oldparams = []
  39. self.params = [AL.OUTPUT_RATE, 0]
  40. self.config = al.newconfig()
  41. self.inited_outrate = 0
  42. self.inited_width = 0
  43. self.inited_nchannels = 0
  44. self.converter = None
  45. self.port = None
  46. return
  47. def __del__(self):
  48. if self.port:
  49. self.stop()
  50. if self.oldparams:
  51. import al, AL
  52. al.setparams(AL.DEFAULT_DEVICE, self.oldparams)
  53. self.oldparams = []
  54. def wait(self):
  55. if not self.port:
  56. return
  57. import time
  58. while self.port.getfilled() > 0:
  59. time.sleep(0.1)
  60. self.stop()
  61. def stop(self):
  62. if self.port:
  63. self.port.closeport()
  64. self.port = None
  65. if self.oldparams:
  66. import al, AL
  67. al.setparams(AL.DEFAULT_DEVICE, self.oldparams)
  68. self.oldparams = []
  69. def setoutrate(self, rate):
  70. for (raw, cooked) in self.frameratelist:
  71. if rate == raw:
  72. self.params[1] = cooked
  73. self.inited_outrate = 1
  74. break
  75. else:
  76. raise error, 'bad output rate'
  77. def setsampwidth(self, width):
  78. for (raw, cooked) in self.sampwidthlist:
  79. if width == raw:
  80. self.config.setwidth(cooked)
  81. self.inited_width = 1
  82. break
  83. else:
  84. if width == 0:
  85. import AL
  86. self.inited_width = 0
  87. self.config.setwidth(AL.SAMPLE_16)
  88. self.converter = self.ulaw2lin
  89. else:
  90. raise error, 'bad sample width'
  91. def setnchannels(self, nchannels):
  92. for (raw, cooked) in self.nchannelslist:
  93. if nchannels == raw:
  94. self.config.setchannels(cooked)
  95. self.inited_nchannels = 1
  96. break
  97. else:
  98. raise error, 'bad # of channels'
  99. def writeframes(self, data):
  100. if not (self.inited_outrate and self.inited_nchannels):
  101. raise error, 'params not specified'
  102. if not self.port:
  103. import al, AL
  104. self.port = al.openport('Python', 'w', self.config)
  105. self.oldparams = self.params[:]
  106. al.getparams(AL.DEFAULT_DEVICE, self.oldparams)
  107. al.setparams(AL.DEFAULT_DEVICE, self.params)
  108. if self.converter:
  109. data = self.converter(data)
  110. self.port.writesamps(data)
  111. def getfilled(self):
  112. if self.port:
  113. return self.port.getfilled()
  114. else:
  115. return 0
  116. def getfillable(self):
  117. if self.port:
  118. return self.port.getfillable()
  119. else:
  120. return self.config.getqueuesize()
  121. # private methods
  122. ## if 0: access *: private
  123. def ulaw2lin(self, data):
  124. import audioop
  125. return audioop.ulaw2lin(data, 2)
  126. class Play_Audio_sun:
  127. ## if 0: access outrate, sampwidth, nchannels, inited_outrate, inited_width, \
  128. ## inited_nchannels, converter: private
  129. def __init__(self):
  130. self.outrate = 0
  131. self.sampwidth = 0
  132. self.nchannels = 0
  133. self.inited_outrate = 0
  134. self.inited_width = 0
  135. self.inited_nchannels = 0
  136. self.converter = None
  137. self.port = None
  138. return
  139. def __del__(self):
  140. self.stop()
  141. def setoutrate(self, rate):
  142. self.outrate = rate
  143. self.inited_outrate = 1
  144. def setsampwidth(self, width):
  145. self.sampwidth = width
  146. self.inited_width = 1
  147. def setnchannels(self, nchannels):
  148. self.nchannels = nchannels
  149. self.inited_nchannels = 1
  150. def writeframes(self, data):
  151. if not (self.inited_outrate and self.inited_width and self.inited_nchannels):
  152. raise error, 'params not specified'
  153. if not self.port:
  154. import sunaudiodev, SUNAUDIODEV
  155. self.port = sunaudiodev.open('w')
  156. info = self.port.getinfo()
  157. info.o_sample_rate = self.outrate
  158. info.o_channels = self.nchannels
  159. if self.sampwidth == 0:
  160. info.o_precision = 8
  161. self.o_encoding = SUNAUDIODEV.ENCODING_ULAW
  162. # XXX Hack, hack -- leave defaults
  163. else:
  164. info.o_precision = 8 * self.sampwidth
  165. info.o_encoding = SUNAUDIODEV.ENCODING_LINEAR
  166. self.port.setinfo(info)
  167. if self.converter:
  168. data = self.converter(data)
  169. self.port.write(data)
  170. def wait(self):
  171. if not self.port:
  172. return
  173. self.port.drain()
  174. self.stop()
  175. def stop(self):
  176. if self.port:
  177. self.port.flush()
  178. self.port.close()
  179. self.port = None
  180. def getfilled(self):
  181. if self.port:
  182. return self.port.obufcount()
  183. else:
  184. return 0
  185. ## # Nobody remembers what this method does, and it's broken. :-(
  186. ## def getfillable(self):
  187. ## return BUFFERSIZE - self.getfilled()
  188. def AudioDev():
  189. # Dynamically try to import and use a platform specific module.
  190. try:
  191. import al
  192. except ImportError:
  193. try:
  194. import sunaudiodev
  195. return Play_Audio_sun()
  196. except ImportError:
  197. try:
  198. import Audio_mac
  199. except ImportError:
  200. raise error, 'no audio device'
  201. else:
  202. return Audio_mac.Play_Audio_mac()
  203. else:
  204. return Play_Audio_sgi()
  205. def test(fn = None):
  206. import sys
  207. if sys.argv[1:]:
  208. fn = sys.argv[1]
  209. else:
  210. fn = 'f:just samples:just.aif'
  211. import aifc
  212. af = aifc.open(fn, 'r')
  213. print fn, af.getparams()
  214. p = AudioDev()
  215. p.setoutrate(af.getframerate())
  216. p.setsampwidth(af.getsampwidth())
  217. p.setnchannels(af.getnchannels())
  218. BUFSIZ = af.getframerate()/af.getsampwidth()/af.getnchannels()
  219. while 1:
  220. data = af.readframes(BUFSIZ)
  221. if not data: break
  222. print len(data)
  223. p.writeframes(data)
  224. p.wait()
  225. if __name__ == '__main__':
  226. test()