0

In my code, I am using Tkinter, PyAudio and the SpeechRecognition library. The speech recognition library uses PyAudio for interfacing with the PC's microphones (im on Win 10), and allows you to select the microphone input to use through an index value. This index refers to a list of microphones the system has available. This list contains multiple dictionaries, each dictionary containing information on each audio device connected to the system. Each of these dictionaries contains a field called "index", which stores an integer value representing where that dictionary lays in the overall list. This list is fetched by the SpeechRecognition library through the PyAudio library.

I can use the same method the speech recognition library does with the PyAudio library to generate my own list of microphones using

def getMicrophones(self):
        import pyaudio
        p = pyaudio.PyAudio()
        temp = []
        for i in range(p.get_device_count()):
            if p.get_device_info_by_index(i).get("maxInputChannels") > 0: #if maxInputChannels > 0, then device is a microphone
                temp.append(p.get_device_info_by_index(i).get("name"))
        return temp

This function fetches each dictionary separately from this list, and, if the maxInputChannels field has a value > 0, the value of the name field is stored in a separate list. I can then display this list in a tkinter OptionMenu with the below code:

self.micOptions = self.getMicrophones()
self.selectedMicrophone = tk.StringVar() #Tkinter variable needs to be made so that the dropdown can hold a value
self.selectedMicrophone.set(self.micOptions[0]) #Set default OptionMenu value
self.micDropDown = tk.OptionMenu(self, self.selectedMicrophone, *self.micOptions)

Here's the problem: These microphone names are taken from a dictionary that is returned by p.get_device_info_by_index(i) using .get("name"), as seen above. The names are then placed into a list to be used, as explained.

Overall, my question would be, how can i use the selected string (name) of the microphone from the OptionMenu in order to get back to the original dictionary it was taken from, so that i can then access its index field to use later on. Any help is much appreciated, as i feel like there would definitely be some best practices for this.

carbaretta
  • 303
  • 1
  • 6
  • Does this answer your question? [Tkinter bringing chosen option from optionMenu into a variable for further use](https://stackoverflow.com/a/52869393/7414759) – stovfl Jun 18 '20 at 15:07
  • @stovfl not quite, as this is looking at how to get the index of an item in a list being displayed in an option menu. I'm looking for how to get the value of a field that is called "index" from a dictionary. I suppose that wasnt' clear, thanks anyway – carbaretta Jun 18 '20 at 15:10
  • ***get the value of a field that is called "index" from a dictionary.***: [Edit] your example and show the usage of `dict` and `index`. – stovfl Jun 18 '20 at 15:13
  • You can return a dictionary with the name as key and the index as value. – acw1668 Jun 18 '20 at 16:02

1 Answers1

1

Extends a tk.OptionMenu to return a selected audio device.

return self._devices.get(self.selected.get(), None)

Core Point:

Instead of saving the index of a device, which is not in sync with get_device_count(), store the whole device into a dict with key == device['name'].

                device = p.get_device_info_by_index(i)
                devices[device.get("name")] = device

Imports, Simulated PyAudio:

import tkinter as tk


# Simulating PyAudio
class PyAudio:
    def __init__(self):
        self._devices = [{'name': 'Device 1', 'maxInputChannels': 0},
                         {'name': 'Device 2', 'maxInputChannels': 1},
                         {'name': 'Device 3', 'maxInputChannels': 0},
                         {'name': 'Device 4', 'maxInputChannels': 2},
                         {'name': 'Device 5', 'maxInputChannels': 3},
                         ]

    def get_device_count(self):
        return len(self._devices)

    def get_device_info_by_index(self, i):
        return self._devices[i]

Extends tk.OptionMenu:

class Microphones(tk.OptionMenu):
    def __init__(self, parent, **kwargs):
        self.selected = tk.StringVar()
        self._devices = kwargs.pop('devices', ())
        options = tuple(self._devices.keys())

        super().__init__(parent, self.selected, *options, **kwargs)
        self.selected.set(options[0])

    @property
    def device(self):
        return self._devices.get(self.selected.get(), None)

Usage:

class App(tk.Tk):
    def __init__(self):
        super().__init__()

        self.micDropDown = Microphones(self, devices=self.getMicrophones(), command=self.on_selected)
        self.micDropDown.pack()

    def on_selected(self, event):
        print(f'on_selected({self.micDropDown.selected.get()}, '
              f'device={self.micDropDown.device})')

    def getMicrophones(self):
        """
        import pyaudio
        p = pyaudio.PyAudio()
        """
        p = PyAudio()
        devices = {}
        for i in range(p.get_device_count()):
            # if maxInputChannels > 0, then device is a microphone
            if p.get_device_info_by_index(i).get("maxInputChannels") > 0:
                device = p.get_device_info_by_index(i)
                devices[device.get("name")] = device
        return devices


if __name__ == '__main__':
    App().mainloop()
stovfl
  • 14,998
  • 7
  • 24
  • 51