import sys from io import StringIO from . import puremixer from .music1 import Music class Sound: # Mono only has_sound = has_music = 0 # until initialized BUFFERTIME = 0.09 FLOPTIME = 0.07 Formats = [ ('U8', 8, 0, None), ('S8', 8, 1, None), ('S16_NE', 16, 1, None), ('S16_LE', 16, 1, 'little'), ('S16_BE', 16, 1, 'big'), ('U16_LE', 16, 0, 'little'), ('U16_BE', 16, 0, 'big'), ] def __init__(self, freq=44100, fmt='S16_NE'): self.f = None self.freq = int(freq) self.format = fmt.upper() self.params = p = {} for name, p['bits'], p['signed'], p['byteorder'] in self.Formats: if name == self.format: break else: print('available sound formats:', file=sys.stderr) for name, bits, signed, byteorder in self.Formats: print(' %-8s %s' % (name, nicefmttext( bits, signed, byteorder)), file=sys.stderr) sys.exit(2) import linuxaudiodev try: f = linuxaudiodev.open('w') f.setparameters(self.freq, p['bits'], 1, getattr(linuxaudiodev, 'AFMT_' + self.format)) except Exception as e: print("sound disabled: %s: %s" % ( e.__class__.__name__, e), file=sys.stderr) return self.f = f self.mixer = mixer = puremixer.PureMixer(**p) buffertime = self.BUFFERTIME self.bufsize = int(mixer.bytespersample*mixer.freq*buffertime + 255.5) & ~ 255 if self.bufsize > f.bufsize(): self.bufsize = f.bufsize() buffertime = self.bufsize / float(freq) self.buffertime = buffertime self.mixer_channels = [] self.mixer_accum = {} self.has_sound = 1 self.has_music = 1 def close(self): self.f.close() self.f = None def sound(self, f): return self.mixer.wavesample(f.fopen()) def flop(self): self.mixer_accum = {} if self.f is None: return for i in range(3): bufsize = self.bufsize - self.f.obufcount() if bufsize <= 0: break self.f.write(self.mixer.mix(self.mixer_channels, bufsize)) #cnt = getattr(self, 'CNT', 0) #import time #print cnt, time.time() #self.CNT = cnt+1 return self.FLOPTIME def play(self, sound, lvolume, rvolume): # volume ignored if sound not in self.mixer_accum: self.mixer_channels.append(StringIO(sound)) self.mixer_accum[sound] = 1 def play_musics(self, musics, loop_from): self.cmusics = musics, loop_from, -1 if self.mixer_channels[:1] != [self]: self.mixer_channels.insert(0, self) def read(self, size): "Provide some more data to self.mixer.poll()." musics, loop_from, c = self.cmusics if c < 0: data = '' else: data = musics[c].mixed.decode(self.mixer, size) if not data: c += 1 if c >= len(musics): # end c = loop_from if c >= len(musics): return '' self.cmusics = musics, loop_from, c try: mixed = musics[c].mixed except AttributeError: mixed = musics[c].mixed = Music(musics[c].freezefilename()) mixed.openchannel() data = mixed.decode(self.mixer, size) if 0 < len(data) < size: data += self.read(size - len(data)) return data def fadeout(self, millisec): self.cmusics = [], 0, -1 def imperror(): try: import linuxaudiodev except ImportError: if sys.platform.startswith('linux'): return 'linuxaudiodev module not installed' else: return 'only available on Linux' def nicefmttext(bits, signed, byteorder): s = '%s %d bits' % (signed and 'signed' or 'unsigned', bits) if byteorder: s += ' %s endian' % byteorder return s def htmloptionstext(nameval): from . import modes l = ['Sampling <%s>' % nameval('select', 'fmt')] for name, bits, signed, byteorder in Sound.Formats: l.append('<'+nameval('option', 'fmt', name, default='S16_NE')+'>'+ nicefmttext(bits, signed, byteorder)) l+= [' rate ', '<%s size=5>Hz' % nameval('text', 'freq', default='44100'), '
', modes.musichtmloptiontext(nameval)] return '\n'.join(l)