class AudioBackend(QtCore.QObject): underflow = QtCore.pyqtSignal() new_data_available_from_callback = QtCore.pyqtSignal( bytes, int, float, int) new_data_available = QtCore.pyqtSignal(ndarray, float, int) def callback(self, in_data, frame_count, time_info, status): # do the minimum from here to prevent overflows, just pass the data to the main thread input_time = time_info['input_buffer_adc_time'] # some API drivers in PortAudio do not return a valid time, so fallback to the current stream time if input_time == 0.: input_time = time_info['current_time'] if input_time == 0.: input_time = self.stream.get_time() self.new_data_available_from_callback.emit(in_data, frame_count, input_time, status) return (None, 0) def __init__(self, logger): QtCore.QObject.__init__(self) self.logger = logger self.duo_input = False self.terminated = False self.logger.push("Initializing PyAudio") self.pa = PyAudio() # look for devices self.input_devices = self.get_input_devices() self.output_devices = self.get_output_devices() self.device = None self.first_channel = None self.second_channel = None # we will try to open all the input devices until one # works, starting by the default input device for device in self.input_devices: self.logger.push("Opening the stream") try: self.stream = self.open_stream(device) self.stream.start_stream() self.device = device self.logger.push("Success") break except: self.logger.push("Fail") if self.device is not None: self.first_channel = 0 nchannels = self.get_current_device_nchannels() if nchannels == 1: self.second_channel = 0 else: self.second_channel = 1 # counter for the number of input buffer overflows self.xruns = 0 self.chunk_number = 0 self.new_data_available_from_callback.connect(self.handle_new_data) def close(self): if self.stream is not None: self.stream.stop_stream() self.stream.close() self.stream = None if not self.terminated: # call terminate on PortAudio self.logger.push("Terminating PortAudio") self.pa.terminate() self.logger.push("PortAudio terminated") # avoid calling PortAudio methods in the callback/slots self.terminated = True # method def get_readable_devices_list(self): devices_list = [] default_device_index = self.get_default_input_device() for device in self.input_devices: dev_info = self.pa.get_device_info_by_index(device) api = self.pa.get_host_api_info_by_index( dev_info['hostApi'])['name'] if device is default_device_index: extra_info = ' (system default)' else: extra_info = '' nchannels = self.pa.get_device_info_by_index( device)['maxInputChannels'] desc = "%s (%d channels) (%s) %s" % (dev_info['name'], nchannels, api, extra_info) devices_list += [desc] return devices_list # method def get_readable_output_devices_list(self): devices_list = [] default_device_index = self.get_default_output_device() for device in self.output_devices: dev_info = self.pa.get_device_info_by_index(device) api = self.pa.get_host_api_info_by_index( dev_info['hostApi'])['name'] if device is default_device_index: extra_info = ' (system default)' else: extra_info = '' nchannels = self.pa.get_device_info_by_index( device)['maxOutputChannels'] desc = "%s (%d channels) (%s) %s" % (dev_info['name'], nchannels, api, extra_info) devices_list += [desc] return devices_list # method def get_default_input_device(self): try: index = self.pa.get_default_input_device_info()['index'] except IOError: index = None return index # method def get_default_output_device(self): try: index = self.pa.get_default_output_device_info()['index'] except IOError: index = None return index # method def get_device_count(self): return self.pa.get_device_count() # method # returns a list of input devices index, starting with the system default def get_input_devices(self): device_count = self.get_device_count() device_range = list(range(0, device_count)) default_input_device = self.get_default_input_device() if default_input_device is not None: # start by the default input device device_range.remove(default_input_device) device_range = [default_input_device] + device_range # select only the input devices by looking at the number of input channels input_devices = [] for device in device_range: n_input_channels = self.pa.get_device_info_by_index( device)['maxInputChannels'] if n_input_channels > 0: input_devices += [device] return input_devices # method # returns a list of output devices index, starting with the system default def get_output_devices(self): device_count = self.get_device_count() device_range = list(range(0, device_count)) default_output_device = self.get_default_output_device() if default_output_device is not None: # start by the default input device device_range.remove(default_output_device) device_range = [default_output_device] + device_range # select only the output devices by looking at the number of output channels output_devices = [] for device in device_range: n_output_channels = self.pa.get_device_info_by_index( device)['maxOutputChannels'] if n_output_channels > 0: output_devices += [device] return output_devices # method. # The index parameter is the index in the self.input_devices list of devices ! # The return parameter is also an index in the same list. def select_input_device(self, index): device = self.input_devices[index] # save current stream in case we need to restore it previous_stream = self.stream previous_device = self.device self.logger.push("Trying to open input device #%d" % (index)) try: self.stream = self.open_stream(device) self.device = device self.stream.start_stream() success = True except: self.logger.push("Fail") success = False if self.stream is not None: self.stream.close() # restore previous stream self.stream = previous_stream self.device = previous_device if success: self.logger.push("Success") previous_stream.close() self.first_channel = 0 nchannels = self.get_current_device_nchannels() if nchannels == 1: self.second_channel = 0 else: self.second_channel = 1 return success, self.input_devices.index(self.device) # method def select_first_channel(self, index): self.first_channel = index success = True return success, self.first_channel # method def select_second_channel(self, index): self.second_channel = index success = True return success, self.second_channel # method def open_stream(self, device): # by default we open the device stream with all the channels # (interleaved in the data buffer) max_input_channels = self.pa.get_device_info_by_index( device)['maxInputChannels'] stream = self.pa.open(format=paInt16, channels=max_input_channels, rate=SAMPLING_RATE, input=True, input_device_index=device, stream_callback=self.callback, frames_per_buffer=FRAMES_PER_BUFFER) lat_ms = 1000 * stream.get_input_latency() self.logger.push("Device claims %d ms latency" % (lat_ms)) return stream # method def open_output_stream(self, device, callback): # by default we open the device stream with all the channels # (interleaved in the data buffer) max_output_channels = self.pa.get_device_info_by_index( device)['maxOutputChannels'] stream = self.pa.open(format=paInt16, channels=max_output_channels, rate=SAMPLING_RATE, output=True, frames_per_buffer=FRAMES_PER_BUFFER, output_device_index=device, stream_callback=callback) return stream def is_output_format_supported(self, device, output_format): max_output_channels = self.pa.get_device_info_by_index( device)['maxOutputChannels'] success = self.pa.is_format_supported( SAMPLING_RATE, output_device=device, output_channels=max_output_channels, output_format=output_format) return success # method # return the index of the current input device in the input devices list # (not the same as the PortAudio index, since the latter is the index # in the list of *all* devices, not only input ones) def get_readable_current_device(self): return self.input_devices.index(self.device) # method def get_readable_current_channels(self): dev_info = self.pa.get_device_info_by_index(self.device) nchannels = dev_info['maxInputChannels'] if nchannels == 2: channels = ['L', 'R'] else: channels = [] for channel in range(0, dev_info['maxInputChannels']): channels += ["%d" % channel] return channels # method def get_current_first_channel(self): return self.first_channel # method def get_current_second_channel(self): return self.second_channel # method def get_current_device_nchannels(self): return self.pa.get_device_info_by_index( self.device)['maxInputChannels'] def get_device_outputchannels_count(self, device): return self.pa.get_device_info_by_index(device)['maxOutputChannels'] def handle_new_data(self, in_data, frame_count, input_time, status): if self.terminated: return if status & paInputOverflow: print("Stream overflow!") self.xruns += 1 self.underflow.emit() intdata_all_channels = fromstring(in_data, int16) int16info = iinfo(int16) norm_coeff = max(abs(int16info.min), int16info.max) floatdata_all_channels = intdata_all_channels.astype(float64) / float( norm_coeff) channel = self.get_current_first_channel() nchannels = self.get_current_device_nchannels() if self.duo_input: channel_2 = self.get_current_second_channel() if len(floatdata_all_channels) != frame_count * nchannels: print( "Incoming data is not consistent with current channel settings." ) return floatdata1 = floatdata_all_channels[channel::nchannels] if self.duo_input: floatdata2 = floatdata_all_channels[channel_2::nchannels] floatdata = vstack((floatdata1, floatdata2)) else: floatdata = floatdata1 floatdata.shape = (1, floatdata.size) self.new_data_available.emit(floatdata, input_time, status) self.chunk_number += 1 def set_single_input(self): self.duo_input = False def set_duo_input(self): self.duo_input = True # returns the stream time in seconds def get_stream_time(self): try: return self.stream.get_time() except OSError: return 0 def pause(self): self.stream.stop_stream() def restart(self): self.stream.start_stream()
def ask_for_device( p: pyaudio.PyAudio, as_input: bool = False ) -> Optional[int]: # todo return device_info? and filter for input/output """returns the device_id selected by the user""" if p.get_device_count() == 0: raise Exception("No devices available") default_device_index = p.get_default_input_device_info( )["index"] if as_input else p.get_default_output_device_info()["index"] for i in (get_input_device_indexes(p) if as_input else get_output_device_indexes(p)): info = p.get_device_info_by_index(i) index = info["index"] api_name = p.get_host_api_info_by_index(info["hostApi"])["name"] device_name = info["name"] print(f"{index}: \t {api_name} \t {device_name}") device_id = None while device_id is None: user_input = input("Choose device index: ") if user_input: try: device_id = int(user_input) except ValueError: print(f"Could not cast to int: {user_input}") else: print(f"Using default device index: {default_device_index}") device_id = default_device_index return device_id
def get_input(self): p = PyAudio() info = p.get_host_api_info_by_index(0) numdevices = info.get('deviceCount') devices = [] for i in range(0, numdevices): devices.append(p.get_device_info_by_host_api_device_index(0, i)) print("Input Device id ", i, " - ", p.get_device_info_by_host_api_device_index(0, i).get('name')) return devices[1]
def get_api_by_name(p: pyaudio.PyAudio, api_name) -> dict: info_api = [ p.get_host_api_info_by_index(i) for i in range(0, p.get_host_api_count()) ] api_info = None for api in info_api: if api["name"] == api_name: assert api_info is None # Multiple ASIO api's found api_info = api assert api_info is not None # No api's found return api_info
def _config_pyaudio_mic(audio: pyaudio.PyAudio): """Do one-time setup of PyAudio to work with the system mic.""" print('----------------------record device list---------------------') info = audio.get_host_api_info_by_index(0) numdevices = info.get('deviceCount') for i in range(0, numdevices): if (audio.get_device_info_by_host_api_device_index( 0, i).get('maxInputChannels')) > 0: print( 'Input Device id ', i, ' - ', audio.get_device_info_by_host_api_device_index(0, i).get('name')) print('-------------------------------------------------------------') cfg['portaudio_device_index'] = int(input()) print('recording via index ' + str(cfg['portaudio_device_index']))
def get_pulse_device(): """ Detects pulse device :return: id of the pulse device :rtype: int """ # print("The following sound devices are available.") p = PyAudio() info = p.get_host_api_info_by_index(0) numdevices = info.get("deviceCount") audio_device = 0 for i in range(0, numdevices): if (p.get_device_info_by_host_api_device_index( 0, i).get("maxOutputChannels")) > 0: device_name = p.get_device_info_by_host_api_device_index( 0, i).get("name") # print("Input Device id ", i, " - ", device_name) if device_name.find("pulse") != -1: audio_device = i return audio_device
def check_devices(): """Imprime los puertos de entrada y salida con nombre e índice. Basado en: https://stackoverflow.com/questions/36894315/how-to-select-a-specific-input-device-with-pyaudio """ from pyaudio import PyAudio p = PyAudio() info = p.get_host_api_info_by_index(0) numdevices = info.get('deviceCount') for i in range(0, numdevices): if (p.get_device_info_by_host_api_device_index( 0, i).get('maxInputChannels')) > 0: print("Input Device id ", i, " - ", p.get_device_info_by_host_api_device_index(0, i).get('name')) else: print("Output Device id ", i, " - ", p.get_device_info_by_host_api_device_index(0, i).get('name'))
class AudioDevice(QtCore.QObject): def __init__(self, logger): QtCore.QObject.__init__(self) self.logger = logger self.duo_input = False self.logger.push("Initializing PyAudio") self.pa = PyAudio() # look for devices self.input_devices = self.get_input_devices() self.output_devices = self.get_output_devices() for device in self.input_devices: self.logger.push("Opening the stream") self.stream = self.open_stream(device) self.device = device self.logger.push("Trying to read from input device %d" % device) if self.try_input_stream(self.stream): self.logger.push("Success") break else: self.logger.push("Fail") self.first_channel = 0 nchannels = self.get_current_device_nchannels() if nchannels == 1: self.second_channel = 0 else: self.second_channel = 1 # counter for the number of input buffer overflows self.xruns = 0 # method def get_readable_devices_list(self): devices_list = [] default_device_index = self.get_default_input_device() for device in self.input_devices: dev_info = self.pa.get_device_info_by_index(device) api = self.pa.get_host_api_info_by_index(dev_info ['hostApi'])['name'] if device is default_device_index: extra_info = ' (system default)' else: extra_info = '' nchannels = self.pa.get_device_info_by_index(device)[ 'maxInputChannels'] desc = "%s (%d channels) (%s) %s" % (dev_info['name'], nchannels, api, extra_info) devices_list += [desc] return devices_list # method def get_readable_output_devices_list(self): devices_list = [] default_device_index = self.get_default_output_device() for device in self.output_devices: dev_info = self.pa.get_device_info_by_index(device) api = self.pa.get_host_api_info_by_index(dev_info['hostApi'] )['name'] if device is default_device_index: extra_info = ' (system default)' else: extra_info = '' nchannels = self.pa.get_device_info_by_index(device)[ 'maxOutputChannels'] desc = "%s (%d channels) (%s) %s" % (dev_info['name'], nchannels, api, extra_info) devices_list += [desc] return devices_list # method def get_default_input_device(self): return self.pa.get_default_input_device_info()['index'] # method def get_default_output_device(self): return self.pa.get_default_output_device_info()['index'] # method def get_device_count(self): # FIXME only input devices should be chosen, not all of them ! return self.pa.get_device_count() # method # returns a list of input devices index, starting with the system default def get_input_devices(self): device_count = self.get_device_count() default_input_device = self.get_default_input_device() device_range = range(0, device_count) # start by the default input device device_range.remove(default_input_device) device_range = [default_input_device] + device_range # select only the input devices by looking at the number of input channels input_devices = [] for device in device_range: n_input_channels = self.pa.get_device_info_by_index(device)[ 'maxInputChannels'] if n_input_channels > 0: input_devices += [device] return input_devices # method # returns a list of output devices index, starting with the system default def get_output_devices(self): device_count = self.get_device_count() default_output_device = self.get_default_output_device() device_range = range(0, device_count) # start by the default input device device_range.remove(default_output_device) device_range = [default_output_device] + device_range # select only the output devices by looking at the number of output channels output_devices = [] for device in device_range: n_output_channels = self.pa.get_device_info_by_index(device)[ 'maxOutputChannels'] if n_output_channels > 0: output_devices += [device] return output_devices # method def select_input_device(self, device): # save current stream in case we need to restore it previous_stream = self.stream previous_device = self.device self.stream = self.open_stream(device) self.device = device self.logger.push("Trying to read from input device #%d" % (device)) if self.try_input_stream(self.stream): self.logger.push("Success") previous_stream.close() success = True self.first_channel = 0 nchannels = self.get_current_device_nchannels() if nchannels == 1: self.second_channel = 0 else: self.second_channel = 1 else: self.logger.push("Fail") self.stream.close() self.stream = previous_stream self.device = previous_device success = False return success, self.device # method def select_first_channel(self, index): self.first_channel = index success = True return success, self.first_channel # method def select_second_channel(self, index): self.second_channel = index success = True return success, self.second_channel # method def open_stream(self, device): ''' by default we open the device stream with all the channels # (interleaved in the data buffer)''' maxInputChannels = self.pa.get_device_info_by_index(device)[ 'maxInputChannels'] stream = self.pa.open(format=paInt32, channels=maxInputChannels, rate=SAMPLING_RATE, input=True, frames_per_buffer=FRAMES_PER_BUFFER, input_device_index=device) return stream # method # return the index of the current input device in the input devices list # (not the same as the PortAudio index, since the latter is the index # in the list of *all* devices, not only input ones) def get_readable_current_device(self): i = 0 for device in self.input_devices: if device == self.device: break else: i += 1 return i # method def get_readable_current_channels(self): dev_info = self.pa.get_device_info_by_index(self.device) nchannels = dev_info['maxInputChannels'] if nchannels == 2: channels = ['L', 'R'] else: channels = [] for channel in range(0, dev_info['maxInputChannels']): channels += ["%d" % channel] return channels # method def get_current_first_channel(self): return self.first_channel # method def get_current_second_channel(self): return self.second_channel # method def get_current_device_nchannels(self): return self.pa.get_device_info_by_index(self.device)[ 'maxInputChannels'] # method # return True on success def try_input_stream(self, stream): n_try = 0 while (stream.get_read_available() < FRAMES_PER_BUFFER and n_try < 1000000): n_try += 1 if n_try == 1000000: return False else: lat_ms = 1000 * stream.get_input_latency() self.logger.push("Device claims %d ms latency" % (lat_ms)) return True # try to update the audio buffer # return the number of chunks retrieved, and the time elapsed def update(self, ringbuffer): t = QtCore.QTime() t.start() channel = self.get_current_first_channel() nchannels = self.get_current_device_nchannels() if self.duo_input: channel_2 = self.get_current_second_channel() chunks = 0 available = self.stream.get_read_available() available = int(floor(available / FRAMES_PER_BUFFER)) for _ in range(0, available): try: rawdata = self.stream.read(FRAMES_PER_BUFFER) except IOError as inst: # FIXME specialize this exception handling code # to treat overflow errors particularly self.xruns += 1 print "Caught an IOError on stream read.", inst break intdata_all_channels = fromstring(rawdata, int32) int32info = iinfo(int32) norm_coeff = max(abs(int32info.min), int32info.max) floatdata_all_channels = (intdata_all_channels.astype(float64) / float(norm_coeff)) floatdata1 = floatdata_all_channels[channel::nchannels] if self.duo_input: floatdata2 = floatdata_all_channels[channel_2::nchannels] floatdata = vstack((floatdata1, floatdata2)) else: floatdata = floatdata1 floatdata.shape = (1, FRAMES_PER_BUFFER) # update the circular buffer ringbuffer.push(floatdata) chunks += 1 return (chunks, t.elapsed(), chunks * FRAMES_PER_BUFFER) def set_single_input(self): self.duo_input = False def set_duo_input(self): self.duo_input = True # returns the stream time in seconds def get_stream_time(self): return self.stream.get_time()
from pyaudio import PyAudio p = PyAudio() info = p.get_host_api_info_by_index(0) numdevices = info.get('deviceCount') for i in range(0, numdevices): if (p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0: print("Input Device id ", i, " - ", p.get_device_info_by_host_api_device_index(0, i).get('name'))
# info = a.get_device_info_by_index(i) # if info['maxInputChannels'] > 0: # print i, '------' # print info #DISABLED = features.WINXP and not features.WINE # bool DISABLED = True # 1/19/2015: Always disabled until offline ASR is implemented if not DISABLED: try: from pyaudio import PyAudio # Not sure if this might hang on Windows XP AUDIO = PyAudio() #AUDIO.terminate() HOST_INDEX = 0 # paMME HOST_INFO = AUDIO.get_host_api_info_by_index(HOST_INDEX) HOST_DEVICE_COUNT = HOST_INFO['deviceCount'] INPUTDEVICES = None def inputdevices(): # yield dict global INPUTDEVICES if INPUTDEVICES is None: INPUTDEVICES = [] for i in range(HOST_DEVICE_COUNT): info = AUDIO.get_device_info_by_host_api_device_index( HOST_INDEX, i) if info.get('name') and info.get( 'maxInputChannels'): # > 0 INPUTDEVICES.append(info) return INPUTDEVICES
class AudioDevice(QtCore.QObject): def __init__(self, logger): QtCore.QObject.__init__(self) self.logger = logger self.duo_input = False self.logger.push("Initializing PyAudio") self.pa = PyAudio() # look for devices self.input_devices = self.get_input_devices() self.output_devices = self.get_output_devices() for device in self.input_devices: self.logger.push("Opening the stream") self.stream = self.open_stream(device) self.device = device self.logger.push("Trying to read from input device %d" % device) if self.try_input_stream(self.stream): self.logger.push("Success") break else: self.logger.push("Fail") self.first_channel = 0 nchannels = self.get_current_device_nchannels() if nchannels == 1: self.second_channel = 0 else: self.second_channel = 1 # counter for the number of input buffer overflows self.xruns = 0 # method def get_readable_devices_list(self): devices_list = [] default_device_index = self.get_default_input_device() for device in self.input_devices: dev_info = self.pa.get_device_info_by_index(device) api = self.pa.get_host_api_info_by_index( dev_info['hostApi'])['name'] if device is default_device_index: extra_info = ' (system default)' else: extra_info = '' nchannels = self.pa.get_device_info_by_index( device)['maxInputChannels'] desc = "%s (%d channels) (%s) %s" % (dev_info['name'], nchannels, api, extra_info) devices_list += [desc] return devices_list # method def get_readable_output_devices_list(self): devices_list = [] default_device_index = self.get_default_output_device() for device in self.output_devices: dev_info = self.pa.get_device_info_by_index(device) api = self.pa.get_host_api_info_by_index( dev_info['hostApi'])['name'] if device is default_device_index: extra_info = ' (system default)' else: extra_info = '' nchannels = self.pa.get_device_info_by_index( device)['maxOutputChannels'] desc = "%s (%d channels) (%s) %s" % (dev_info['name'], nchannels, api, extra_info) devices_list += [desc] return devices_list # method def get_default_input_device(self): return self.pa.get_default_input_device_info()['index'] # method def get_default_output_device(self): return self.pa.get_default_output_device_info()['index'] # method def get_device_count(self): # FIXME only input devices should be chosen, not all of them ! return self.pa.get_device_count() # method # returns a list of input devices index, starting with the system default def get_input_devices(self): device_count = self.get_device_count() default_input_device = self.get_default_input_device() device_range = range(0, device_count) # start by the default input device device_range.remove(default_input_device) device_range = [default_input_device] + device_range # select only the input devices by looking at the number of input channels input_devices = [] for device in device_range: n_input_channels = self.pa.get_device_info_by_index( device)['maxInputChannels'] if n_input_channels > 0: input_devices += [device] return input_devices # method # returns a list of output devices index, starting with the system default def get_output_devices(self): device_count = self.get_device_count() default_output_device = self.get_default_output_device() device_range = range(0, device_count) # start by the default input device device_range.remove(default_output_device) device_range = [default_output_device] + device_range # select only the output devices by looking at the number of output channels output_devices = [] for device in device_range: n_output_channels = self.pa.get_device_info_by_index( device)['maxOutputChannels'] if n_output_channels > 0: output_devices += [device] return output_devices # method def select_input_device(self, device): # save current stream in case we need to restore it previous_stream = self.stream previous_device = self.device self.stream = self.open_stream(device) self.device = device self.logger.push("Trying to read from input device #%d" % (device)) if self.try_input_stream(self.stream): self.logger.push("Success") previous_stream.close() success = True self.first_channel = 0 nchannels = self.get_current_device_nchannels() if nchannels == 1: self.second_channel = 0 else: self.second_channel = 1 else: self.logger.push("Fail") self.stream.close() self.stream = previous_stream self.device = previous_device success = False return success, self.device # method def select_first_channel(self, index): self.first_channel = index success = True return success, self.first_channel # method def select_second_channel(self, index): self.second_channel = index success = True return success, self.second_channel # method def open_stream(self, device): ''' by default we open the device stream with all the channels # (interleaved in the data buffer)''' maxInputChannels = self.pa.get_device_info_by_index( device)['maxInputChannels'] stream = self.pa.open(format=paInt32, channels=maxInputChannels, rate=SAMPLING_RATE, input=True, frames_per_buffer=FRAMES_PER_BUFFER, input_device_index=device) return stream # method # return the index of the current input device in the input devices list # (not the same as the PortAudio index, since the latter is the index # in the list of *all* devices, not only input ones) def get_readable_current_device(self): i = 0 for device in self.input_devices: if device == self.device: break else: i += 1 return i # method def get_readable_current_channels(self): dev_info = self.pa.get_device_info_by_index(self.device) nchannels = dev_info['maxInputChannels'] if nchannels == 2: channels = ['L', 'R'] else: channels = [] for channel in range(0, dev_info['maxInputChannels']): channels += ["%d" % channel] return channels # method def get_current_first_channel(self): return self.first_channel # method def get_current_second_channel(self): return self.second_channel # method def get_current_device_nchannels(self): return self.pa.get_device_info_by_index( self.device)['maxInputChannels'] # method # return True on success def try_input_stream(self, stream): n_try = 0 while (stream.get_read_available() < FRAMES_PER_BUFFER and n_try < 1000000): n_try += 1 if n_try == 1000000: return False else: lat_ms = 1000 * stream.get_input_latency() self.logger.push("Device claims %d ms latency" % (lat_ms)) return True # try to update the audio buffer # return the number of chunks retrieved, and the time elapsed def update(self, ringbuffer): t = QtCore.QTime() t.start() channel = self.get_current_first_channel() nchannels = self.get_current_device_nchannels() if self.duo_input: channel_2 = self.get_current_second_channel() chunks = 0 available = self.stream.get_read_available() available = int(floor(available / FRAMES_PER_BUFFER)) for _ in range(0, available): try: rawdata = self.stream.read(FRAMES_PER_BUFFER) except IOError as inst: # FIXME specialize this exception handling code # to treat overflow errors particularly self.xruns += 1 print "Caught an IOError on stream read.", inst break intdata_all_channels = fromstring(rawdata, int32) int32info = iinfo(int32) norm_coeff = max(abs(int32info.min), int32info.max) floatdata_all_channels = (intdata_all_channels.astype(float64) / float(norm_coeff)) floatdata1 = floatdata_all_channels[channel::nchannels] if self.duo_input: floatdata2 = floatdata_all_channels[channel_2::nchannels] floatdata = vstack((floatdata1, floatdata2)) else: floatdata = floatdata1 floatdata.shape = (1, FRAMES_PER_BUFFER) # update the circular buffer ringbuffer.push(floatdata) chunks += 1 return (chunks, t.elapsed(), chunks * FRAMES_PER_BUFFER) def set_single_input(self): self.duo_input = False def set_duo_input(self): self.duo_input = True # returns the stream time in seconds def get_stream_time(self): return self.stream.get_time()
class AudioBackend(QtCore.QObject): underflow = QtCore.pyqtSignal() new_data_available_from_callback = QtCore.pyqtSignal(bytes, int, float, int) new_data_available = QtCore.pyqtSignal(ndarray, float, int) def callback(self, in_data, frame_count, time_info, status): #do the minimum from here to prevent overflows, just pass the data to the main thread input_time = time_info['input_buffer_adc_time'] # some API drivers in PortAudio do not return a valid time, so fallback to the current stream time if input_time == 0.: input_time = time_info['current_time'] if input_time == 0.: input_time = self.stream.get_time() self.new_data_available_from_callback.emit(in_data, frame_count, input_time, status) return (None, 0) def __init__(self, logger): QtCore.QObject.__init__(self) self.logger = logger self.duo_input = False self.logger.push("Initializing PyAudio") self.pa = PyAudio() # look for devices self.input_devices = self.get_input_devices() self.output_devices = self.get_output_devices() self.device = None self.first_channel = None self.second_channel = None # we will try to open all the input devices until one # works, starting by the default input device for device in self.input_devices: self.logger.push("Opening the stream") try: self.stream = self.open_stream(device) self.stream.start_stream() self.device = device self.logger.push("Success") break except: self.logger.push("Fail") if self.device is not None: self.first_channel = 0 nchannels = self.get_current_device_nchannels() if nchannels == 1: self.second_channel = 0 else: self.second_channel = 1 # counter for the number of input buffer overflows self.xruns = 0 self.chunk_number = 0 self.new_data_available_from_callback.connect(self.handle_new_data) def close(self): self.stream.stop_stream() self.stream.close() self.stream = None # method def get_readable_devices_list(self): devices_list = [] default_device_index = self.get_default_input_device() for device in self.input_devices: dev_info = self.pa.get_device_info_by_index(device) api = self.pa.get_host_api_info_by_index(dev_info['hostApi'])['name'] if device is default_device_index: extra_info = ' (system default)' else: extra_info = '' nchannels = self.pa.get_device_info_by_index(device)['maxInputChannels'] desc = "%s (%d channels) (%s) %s" %(dev_info['name'], nchannels, api, extra_info) devices_list += [desc] return devices_list # method def get_readable_output_devices_list(self): devices_list = [] default_device_index = self.get_default_output_device() for device in self.output_devices: dev_info = self.pa.get_device_info_by_index(device) api = self.pa.get_host_api_info_by_index(dev_info['hostApi'])['name'] if device is default_device_index: extra_info = ' (system default)' else: extra_info = '' nchannels = self.pa.get_device_info_by_index(device)['maxOutputChannels'] desc = "%s (%d channels) (%s) %s" %(dev_info['name'], nchannels, api, extra_info) devices_list += [desc] return devices_list # method def get_default_input_device(self): try: index = self.pa.get_default_input_device_info()['index'] except IOError: index = None return index # method def get_default_output_device(self): try: index = self.pa.get_default_output_device_info()['index'] except IOError: index = None return # method def get_device_count(self): # FIXME only input devices should be chosen, not all of them ! return self.pa.get_device_count() # method # returns a list of input devices index, starting with the system default def get_input_devices(self): device_count = self.get_device_count() device_range = list(range(0, device_count)) default_input_device = self.get_default_input_device() if default_input_device is not None: # start by the default input device device_range.remove(default_input_device) device_range = [default_input_device] + device_range # select only the input devices by looking at the number of input channels input_devices = [] for device in device_range: n_input_channels = self.pa.get_device_info_by_index(device)['maxInputChannels'] if n_input_channels > 0: input_devices += [device] return input_devices # method # returns a list of output devices index, starting with the system default def get_output_devices(self): device_count = self.get_device_count() device_range = list(range(0, device_count)) default_output_device = self.get_default_output_device() if default_output_device is not None: # start by the default input device device_range.remove(default_output_device) device_range = [default_output_device] + device_range # select only the output devices by looking at the number of output channels output_devices = [] for device in device_range: n_output_channels = self.pa.get_device_info_by_index(device)['maxOutputChannels'] if n_output_channels > 0: output_devices += [device] return output_devices # method. # The index parameter is the index in the self.input_devices list of devices ! # The return parameter is also an index in the same list. def select_input_device(self, index): device = self.input_devices[index] # save current stream in case we need to restore it previous_stream = self.stream previous_device = self.device self.logger.push("Trying to open input device #%d" % (index)) try: self.stream = self.open_stream(device) self.device = device self.stream.start_stream() success = True except: self.logger.push("Fail") success = False if self.stream is not None: self.stream.close() # restore previous stream self.stream = previous_stream self.device = previous_device if success: self.logger.push("Success") previous_stream.close() self.first_channel = 0 nchannels = self.get_current_device_nchannels() if nchannels == 1: self.second_channel = 0 else: self.second_channel = 1 return success, self.input_devices.index(self.device) # method def select_first_channel(self, index): self.first_channel = index success = True return success, self.first_channel # method def select_second_channel(self, index): self.second_channel = index success = True return success, self.second_channel # method def open_stream(self, device): # by default we open the device stream with all the channels # (interleaved in the data buffer) maxInputChannels = self.pa.get_device_info_by_index(device)['maxInputChannels'] stream = self.pa.open(format=paInt16, channels=maxInputChannels, rate=SAMPLING_RATE, input=True, input_device_index=device, stream_callback=self.callback, frames_per_buffer = FRAMES_PER_BUFFER) lat_ms = 1000*stream.get_input_latency() self.logger.push("Device claims %d ms latency" %(lat_ms)) return stream # method # return the index of the current input device in the input devices list # (not the same as the PortAudio index, since the latter is the index # in the list of *all* devices, not only input ones) def get_readable_current_device(self): return self.input_devices.index(self.device) # method def get_readable_current_channels(self): dev_info = self.pa.get_device_info_by_index(self.device) nchannels = dev_info['maxInputChannels'] if nchannels == 2: channels = ['L', 'R'] else: channels = [] for channel in range(0, dev_info['maxInputChannels']): channels += ["%d" %channel] return channels # method def get_current_first_channel(self): return self.first_channel # method def get_current_second_channel(self): return self.second_channel # method def get_current_device_nchannels(self): return self.pa.get_device_info_by_index(self.device)['maxInputChannels'] def handle_new_data(self, in_data, frame_count, input_time, status): if (status & paInputOverflow): print("Stream overflow!") self.xruns += 1 self.underflow.emit() intdata_all_channels = fromstring(in_data, int16) int16info = iinfo(int16) norm_coeff = max(abs(int16info.min), int16info.max) floatdata_all_channels = intdata_all_channels.astype(float64)/float(norm_coeff) channel = self.get_current_first_channel() nchannels = self.get_current_device_nchannels() if self.duo_input: channel_2 = self.get_current_second_channel() floatdata1 = floatdata_all_channels[channel::nchannels] if self.duo_input: floatdata2 = floatdata_all_channels[channel_2::nchannels] floatdata = vstack((floatdata1, floatdata2)) else: floatdata = floatdata1 floatdata.shape = (1, floatdata.size) self.new_data_available.emit(floatdata, input_time, status) self.chunk_number += 1 def set_single_input(self): self.duo_input = False def set_duo_input(self): self.duo_input = True # returns the stream time in seconds def get_stream_time(self): return self.stream.get_time() def pause(self): self.stream.stop_stream() def restart(self): self.stream.start_stream()
def get_audio_info(): audio = PyAudio() info = audio.get_host_api_info_by_index(0) print(info, '\n') for i in range(audio.get_device_count()): print(audio.get_device_info_by_index(i))
class PreciseRunner(object): """ Wrapper to use Precise. Example: >>> def on_act(): ... print('Activation!') ... >>> p = PreciseRunner(PreciseEngine('./precise-engine'), on_activation=on_act) >>> p.start() >>> from time import sleep; sleep(10) >>> p.stop() Args: engine (Engine): Object containing info on the binary engine trigger_level (int): Number of chunk activations needed to trigger on_activation Higher values add latency but reduce false positives sensitivity (float): From 0.0 to 1.0, relates to the network output level required to consider a chunk "active" stream (BinaryIO): Binary audio stream to read 16000 Hz 1 channel int16 audio from. If not given, the microphone is used on_prediction (Callable): callback for every new prediction on_activation (Callable): callback for when the wake word is heard """ def __init__(self, engine, trigger_level=3, sensitivity=0.5, stream=None, on_prediction=lambda x: None, on_activation=lambda: None): self.engine = engine self.trigger_level = trigger_level self.sensitivity = sensitivity self.stream = stream self.on_prediction = on_prediction self.on_activation = on_activation self.chunk_size = engine.chunk_size self.read_divisor = 1 print('sensitivity: ', self.sensitivity) self.pa = None self.thread = None self.running = False self.is_paused = False self.detector = TriggerDetector(self.chunk_size, sensitivity, trigger_level) atexit.register(self.stop) def _calc_read_divisor(self): """ pyaudio.Stream.read takes samples as n, not bytes so read(n) should be read(n // sample_depth """ try: import pyaudio if isinstance(self.stream, pyaudio.Stream): return 2 except ImportError: pass return 1 def start(self): """Start listening from stream""" if self.stream is None: from pyaudio import PyAudio, paInt16 self.pa = PyAudio() print("================================================") info = self.pa.get_host_api_info_by_index(0) numdevices = info.get('deviceCount') for i in range(0, numdevices): if (self.pa.get_device_info_by_host_api_device_index( 0, i).get('maxInputChannels')) > 0: print( "Input Device id ", i, " - ", self.pa.get_device_info_by_host_api_device_index( 0, i).get('name'), self.pa.get_device_info_by_index(i).get( 'defaultSampleRate')) print("================================================") self.stream = self.pa.open(rate=16000, channels=1, format=paInt16, input=True, frames_per_buffer=self.chunk_size, input_device_index=2) self.read_divisor = self._calc_read_divisor() self.engine.start() self.running = True self.is_paused = False self.thread = Thread(target=self._handle_predictions) self.thread.daemon = True self.thread.start() def stop(self): """Stop listening and close stream""" if self.thread: self.running = False if isinstance(self.stream, ReadWriteStream): self.stream.write(b'\0' * self.chunk_size) self.thread.join() self.thread = None self.engine.stop() if self.pa: self.pa.terminate() self.stream.stop_stream() self.stream = self.pa = None def pause(self): self.is_paused = True def play(self): self.is_paused = False def _handle_predictions(self): """Continuously check Precise process output""" while self.running: # print('chunk_size: ', self.chunk_size, ' read_divisor: ', self.read_divisor) chunk = self.stream.read(self.chunk_size // self.read_divisor, exception_on_overflow=False) if self.is_paused: continue prob = self.engine.get_prediction(chunk) self.on_prediction(prob) if self.detector.update(prob): self.on_activation()