class Monitor(threading.Thread): '''audio monitor thread''' def __init__(self, device, rate, period): threading.Thread.__init__(self) self.pcm = PCM(PCM_PLAYBACK, PCM_NORMAL, device) self.pcm.setformat(PCM_FORMAT_S16_LE) self.pcm.setchannels(2) self.pcm.setrate(rate) self.pcm.setperiodsize(period) self.queue = Queue.Queue(256) self.__stop = False def run(self): '''run loop''' while True: qsize = self.queue.qsize() if qsize == 0 and self.__stop: break try: block = self.queue.get(False) self.pcm.write(block) except Queue.Empty: time.sleep(0.01) def stop(self): '''set stop flag''' self.__stop = True
class VoIPAlsaOutgoingCall(VoIPOutgoingCall): def __init__(self, *args, **kwargs): super(VoIPAlsaOutgoingCall, self).__init__(*args, **kwargs) self.playback_device = None self.capture_device = None self.ctrl.set_send_audio_frame_callback(self._read_frame) self.ctrl.set_recv_audio_frame_callback(self._write_frame) def build_capture_device(self): self.capture_device = PCM(PCM_CAPTURE, PCM_NONBLOCK) self.capture_device.setrate(48000) self.capture_device.setperiodsize(960) self.capture_device.setchannels(1) def build_playback_device(self): self.playback_device = PCM(mode=PCM_NONBLOCK) self.playback_device.setrate(48000) self.playback_device.setperiodsize(960) self.playback_device.setchannels(1) def _read_frame(self, length: int): if not self.capture_device: self.build_capture_device() _, frame = self.capture_device.read() return frame def _write_frame(self, frame: bytes): if not self.playback_device: self.build_playback_device() self.playback_device.write(frame)
#!/usr/bin/python2.7 # # Robot Standard Language # v0.1 # # Copyright(C) Joshua Davis # http://covert.codes # Open source, do what you want with it. import numpy from numpy.fft import fft, ifft from numpy.random import random_sample from alsaaudio import PCM, PCM_NONBLOCK, PCM_FORMAT_FLOAT_LE pcm = PCM() #mode = PCM_NONBLOCK) pcm.setrate(4000) pcm.setformat(PCM_FORMAT_FLOAT_LE) pcm.setchannels(1) pcm.setperiodsize(4096) def sin_wave(x, freq=100): sample = numpy.arange(100*4096, (x+1)*4096, dtype=numpy.float32) sample *= numpy.pi * 2 / 44100 sample *= freq return numpy.sin(sample) for x in xrange(1000): sample = sin_wave(x, 100) pcm.write(sample.tostring())
import numpy from numpy.fft import fft, ifft from numpy.random import random_sample from alsaaudio import PCM, PCM_NONBLOCK, PCM_FORMAT_FLOAT_LE pcm = PCM() #mode=PCM_NONBLOCK) pcm.setrate(44100) pcm.setformat(PCM_FORMAT_FLOAT_LE) pcm.setchannels(1) pcm.setperiodsize(4096) def sine_wave(x, freq=100): sample = numpy.arange(x * 4096, (x + 1) * 4096, dtype=numpy.float32) sample *= numpy.pi * 2 / 44100 sample *= freq return numpy.sin(sample) for x in xrange(1000): sample = sine_wave(x, 100) pcm.write(sample.tostring())
class WavPlayer(Thread): """ Un thread que ejecuta archivos de sonido. A medida que recibe mensajes para ejecutar archivos wav, los guarda en un cache interno asi no los tiene que cargar en cada repeticion. Para ejecutar un wav, se debe llamar al metodo play(file), donde file es la direccion completa al archivo wav. También se pueden encolar varios wav a decir en una lista mediante el método queue_play(). """ PAUSE_TOKEN = '' def __init__(self, as_daemon=True): Thread.__init__(self) self.daemon = as_daemon self._cache = {} self.__running = False self._device = None self._mixers = {} self._default_mixer = None self._queue = [] self.__init_alsa() def __init_alsa(self): try: self._device = PCM() except ALSAAudioError as e: logger.error('ERROR: Error al inicializar dispositivo ALSA: %s' % str(e)) return else: for mixer in MIXER_PRIO: try: self._mixers[mixer] = Mixer(mixer) except ALSAAudioError as e: err = 'Warning: Error al inicializar mixer ALSA: %s' logger.warning(err % str(e)) else: if self._default_mixer is None: self._default_mixer = mixer def _get_wav(self, file_name): """ Returns a WaveData instance from the self._wav_file property, keeping it in a cache dictionary for subsequent calls. """ if file_name not in self._cache: try: wav_file = waveOpen(file_name, 'rb') nc, sw, fr, nf, comptype, compname = wav_file.getparams() self._cache[file_name] = WavData(nc, fr, nf, wav_file.readframes(nf)) wav_file.close() except Exception as exc: self._cache[file_name] = None print(exc) return self._cache[file_name] def _play(self, wav): """ Plays a sound in a ALSA device """ self._device.setchannels(wav.nchannels) self._device.setrate(wav.frate) self._device.setperiodsize(wav.nframes) self._device.write(wav.data) def run(self): """ Starts the loop waiting for audio files to be played """ self.__running = True while self.__running: if self._queue: now_filename, now_message = self._queue.pop(0) if now_message == WavPlayer.PAUSE_TOKEN: time.sleep(SPEECH_PAUSE) continue wav = self._get_wav(now_filename) if wav is not None: inicio = datetime.now() self._play(wav) # calculamos cuanto tiempo quedo "colgado" hasta # volver del _play, para descontar eso de la espera # (el _play empieza a reproducir pero no retorna # instantaneamente si el archivo es grande) dcarga = datetime.now() - inicio # asumiendo que la carga no dura mas de un dia segundos_carga = dcarga.seconds + \ dcarga.microseconds / 1000000.0 sleep_time = (wav.nframes / wav.frate) - segundos_carga if sleep_time > 0: time.sleep(sleep_time) # NO SACAR, sino audioplayer se come el 100% de cpu time.sleep(0.1) def play(self, wav_file): """ Assigns a wave file to be played. Arguments: wav_file -- File path of the audio file. """ if wav_file is not None: self._queue = [(wav_file, None)] def queue_play(self, wav_file, mensaje=None): if wav_file is not None: self._queue.append((wav_file, mensaje)) def empty_queue(self): self._queue = [] def stop(self): """ Stops the thread. It can't be started again, so it also closes the opened audio device """ self.empty_queue() self.__running = False self.close() def pending_files(self): return len(self._queue) > 0 def close(self): """ Closes the audio output opened in the constructor. Useful to call from outside if as_daemon=False (instantiated only to set the volume for example) """ if self._device: self._device.close() def set_volume(self, level): """ Sets volume """ mixer_name = self._default_mixer num_vals = len(VALORES_VOLUMEN) if mixer_name is not None and level >= 0 and level <= num_vals: mixer = self._mixers[mixer_name] log_value = VALORES_VOLUMEN[level] mixer.setvolume(log_value, MIXER_CHANNEL_ALL) def get_volume(self, mixer=None): """ Returns the volume in a Mixer. If mixer is None, returns the volume from the most relevant, the default """ index = None if mixer is None: mixer = self._default_mixer if mixer is not None: log_value = int(self._mixers[mixer].getvolume()[0]) try: index = VALORES_VOLUMEN.index(log_value) except ValueError: pass return index