Пример #1
0
    def input_device_changed(self, index):
        self.parent().ui.actionStart.setChecked(False)

        success, index = AudioBackend().select_input_device(index)

        self.comboBox_inputDevice.setCurrentIndex(index)

        if not success:
            # Note: the error message is a child of the settings dialog, so that
            # that dialog remains on top when the error message is closed
            error_message = QtWidgets.QErrorMessage(self)
            error_message.setWindowTitle("Input device error")
            error_message.showMessage(
                "Impossible to use the selected input device, reverting to the previous one"
            )

        # reset the channels
        channels = AudioBackend().get_readable_current_channels()

        self.comboBox_firstChannel.clear()
        self.comboBox_secondChannel.clear()

        for channel in channels:
            self.comboBox_firstChannel.addItem(channel)
            self.comboBox_secondChannel.addItem(channel)

        first_channel = AudioBackend().get_current_first_channel()
        self.comboBox_firstChannel.setCurrentIndex(first_channel)
        second_channel = AudioBackend().get_current_second_channel()
        self.comboBox_secondChannel.setCurrentIndex(second_channel)

        self.parent().ui.actionStart.setChecked(True)
Пример #2
0
    def __init__(self, logger):
        QMainWindow.__init__(self)

        # exception hook that logs to console, file, and display a message box
        self.errorDialogOpened = False
        sys.excepthook = self.excepthook

        # logger
        self.logger = logger

        # Setup the user interface
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # Initialize the audio data ring buffer
        self.audiobuffer = AudioBuffer(self.logger)

        # Initialize the audio backend
        self.audiobackend = AudioBackend(self.logger)

        # signal containing new data from the audio callback thread, processed as numpy array
        self.audiobackend.new_data_available.connect(self.audiobuffer.handle_new_data)

        # this timer is used to update widgets that just need to display as fast as they can
        self.display_timer = QtCore.QTimer()
        self.display_timer.setInterval(SMOOTH_DISPLAY_TIMER_PERIOD_MS)  # constant timing

        # slow timer
        self.slow_timer = QtCore.QTimer()
        self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS)  # constant timing

        self.about_dialog = About_Dialog(self, self.logger, self.audiobackend, self.slow_timer)
        self.settings_dialog = Settings_Dialog(self, self.logger, self.audiobackend)

        self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger, "central_widget", 0)
        self.centralLayout = QVBoxLayout(self.ui.centralwidget)
        self.centralLayout.setContentsMargins(0, 0, 0, 0)
        self.centralLayout.addWidget(self.centralwidget)

        self.dockmanager = DockManager(self, self.logger)

        # timer ticks
        self.display_timer.timeout.connect(self.centralwidget.canvasUpdate)
        self.display_timer.timeout.connect(self.dockmanager.canvasUpdate)

        # toolbar clicks
        self.ui.actionStart.triggered.connect(self.timer_toggle)
        self.ui.actionSettings.triggered.connect(self.settings_called)
        self.ui.actionAbout.triggered.connect(self.about_called)
        self.ui.actionNew_dock.triggered.connect(self.dockmanager.new_dock)

        # restore the settings and widgets geometries
        self.restoreAppState()

        # start timers
        self.timer_toggle()
        self.slow_timer.start()

        self.logger.push("Init finished, entering the main loop")
Пример #3
0
    def stats_update(self):
        if not self.LabelStats.isVisible():
            return

        label = "Chunk #%d\n"\
            "Number of overflowed inputs (XRUNs): %d"\
            % (AudioBackend().chunk_number,
               AudioBackend().xruns)

        self.LabelStats.setText(label)
Пример #4
0
 def timer_toggle(self):
     if self.display_timer.isActive():
         self.logger.info("Timer stop")
         self.display_timer.stop()
         self.ui.actionStart.setText("Start")
         AudioBackend().pause()
         self.dockmanager.pause()
     else:
         self.logger.info("Timer start")
         self.display_timer.start()
         self.ui.actionStart.setText("Stop")
         AudioBackend().restart()
         self.dockmanager.restart()
Пример #5
0
    def device_changed(self, index):
        device = AudioBackend().output_devices[index]

        # save current stream in case we need to restore it
        previous_stream = self.stream
        previous_device = self.device

        error_message = ""

        Logger().push("Trying to write to output device " + device['name'])

        # first see if the format is supported by PortAudio
        try:
            success = AudioBackend().is_output_format_supported(device, np.int16)
        except Exception as exception:
            Logger().push("Format is not supported: " + str(exception))
            success = False

        if success:
            try:
                self.stream = AudioBackend().open_output_stream(device, self.audio_callback)
                self.device = device
                self.stream.start()
                if self.state not in [STARTING, PLAYING]:
                    self.stream.stop()
                success = True
            except OSError as error:
                Logger().push("Fail: " + str(error))
                success = False

        if success:
            Logger().push("Success")
            previous_stream.stop()
        else:
            if self.stream is not None:
                self.stream.stop()
            # restore previous stream
            self.stream = previous_stream
            self.device = previous_device

            # Note: the error message is a child of the settings dialog, so that
            # that dialog remains on top when the error message is closed
            error_message = QtWidgets.QErrorMessage(self.settings_dialog)
            error_message.setWindowTitle("Output device error")
            error_message.showMessage("Impossible to use the selected output device, reverting to the previous one. Reason is: " + error_message)

        self.settings_dialog.combobox_output_device.setCurrentIndex(AudioBackend().output_devices.index(self.device))
Пример #6
0
	def __init__(self, logger):
		QMainWindow.__init__(self)

		# logger
		self.logger = logger

		# Setup the user interface
		self.ui = Ui_MainWindow()
		self.ui.setupUi(self)

		# sharedGLWidget is a hidden GL widget that will be used as a parent
		# to all QGLWidgets so that they have the same GL context, and can
		# share display lists, etc.
		self.sharedGLWidget = QtOpenGL.QGLWidget(self)
		self.sharedGLWidget.hide()

		# Initialize the audio data ring buffer
		self.audiobuffer = AudioBuffer(self.logger)

		# Initialize the audio backend
		self.audiobackend = AudioBackend(self.logger)

		# this timer is used to update widgets that just need to display as fast as they can
		self.display_timer = QtCore.QTimer()
		self.display_timer.setInterval(SMOOTH_DISPLAY_TIMER_PERIOD_MS) # constant timing

		# slow timer
		self.slow_timer = QtCore.QTimer()
		self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS) # constant timing

		self.about_dialog = About_Dialog(self, self.logger, self.audiobackend, self.slow_timer)
		self.settings_dialog = Settings_Dialog(self, self.logger, self.audiobackend)

		self.centralwidget = CentralWidget(self.ui.centralwidget, self.sharedGLWidget, self.logger, "central_widget", 0)
		self.centralLayout = QVBoxLayout(self.ui.centralwidget)
		self.centralLayout.setContentsMargins(0, 0, 0, 0)
		self.centralLayout.addWidget(self.centralwidget)

		self.dockmanager = DockManager(self, self.sharedGLWidget, self.logger)

		# timer ticks
		self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.update_buffer)
		self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.centralwidget.update)
		self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.dockmanager.update)

		# toolbar clicks
		self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'), self.timer_toggle)
		self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'), self.settings_called)
		self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'), self.about_called)
		self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'), self.dockmanager.new_dock)

		# restore the settings and widgets geometries
		self.restoreAppState()

		# start timers
		self.timer_toggle()
		self.slow_timer.start()
		
		self.logger.push("Init finished, entering the main loop")
Пример #7
0
    def __init__(self, parent):
        QtWidgets.QDialog.__init__(self, parent)
        Ui_Settings_Dialog.__init__(self)

        self.logger = logging.getLogger(__name__)

        # Setup the user interface
        self.setupUi(self)

        devices = AudioBackend().get_readable_devices_list()

        if devices == []:
            # no audio input device: display a message and exit
            QtWidgets.QMessageBox.critical(self, no_input_device_title,
                                           no_input_device_message)
            QtCore.QTimer.singleShot(0, self.exitOnInit)
            sys.exit(1)
            return

        for device in devices:
            self.comboBox_inputDevice.addItem(device)

        channels = AudioBackend().get_readable_current_channels()
        for channel in channels:
            self.comboBox_firstChannel.addItem(channel)
            self.comboBox_secondChannel.addItem(channel)

        current_device = AudioBackend().get_readable_current_device()
        self.comboBox_inputDevice.setCurrentIndex(current_device)

        first_channel = AudioBackend().get_current_first_channel()
        self.comboBox_firstChannel.setCurrentIndex(first_channel)
        second_channel = AudioBackend().get_current_second_channel()
        self.comboBox_secondChannel.setCurrentIndex(second_channel)

        # signals
        self.comboBox_inputDevice.currentIndexChanged.connect(
            self.input_device_changed)
        self.comboBox_firstChannel.activated.connect(
            self.first_channel_changed)
        self.comboBox_secondChannel.activated.connect(
            self.second_channel_changed)
        self.radioButton_single.toggled.connect(
            self.single_input_type_selected)
        self.radioButton_duo.toggled.connect(self.duo_input_type_selected)
Пример #8
0
    def draw(self, painter, xMap, yMap, rect):
        # update the spectrogram according to possibly new canvas dimensions
        self.frequency_resampler.setnsamples(rect.height())
        self.resampler.set_height(rect.height())
        self.canvasscaledspectrogram.setcanvas_height(rect.height())
        # print self.jitter_s, self.T, rect.width(), rect.width()*(1 + self.jitter_s/self.T)
        jitter_pix = rect.width() * self.jitter_s / self.T
        self.canvasscaledspectrogram.setcanvas_width(rect.width() + jitter_pix)

        screen_rate_frac = Fraction(rect.width(), int(self.T * 1000))
        self.resampler.set_ratio(self.sfft_rate_frac, screen_rate_frac)

        # time advance
        # This function is meant to be called at paintevent time, for better time sync.

        pixmap = self.canvasscaledspectrogram.getpixmap()
        offset = self.canvasscaledspectrogram.getpixmapoffset(
            delay=jitter_pix / 2)

        if self.isPlaying:
            delta_t = self.timer.nsecsElapsed() * 1e-9
            self.timer.restart()
            pixel_advance = delta_t / (self.T + self.jitter_s) * rect.width()
            self.canvasscaledspectrogram.addPixelAdvance(pixel_advance)

            time = AudioBackend().get_stream_time()
            time_delay = time - self.last_data_time
            pixel_delay = rect.width() * time_delay / self.T

            draw_delay = time - self.last_time

            self.last_time = time

            offset += pixel_delay

        rolling = True
        if rolling:
            # draw the whole canvas with a selected portion of the pixmap

            hints = painter.renderHints()
            # enable bilinear pixmap transformation
            painter.setRenderHints(hints
                                   | QtGui.QPainter.SmoothPixmapTransform)
            # Note: nstead of a generic bilinear transformation, a specialized one could be more efficient,
            # since no transformation is needed in y, and the sampling rate is already known to be ok in x.
            sw = rect.width()
            sh = rect.height()

            source_rect = QtCore.QRectF(offset, 0, sw, sh)
            # QRectF since the offset and width may be non-integer
            painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect)
        else:
            sw = rect.width()
            sh = rect.height()
            source_rect = QtCore.QRectF(0, 0, sw, sh)
            painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect)
Пример #9
0
    def __init__(self, parent):
        super().__init__(parent)

        self.setObjectName("Spectrogram_Widget")
        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.PlotZoneImage = ImagePlot(self)
        self.PlotZoneImage.setObjectName("PlotZoneImage")
        self.gridLayout.addWidget(self.PlotZoneImage, 0, 1, 1, 1)

        self.audiobuffer = None

        # initialize the class instance that will do the fft
        self.proc = audioproc()

        self.maxfreq = DEFAULT_MAXFREQ
        self.proc.set_maxfreq(self.maxfreq)
        self.minfreq = DEFAULT_MINFREQ
        self.fft_size = 2**DEFAULT_FFT_SIZE * 32
        self.proc.set_fftsize(self.fft_size)
        self.spec_min = DEFAULT_SPEC_MIN
        self.spec_max = DEFAULT_SPEC_MAX
        self.weighting = DEFAULT_WEIGHTING

        self.update_weighting()
        self.freq = self.proc.get_freq_scale()

        self.timerange_s = DEFAULT_TIMERANGE
        self.canvas_width = 100.

        self.old_index = 0
        self.overlap = 3. / 4.
        self.overlap_frac = Fraction(3, 4)
        self.dT_s = self.fft_size * (1. - self.overlap) / float(SAMPLING_RATE)

        self.PlotZoneImage.setlog10freqscale()  # DEFAULT_FREQ_SCALE = 1 #log10
        self.PlotZoneImage.setfreqrange(self.minfreq, self.maxfreq)
        self.PlotZoneImage.setspecrange(self.spec_min, self.spec_max)
        self.PlotZoneImage.setweighting(self.weighting)
        self.PlotZoneImage.settimerange(self.timerange_s, self.dT_s)
        self.update_jitter()

        sfft_rate_frac = Fraction(SAMPLING_RATE, self.fft_size) / (
            Fraction(1) - self.overlap_frac) / 1000
        self.PlotZoneImage.set_sfft_rate(sfft_rate_frac)

        # initialize the settings dialog
        self.settings_dialog = Spectrogram_Settings_Dialog(self)

        AudioBackend().underflow.connect(
            self.PlotZoneImage.plotImage.canvasscaledspectrogram.syncOffsets)

        self.last_data_time = 0.

        self.mustRestart = False
Пример #10
0
    def device_changed(self, index):
        device = AudioBackend().output_devices[index]

        # save current stream in case we need to restore it
        previous_stream = self.stream
        previous_device = self.device

        self.logger.info("Trying to write to output device '%s'",
                         device['name'])

        # first see if the format is supported by PortAudio
        try:
            AudioBackend().is_output_format_supported(device, np.int16)
        except sounddevice.PortAudioError as err:
            self.on_device_change_error(
                previous_stream, previous_device,
                "Format is not supported: {0}".format(err))
            return

        try:
            self.stream = AudioBackend().open_output_stream(
                device, self.audio_callback)
            self.device = device
            self.stream.start()
            if self.state not in [STARTING, PLAYING]:
                self.stream.stop()
        except (sounddevice.PortAudioError, OSError) as err:
            self.on_device_change_error(
                previous_stream, previous_device,
                "Failed to open output device: {0}".format(err))
            return

        self.logger.info("Success")
        previous_stream.stop()

        self.settings_dialog.combobox_output_device.setCurrentIndex(
            AudioBackend().output_devices.index(self.device))
Пример #11
0
    def second_channel_changed(self, index):
        self.parent().ui.actionStart.setChecked(False)

        success, index = AudioBackend().select_second_channel(index)

        self.comboBox_secondChannel.setCurrentIndex(index)

        if not success:
            # Note: the error message is a child of the settings dialog, so that
            # that dialog remains on top when the error message is closed
            error_message = QtWidgets.QErrorMessage(self)
            error_message.setWindowTitle("Input device error")
            error_message.showMessage(
                "Impossible to use the selected channel as the second channel, reverting to the previous one"
            )

        self.parent().ui.actionStart.setChecked(True)
Пример #12
0
    def on_device_change_error(self, previous_stream, previous_device,
                               message):
        self.logger.exception(message)

        if self.stream is not None:
            self.stream.stop()

        # restore previous stream
        self.stream = previous_stream
        self.device = previous_device

        # Note: the error message is a child of the settings dialog, so that
        # that dialog remains on top when the error message is closed
        error_message = QtWidgets.QErrorMessage(self.settings_dialog)
        error_message.setWindowTitle("Output device error")
        error_message.showMessage(
            "Impossible to use the selected output device, reverting to the previous one. Reason is: "
            + message)

        self.settings_dialog.combobox_output_device.setCurrentIndex(
            AudioBackend().output_devices.index(self.device))
Пример #13
0
class Friture(
        QMainWindow, ):
    def __init__(self, logger):
        QMainWindow.__init__(self)

        # exception hook that logs to console, file, and display a message box
        self.errorDialogOpened = False
        sys.excepthook = self.excepthook

        # logger
        self.logger = logger

        # Setup the user interface
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # Initialize the audio data ring buffer
        self.audiobuffer = AudioBuffer(self.logger)

        # Initialize the audio backend
        self.audiobackend = AudioBackend(self.logger)

        # signal containing new data from the audio callback thread, processed as numpy array
        self.audiobackend.new_data_available.connect(
            self.audiobuffer.handle_new_data)

        # this timer is used to update widgets that just need to display as fast as they can
        self.display_timer = QtCore.QTimer()
        self.display_timer.setInterval(
            SMOOTH_DISPLAY_TIMER_PERIOD_MS)  # constant timing

        # slow timer
        self.slow_timer = QtCore.QTimer()
        self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS)  # constant timing

        self.about_dialog = About_Dialog(self, self.logger, self.audiobackend,
                                         self.slow_timer)
        self.settings_dialog = Settings_Dialog(self, self.logger,
                                               self.audiobackend)

        self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger,
                                           "central_widget", 0)
        self.centralLayout = QVBoxLayout(self.ui.centralwidget)
        self.centralLayout.setContentsMargins(0, 0, 0, 0)
        self.centralLayout.addWidget(self.centralwidget)

        self.dockmanager = DockManager(self, self.logger)

        # timer ticks
        self.display_timer.timeout.connect(self.centralwidget.canvasUpdate)
        self.display_timer.timeout.connect(self.dockmanager.canvasUpdate)

        # toolbar clicks
        self.ui.actionStart.triggered.connect(self.timer_toggle)
        self.ui.actionSettings.triggered.connect(self.settings_called)
        self.ui.actionAbout.triggered.connect(self.about_called)
        self.ui.actionNew_dock.triggered.connect(self.dockmanager.new_dock)

        # restore the settings and widgets geometries
        self.restoreAppState()

        # start timers
        self.timer_toggle()
        self.slow_timer.start()

        self.logger.push("Init finished, entering the main loop")

    # exception hook that logs to console, file, and display a message box
    def excepthook(self, exception_type, exception_value, traceback_object):
        gui_message = fileexcepthook(exception_type, exception_value,
                                     traceback_object)

        # we do not want to flood the user with message boxes when the error happens repeatedly on each timer event
        if not self.errorDialogOpened:
            self.errorDialogOpened = True
            errorBox(gui_message)
            self.errorDialogOpened = False

    # slot
    def settings_called(self):
        self.settings_dialog.show()

    # slot
    def about_called(self):
        self.about_dialog.show()

    # event handler
    def closeEvent(self, event):
        self.audiobackend.close()
        self.saveAppState()
        event.accept()

    # method
    def saveAppState(self):
        settings = QtCore.QSettings("Friture", "Friture")

        settings.beginGroup("Docks")
        self.dockmanager.saveState(settings)
        settings.endGroup()

        settings.beginGroup("CentralWidget")
        self.centralwidget.saveState(settings)
        settings.endGroup()

        settings.beginGroup("MainWindow")
        windowGeometry = self.saveGeometry()
        settings.setValue("windowGeometry", windowGeometry)
        windowState = self.saveState()
        settings.setValue("windowState", windowState)
        settings.endGroup()

        settings.beginGroup("AudioBackend")
        self.settings_dialog.saveState(settings)
        settings.endGroup()

    # method
    def restoreAppState(self):
        settings = QtCore.QSettings("Friture", "Friture")

        settings.beginGroup("Docks")
        self.dockmanager.restoreState(settings)
        settings.endGroup()

        settings.beginGroup("CentralWidget")
        self.centralwidget.restoreState(settings)
        settings.endGroup()

        settings.beginGroup("MainWindow")
        self.restoreGeometry(
            settings.value("windowGeometry", type=QtCore.QByteArray))
        self.restoreState(settings.value("windowState",
                                         type=QtCore.QByteArray))
        settings.endGroup()

        settings.beginGroup("AudioBackend")
        self.settings_dialog.restoreState(settings)
        settings.endGroup()

    # slot
    def timer_toggle(self):
        if self.display_timer.isActive():
            self.logger.push("Timer stop")
            self.display_timer.stop()
            self.ui.actionStart.setText("Start")
            self.audiobackend.pause()
            self.centralwidget.pause()
            self.dockmanager.pause()
        else:
            self.logger.push("Timer start")
            self.display_timer.start()
            self.ui.actionStart.setText("Stop")
            self.audiobackend.restart()
            self.centralwidget.restart()
            self.dockmanager.restart()
Пример #14
0
	def __init__(self, logger):
		QMainWindow.__init__(self)

		# logger
		self.logger = logger

		# Setup the user interface
		self.ui = Ui_MainWindow()
		self.ui.setupUi(self)
		
		self.settings_dialog = Settings_Dialog(self)
		self.about_dialog = About_Dialog(self)
		
		self.chunk_number = 0
		
		self.buffer_timer_time = 0.
		
		self.cpu_percent = 0.

		# Initialize the audio data ring buffer
		self.audiobuffer = AudioBuffer()

		# Initialize the audio backend
		self.audiobackend = AudioBackend(self.logger)
   
		devices = self.audiobackend.get_readable_devices_list()
		for device in devices:
			self.settings_dialog.comboBox_inputDevice.addItem(device)

		channels = self.audiobackend.get_readable_current_channels()
		for channel in channels:
			self.settings_dialog.comboBox_firstChannel.addItem(channel)
			self.settings_dialog.comboBox_secondChannel.addItem(channel)

		current_device = self.audiobackend.get_readable_current_device()
		self.settings_dialog.comboBox_inputDevice.setCurrentIndex(current_device)

		first_channel = self.audiobackend.get_current_first_channel()
		self.settings_dialog.comboBox_firstChannel.setCurrentIndex(first_channel)
		second_channel = self.audiobackend.get_current_second_channel()
		self.settings_dialog.comboBox_secondChannel.setCurrentIndex(second_channel)

		# this timer is used to update widgets that just need to display as fast as they can
		self.display_timer = QtCore.QTimer()
		self.display_timer.setInterval(SMOOTH_DISPLAY_TIMER_PERIOD_MS) # constant timing

		# slow timer
		self.slow_timer = QtCore.QTimer()
		self.slow_timer.setInterval(1000) # constant timing

		self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger, "central_widget", 0)
		self.centralLayout = QVBoxLayout(self.ui.centralwidget)
		self.centralLayout.setContentsMargins(0, 0, 0, 0)
		self.centralLayout.addWidget(self.centralwidget)

		# timer ticks
		self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.update_buffer)
		self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.statistics)

		# timer ticks
		self.connect(self.slow_timer, QtCore.SIGNAL('timeout()'), self.get_cpu_percent)
		
		# toolbar clicks
		self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'), self.timer_toggle)
		self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'), self.settings_called)
		self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'), self.about_called)
		self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'), self.new_dock_called)
		
		# settings signals
		self.connect(self.settings_dialog.comboBox_inputDevice, QtCore.SIGNAL('currentIndexChanged(int)'), self.input_device_changed)
  		self.connect(self.settings_dialog.comboBox_firstChannel, QtCore.SIGNAL('currentIndexChanged(int)'), self.first_channel_changed)
		self.connect(self.settings_dialog.comboBox_secondChannel, QtCore.SIGNAL('currentIndexChanged(int)'), self.second_channel_changed)
		self.connect(self.settings_dialog.radioButton_single, QtCore.SIGNAL('toggled(bool)'), self.single_input_type_selected)
		self.connect(self.settings_dialog.radioButton_duo, QtCore.SIGNAL('toggled(bool)'), self.duo_input_type_selected)
		self.connect(self.settings_dialog.doubleSpinBox_delay, QtCore.SIGNAL('valueChanged(double)'), self.delay_changed)

		# log change
		self.connect(self.logger, QtCore.SIGNAL('logChanged'), self.log_changed)
		self.connect(self.about_dialog.log_scrollarea.verticalScrollBar(), QtCore.SIGNAL('rangeChanged(int,int)'), self.log_scroll_range_changed)

		# restore the settings and widgets geometries
		self.restoreAppState()

		# start timers
		self.timer_toggle()
		self.slow_timer.start()
		
		self.logger.push("Init finished, entering the main loop")
Пример #15
0
 def restart(self):
     self.isPlaying = True
     self.last_time = AudioBackend().get_stream_time()
     self.timer.restart()
Пример #16
0
    def __init__(self, logger):
        QMainWindow.__init__(self)

        # logger
        self.logger = logger

        # Setup the user interface
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.settings_dialog = Settings_Dialog(self)
        self.about_dialog = About_Dialog(self)

        self.chunk_number = 0

        self.buffer_timer_time = 0.

        self.cpu_percent = 0.

        # Initialize the audio data ring buffer
        self.audiobuffer = AudioBuffer()

        # Initialize the audio backend
        self.audiobackend = AudioBackend(self.logger)

        devices = self.audiobackend.get_readable_devices_list()
        for device in devices:
            self.settings_dialog.comboBox_inputDevice.addItem(device)

        channels = self.audiobackend.get_readable_current_channels()
        for channel in channels:
            self.settings_dialog.comboBox_firstChannel.addItem(channel)
            self.settings_dialog.comboBox_secondChannel.addItem(channel)

        current_device = self.audiobackend.get_readable_current_device()
        self.settings_dialog.comboBox_inputDevice.setCurrentIndex(
            current_device)

        first_channel = self.audiobackend.get_current_first_channel()
        self.settings_dialog.comboBox_firstChannel.setCurrentIndex(
            first_channel)
        second_channel = self.audiobackend.get_current_second_channel()
        self.settings_dialog.comboBox_secondChannel.setCurrentIndex(
            second_channel)

        # this timer is used to update widgets that just need to display as fast as they can
        self.display_timer = QtCore.QTimer()
        self.display_timer.setInterval(
            SMOOTH_DISPLAY_TIMER_PERIOD_MS)  # constant timing

        # slow timer
        self.slow_timer = QtCore.QTimer()
        self.slow_timer.setInterval(1000)  # constant timing

        self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger,
                                           "central_widget", 0)
        self.centralLayout = QVBoxLayout(self.ui.centralwidget)
        self.centralLayout.setContentsMargins(0, 0, 0, 0)
        self.centralLayout.addWidget(self.centralwidget)

        # timer ticks
        self.connect(self.display_timer, QtCore.SIGNAL('timeout()'),
                     self.update_buffer)
        self.connect(self.display_timer, QtCore.SIGNAL('timeout()'),
                     self.statistics)

        # timer ticks
        self.connect(self.slow_timer, QtCore.SIGNAL('timeout()'),
                     self.get_cpu_percent)

        # toolbar clicks
        self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'),
                     self.timer_toggle)
        self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'),
                     self.settings_called)
        self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'),
                     self.about_called)
        self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'),
                     self.new_dock_called)

        # settings signals
        self.connect(self.settings_dialog.comboBox_inputDevice,
                     QtCore.SIGNAL('currentIndexChanged(int)'),
                     self.input_device_changed)
        self.connect(self.settings_dialog.comboBox_firstChannel,
                     QtCore.SIGNAL('currentIndexChanged(int)'),
                     self.first_channel_changed)
        self.connect(self.settings_dialog.comboBox_secondChannel,
                     QtCore.SIGNAL('currentIndexChanged(int)'),
                     self.second_channel_changed)
        self.connect(self.settings_dialog.radioButton_single,
                     QtCore.SIGNAL('toggled(bool)'),
                     self.single_input_type_selected)
        self.connect(self.settings_dialog.radioButton_duo,
                     QtCore.SIGNAL('toggled(bool)'),
                     self.duo_input_type_selected)
        self.connect(self.settings_dialog.doubleSpinBox_delay,
                     QtCore.SIGNAL('valueChanged(double)'), self.delay_changed)

        # log change
        self.connect(self.logger, QtCore.SIGNAL('logChanged'),
                     self.log_changed)
        self.connect(self.about_dialog.log_scrollarea.verticalScrollBar(),
                     QtCore.SIGNAL('rangeChanged(int,int)'),
                     self.log_scroll_range_changed)

        # restore the settings and widgets geometries
        self.restoreAppState()

        # start timers
        self.timer_toggle()
        self.slow_timer.start()

        self.logger.push("Init finished, entering the main loop")
Пример #17
0
class Friture(QMainWindow, ):
	def __init__(self, logger):
		QMainWindow.__init__(self)

		# logger
		self.logger = logger

		# Setup the user interface
		self.ui = Ui_MainWindow()
		self.ui.setupUi(self)
		
		self.settings_dialog = Settings_Dialog(self)
		self.about_dialog = About_Dialog(self)
		
		self.chunk_number = 0
		
		self.buffer_timer_time = 0.
		
		self.cpu_percent = 0.

		# Initialize the audio data ring buffer
		self.audiobuffer = AudioBuffer()

		# Initialize the audio backend
		self.audiobackend = AudioBackend(self.logger)
   
		devices = self.audiobackend.get_readable_devices_list()
		for device in devices:
			self.settings_dialog.comboBox_inputDevice.addItem(device)

		channels = self.audiobackend.get_readable_current_channels()
		for channel in channels:
			self.settings_dialog.comboBox_firstChannel.addItem(channel)
			self.settings_dialog.comboBox_secondChannel.addItem(channel)

		current_device = self.audiobackend.get_readable_current_device()
		self.settings_dialog.comboBox_inputDevice.setCurrentIndex(current_device)

		first_channel = self.audiobackend.get_current_first_channel()
		self.settings_dialog.comboBox_firstChannel.setCurrentIndex(first_channel)
		second_channel = self.audiobackend.get_current_second_channel()
		self.settings_dialog.comboBox_secondChannel.setCurrentIndex(second_channel)

		# this timer is used to update widgets that just need to display as fast as they can
		self.display_timer = QtCore.QTimer()
		self.display_timer.setInterval(SMOOTH_DISPLAY_TIMER_PERIOD_MS) # constant timing

		# slow timer
		self.slow_timer = QtCore.QTimer()
		self.slow_timer.setInterval(1000) # constant timing

		self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger, "central_widget", 0)
		self.centralLayout = QVBoxLayout(self.ui.centralwidget)
		self.centralLayout.setContentsMargins(0, 0, 0, 0)
		self.centralLayout.addWidget(self.centralwidget)

		# timer ticks
		self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.update_buffer)
		self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.statistics)

		# timer ticks
		self.connect(self.slow_timer, QtCore.SIGNAL('timeout()'), self.get_cpu_percent)
		
		# toolbar clicks
		self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'), self.timer_toggle)
		self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'), self.settings_called)
		self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'), self.about_called)
		self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'), self.new_dock_called)
		
		# settings signals
		self.connect(self.settings_dialog.comboBox_inputDevice, QtCore.SIGNAL('currentIndexChanged(int)'), self.input_device_changed)
  		self.connect(self.settings_dialog.comboBox_firstChannel, QtCore.SIGNAL('currentIndexChanged(int)'), self.first_channel_changed)
		self.connect(self.settings_dialog.comboBox_secondChannel, QtCore.SIGNAL('currentIndexChanged(int)'), self.second_channel_changed)
		self.connect(self.settings_dialog.radioButton_single, QtCore.SIGNAL('toggled(bool)'), self.single_input_type_selected)
		self.connect(self.settings_dialog.radioButton_duo, QtCore.SIGNAL('toggled(bool)'), self.duo_input_type_selected)
		self.connect(self.settings_dialog.doubleSpinBox_delay, QtCore.SIGNAL('valueChanged(double)'), self.delay_changed)

		# log change
		self.connect(self.logger, QtCore.SIGNAL('logChanged'), self.log_changed)
		self.connect(self.about_dialog.log_scrollarea.verticalScrollBar(), QtCore.SIGNAL('rangeChanged(int,int)'), self.log_scroll_range_changed)

		# restore the settings and widgets geometries
		self.restoreAppState()

		# start timers
		self.timer_toggle()
		self.slow_timer.start()
		
		self.logger.push("Init finished, entering the main loop")
	
	# slot
	# update the log widget with the new log content
	def log_changed(self):
		self.about_dialog.LabelLog.setText(self.logger.text())
	
	# slot
	# scroll the log widget so that the last line is visible
	def log_scroll_range_changed(self, min, max):
		scrollbar = self.about_dialog.log_scrollarea.verticalScrollBar()
		scrollbar.setValue(max)
	
	# slot
	def settings_called(self):
		self.settings_dialog.show()
	
	# slot
	def about_called(self):
		self.about_dialog.show()
	
	# slot
	def new_dock_called(self):
		# the dock objectName is unique
		docknames = [dock.objectName() for dock in self.docks]
		dockindexes = [int(str(name).partition(' ')[-1]) for name in docknames]
		if len(dockindexes) == 0:
			index = 1
		else:
			index = max(dockindexes)+1
		name = "Dock %d" %index
		new_dock = Dock(self, self.logger, name)
		self.addDockWidget(QtCore.Qt.TopDockWidgetArea, new_dock)
		
		self.docks += [new_dock]
	
	#slot
	def dock_closed(self, dock):
		self.docks.remove(dock)
	
	# event handler
	def closeEvent(self, event):
		self.saveAppState()
		event.accept()
	
	# method
	def saveAppState(self):
		settings = QtCore.QSettings("Friture", "Friture")
		
		settings.beginGroup("Docks")
		docknames = [dock.objectName() for dock in self.docks]
		settings.setValue("dockNames", docknames)
		for dock in self.docks:
			settings.beginGroup(dock.objectName())
			dock.saveState(settings)
			settings.endGroup()
		settings.endGroup()
		
		settings.beginGroup("CentralWidget")
		self.centralwidget.saveState(settings)
		settings.endGroup()
		
		settings.beginGroup("MainWindow")
		windowGeometry = self.saveGeometry()
		settings.setValue("windowGeometry", windowGeometry)
		windowState = self.saveState()
		settings.setValue("windowState", windowState)
		settings.endGroup()

		settings.beginGroup("AudioBackend")
		self.settings_dialog.saveState(settings)
		settings.endGroup()
	
	# method
	def restoreAppState(self):
		settings = QtCore.QSettings("Friture", "Friture")

		settings.beginGroup("Docks")
		if settings.contains("dockNames"):
			docknames = settings.value("dockNames", []).toList()
			docknames = [dockname.toString() for dockname in docknames]
			# list of docks
			self.docks = [Dock(self, self.logger, name) for name in docknames]
			for dock in self.docks:
				settings.beginGroup(dock.objectName())
				dock.restoreState(settings)
				settings.endGroup()
		else:
			self.logger.push("First launch, display a default set of docks")
			self.docks = []
			self.docks += [Dock(self, self.logger, "Dock 0", type = 3)] #spectrogram
			self.docks += [Dock(self, self.logger, "Dock 1", type = 4)] #octave spectrum
			#self.docks += [Dock(self, self.logger, "Dock 2", type = 1)] #scope
			#self.docks += [Dock(self, self.logger, "Dock 3", type = 0)] #level
			for dock in self.docks:
				self.addDockWidget(QtCore.Qt.TopDockWidgetArea, dock)

		settings.endGroup()

		settings.beginGroup("CentralWidget")
		self.centralwidget.restoreState(settings)
		settings.endGroup()

		settings.beginGroup("MainWindow")
		self.restoreGeometry(settings.value("windowGeometry").toByteArray())
		self.restoreState(settings.value("windowState").toByteArray())
		settings.endGroup()
  
  		settings.beginGroup("AudioBackend")
		self.settings_dialog.restoreState(settings)
		settings.endGroup()

	# slot
	def timer_toggle(self):
		if self.display_timer.isActive():
			self.logger.push("Timer stop")
			self.display_timer.stop()
			self.ui.actionStart.setText("Start")
			for dock in self.docks:
				dock.custom_timer_stop()
			self.centralwidget.custom_timer_stop()
		else:
			self.logger.push("Timer start")
			self.display_timer.start()
			self.ui.actionStart.setText("Stop")
			for dock in self.docks:
				dock.custom_timer_start()
			self.centralwidget.custom_timer_start()

	# slot
	def update_buffer(self):
     		(chunks, t, newpoints) = self.audiobackend.update(self.audiobuffer.ringbuffer)
     		self.audiobuffer.set_newdata(newpoints)
		self.chunk_number += chunks
		self.buffer_timer_time = (95.*self.buffer_timer_time + 5.*t)/100.

	def get_cpu_percent(self):
		self.cpu_percent = psutil.cpu_percent()

	# method
	def statistics(self):
		if not self.about_dialog.LabelStats.isVisible():
		    return
		    
		label = "Chunk #%d\n"\
		"Audio buffer retrieval: %.02f ms\n"\
		"Global CPU usage: %d %%"\
		% (self.chunk_number, self.buffer_timer_time, self.cpu_percent)
		
		self.about_dialog.LabelStats.setText(label)

	# slot
	def input_device_changed(self, index):
		self.ui.actionStart.setChecked(False)
		
		success, index = self.audiobackend.select_input_device(index)
		
		self.settings_dialog.comboBox_inputDevice.setCurrentIndex(index)
		
		if not success:
			# Note: the error message is a child of the settings dialog, so that
			# that dialog remains on top when the error message is closed
			error_message = QErrorMessage(self.settings_dialog)
			error_message.setWindowTitle("Input device error")
			error_message.showMessage("Impossible to use the selected input device, reverting to the previous one")
		
		# reset the channels
		first_channel = self.audiobackend.get_current_first_channel()
		self.settings_dialog.comboBox_firstChannel.setCurrentIndex(first_channel)
		second_channel = self.audiobackend.get_current_second_channel()
		self.settings_dialog.comboBox_secondChannel.setCurrentIndex(second_channel)  
  
		self.ui.actionStart.setChecked(True)

	# slot
	def first_channel_changed(self, index):
		self.ui.actionStart.setChecked(False)
		
		success, index = self.audiobackend.select_first_channel(index)
		
		self.settings_dialog.comboBox_firstChannel.setCurrentIndex(index)
		
		if not success:
			# Note: the error message is a child of the settings dialog, so that
			# that dialog remains on top when the error message is closed
			error_message = QErrorMessage(self.settings_dialog)
			error_message.setWindowTitle("Input device error")
			error_message.showMessage("Impossible to use the selected channel as the first channel, reverting to the previous one")
		
		self.ui.actionStart.setChecked(True)

	# slot
	def second_channel_changed(self, index):
		self.ui.actionStart.setChecked(False)
		
		success, index = self.audiobackend.select_second_channel(index)
		
		self.settings_dialog.comboBox_secondChannel.setCurrentIndex(index)
		
		if not success:
			# Note: the error message is a child of the settings dialog, so that
			# that dialog remains on top when the error message is closed
			error_message = QErrorMessage(self.settings_dialog)
			error_message.setWindowTitle("Input device error")
			error_message.showMessage("Impossible to use the selected channel as the second channel, reverting to the previous one")
		
		self.ui.actionStart.setChecked(True)

	# slot
	def single_input_type_selected(self, checked):
                if checked:
                    self.settings_dialog.groupBox_second.setEnabled(False)
                    self.settings_dialog.label_delay.setEnabled(False)
                    self.settings_dialog.doubleSpinBox_delay.setEnabled(False)
                    self.audiobackend.set_single_input()
                    self.logger.push("Switching to single input")

	# slot
	def duo_input_type_selected(self, checked):
                if checked:
                    self.settings_dialog.groupBox_second.setEnabled(True)
                    self.settings_dialog.label_delay.setEnabled(True)
                    self.settings_dialog.doubleSpinBox_delay.setEnabled(True)
                    self.audiobackend.set_duo_input()
                    self.logger.push("Switching to difference between two inputs")

	# slot
	def delay_changed(self, delay_ms):
                    self.delay_ms = delay_ms
                    self.logger.push("Delay changed to %f" %delay_ms)
                    self.audiobuffer.set_delay_ms(delay_ms)
Пример #18
0
 def duo_input_type_selected(self, checked):
     if checked:
         self.groupBox_second.setEnabled(True)
         AudioBackend().set_duo_input()
         Logger().push("Switching to difference between two inputs")
Пример #19
0
class Friture(QMainWindow, ):

    def __init__(self, logger):
        QMainWindow.__init__(self)

        # logger
        self.logger = logger

        # Setup the user interface
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # Initialize the audio data ring buffer
        self.audiobuffer = AudioBuffer(self.logger)

        # Initialize the audio backend
        self.audiobackend = AudioBackend(self.logger)

        # signal containing new data from the audio callback thread, processed as numpy array
        self.audiobackend.new_data_available.connect(self.audiobuffer.handle_new_data)

        # this timer is used to update widgets that just need to display as fast as they can
        self.display_timer = QtCore.QTimer()
        self.display_timer.setInterval(SMOOTH_DISPLAY_TIMER_PERIOD_MS)  # constant timing

        # slow timer
        self.slow_timer = QtCore.QTimer()
        self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS)  # constant timing

        self.about_dialog = About_Dialog(self, self.logger, self.audiobackend, self.slow_timer)
        self.settings_dialog = Settings_Dialog(self, self.logger, self.audiobackend)

        self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger, "central_widget", 0)
        self.centralLayout = QVBoxLayout(self.ui.centralwidget)
        self.centralLayout.setContentsMargins(0, 0, 0, 0)
        self.centralLayout.addWidget(self.centralwidget)

        self.dockmanager = DockManager(self, self.logger)

        # timer ticks
        self.display_timer.timeout.connect(self.centralwidget.canvasUpdate)
        self.display_timer.timeout.connect(self.dockmanager.canvasUpdate)

        # toolbar clicks
        self.ui.actionStart.triggered.connect(self.timer_toggle)
        self.ui.actionSettings.triggered.connect(self.settings_called)
        self.ui.actionAbout.triggered.connect(self.about_called)
        self.ui.actionNew_dock.triggered.connect(self.dockmanager.new_dock)

        # restore the settings and widgets geometries
        self.restoreAppState()

        # start timers
        self.timer_toggle()
        self.slow_timer.start()

        self.logger.push("Init finished, entering the main loop")

    # slot
    def settings_called(self):
        self.settings_dialog.show()

    # slot
    def about_called(self):
        self.about_dialog.show()

    # event handler
    def closeEvent(self, event):
        self.audiobackend.close()
        self.saveAppState()
        event.accept()

    # method
    def saveAppState(self):
        settings = QtCore.QSettings("Friture", "Friture")

        settings.beginGroup("Docks")
        self.dockmanager.saveState(settings)
        settings.endGroup()

        settings.beginGroup("CentralWidget")
        self.centralwidget.saveState(settings)
        settings.endGroup()

        settings.beginGroup("MainWindow")
        windowGeometry = self.saveGeometry()
        settings.setValue("windowGeometry", windowGeometry)
        windowState = self.saveState()
        settings.setValue("windowState", windowState)
        settings.endGroup()

        settings.beginGroup("AudioBackend")
        self.settings_dialog.saveState(settings)
        settings.endGroup()

    # method
    def restoreAppState(self):
        settings = QtCore.QSettings("Friture", "Friture")

        settings.beginGroup("Docks")
        self.dockmanager.restoreState(settings)
        settings.endGroup()

        settings.beginGroup("CentralWidget")
        self.centralwidget.restoreState(settings)
        settings.endGroup()

        settings.beginGroup("MainWindow")
        self.restoreGeometry(settings.value("windowGeometry", type=QtCore.QByteArray))
        self.restoreState(settings.value("windowState", type=QtCore.QByteArray))
        settings.endGroup()

        settings.beginGroup("AudioBackend")
        self.settings_dialog.restoreState(settings)
        settings.endGroup()

    # slot
    def timer_toggle(self):
        if self.display_timer.isActive():
            self.logger.push("Timer stop")
            self.display_timer.stop()
            self.ui.actionStart.setText("Start")
            self.audiobackend.pause()
            self.centralwidget.pause()
            self.dockmanager.pause()
        else:
            self.logger.push("Timer start")
            self.display_timer.start()
            self.ui.actionStart.setText("Stop")
            self.audiobackend.restart()
            self.centralwidget.restart()
            self.dockmanager.restart()
Пример #20
0
class Friture(
        QMainWindow, ):
    def __init__(self, logger):
        QMainWindow.__init__(self)

        # logger
        self.logger = logger

        # Setup the user interface
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # Initialize the audio data ring buffer
        self.audiobuffer = AudioBuffer(self.logger)

        # Initialize the audio backend
        self.audiobackend = AudioBackend(self.logger)

        # this timer is used to update widgets that just need to display as fast as they can
        self.display_timer = QtCore.QTimer()
        self.display_timer.setInterval(
            SMOOTH_DISPLAY_TIMER_PERIOD_MS)  # constant timing

        # slow timer
        self.slow_timer = QtCore.QTimer()
        self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS)  # constant timing

        self.about_dialog = About_Dialog(self, self.logger, self.audiobackend,
                                         self.slow_timer)
        self.settings_dialog = Settings_Dialog(self, self.logger,
                                               self.audiobackend)

        self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger,
                                           "central_widget", 0)
        self.centralLayout = QVBoxLayout(self.ui.centralwidget)
        self.centralLayout.setContentsMargins(0, 0, 0, 0)
        self.centralLayout.addWidget(self.centralwidget)

        self.dockmanager = DockManager(self, self.logger)

        # timer ticks
        self.connect(self.display_timer, QtCore.SIGNAL('timeout()'),
                     self.update_buffer)
        self.connect(self.display_timer, QtCore.SIGNAL('timeout()'),
                     self.centralwidget.update)
        self.connect(self.display_timer, QtCore.SIGNAL('timeout()'),
                     self.dockmanager.update)

        # toolbar clicks
        self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'),
                     self.timer_toggle)
        self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'),
                     self.settings_called)
        self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'),
                     self.about_called)
        self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'),
                     self.dockmanager.new_dock)

        # restore the settings and widgets geometries
        self.restoreAppState()

        # start timers
        self.timer_toggle()
        self.slow_timer.start()

        self.logger.push("Init finished, entering the main loop")

    # slot
    def settings_called(self):
        self.settings_dialog.show()

    # slot
    def about_called(self):
        self.about_dialog.show()

    # event handler
    def closeEvent(self, event):
        self.saveAppState()
        event.accept()

    # method
    def saveAppState(self):
        settings = QtCore.QSettings("Friture", "Friture")

        settings.beginGroup("Docks")
        self.dockmanager.saveState(settings)
        settings.endGroup()

        settings.beginGroup("CentralWidget")
        self.centralwidget.saveState(settings)
        settings.endGroup()

        settings.beginGroup("MainWindow")
        windowGeometry = self.saveGeometry()
        settings.setValue("windowGeometry", windowGeometry)
        windowState = self.saveState()
        settings.setValue("windowState", windowState)
        settings.endGroup()

        settings.beginGroup("AudioBackend")
        self.settings_dialog.saveState(settings)
        settings.endGroup()

    # method
    def restoreAppState(self):
        settings = QtCore.QSettings("Friture", "Friture")

        settings.beginGroup("Docks")
        self.dockmanager.restoreState(settings)
        settings.endGroup()

        settings.beginGroup("CentralWidget")
        self.centralwidget.restoreState(settings)
        settings.endGroup()

        settings.beginGroup("MainWindow")
        self.restoreGeometry(settings.value("windowGeometry").toByteArray())
        self.restoreState(settings.value("windowState").toByteArray())
        settings.endGroup()

        settings.beginGroup("AudioBackend")
        self.settings_dialog.restoreState(settings)
        settings.endGroup()

    # slot
    def timer_toggle(self):
        if self.display_timer.isActive():
            self.logger.push("Timer stop")
            self.display_timer.stop()
            self.ui.actionStart.setText("Start")
        else:
            self.logger.push("Timer start")
            self.display_timer.start()
            self.ui.actionStart.setText("Stop")

    # slot
    def update_buffer(self):
        newpoints = self.audiobackend.update(self.audiobuffer.ringbuffer)
        self.audiobuffer.set_newdata(newpoints)
Пример #21
0
class Friture(QMainWindow, ):
	def __init__(self, logger):
		QMainWindow.__init__(self)

		# logger
		self.logger = logger

		# Setup the user interface
		self.ui = Ui_MainWindow()
		self.ui.setupUi(self)

		# sharedGLWidget is a hidden GL widget that will be used as a parent
		# to all QGLWidgets so that they have the same GL context, and can
		# share display lists, etc.
		self.sharedGLWidget = QtOpenGL.QGLWidget(self)
		self.sharedGLWidget.hide()

		# Initialize the audio data ring buffer
		self.audiobuffer = AudioBuffer(self.logger)

		# Initialize the audio backend
		self.audiobackend = AudioBackend(self.logger)

		# this timer is used to update widgets that just need to display as fast as they can
		self.display_timer = QtCore.QTimer()
		self.display_timer.setInterval(SMOOTH_DISPLAY_TIMER_PERIOD_MS) # constant timing

		# slow timer
		self.slow_timer = QtCore.QTimer()
		self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS) # constant timing

		self.about_dialog = About_Dialog(self, self.logger, self.audiobackend, self.slow_timer)
		self.settings_dialog = Settings_Dialog(self, self.logger, self.audiobackend)

		self.centralwidget = CentralWidget(self.ui.centralwidget, self.sharedGLWidget, self.logger, "central_widget", 0)
		self.centralLayout = QVBoxLayout(self.ui.centralwidget)
		self.centralLayout.setContentsMargins(0, 0, 0, 0)
		self.centralLayout.addWidget(self.centralwidget)

		self.dockmanager = DockManager(self, self.sharedGLWidget, self.logger)

		# timer ticks
		self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.update_buffer)
		self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.centralwidget.update)
		self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.dockmanager.update)

		# toolbar clicks
		self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'), self.timer_toggle)
		self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'), self.settings_called)
		self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'), self.about_called)
		self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'), self.dockmanager.new_dock)

		# restore the settings and widgets geometries
		self.restoreAppState()

		# start timers
		self.timer_toggle()
		self.slow_timer.start()
		
		self.logger.push("Init finished, entering the main loop")
	
	# slot
	def settings_called(self):
		self.settings_dialog.show()
	
	# slot
	def about_called(self):
		self.about_dialog.show()
	
	# event handler
	def closeEvent(self, event):
		self.saveAppState()
		event.accept()
	
	# method
	def saveAppState(self):
		settings = QtCore.QSettings("Friture", "Friture")
		
		settings.beginGroup("Docks")
		self.dockmanager.saveState(settings)
		settings.endGroup()
		
		settings.beginGroup("CentralWidget")
		self.centralwidget.saveState(settings)
		settings.endGroup()
		
		settings.beginGroup("MainWindow")
		windowGeometry = self.saveGeometry()
		settings.setValue("windowGeometry", windowGeometry)
		windowState = self.saveState()
		settings.setValue("windowState", windowState)
		settings.endGroup()

		settings.beginGroup("AudioBackend")
		self.settings_dialog.saveState(settings)
		settings.endGroup()
	
	# method
	def restoreAppState(self):
		settings = QtCore.QSettings("Friture", "Friture")

		settings.beginGroup("Docks")
		self.dockmanager.restoreState(settings)
		settings.endGroup()

		settings.beginGroup("CentralWidget")
		self.centralwidget.restoreState(settings)
		settings.endGroup()

		settings.beginGroup("MainWindow")
		self.restoreGeometry(settings.value("windowGeometry").toByteArray())
		self.restoreState(settings.value("windowState").toByteArray())
		settings.endGroup()
  
  		settings.beginGroup("AudioBackend")
		self.settings_dialog.restoreState(settings)
		settings.endGroup()

	# slot
	def timer_toggle(self):
		if self.display_timer.isActive():
			self.logger.push("Timer stop")
			self.display_timer.stop()
			self.ui.actionStart.setText("Start")
			self.centralwidget.pause()
			self.dockmanager.pause()
		else:
			self.logger.push("Timer start")
			self.display_timer.start()
			self.ui.actionStart.setText("Stop")
			self.centralwidget.restart()
			self.dockmanager.restart()

	# slot
	def update_buffer(self):
     		newpoints = self.audiobackend.update(self.audiobuffer.ringbuffer)
     		self.audiobuffer.set_newdata(newpoints)
Пример #22
0
 def closeEvent(self, event):
     AudioBackend().close()
     self.saveAppState()
     event.accept()
Пример #23
0
    def audio_callback(self, out_data, frame_count, time_info, status):
        if status:
            print(status, flush=True)

        N = frame_count

        if self.state == STOPPED:
            out_data.fill(0)
            return

        # if we cannot write any sample, return now
        if N == 0:
            return

        t = self.t + np.arange(0, N / float(SAMPLING_RATE), 1. / float(SAMPLING_RATE))

        name = self.combobox_generator_kind.currentText()

        generators = [generator for generator in self.generators if generator.name == name]

        if len(generators) == 0:
            print("generator error : index of signal type not found")
            out_data.fill(0)
            return

        if len(generators) > 1:
            print("generator error : 2 (or more) generators have the same name")
            out_data.fill(0)
            return

        generator = generators[0]
        floatdata = generator.signal(t)

        # add smooth ramps at start/stop to avoid undesirable bursts
        if self.state == STARTING:
            # add a ramp at the start
            t_ramp = self.t_start + np.arange(0, N / float(SAMPLING_RATE), 1. / float(SAMPLING_RATE))
            t_ramp = np.clip(t_ramp, 0., RAMP_LENGTH)
            floatdata *= t_ramp / RAMP_LENGTH
            self.t_start += N / float(SAMPLING_RATE)
            if self.t_start > RAMP_LENGTH:
                self.state = PLAYING

        if self.state == STOPPING:
            print("stopping", self.t_stop, N)
            # add a ramp at the end
            t_ramp = self.t_stop - np.arange(0, N / float(SAMPLING_RATE), 1. / float(SAMPLING_RATE))
            t_ramp = np.clip(t_ramp, 0., RAMP_LENGTH)
            floatdata *= t_ramp / RAMP_LENGTH
            self.t_stop -= N / float(SAMPLING_RATE)

            if self.t_stop < 0.:
                self.state = STOPPED
                self.stream_stop_ramp_finished.emit()

        # output channels are interleaved
        # we output to all channels simultaneously with the same data
        maxOutputChannels = AudioBackend().get_device_outputchannels_count(self.device)
        floatdata = np.tile(floatdata, (maxOutputChannels, 1)).transpose()

        int16info = np.iinfo(np.int16)
        norm_coeff = min(abs(int16info.min), int16info.max)
        intdata = (np.clip(floatdata, int16info.min, int16info.max) * norm_coeff).astype(np.int16)

        # update the time counter
        self.t += N / float(SAMPLING_RATE)

        # data copy
        out_data[:]  = intdata
Пример #24
0
    def __init__(self):
        QMainWindow.__init__(self)

        self.logger = logging.getLogger(__name__)

        # exception hook that logs to console, file, and display a message box
        self.errorDialogOpened = False
        sys.excepthook = self.excepthook

        # Setup the user interface
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # Initialize the audio data ring buffer
        self.audiobuffer = AudioBuffer()

        # Initialize the audio backend
        # signal containing new data from the audio callback thread, processed as numpy array
        AudioBackend().new_data_available.connect(
            self.audiobuffer.handle_new_data)

        # this timer is used to update widgets that just need to display as fast as they can
        self.display_timer = QtCore.QTimer()
        self.display_timer.setInterval(
            SMOOTH_DISPLAY_TIMER_PERIOD_MS)  # constant timing

        # slow timer
        self.slow_timer = QtCore.QTimer()
        self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS)  # constant timing

        self.about_dialog = About_Dialog(self, self.slow_timer)
        self.settings_dialog = Settings_Dialog(self)

        self.level_widget = Levels_Widget(self)
        self.level_widget.set_buffer(self.audiobuffer)
        self.audiobuffer.new_data_available.connect(
            self.level_widget.handle_new_data)

        self.hboxLayout = QHBoxLayout(self.ui.centralwidget)
        self.hboxLayout.setContentsMargins(0, 0, 0, 0)
        self.hboxLayout.addWidget(self.level_widget)

        self.centralLayout = TileLayout()
        self.centralLayout.setContentsMargins(0, 0, 0, 0)
        self.hboxLayout.addLayout(self.centralLayout)

        self.dockmanager = DockManager(self)

        # timer ticks
        self.display_timer.timeout.connect(self.dockmanager.canvasUpdate)
        self.display_timer.timeout.connect(self.level_widget.canvasUpdate)
        self.display_timer.timeout.connect(AudioBackend().fetchAudioData)

        # toolbar clicks
        self.ui.actionStart.triggered.connect(self.timer_toggle)
        self.ui.actionSettings.triggered.connect(self.settings_called)
        self.ui.actionAbout.triggered.connect(self.about_called)
        self.ui.actionNew_dock.triggered.connect(self.dockmanager.new_dock)

        # restore the settings and widgets geometries
        self.restoreAppState()

        # make sure the toolbar is shown
        # in case it was closed by mistake (before it was made impossible)
        self.ui.toolBar.setVisible(True)

        # prevent from hiding or moving the toolbar
        self.ui.toolBar.toggleViewAction().setVisible(True)
        self.ui.toolBar.setMovable(False)
        self.ui.toolBar.setFloatable(False)

        # start timers
        self.timer_toggle()
        self.slow_timer.start()

        self.logger.info("Init finished, entering the main loop")
Пример #25
0
    def __init__(self, parent):
        super().__init__(parent)

        self.audiobuffer = None

        self.setObjectName("Generator_Widget")
        self.grid_layout = QtWidgets.QGridLayout(self)
        self.grid_layout.setObjectName("grid_layout")

        self.generators = []
        self.generators.append(SineGenerator(self))
        self.generators.append(WhiteGenerator(self))
        self.generators.append(PinkGenerator(self))
        self.generators.append(SweepGenerator(self))
        self.generators.append(BurstGenerator(self))

        self.combobox_generator_kind = QtWidgets.QComboBox(self)
        self.combobox_generator_kind.setObjectName("combobox_generator_kind")

        self.stacked_settings_layout = QtWidgets.QStackedLayout()

        for generator in self.generators:
            self.combobox_generator_kind.addItem(generator.name)
            self.stacked_settings_layout.addWidget(generator.settingsWidget())

        self.combobox_generator_kind.setCurrentIndex(DEFAULT_GENERATOR_KIND_INDEX)

        self.t = 0.
        self.t_start = 0.
        self.t_stop = RAMP_LENGTH
        self.state = STOPPED

        self.stream_stop_ramp_finished.connect(self.stop_stream_after_ramp)

        self.device = None
        self.stream = None

        # we will try to open all the output devices until one
        # works, starting by the default input device
        for device in AudioBackend().output_devices:
            Logger().push("Opening the stream for device: "+ device['name'])
            try:
                self.stream = AudioBackend().open_output_stream(device, self.audio_callback)
                self.stream.start()
                self.stream.stop()
                self.device = device
                Logger().push("Stream opened successfully")
                break
            except Exception as exception:
                Logger().push("Failed to open stream: " + str(exception))

        self.start_stop_button = QtWidgets.QPushButton(self)

        startStopIcon = QtGui.QIcon()
        startStopIcon.addPixmap(QtGui.QPixmap(":/images-src/start.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        startStopIcon.addPixmap(QtGui.QPixmap(":/images-src/stop.svg"), QtGui.QIcon.Normal, QtGui.QIcon.On)
        startStopIcon.addPixmap(QtGui.QPixmap(":/images-src/stop.svg"), QtGui.QIcon.Active, QtGui.QIcon.On)
        startStopIcon.addPixmap(QtGui.QPixmap(":/images-src/stop.svg"), QtGui.QIcon.Selected, QtGui.QIcon.On)
        startStopIcon.addPixmap(QtGui.QPixmap(":/images-src/stop.svg"), QtGui.QIcon.Disabled, QtGui.QIcon.On)
        self.start_stop_button.setIcon(startStopIcon)

        self.start_stop_button.setObjectName("generatorStartStop")
        self.start_stop_button.setText("Start")
        self.start_stop_button.setToolTip("Start/Stop generator")
        self.start_stop_button.setCheckable(True)
        self.start_stop_button.setChecked(False)

        self.grid_layout.addWidget(self.start_stop_button, 0, 0, 1, 1)
        self.grid_layout.addWidget(self.combobox_generator_kind, 1, 0, 1, 1)
        self.grid_layout.addLayout(self.stacked_settings_layout, 2, 0, 1, 1)

        self.combobox_generator_kind.activated.connect(self.stacked_settings_layout.setCurrentIndex)
        self.start_stop_button.toggled.connect(self.start_stop_button_toggle)

        # initialize the settings dialog
        devices = AudioBackend().get_readable_output_devices_list()
        if self.device is not None:
            device_index = AudioBackend().output_devices.index(self.device)
        else:
            device_index = None
        self.settings_dialog = Generator_Settings_Dialog(self, devices, device_index)

        self.settings_dialog.combobox_output_device.currentIndexChanged.connect(self.device_changed)
Пример #26
0
 def single_input_type_selected(self, checked):
     if checked:
         self.groupBox_second.setEnabled(False)
         AudioBackend().set_single_input()
         Logger().push("Switching to single input")
Пример #27
0
class Friture(
        QMainWindow, ):
    def __init__(self, logger):
        QMainWindow.__init__(self)

        # logger
        self.logger = logger

        # Setup the user interface
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.settings_dialog = Settings_Dialog(self)
        self.about_dialog = About_Dialog(self)

        self.chunk_number = 0

        self.buffer_timer_time = 0.

        self.cpu_percent = 0.

        # Initialize the audio data ring buffer
        self.audiobuffer = AudioBuffer()

        # Initialize the audio backend
        self.audiobackend = AudioBackend(self.logger)

        devices = self.audiobackend.get_readable_devices_list()
        for device in devices:
            self.settings_dialog.comboBox_inputDevice.addItem(device)

        channels = self.audiobackend.get_readable_current_channels()
        for channel in channels:
            self.settings_dialog.comboBox_firstChannel.addItem(channel)
            self.settings_dialog.comboBox_secondChannel.addItem(channel)

        current_device = self.audiobackend.get_readable_current_device()
        self.settings_dialog.comboBox_inputDevice.setCurrentIndex(
            current_device)

        first_channel = self.audiobackend.get_current_first_channel()
        self.settings_dialog.comboBox_firstChannel.setCurrentIndex(
            first_channel)
        second_channel = self.audiobackend.get_current_second_channel()
        self.settings_dialog.comboBox_secondChannel.setCurrentIndex(
            second_channel)

        # this timer is used to update widgets that just need to display as fast as they can
        self.display_timer = QtCore.QTimer()
        self.display_timer.setInterval(
            SMOOTH_DISPLAY_TIMER_PERIOD_MS)  # constant timing

        # slow timer
        self.slow_timer = QtCore.QTimer()
        self.slow_timer.setInterval(1000)  # constant timing

        self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger,
                                           "central_widget", 0)
        self.centralLayout = QVBoxLayout(self.ui.centralwidget)
        self.centralLayout.setContentsMargins(0, 0, 0, 0)
        self.centralLayout.addWidget(self.centralwidget)

        # timer ticks
        self.connect(self.display_timer, QtCore.SIGNAL('timeout()'),
                     self.update_buffer)
        self.connect(self.display_timer, QtCore.SIGNAL('timeout()'),
                     self.statistics)

        # timer ticks
        self.connect(self.slow_timer, QtCore.SIGNAL('timeout()'),
                     self.get_cpu_percent)

        # toolbar clicks
        self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'),
                     self.timer_toggle)
        self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'),
                     self.settings_called)
        self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'),
                     self.about_called)
        self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'),
                     self.new_dock_called)

        # settings signals
        self.connect(self.settings_dialog.comboBox_inputDevice,
                     QtCore.SIGNAL('currentIndexChanged(int)'),
                     self.input_device_changed)
        self.connect(self.settings_dialog.comboBox_firstChannel,
                     QtCore.SIGNAL('currentIndexChanged(int)'),
                     self.first_channel_changed)
        self.connect(self.settings_dialog.comboBox_secondChannel,
                     QtCore.SIGNAL('currentIndexChanged(int)'),
                     self.second_channel_changed)
        self.connect(self.settings_dialog.radioButton_single,
                     QtCore.SIGNAL('toggled(bool)'),
                     self.single_input_type_selected)
        self.connect(self.settings_dialog.radioButton_duo,
                     QtCore.SIGNAL('toggled(bool)'),
                     self.duo_input_type_selected)
        self.connect(self.settings_dialog.doubleSpinBox_delay,
                     QtCore.SIGNAL('valueChanged(double)'), self.delay_changed)

        # log change
        self.connect(self.logger, QtCore.SIGNAL('logChanged'),
                     self.log_changed)
        self.connect(self.about_dialog.log_scrollarea.verticalScrollBar(),
                     QtCore.SIGNAL('rangeChanged(int,int)'),
                     self.log_scroll_range_changed)

        # restore the settings and widgets geometries
        self.restoreAppState()

        # start timers
        self.timer_toggle()
        self.slow_timer.start()

        self.logger.push("Init finished, entering the main loop")

    # slot
    # update the log widget with the new log content
    def log_changed(self):
        self.about_dialog.LabelLog.setText(self.logger.text())

    # slot
    # scroll the log widget so that the last line is visible
    def log_scroll_range_changed(self, min, max):
        scrollbar = self.about_dialog.log_scrollarea.verticalScrollBar()
        scrollbar.setValue(max)

    # slot
    def settings_called(self):
        self.settings_dialog.show()

    # slot
    def about_called(self):
        self.about_dialog.show()

    # slot
    def new_dock_called(self):
        # the dock objectName is unique
        docknames = [dock.objectName() for dock in self.docks]
        dockindexes = [int(str(name).partition(' ')[-1]) for name in docknames]
        if len(dockindexes) == 0:
            index = 1
        else:
            index = max(dockindexes) + 1
        name = "Dock %d" % index
        new_dock = Dock(self, self.logger, name)
        self.addDockWidget(QtCore.Qt.TopDockWidgetArea, new_dock)

        self.docks += [new_dock]

    #slot
    def dock_closed(self, dock):
        self.docks.remove(dock)

    # event handler
    def closeEvent(self, event):
        self.saveAppState()
        event.accept()

    # method
    def saveAppState(self):
        settings = QtCore.QSettings("Friture", "Friture")

        settings.beginGroup("Docks")
        docknames = [dock.objectName() for dock in self.docks]
        settings.setValue("dockNames", docknames)
        for dock in self.docks:
            settings.beginGroup(dock.objectName())
            dock.saveState(settings)
            settings.endGroup()
        settings.endGroup()

        settings.beginGroup("CentralWidget")
        self.centralwidget.saveState(settings)
        settings.endGroup()

        settings.beginGroup("MainWindow")
        windowGeometry = self.saveGeometry()
        settings.setValue("windowGeometry", windowGeometry)
        windowState = self.saveState()
        settings.setValue("windowState", windowState)
        settings.endGroup()

        settings.beginGroup("AudioBackend")
        self.settings_dialog.saveState(settings)
        settings.endGroup()

    # method
    def restoreAppState(self):
        settings = QtCore.QSettings("Friture", "Friture")

        settings.beginGroup("Docks")
        if settings.contains("dockNames"):
            docknames = settings.value("dockNames", []).toList()
            docknames = [dockname.toString() for dockname in docknames]
            # list of docks
            self.docks = [Dock(self, self.logger, name) for name in docknames]
            for dock in self.docks:
                settings.beginGroup(dock.objectName())
                dock.restoreState(settings)
                settings.endGroup()
        else:
            self.logger.push("First launch, display a default set of docks")
            self.docks = []
            self.docks += [Dock(self, self.logger, "Dock 0",
                                type=3)]  #spectrogram
            self.docks += [Dock(self, self.logger, "Dock 1",
                                type=4)]  #octave spectrum
            #self.docks += [Dock(self, self.logger, "Dock 2", type = 1)] #scope
            #self.docks += [Dock(self, self.logger, "Dock 3", type = 0)] #level
            for dock in self.docks:
                self.addDockWidget(QtCore.Qt.TopDockWidgetArea, dock)

        settings.endGroup()

        settings.beginGroup("CentralWidget")
        self.centralwidget.restoreState(settings)
        settings.endGroup()

        settings.beginGroup("MainWindow")
        self.restoreGeometry(settings.value("windowGeometry").toByteArray())
        self.restoreState(settings.value("windowState").toByteArray())
        settings.endGroup()

        settings.beginGroup("AudioBackend")
        self.settings_dialog.restoreState(settings)
        settings.endGroup()

    # slot
    def timer_toggle(self):
        if self.display_timer.isActive():
            self.logger.push("Timer stop")
            self.display_timer.stop()
            self.ui.actionStart.setText("Start")
            for dock in self.docks:
                dock.custom_timer_stop()
            self.centralwidget.custom_timer_stop()
        else:
            self.logger.push("Timer start")
            self.display_timer.start()
            self.ui.actionStart.setText("Stop")
            for dock in self.docks:
                dock.custom_timer_start()
            self.centralwidget.custom_timer_start()

    # slot
    def update_buffer(self):
        (chunks, t,
         newpoints) = self.audiobackend.update(self.audiobuffer.ringbuffer)
        self.audiobuffer.set_newdata(newpoints)
        self.chunk_number += chunks
        self.buffer_timer_time = (95. * self.buffer_timer_time + 5. * t) / 100.

    def get_cpu_percent(self):
        self.cpu_percent = psutil.cpu_percent()

    # method
    def statistics(self):
        if not self.about_dialog.LabelStats.isVisible():
            return

        label = "Chunk #%d\n"\
        "Audio buffer retrieval: %.02f ms\n"\
        "Global CPU usage: %d %%"\
        % (self.chunk_number, self.buffer_timer_time, self.cpu_percent)

        self.about_dialog.LabelStats.setText(label)

    # slot
    def input_device_changed(self, index):
        self.ui.actionStart.setChecked(False)

        success, index = self.audiobackend.select_input_device(index)

        self.settings_dialog.comboBox_inputDevice.setCurrentIndex(index)

        if not success:
            # Note: the error message is a child of the settings dialog, so that
            # that dialog remains on top when the error message is closed
            error_message = QErrorMessage(self.settings_dialog)
            error_message.setWindowTitle("Input device error")
            error_message.showMessage(
                "Impossible to use the selected input device, reverting to the previous one"
            )

        # reset the channels
        first_channel = self.audiobackend.get_current_first_channel()
        self.settings_dialog.comboBox_firstChannel.setCurrentIndex(
            first_channel)
        second_channel = self.audiobackend.get_current_second_channel()
        self.settings_dialog.comboBox_secondChannel.setCurrentIndex(
            second_channel)

        self.ui.actionStart.setChecked(True)

    # slot
    def first_channel_changed(self, index):
        self.ui.actionStart.setChecked(False)

        success, index = self.audiobackend.select_first_channel(index)

        self.settings_dialog.comboBox_firstChannel.setCurrentIndex(index)

        if not success:
            # Note: the error message is a child of the settings dialog, so that
            # that dialog remains on top when the error message is closed
            error_message = QErrorMessage(self.settings_dialog)
            error_message.setWindowTitle("Input device error")
            error_message.showMessage(
                "Impossible to use the selected channel as the first channel, reverting to the previous one"
            )

        self.ui.actionStart.setChecked(True)

    # slot
    def second_channel_changed(self, index):
        self.ui.actionStart.setChecked(False)

        success, index = self.audiobackend.select_second_channel(index)

        self.settings_dialog.comboBox_secondChannel.setCurrentIndex(index)

        if not success:
            # Note: the error message is a child of the settings dialog, so that
            # that dialog remains on top when the error message is closed
            error_message = QErrorMessage(self.settings_dialog)
            error_message.setWindowTitle("Input device error")
            error_message.showMessage(
                "Impossible to use the selected channel as the second channel, reverting to the previous one"
            )

        self.ui.actionStart.setChecked(True)

    # slot
    def single_input_type_selected(self, checked):
        if checked:
            self.settings_dialog.groupBox_second.setEnabled(False)
            self.settings_dialog.label_delay.setEnabled(False)
            self.settings_dialog.doubleSpinBox_delay.setEnabled(False)
            self.audiobackend.set_single_input()
            self.logger.push("Switching to single input")

    # slot
    def duo_input_type_selected(self, checked):
        if checked:
            self.settings_dialog.groupBox_second.setEnabled(True)
            self.settings_dialog.label_delay.setEnabled(True)
            self.settings_dialog.doubleSpinBox_delay.setEnabled(True)
            self.audiobackend.set_duo_input()
            self.logger.push("Switching to difference between two inputs")

    # slot
    def delay_changed(self, delay_ms):
        self.delay_ms = delay_ms
        self.logger.push("Delay changed to %f" % delay_ms)
        self.audiobuffer.set_delay_ms(delay_ms)
Пример #28
0
class Generator_Widget(QtWidgets.QWidget):

    stream_stop_ramp_finished = QtCore.pyqtSignal()

    def __init__(self, parent):
        super().__init__(parent)

        self.audiobuffer = None

        self.setObjectName("Generator_Widget")
        self.grid_layout = QtWidgets.QGridLayout(self)
        self.grid_layout.setObjectName("grid_layout")

        self.generators = []
        self.generators.append(SineGenerator(self))
        self.generators.append(WhiteGenerator(self))
        self.generators.append(PinkGenerator(self))
        self.generators.append(SweepGenerator(self))
        self.generators.append(BurstGenerator(self))

        self.combobox_generator_kind = QtWidgets.QComboBox(self)
        self.combobox_generator_kind.setObjectName("combobox_generator_kind")

        self.stacked_settings_layout = QtWidgets.QStackedLayout()

        for generator in self.generators:
            self.combobox_generator_kind.addItem(generator.name)
            self.stacked_settings_layout.addWidget(generator.settingsWidget())

        self.combobox_generator_kind.setCurrentIndex(DEFAULT_GENERATOR_KIND_INDEX)

        self.t = 0.
        self.t_start = 0.
        self.t_stop = RAMP_LENGTH
        self.state = STOPPED

        self.stream_stop_ramp_finished.connect(self.stop_stream_after_ramp)

        self.device = None
        self.stream = None

        # we will try to open all the output devices until one
        # works, starting by the default input device
        for device in AudioBackend().output_devices:
            Logger().push("Opening the stream for device: "+ device['name'])
            try:
                self.stream = AudioBackend().open_output_stream(device, self.audio_callback)
                self.stream.start()
                self.stream.stop()
                self.device = device
                Logger().push("Stream opened successfully")
                break
            except Exception as exception:
                Logger().push("Failed to open stream: " + str(exception))

        self.start_stop_button = QtWidgets.QPushButton(self)

        startStopIcon = QtGui.QIcon()
        startStopIcon.addPixmap(QtGui.QPixmap(":/images-src/start.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        startStopIcon.addPixmap(QtGui.QPixmap(":/images-src/stop.svg"), QtGui.QIcon.Normal, QtGui.QIcon.On)
        startStopIcon.addPixmap(QtGui.QPixmap(":/images-src/stop.svg"), QtGui.QIcon.Active, QtGui.QIcon.On)
        startStopIcon.addPixmap(QtGui.QPixmap(":/images-src/stop.svg"), QtGui.QIcon.Selected, QtGui.QIcon.On)
        startStopIcon.addPixmap(QtGui.QPixmap(":/images-src/stop.svg"), QtGui.QIcon.Disabled, QtGui.QIcon.On)
        self.start_stop_button.setIcon(startStopIcon)

        self.start_stop_button.setObjectName("generatorStartStop")
        self.start_stop_button.setText("Start")
        self.start_stop_button.setToolTip("Start/Stop generator")
        self.start_stop_button.setCheckable(True)
        self.start_stop_button.setChecked(False)

        self.grid_layout.addWidget(self.start_stop_button, 0, 0, 1, 1)
        self.grid_layout.addWidget(self.combobox_generator_kind, 1, 0, 1, 1)
        self.grid_layout.addLayout(self.stacked_settings_layout, 2, 0, 1, 1)

        self.combobox_generator_kind.activated.connect(self.stacked_settings_layout.setCurrentIndex)
        self.start_stop_button.toggled.connect(self.start_stop_button_toggle)

        # initialize the settings dialog
        devices = AudioBackend().get_readable_output_devices_list()
        if self.device is not None:
            device_index = AudioBackend().output_devices.index(self.device)
        else:
            device_index = None
        self.settings_dialog = Generator_Settings_Dialog(self, devices, device_index)

        self.settings_dialog.combobox_output_device.currentIndexChanged.connect(self.device_changed)

#        channels = AudioBackend().get_readable_current_output_channels()
#        for channel in channels:
#            self.settings_dialog.comboBox_firstChannel.addItem(channel)
#            self.settings_dialog.comboBox_secondChannel.addItem(channel)

#        current_device = AudioBackend().get_readable_current_output_device()
#        self.settings_dialog.combobox_output_device.setCurrentIndex(current_device)

#        first_channel = AudioBackend().get_current_first_channel()
#        self.settings_dialog.comboBox_firstChannel.setCurrentIndex(first_channel)
#        second_channel = AudioBackend().get_current_second_channel()
#        self.settings_dialog.comboBox_secondChannel.setCurrentIndex(second_channel)

    def device_changed(self, index):
        device = AudioBackend().output_devices[index]

        # save current stream in case we need to restore it
        previous_stream = self.stream
        previous_device = self.device

        error_message = ""

        Logger().push("Trying to write to output device " + device['name'])

        # first see if the format is supported by PortAudio
        try:
            success = AudioBackend().is_output_format_supported(device, np.int16)
        except Exception as exception:
            Logger().push("Format is not supported: " + str(exception))
            success = False

        if success:
            try:
                self.stream = AudioBackend().open_output_stream(device, self.audio_callback)
                self.device = device
                self.stream.start()
                if self.state not in [STARTING, PLAYING]:
                    self.stream.stop()
                success = True
            except OSError as error:
                Logger().push("Fail: " + str(error))
                success = False

        if success:
            Logger().push("Success")
            previous_stream.stop()
        else:
            if self.stream is not None:
                self.stream.stop()
            # restore previous stream
            self.stream = previous_stream
            self.device = previous_device

            # Note: the error message is a child of the settings dialog, so that
            # that dialog remains on top when the error message is closed
            error_message = QtWidgets.QErrorMessage(self.settings_dialog)
            error_message.setWindowTitle("Output device error")
            error_message.showMessage("Impossible to use the selected output device, reverting to the previous one. Reason is: " + error_message)

        self.settings_dialog.combobox_output_device.setCurrentIndex(AudioBackend().output_devices.index(self.device))

    def settings_called(self, checked):
        self.settings_dialog.show()

    # method
    def set_buffer(self, buffer):
        self.audiobuffer = buffer

    # slot
    def start_stop_button_toggle(self, checked):
        if checked:
            self.start_stop_button.setText("Stop")
            if self.state == STOPPED or self.state == STOPPING:
                self.state = STARTING
                self.t_start = 0.
                self.stream.start()
        else:
            self.start_stop_button.setText("Start")
            if self.state == PLAYING or self.state == STARTING:
                self.state = STOPPING
                self.t_stop = RAMP_LENGTH
                # will stop at the end of the ramp

    def stop_stream_after_ramp(self):
        self.stream.stop()

    def handle_new_data(self, floatdata):
        # we do not make anything of the input data in the generator...
        return

    def audio_callback(self, out_data, frame_count, time_info, status):
        if status:
            print(status, flush=True)

        N = frame_count

        if self.state == STOPPED:
            out_data.fill(0)
            return

        # if we cannot write any sample, return now
        if N == 0:
            return

        t = self.t + np.arange(0, N / float(SAMPLING_RATE), 1. / float(SAMPLING_RATE))

        name = self.combobox_generator_kind.currentText()

        generators = [generator for generator in self.generators if generator.name == name]

        if len(generators) == 0:
            print("generator error : index of signal type not found")
            out_data.fill(0)
            return

        if len(generators) > 1:
            print("generator error : 2 (or more) generators have the same name")
            out_data.fill(0)
            return

        generator = generators[0]
        floatdata = generator.signal(t)

        # add smooth ramps at start/stop to avoid undesirable bursts
        if self.state == STARTING:
            # add a ramp at the start
            t_ramp = self.t_start + np.arange(0, N / float(SAMPLING_RATE), 1. / float(SAMPLING_RATE))
            t_ramp = np.clip(t_ramp, 0., RAMP_LENGTH)
            floatdata *= t_ramp / RAMP_LENGTH
            self.t_start += N / float(SAMPLING_RATE)
            if self.t_start > RAMP_LENGTH:
                self.state = PLAYING

        if self.state == STOPPING:
            print("stopping", self.t_stop, N)
            # add a ramp at the end
            t_ramp = self.t_stop - np.arange(0, N / float(SAMPLING_RATE), 1. / float(SAMPLING_RATE))
            t_ramp = np.clip(t_ramp, 0., RAMP_LENGTH)
            floatdata *= t_ramp / RAMP_LENGTH
            self.t_stop -= N / float(SAMPLING_RATE)

            if self.t_stop < 0.:
                self.state = STOPPED
                self.stream_stop_ramp_finished.emit()

        # output channels are interleaved
        # we output to all channels simultaneously with the same data
        maxOutputChannels = AudioBackend().get_device_outputchannels_count(self.device)
        floatdata = np.tile(floatdata, (maxOutputChannels, 1)).transpose()

        int16info = np.iinfo(np.int16)
        norm_coeff = min(abs(int16info.min), int16info.max)
        intdata = (np.clip(floatdata, int16info.min, int16info.max) * norm_coeff).astype(np.int16)

        # update the time counter
        self.t += N / float(SAMPLING_RATE)

        # data copy
        out_data[:]  = intdata

    def canvasUpdate(self):
        return

    def saveState(self, settings):
        settings.setValue("generator kind", self.combobox_generator_kind.currentIndex())

        for generator in self.generators:
            generator.settingsWidget().saveState(settings)

        self.settings_dialog.saveState(settings)

    def restoreState(self, settings):
        generator_kind = settings.value("generator kind", DEFAULT_GENERATOR_KIND_INDEX, type=int)
        self.combobox_generator_kind.setCurrentIndex(generator_kind)
        self.stacked_settings_layout.setCurrentIndex(generator_kind)

        for generator in self.generators:
            generator.settingsWidget().restoreState(settings)

        self.settings_dialog.restoreState(settings)
Пример #29
0
    def __init__(self, logger):
        QMainWindow.__init__(self)

        # logger
        self.logger = logger

        # Setup the user interface
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # Initialize the audio data ring buffer
        self.audiobuffer = AudioBuffer(self.logger)

        # Initialize the audio backend
        self.audiobackend = AudioBackend(self.logger)

        # this timer is used to update widgets that just need to display as fast as they can
        self.display_timer = QtCore.QTimer()
        self.display_timer.setInterval(
            SMOOTH_DISPLAY_TIMER_PERIOD_MS)  # constant timing

        # slow timer
        self.slow_timer = QtCore.QTimer()
        self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS)  # constant timing

        self.about_dialog = About_Dialog(self, self.logger, self.audiobackend,
                                         self.slow_timer)
        self.settings_dialog = Settings_Dialog(self, self.logger,
                                               self.audiobackend)

        self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger,
                                           "central_widget", 0)
        self.centralLayout = QVBoxLayout(self.ui.centralwidget)
        self.centralLayout.setContentsMargins(0, 0, 0, 0)
        self.centralLayout.addWidget(self.centralwidget)

        self.dockmanager = DockManager(self, self.logger)

        # timer ticks
        self.connect(self.display_timer, QtCore.SIGNAL('timeout()'),
                     self.update_buffer)
        self.connect(self.display_timer, QtCore.SIGNAL('timeout()'),
                     self.centralwidget.update)
        self.connect(self.display_timer, QtCore.SIGNAL('timeout()'),
                     self.dockmanager.update)

        # toolbar clicks
        self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'),
                     self.timer_toggle)
        self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'),
                     self.settings_called)
        self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'),
                     self.about_called)
        self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'),
                     self.dockmanager.new_dock)

        # restore the settings and widgets geometries
        self.restoreAppState()

        # start timers
        self.timer_toggle()
        self.slow_timer.start()

        self.logger.push("Init finished, entering the main loop")