コード例 #1
0
ファイル: sidetone.py プロジェクト: tallforasmurf/Sidetone
class SideToneWidget(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        # Save link to main window
        self.main_window = parent
        # Get the status bar
        self.status_bar = parent.statusBar()
        # Slot that will point to a QAudioInput some day
        self.input_device = None
        # Slot that will point to a QAudioOutput in time
        self.otput_device = None
        # set up layout, creating:
        # self.input_info_list, list of QAudioInfo for inputs
        # self.cb_inputs, combox of input names in same order
        # self.otput_info_list, list of QAudioInfo for outputs
        # self.cb_otputs, combox of output names in same order
        # self.volume, volume slider
        # self.mute, mute checkbox
        self._uic()
        # Connect up signals to slots.
        # Changes in the comboxes go to in_device and ot_device
        self.cb_inputs.currentIndexChanged.connect(self.in_dev_change)
        self.cb_otputs.currentIndexChanged.connect(self.ot_dev_change)
        # Mute button goes to mute_change
        self.mute.stateChanged.connect(self.mute_change)
        # Change in volume goes to volume_change
        self.volume.valueChanged.connect(self.volume_change)
        # Start with the mute switch on. This triggers the above two signals.
        self.mute.setChecked(True)

    # Slot for any change in volume (or mute). If we have an output device
    # then convert level to a real and pass to the output device. If the new
    # level is 0, tell the device to stop; if nonzero, tell it to start.
    # Note we don't do anything about the input device level, it is always 1.0
    def volume_change(self, new_level):
        if self.otput_device:  # we have an output device
            self.otput_device.setVolume(self.volume.value() / 100)
            if new_level == 0:  # looks like a mute
                # tell the output device to stop just in case it
                # doesn't know about volume control.
                self.otput_device.stop()
            else:  # non-zero level, if the output is stopped, start it
                if self.otput_device.state() == QAudio.StoppedState:
                    self.otput_device.start()

    # Slot for mute switch. Note that any change to the volume slider
    # generates a signal to the volume_change slot.
    def mute_change(self, onoff):
        if onoff:
            # Mute has been clicked ON. Remember the current volume.
            # Turn the volume to zero.
            self.volume_level = self.volume.value()
            self.volume.setValue(0)
        else:
            # Mute has been clicked OFF. If we do not yet have input and
            # output devices, get them. Then reset the old volume level.
            if self.otput_device is None:
                # We are starting up and have no devices. Fake a call to
                # the checkbox-change entries thus creating devices.
                self.in_dev_change(self.cb_inputs.currentIndex())
                self.ot_dev_change(self.cb_inputs.currentIndex())
            self.volume.setValue(self.volume_level)

    # Slots for changes in the selection of the input- and output-device
    # combo boxes. On startup we have neither an input nor an output device.
    # We do not know which combox the user will fiddle with first. So either
    # has to assume that the other device may not yet exist.
    #
    # On a change of input choice: if we have an input device, get rid of it.
    # Create a new input device. Set its level to 1.0. If we
    # have an output, connect the two.

    def in_dev_change(self, new_index):
        if self.input_device:
            if self.otput_device:
                self.otput_device.stop()
            self.input_device.stop()
            self.input_device = None  # goodby object
        # Get the QAudioDeviceInfo corresponding to this index of the combox.
        audio_info = self.input_info_list[new_index]
        # Create a new QAudioInput based on that.
        preferred_format = audio_info.preferredFormat()
        self.input_device = QAudioInput(audio_info, preferred_format)
        self.input_device.setVolume(1.0)
        self.input_device.setBufferSize(384)
        # If we have an output device, redirect it to this input. This is
        # done by asking the input device for its QIODevice, and passing that
        # to the output device's start() method.
        if self.otput_device:
            self.input_device.start(self.otput_device.start())
            # self.otput_device.start( self.input_device.start() )

    # On a change in the selection of output choice: If we have an output
    # device, get rid of it. Create a new output device. If we have an input
    # device, connect the two. Set the output level from the volume slider.

    def ot_dev_change(self, new_index):
        if self.otput_device:
            if self.input_device:
                self.input_device.stop()
            self.otput_device.stop()
            self.otput_device = None
        audio_info = self.otput_info_list[new_index]
        preferred_format = audio_info.preferredFormat()
        self.otput_device = QAudioOutput(audio_info, preferred_format)
        self.otput_device.setVolume(self.volume.value() / 100)
        # self.otput_device.setBufferSize( 384 )
        if self.input_device:
            self.input_device.start(self.otput_device.start())
            # self.otput_device.start( self.input_device.start() )

    def _uic(self):
        """
    set up our layout which consists of:

                 Big Honkin' Label
        [input combobox]    [output combobox]
               [volume slider]  [x] Mute

    hooking put the signals to useful slots is the job
    of __init__. Here just make the layout.
        """
        self.setMinimumWidth(400)
        # Create the big honkin' label and logo
        icon_pixmap = QPixmap(":/icon.png").scaledToWidth(64)
        icon_label = QLabel()
        icon_label.setPixmap(icon_pixmap)
        text_label = QLabel("Sidetone!")
        hb_label = QHBoxLayout()
        hb_label.addStretch(1)
        hb_label.addWidget(icon_label, 0)
        hb_label.addWidget(text_label, 0)
        hb_label.addStretch(1)

        # Create a list of QAudioInfo objects for inputs
        self.input_info_list = QAudioDeviceInfo.availableDevices(QAudio.AudioInput)
        if 0 == len(self.input_info_list):
            self.input_info_list = [QAudioDeviceInfo.defaultInputDevice()]
        # Create a combo box and populate it with names of inputs
        self.cb_inputs = QComboBox()
        self.cb_inputs.addItems([audio_info.deviceName() for audio_info in self.input_info_list])
        # Create a list of QAudioInfo objects for outputs
        self.otput_info_list = QAudioDeviceInfo.availableDevices(QAudio.AudioOutput)
        if 0 == len(self.otput_info_list):
            self.otput_info_list = [QAudioDeviceInfo.defaultOutputDevice()]
        self.status_bar.showMessage(
            "{} inputs {} otputs".format(len(self.input_info_list), len(self.otput_info_list)), 2000
        )
        # Create a combo box and populate it with names of outputs
        self.cb_otputs = QComboBox()
        self.cb_otputs.addItems([audio_info.deviceName() for audio_info in self.otput_info_list])
        # Lay those two out aligned to the outside
        hb_combos = QHBoxLayout()
        hb_combos.addWidget(self.cb_inputs, 1)
        hb_combos.addStretch(0)
        hb_combos.addWidget(self.cb_otputs, 1)
        # Create a volume slider from 0 to 100.
        self.volume = QSlider(Qt.Horizontal, self)
        self.volume.setMinimum(0)
        self.volume.setMaximum(100)
        self.volume.setTickInterval(10)
        self.volume.setTickPosition(QSlider.TicksBothSides)
        # Create a checkbox "Mute"
        self.mute = QCheckBox("Mute")
        # Put those together in a row squeezed in the center
        hb_volume = QHBoxLayout()
        hb_volume.addStretch(1)
        hb_volume.addWidget(self.volume, 1)
        hb_volume.addWidget(self.mute, 0)
        hb_volume.addStretch(1)
        # Stack all those up as this widget's layout
        vlayout = QVBoxLayout()
        vlayout.addLayout(hb_label)
        vlayout.addLayout(hb_combos)
        vlayout.addLayout(hb_volume)
        self.setLayout(vlayout)
コード例 #2
0
class Visualizer(QMainWindow):
    
    def __init__(self):
        
        super().__init__()
        self.initUI()
        self._buflen = 1440 # 4096


    def initUI(self):
        
        # main window/layout
        window = QWidget()
        layout = QVBoxLayout()
        
        # layout for audio device and sample rate selection
        deviceLayout = QHBoxLayout()
        
        # make audio device selection box and list of available devices
        self.deviceBox = QComboBox()
        defaultDeviceInfo = QAudioDeviceInfo.defaultInputDevice()
        self.availableDevices = [defaultDeviceInfo]
        self.availableDevices += QAudioDeviceInfo.availableDevices(
                                                             QAudio.AudioInput)
        for device in self.availableDevices:
            self.deviceBox.addItem(device.deviceName())
            
        # make sample rate label and combobox
        sRateLabel = QLabel("Sample rate:")
        sRateLabel.setAlignment(Qt.AlignRight)
        
        # user can choose between 44.1 and 48kHz (valid DetectorBank rates)
        self.sRateBox = QComboBox()
        self.sRateBox.addItem("44100")
        self.sRateBox.addItem("48000")
        self.sRateBox.setCurrentIndex(1)
        
        # add device and sr widgets to device layout
        deviceLayout.addWidget(self.deviceBox)
        deviceLayout.addWidget(sRateLabel)
        deviceLayout.addWidget(self.sRateBox)
        
        # add device layout to main layout
        layout.addLayout(deviceLayout)
        
        # DetectorBank parameters layout
        # two rows of three parameters
        # each param needs label and edit, 
        # and a 'Start' button will be added at the bottom
        # so grid should be 3x6
        detBankParamLayout = QGridLayout()
        
        # label and lineedit for each
        bandwidthLabel = QLabel("Bandwidth (cents):")
        dampingLabel = QLabel("Damping:")
        gainLabel = QLabel("Gain:")
        edoLabel = QLabel("EDO:")
        lwrLabel = QLabel("Lower note:")
        uprLabel = QLabel("Upper note:")
        
        self.bandwidthEdit = QLineEdit("0")
        self.dampingEdit = QLineEdit("0.0001")
        self.gainEdit = QLineEdit("25")
        self.edoEdit = QLineEdit("12")
        self.lwrEdit = QLineEdit("A1")
        self.uprEdit = QLineEdit("A7")
        
        # store all in lists
        detBankParamLabels = [bandwidthLabel, dampingLabel, gainLabel, 
                              edoLabel, lwrLabel, uprLabel]
        
        detBankParamEdits = [self.bandwidthEdit, self.dampingEdit, 
                             self.gainEdit, self.edoEdit, self.lwrEdit, 
                             self.uprEdit]
         
        # fill first two rows of grid with labels and edits
        row = 0

        for row in range(2):
            widgetNum = 0
        
            for i in range((row*3), (row*3)+3):
                detBankParamLayout.addWidget(detBankParamLabels[i], row, 
                                             widgetNum)
                widgetNum += 1
                detBankParamLayout.addWidget(detBankParamEdits[i], row, 
                                             widgetNum)
                widgetNum += 1
            
        # align labels to the right (next to the edit)
        for i in range(len(detBankParamLabels)):
            detBankParamLabels[i].setAlignment(Qt.AlignRight)
                
        # button to make DetectorBank and start visualisation
        row += 1
        startButton = QPushButton("&Start!")
        detBankParamLayout.addWidget(startButton, row, 5)
        startButton.clicked.connect(self.start)
        
        # add grid of detbank params (and start button) to main layout
        layout.addLayout(detBankParamLayout)
        
        window.setLayout(layout)
        self.setCentralWidget(window)
        self.show()
        
        
    def initializeAudio(self, deviceInfo):
        """ Make a QAudioInput from the given device """
                
        # make buffers of 40ms of samples
        self.refRate = 0.04
        
        # mono, 32-bit float audio
        fmt = QAudioFormat()
        fmt.setSampleRate(self.getSampleRate())
        fmt.setChannelCount(1)
        fmt.setSampleSize(32)
        fmt.setSampleType(QAudioFormat.Float)
        fmt.setByteOrder(QAudioFormat.LittleEndian)
        fmt.setCodec("audio/pcm")
        
        if not deviceInfo.isFormatSupported(fmt):
            fmt = deviceInfo.nearestFormat(fmt)
            
        self.audioInput = QAudioInput(deviceInfo, fmt)
        self.audioInput.setBufferSize(4*self.buflen) # set size in bytes

        
    def startAudio(self):
        self.audioDevice = self.audioInput.start()
        self.audioDevice.readyRead.connect(self.updatePlot)
        
        
    def start(self):
        """ Initialise audio, make DetectorBank, open PlotData window and 
            start audio 
        """
        
        print('Initializing audio...')
        deviceIdx = self.deviceBox.currentIndex()
        device = self.availableDevices[deviceIdx]
        self.initializeAudio(device)
        
        print('Making DetectorBank...')
        pitchOffset = self.makeDetectorBank()
        
        print('Making PlotData object...')
        self.pd = PlotData(self.db.getChans(), pitchOffset)
#        self.pd.show()
        
        print('Starting audio...')
        self.startAudio()
        
    
    def updatePlot(self):
        
        # get data as float32
        # 4*buflen is number of bytes
        data = self.audioDevice.read(4*self.buflen)
        data = np.frombuffer(data, dtype=np.int16)
        data = np.array(data/2**15, dtype=np.dtype('float32'))
        
        # set DetectorBank input
        self.db.setInputBuffer(data)
          
        # fill z with detector output
        self.db.getZ(self.z)
        
#        self.db.absZ(self.r, self.z)
        
        self.pd.update(self.z)
        
#        self.close()
        
        
    def makeDetectorBank(self):
        """ Make DetectorBank from given parameters """
        
        sr = self.getSampleRate()
        bandwidth_cents = float(self.bandwidthEdit.text())
        dmp = float(self.dampingEdit.text())
        gain = float(self.gainEdit.text())
        edo = float(self.edoEdit.text())
        lwr = self.lwrEdit.text()
        upr = self.uprEdit.text()
        
        lwr, pitchOffset = getNoteNum(lwr, edo)
        upr, _ = getNoteNum(upr, edo)
        upr += 1 # include upr note in DetectorBank
        
        # make and fill frequency and bandwidth arrays
        freq = np.zeros(int(upr-lwr))
        bw = np.zeros(len(freq))
        for i in range(len(freq)):
            k = lwr+i
            freq[i] = 440*2**(k/edo)
            # if non-minimum bandwidth detectors requested, find B in Hz
            if bandwidth_cents != 0:
                bw[i] = centsToHz(freq[i], bandwidth_cents, edo)
                
        # combine into stacked array
        det_char = np.stack((freq,bw), axis=1)
        
        # (almost) empty input buffer
        buffer = np.zeros(1, dtype=np.float32)
        
        # DetectorBank features
        method = DetectorBank.runge_kutta
        f_norm = DetectorBank.freq_unnormalized
        a_norm = DetectorBank.amp_unnormalized
        
        self.db = DetectorBank(sr, buffer, 4, det_char, method|f_norm|a_norm, 
                                dmp, gain)
        
        # create empty output array
        self.z = np.zeros((int(self.db.getChans()),self.buflen), 
                          dtype=np.complex128)
        
        self.r = np.zeros(self.z.shape)
        
        print("Made DetectorBank with {} channels, with a sample rate of {}Hz"
              .format(self.db.getChans(), self.db.getSR()))
        
        return pitchOffset
        
    ## get and/or set various values
    def getSampleRate(self, returnType=int):
        return returnType(self.sRateBox.currentText())
    
    @property
    def refreshRate(self):
        return self._refRate
    
    @refreshRate.setter
    def refreshRate(self, value):
        self._refRate = value
        self.buflen = self._refRate * self.getSampleRate()
    
    @property
    def buflen(self):
        return self._buflen
        
    @buflen.setter
    def buflen(self, value):
        self._buflen = int(value)
コード例 #3
0
ファイル: sidetone.py プロジェクト: duckpuppy/Sidetone
class SideToneWidget(QWidget):
    def __init__(self, parent, the_settings):
        super().__init__(parent)
        # Save link to main window
        self.main_window = parent
        # Save link to settings object
        self.settings = the_settings
        # Get the status bar
        self.status_bar = parent.statusBar()
        # just the time
        self.time = QTime()
        self.time.start()
        # Slot that will point to a QAudioInput some day
        self.input_device = None
        # Slot that will point to a QAudioOutput in time
        self.otput_device = None
        # set up layout, creating:
        #   self.input_info_list, list of QAudioInfo for inputs
        #   self.cb_inputs, combox of input names in same order
        #   self.otput_info_list, list of QAudioInfo for outputs
        #   self.cb_otputs, combox of output names in same order
        #   self.volume, volume slider
        #   self.mute, mute checkbox
        self._uic()
        # Connect up signals to slots. Up to this point, the changes that
        # _uic() made in e.g. the volume or mute, or the combobox selections,
        # raised signals that were not connected. Now connect the signals
        # so that user-changes go to our slots for handling.
        # Mute button goes to mute_change
        self.mute.stateChanged.connect(self.mute_change)
        # Change in volume goes to volume_change
        self.volume.valueChanged.connect(self.volume_change)
        # Changes in the combox selections go to in_device and ot_device
        self.cb_inputs.currentIndexChanged.connect(self.in_dev_change)
        self.cb_otputs.currentIndexChanged.connect(self.ot_dev_change)
        # Now pretend the user has made a selection of the in and out devices.
        # That should result in activating everythings.
        self.in_dev_change(self.cb_inputs.currentIndex())
        self.ot_dev_change(self.cb_otputs.currentIndex())

    # Method to disconnect the input and output devices, if they exist.
    # This is called prior to any change in device selection.
    # Note that the QWidget class has an existing method disconnect(),
    # and we do not want to override that, so that name is not used.

    def disconnect_devices(self):

        # If an output device exists, make it stop. That prevents it
        # trying to pull any data from the input device if any.
        if self.otput_device is not None:
            self.otput_device.stop()
        # If an input device exists, make it stop also. That means it
        # loses track of the output device it was formerly connected to.
        if self.input_device is not None:
            self.input_device.stop()

    # Method to connect the input and output devices, if both exist. This is
    # called after making any change in device selection.

    def reconnect_devices(self):

        if (self.input_device is not None) \
           and (self.otput_device is not None ) :

            # Connect the devices by asking the OUTput device for its
            # QIODevice, and passing that to the INput device's start()
            # method. This could equally well be done the other way,
            # by passing the input dev's IODevice to the output device.

            self.input_device.start(self.otput_device.start())
            #self.otput_device.start( self.input_device.start() )

            # In case the output device was just created, set its volume.
            self.set_volume()

    # Method to set the volume on the output device. (The input device volume
    # is always 1.0.) This is called on any change of the volume slider or
    # of the Mute button or of the output device choice.

    def set_volume(self):
        if self.mute.isChecked():
            # Mute is ON, set volume to 0 regardless of volume slider
            volume = 0.0
        else:
            # Mute is OFF, set volume to float version of volume slider
            volume = self.volume.value() / 100
        if self.otput_device:
            # an output device exists (almost always true), set it
            self.otput_device.setVolume(volume)

    # Slot entered upon any change in the volume slider widget.
    def volume_change(self, new_level):
        if self.mute.isChecked():
            # The Mute button is ON; assume the user wants it OFF, else why
            # move the slider? Note this causes a call to set_volume().
            self.mute.setChecked(False)
        else:
            # The Mute button is OFF, just change the volume.
            self.set_volume()

    # Slot entered upon toggling of the mute switch, by the user or by the
    # code calling mute.setChecked(). Make sure the volume is set appropriately.
    def mute_change(self, onoff):
        self.set_volume()

    # Slots for selection of the input and output devices. On startup we have
    # neither an input nor an output device. We do not know which combox the
    # user will fiddle with first.

    # Slot entered upon any change in the selection of the input device
    # combo box. The argument is the new index of the list of values.

    def in_dev_change(self, new_index):

        # Disconnect and stop the devices if they are connected.
        self.disconnect_devices()

        self.input_device = None  # device object goes out of scope

        # Get the QAudioDeviceInfo corresponding to this index of the combox.
        audio_info = self.input_info_list[new_index]

        # Create a new QAudioInput based on that.
        preferred_format = audio_info.preferredFormat()
        self.input_device = QAudioInput(audio_info, preferred_format)

        # the input device volume is always 1.0, wide open.
        self.input_device.setVolume(1.0)

        # The choice of buffer size has a major impact on the lag. It needs
        # to be small or there is severe echo; but if it is too small, there
        # is a sputtering or "motor-boating" effect.
        self.input_device.setBufferSize(384)

        # hook up possible debug status display
        self.input_device.stateChanged.connect(self.in_dev_state_change)

        # reconnect the devices if possible.

        self.reconnect_devices()

    # Slot entered upon any change in the selection of output. The argument
    # is the index to the list of output devices in the combobox.

    def ot_dev_change(self, new_index):
        print('index', new_index)
        # Disconnect and stop the devices if they are connected.
        self.disconnect_devices()

        self.otput_device = None  # device object goes out of scope

        # Get the QAudioDeviceInfo corresponding to this index of the combox.
        audio_info = self.otput_info_list[new_index]

        # Create a new QAudioOutput based on that.
        preferred_format = audio_info.preferredFormat()
        self.otput_device = QAudioOutput(audio_info, preferred_format)
        self.otput_device.setVolume(0)  # reconnect will set correct volume

        # hook up possible debug status display
        self.otput_device.stateChanged.connect(self.ot_dev_state_change)

        # reconnect the devices if possible. Which also sets the volume.

        self.reconnect_devices()

    # Show some text in the main-window status bar for 1 second, more or less.
    def show_status(self, text, duration=1000):
        self.status_bar.showMessage(text, duration)

    # Slots called on any "state" change of an audio device. Optionally
    # show the state in the main window status bar.
    def in_dev_state_change(self, new_state):
        #self.show_status(
        #'{} in dev state {}'.format(self.time.elapsed(),int(new_state))
        #)
        pass

    def ot_dev_state_change(self, new_state):
        #self.show_status(
        #'{} ot dev state {}'.format(self.time.elapsed(),int(new_state))
        #)
        pass

    # Close events are only received by a top-level widget. When our top-level
    # widget gets one, indicating the app is done, it calls this method.

    def closeEvent(self, event):
        # if we have devices, make them stop.
        self.disconnect_devices()

        # if the devices exist, reset them and then trash them.
        if self.otput_device is not None:
            self.otput_device.reset()
            self.otput_device = None
        if self.input_device is not None:
            self.input_device.reset()
            self.input_device = None

        # Save the current selection of the input and output combo boxes,
        # in the settings file.
        in_dev_name = self.cb_inputs.currentText()
        self.settings.setValue('in_dev_name', in_dev_name)
        ot_dev_name = self.cb_otputs.currentText()
        self.settings.setValue('ot_dev_name', ot_dev_name)

        # Save the volume setting and mute status in the settings.
        self.settings.setValue('volume', self.volume.value())
        self.settings.setValue('mute_status', int(self.mute.isChecked()))

    def _uic(self):
        '''
    set up our layout which consists of:

                 Big Honkin' Label
        [input combobox]    [output combobox]
               [volume slider]  [x] Mute

    Hooking the signals to useful slots is the job
    of __init__. Here just make the layout.
        '''
        self.setMinimumWidth(400)
        # Create the big honkin' label and logo
        icon_pixmap = QPixmap(':/icon.png').scaledToWidth(64)
        icon_label = QLabel()
        icon_label.setPixmap(icon_pixmap)
        text_label = QLabel("Sidetone!")
        hb_label = QHBoxLayout()
        hb_label.addStretch(1)
        hb_label.addWidget(icon_label, 0)
        hb_label.addWidget(text_label, 0)
        hb_label.addStretch(1)

        # Create a list of QAudioInfo objects for inputs
        self.input_info_list = QAudioDeviceInfo.availableDevices(
            QAudio.AudioInput)
        if 0 == len(self.input_info_list):
            self.input_info_list = [QAudioDeviceInfo.defaultInputDevice()]
        # Make a list of the name-strings for those items.
        in_dev_names = [
            audio_info.deviceName() for audio_info in self.input_info_list
        ]
        # Create a combo box and populate it with those names
        self.cb_inputs = QComboBox()
        self.cb_inputs.addItems(in_dev_names)
        # If the in_dev_name from the previous run is in the current list,
        # make it current, otherwise pick the first item.
        in_dev_name = self.settings.value('in_dev_name', 'unknown')
        if in_dev_name in in_dev_names:
            self.cb_inputs.setCurrentIndex(in_dev_names.index(in_dev_name))
        else:
            self.cb_inputs.setCurrentIndex(0)

        # Create a list of QAudioInfo objects for outputs
        self.otput_info_list = QAudioDeviceInfo.availableDevices(
            QAudio.AudioOutput)
        if 0 == len(self.otput_info_list):
            self.otput_info_list = [QAudioDeviceInfo.defaultOutputDevice()]
        # Make a list of the name-strings of those things
        ot_dev_names = [
            audio_info.deviceName() for audio_info in self.otput_info_list
        ]
        # Create a combo box and populate it with those names
        self.cb_otputs = QComboBox()
        self.cb_otputs.addItems(ot_dev_names)
        # If the ot_dev_name from the previous run is in the current list,
        # make it the current choice in the box.
        ot_dev_name = self.settings.value('ot_dev_name', 'unknown')
        if ot_dev_name in ot_dev_names:
            self.cb_otputs.setCurrentIndex(ot_dev_names.index(ot_dev_name))
        else:
            self.cb_otputs.setCurrentIndex(0)

        #self.show_status(
        #'{} inputs {} otputs'.format(len(self.input_info_list),len(self.otput_info_list))
        #)
        # Create a combo box and populate it with names of outputs

        # Lay those two out aligned to the outside
        hb_combos = QHBoxLayout()
        hb_combos.addWidget(self.cb_inputs, 1)
        hb_combos.addStretch(0)
        hb_combos.addWidget(self.cb_otputs, 1)

        # Create a volume slider from 0 to 100.
        self.volume = QSlider(Qt.Horizontal, self)
        self.volume.setMinimum(0)
        self.volume.setMaximum(100)
        self.volume.setTickInterval(10)
        self.volume.setTickPosition(QSlider.TicksBothSides)
        # set the volume slider to the value from the previous run, or zero.
        self.volume.setValue(self.settings.value('volume', 0))

        # Create a checkbox "Mute"
        self.mute = QCheckBox('Mute')
        # Set it to the value at the end of the last run, or to True
        self.mute.setChecked(bool(self.settings.value('mute_status', 1)))

        # Put those together in a row squeezed in the center
        hb_volume = QHBoxLayout()
        hb_volume.addStretch(1)
        hb_volume.addWidget(self.volume, 1)
        hb_volume.addWidget(self.mute, 0)
        hb_volume.addStretch(1)

        # Stack all those up as this widget's layout
        vlayout = QVBoxLayout()
        vlayout.addLayout(hb_label)
        vlayout.addLayout(hb_combos)
        vlayout.addLayout(hb_volume)
        self.setLayout(vlayout)
コード例 #4
0
ファイル: sidetone.py プロジェクト: henryaj/Sidetone
class SideToneWidget(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        # Save link to main window
        self.main_window = parent
        # Get the status bar
        self.status_bar = parent.statusBar()
        # Slot that will point to a QAudioInput some day
        self.input_device = None
        # Slot that will point to a QAudioOutput in time
        self.otput_device = None
        # set up layout, creating:
        # self.input_info_list, list of QAudioInfo for inputs
        # self.cb_inputs, combox of input names in same order
        # self.otput_info_list, list of QAudioInfo for outputs
        # self.cb_otputs, combox of output names in same order
        # self.volume, volume slider
        # self.mute, mute checkbox
        self._uic()
        # Connect up signals to slots.
        # Changes in the comboxes go to in_device and ot_device
        self.cb_inputs.currentIndexChanged.connect(self.in_dev_change)
        self.cb_otputs.currentIndexChanged.connect(self.ot_dev_change)
        # Mute button goes to mute_change
        self.mute.stateChanged.connect(self.mute_change)
        # Change in volume goes to volume_change
        self.volume.valueChanged.connect(self.volume_change)
        # Start with the mute switch on. This triggers the above two signals.
        self.mute.setChecked(True)

    # Slot for any change in volume (or mute). If we have an output device
    # then convert level to a real and pass to the output device. If the new
    # level is 0, tell the device to stop; if nonzero, tell it to start.
    # Note we don't do anything about the input device level, it is always 1.0
    def volume_change(self, new_level):
        if self.otput_device:  # we have an output device
            self.otput_device.setVolume(self.volume.value() / 100)
            if new_level == 0:  # looks like a mute
                # tell the output device to stop just in case it
                # doesn't know about volume control.
                self.otput_device.stop()
            else:  # non-zero level, if the output is stopped, start it
                if self.otput_device.state() == QAudio.StoppedState:
                    self.otput_device.start()

    # Slot for mute switch. Note that any change to the volume slider
    # generates a signal to the volume_change slot.
    def mute_change(self, onoff):
        if onoff:
            # Mute has been clicked ON. Remember the current volume.
            # Turn the volume to zero.
            self.volume_level = self.volume.value()
            self.volume.setValue(0)
        else:
            # Mute has been clicked OFF. If we do not yet have input and
            # output devices, get them. Then reset the old volume level.
            if self.otput_device is None:
                # We are starting up and have no devices. Fake a call to
                # the checkbox-change entries thus creating devices.
                self.in_dev_change(self.cb_inputs.currentIndex())
                self.ot_dev_change(self.cb_inputs.currentIndex())
            self.volume.setValue(self.volume_level)

    # Slots for changes in the selection of the input- and output-device
    # combo boxes. On startup we have neither an input nor an output device.
    # We do not know which combox the user will fiddle with first. So either
    # has to assume that the other device may not yet exist.
    #
    # On a change of input choice: if we have an input device, get rid of it.
    # Create a new input device. Set its level to 1.0. If we
    # have an output, connect the two.

    def in_dev_change(self, new_index):
        if self.input_device:
            if self.otput_device:
                self.otput_device.stop()
            self.input_device.stop()
            self.input_device = None  # goodby object
        # Get the QAudioDeviceInfo corresponding to this index of the combox.
        audio_info = self.input_info_list[new_index]
        # Create a new QAudioInput based on that.
        preferred_format = audio_info.preferredFormat()
        self.input_device = QAudioInput(audio_info, preferred_format)
        self.input_device.setVolume(1.0)
        self.input_device.setBufferSize(384)
        # If we have an output device, redirect it to this input. This is
        # done by asking the input device for its QIODevice, and passing that
        # to the output device's start() method.
        if self.otput_device:
            self.input_device.start(self.otput_device.start())
            #self.otput_device.start( self.input_device.start() )

    # On a change in the selection of output choice: If we have an output
    # device, get rid of it. Create a new output device. If we have an input
    # device, connect the two. Set the output level from the volume slider.

    def ot_dev_change(self, new_index):
        if self.otput_device:
            if self.input_device:
                self.input_device.stop()
            self.otput_device.stop()
            self.otput_device = None
        audio_info = self.otput_info_list[new_index]
        preferred_format = audio_info.preferredFormat()
        self.otput_device = QAudioOutput(audio_info, preferred_format)
        self.otput_device.setVolume(self.volume.value() / 100)
        #self.otput_device.setBufferSize( 384 )
        if self.input_device:
            self.input_device.start(self.otput_device.start())
            #self.otput_device.start( self.input_device.start() )

    def _uic(self):
        '''
    set up our layout which consists of:

                 Big Honkin' Label
        [input combobox]    [output combobox]
               [volume slider]  [x] Mute

    hooking put the signals to useful slots is the job
    of __init__. Here just make the layout.
        '''
        self.setMinimumWidth(400)
        # Create the big honkin' label and logo
        icon_pixmap = QPixmap(':/icon.png').scaledToWidth(64)
        icon_label = QLabel()
        icon_label.setPixmap(icon_pixmap)
        text_label = QLabel("Sidetone!")
        hb_label = QHBoxLayout()
        hb_label.addStretch(1)
        hb_label.addWidget(icon_label, 0)
        hb_label.addWidget(text_label, 0)
        hb_label.addStretch(1)

        # Create a list of QAudioInfo objects for inputs
        self.input_info_list = QAudioDeviceInfo.availableDevices(
            QAudio.AudioInput)
        if 0 == len(self.input_info_list):
            self.input_info_list = [QAudioDeviceInfo.defaultInputDevice()]
        # Create a combo box and populate it with names of inputs
        self.cb_inputs = QComboBox()
        self.cb_inputs.addItems(
            [audio_info.deviceName() for audio_info in self.input_info_list])
        # Create a list of QAudioInfo objects for outputs
        self.otput_info_list = QAudioDeviceInfo.availableDevices(
            QAudio.AudioOutput)
        if 0 == len(self.otput_info_list):
            self.otput_info_list = [QAudioDeviceInfo.defaultOutputDevice()]
        self.status_bar.showMessage(
            '{} inputs {} otputs'.format(len(self.input_info_list),
                                         len(self.otput_info_list)), 2000)
        # Create a combo box and populate it with names of outputs
        self.cb_otputs = QComboBox()
        self.cb_otputs.addItems(
            [audio_info.deviceName() for audio_info in self.otput_info_list])
        # Lay those two out aligned to the outside
        hb_combos = QHBoxLayout()
        hb_combos.addWidget(self.cb_inputs, 1)
        hb_combos.addStretch(0)
        hb_combos.addWidget(self.cb_otputs, 1)
        # Create a volume slider from 0 to 100.
        self.volume = QSlider(Qt.Horizontal, self)
        self.volume.setMinimum(0)
        self.volume.setMaximum(100)
        self.volume.setTickInterval(10)
        self.volume.setTickPosition(QSlider.TicksBothSides)
        # Create a checkbox "Mute"
        self.mute = QCheckBox('Mute')
        # Put those together in a row squeezed in the center
        hb_volume = QHBoxLayout()
        hb_volume.addStretch(1)
        hb_volume.addWidget(self.volume, 1)
        hb_volume.addWidget(self.mute, 0)
        hb_volume.addStretch(1)
        # Stack all those up as this widget's layout
        vlayout = QVBoxLayout()
        vlayout.addLayout(hb_label)
        vlayout.addLayout(hb_combos)
        vlayout.addLayout(hb_volume)
        self.setLayout(vlayout)
コード例 #5
0
ファイル: myMainWindow.py プロジェクト: likeke201/qt_code
class QmyMainWindow(QMainWindow): 

   def __init__(self, parent=None):
      super().__init__(parent)    #调用父类构造函数,创建窗体
      self.ui=Ui_MainWindow()     #创建UI对象
      self.ui.setupUi(self)       #构造UI界面

      self.ui.progBar_Max.setMaximum(256)
      self.ui.progBar_Min.setMaximum(256)
      self.ui.progBar_Diff.setMaximum(256)

      self.ui.sliderVolumn.setMaximum(100)
      self.ui.sliderVolumn.setValue(100)

      self.ui.comboDevices.clear()
      self.__deviceList=QAudioDeviceInfo.availableDevices(QAudio.AudioInput) #音频输入设备列表
      for i in range(len(self.__deviceList)):
         device=self.__deviceList[i]    #QAudioDeviceInfo类
         self.ui.comboDevices.addItem(device.deviceName())

##      self.__deviceInfo =None   #当前设备信息,QAudioDeviceInfo
      self.audioDevice=None       #音频输入设备,QAudioInput
      self.BUFFER_SIZE=4000

      self.ioDevice=None      #第1种读取方法,内建的IODevice

##      self.externalReader=None        #第2种读取方法,外建的IODevice

      self.recordFile=QFile()      #第3种读取方法,使用QFile直接写入文件

      if len(self.__deviceList)>0:
         self.ui.comboDevices.setCurrentIndex(0) #触发comboDevices的信号currentIndexChanged()
##         self.__deviceInfo =deviceList[0]
      else:
         self.ui.actStart.setEnabled(False)
         self.ui.actDeviceTest.setEnabled(False)
         self.ui.groupBoxDevice.setTitle("支持的音频输入设置(无设备)")
      

##  ==============自定义功能函数========================
   def __getSampleTypeStr(self,sampleType):
      result="Unknown"
      if sampleType==QAudioFormat.SignedInt:
         result = "SignedInt"
      elif sampleType==QAudioFormat.UnSignedInt:
         result = "UnSignedInt"
      elif sampleType==QAudioFormat.Float:
         result = "Float"
      elif sampleType==QAudioFormat.Unknown:
         result = "Unknown"
         
      return result

   def __getByteOrderStr(self,endian):
      if (endian==QAudioFormat.LittleEndian):
         return "LittleEndian"
      else:
         return "BigEndian"
      

##  ==============event处理函数==========================
        
        
##  ==========由connectSlotsByName()自动连接的槽函数============
      
   @pyqtSlot(int)   ##选择音频输入设备
   def on_comboDevices_currentIndexChanged(self,index):        
      deviceInfo =self.__deviceList[index]   #当前音频设备,QAudioDeviceInfo类型
      self.ui.comboCodec.clear()             
      codecs = deviceInfo.supportedCodecs()  #支持的音频编码,字符串列表
      for strLine in codecs:
         self.ui.comboCodec.addItem(strLine)

      self.ui.comboSampleRate.clear()  #支持的采样率
      sampleRates = deviceInfo.supportedSampleRates()  #QList<int>
      for i in  sampleRates:
         self.ui.comboSampleRate.addItem("%d"% i)

      self.ui.comboChannels.clear()    #支持的通道数
      Channels = deviceInfo.supportedChannelCounts() #QList<int> 
      for i in Channels:
         self.ui.comboChannels.addItem("%d"%i )

      self.ui.comboSampleTypes.clear() #支持的采样点类型
      sampleTypes = deviceInfo.supportedSampleTypes() #QList<QAudioFormat::SampleType>
      for i in  sampleTypes:
         sampTypeStr=self.__getSampleTypeStr(i)
         self.ui.comboSampleTypes.addItem(sampTypeStr,i)
         
      self.ui.comboSampleSizes.clear() #采样点大小
      sampleSizes = deviceInfo.supportedSampleSizes() #QList<int>
      for i in  sampleSizes:
         self.ui.comboSampleSizes.addItem("%d"%i)

      self.ui.comboByteOrder.clear() #字节序
      endians = deviceInfo.supportedByteOrders()  #QList<QAudioFormat::Endian> 
      for i in endians:
         self.ui.comboByteOrder.addItem(self.__getByteOrderStr(i))

   @pyqtSlot()   ##使用内建IODevice
   def on_radioSaveMode_Inner_clicked(self):
      self.ui.groupBox_disp.setVisible(True)
      
   @pyqtSlot()   ##使用QFile对象(test.raw)
   def on_radioSaveMode_QFile_clicked(self):
      self.ui.groupBox_disp.setVisible(False)

   @pyqtSlot(int)   ##调节录音音量
   def on_sliderVolumn_valueChanged(self,value):
      self.ui.LabVol.setText("录音音量(%d%%)"%value)


   @pyqtSlot()   ##测试音频输入设备是否支持选择的设置
   def on_actDeviceTest_triggered(self):
      settings=QAudioFormat()
      settings.setCodec(self.ui.comboCodec.currentText())
      settings.setSampleRate(int(self.ui.comboSampleRate.currentText()))
      settings.setChannelCount(int(self.ui.comboChannels.currentText()))

      k=self.ui.comboSampleTypes.currentData()
      settings.setSampleType(k)  #QAudioFormat.SampleType

      settings.setSampleSize(int(self.ui.comboSampleSizes.currentText()))

      if (self.ui.comboByteOrder.currentText()=="LittleEndian"):
         settings.setByteOrder(QAudioFormat.LittleEndian)
      else:
         settings.setByteOrder(QAudioFormat.BigEndian)

      index=self.ui.comboDevices.currentIndex()
      deviceInfo =self.__deviceList[index]  #当前音频设备

      if deviceInfo.isFormatSupported(settings):
         QMessageBox.information(self,"消息","测试成功,设备支持此设置")
      else:
         QMessageBox.critical(self,"错误","测试失败,设备不支持此设置")

   @pyqtSlot()   ##开始音频输入
   def on_actStart_triggered(self):
      audioFormat=QAudioFormat() #使用固定格式
      audioFormat.setSampleRate(8000)
      audioFormat.setChannelCount(1)
      audioFormat.setSampleSize(8)
      audioFormat.setCodec("audio/pcm")
      audioFormat.setByteOrder(QAudioFormat.LittleEndian)
      audioFormat.setSampleType(QAudioFormat.UnSignedInt)

      index=self.ui.comboDevices.currentIndex()
      deviceInfo =self.__deviceList[index]  #当前音频设备

      if (False== deviceInfo.isFormatSupported(audioFormat)):
         QMessageBox.critical(self,"错误","测试失败,输入设备不支持此设置")
         return

      self.audioDevice = QAudioInput(deviceInfo,audioFormat) #音频输入设备
      self.audioDevice.setBufferSize(self.BUFFER_SIZE)   #设置的缓冲区大小,字节数,并不一定等于实际数据块大小
      self.audioDevice.stateChanged.connect(self.do_stateChanged)  #状态变化


   ##1. 使用 start()->QIODevice 启动,返回的内置的IODevice, pull mode,利用readyRead()信号读出数据
      if self.ui.radioSaveMode_Inner.isChecked():
         self.ioDevice=self.audioDevice.start()  #返回内建的IODevice
         self.ioDevice.readyRead.connect(self.do_IO_readyRead)

   ## 2. 自定义流设备QWAudioBlockReader,push mode, start(QIODevice),不行
   ##      if self.ui.radioSaveMode_External.isChecked():
   ##         self.externalReader = QmyAudioReader()
   ##         self.externalReader.open(QIODevice.WriteOnly)
   ##         self.externalReader.updateBlockInfo.connect(self.do_updateBlockInfo)
   ##         self.audioDevice.start(self.externalReader)  #使用外建的IODevice
         
      
   ##3. 写入文件,用 start(QIODevice)启动
      if self.ui.radioSaveMode_QFile.isChecked():
         self.recordFile.setFileName("test.raw") 
         self.recordFile.open(QIODevice.WriteOnly)
         self.audioDevice.start(self.recordFile)  


   @pyqtSlot()   ##停止音频输入
   def on_actStop_triggered(self):
      self.audioDevice.stop()
      self.audioDevice.deleteLater()
      
   ##1. 使用 QIODevice  =start()返回的内置的IODevice, pull mode,利用readyRead()信号读出数据
      #无需处理,停止后self.ioDevice自动变为无效

   ##2. 外部IODevice接收音频输入数据的流设备
   ##      if self.ui.radioSaveMode_External.isChecked():
   ##         self.externalReader.close()
   ##         self.externalReader.updateBlockSize.disconnect(self.do_updateBlockInfo)
   ##         del self.externalReader  #删除外建设备
      
   ##3. 写入文件,start(QIODevice) 启动
      if self.ui.radioSaveMode_QFile.isChecked():
         self.recordFile.close() #关闭文件
      

        
##  =============自定义槽函数===============================        
##1. 使用QIODevice* start()返回的内置的IODevice, pull mode,利用readyRead()信号读出数据
   def do_IO_readyRead(self):    ##内建IODevice,读取缓冲区数据
      self.ui.LabBufferSize.setText("bufferSize()=%d"
                                    %self.audioDevice.bufferSize())

      byteCount = self.audioDevice.bytesReady() #可以读取的字节数
      self.ui.LabBytesReady.setText("bytesReady()=%d"%byteCount)

      if byteCount>self.BUFFER_SIZE:
         byteCount=self.BUFFER_SIZE
      
      buffer=self.ioDevice.read(byteCount)  #返回的是bytes类型,便于处理
   ##      buffer=self.ioDevice.readAll()  #不能用readAll()读取,返回的是QByteArray,需要再转换为bytes
      
   ##      print(type(buffer))
   ##      print(buffer)
      
      maxSize=len(buffer)
   ##      print(maxSize)

      self.ui.LabBlockSize.setText("IODevice数据字节数=%d"%maxSize)

      maxV=0
      minV=255
      for k in range(maxSize):
         V=buffer[k]  #取一个字节,整数
         if V>maxV:
            maxV=V   #求最大值
         if V<minV:
            minV=V   #求最小值

      self.ui.progBar_Max.setValue(maxV)
      self.ui.progBar_Min.setValue(minV)
      self.ui.progBar_Diff.setValue(maxV-minV)


   def do_stateChanged(self,state):    ##设备状态变化
      isStoped=(state== QAudio.StoppedState) #停止状态
      self.ui.groupBox_saveMode.setEnabled(isStoped)
      self.ui.sliderVolumn.setEnabled(isStoped)
      self.ui.actStart.setEnabled(isStoped)
      self.ui.actStop.setEnabled(not isStoped)
      self.ui.actDeviceTest.setEnabled(isStoped)
      self.ui.sliderVolumn.setEnabled(isStoped)

      if  state== QAudio.ActiveState:
         self.ui.statusBar.showMessage("state: ActiveState")
      elif state== QAudio.SuspendedState:
         self.ui.statusBar.showMessage("state: SuspendedState")
      elif state== QAudio.StoppedState:
         self.ui.statusBar.showMessage("state: StoppedState")
      elif state== QAudio.IdleState:
         self.ui.statusBar.showMessage("state: IdleState")
      elif state== QAudio.InterruptedState:
         self.ui.statusBar.showMessage("state: InterruptedState")