Beispiel #1
6
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()
Beispiel #2
0
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
Beispiel #3
0
 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]
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #7
0
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()
Beispiel #9
0
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'))
Beispiel #10
0
#  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()
Beispiel #12
0
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()
Beispiel #13
0
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))
Beispiel #14
0
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()