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