def select_input_device(self, index): device = self.input_devices[index] # save current stream in case we need to restore it previous_stream = self.stream previous_device = self.device Logger().push("Trying to open input device #%d" % (index)) try: self.stream = self.open_stream(device) self.device = device self.stream.start() success = True except Exception as exception: Logger().push("Fail: " + str(exception)) success = False if self.stream is not None: self.stream.stop() # restore previous stream self.stream = previous_stream self.device = previous_device if success: Logger().push("Success") previous_stream.stop() self.first_channel = 0 nchannels = self.device['max_input_channels'] if nchannels == 1: self.second_channel = 0 else: self.second_channel = 1 return success, self.input_devices.index(self.device)
def setdataTwoChannels(self, x, y, y2): if self.canvas_width != self.canvasWidget.width(): Logger().push("timeplot : changed canvas width") self.canvas_width = self.canvasWidget.width() self.update_xscale() if not self.dual_channel: self.dual_channel = True self.canvasWidget.attach(self.curve2) # enable the legend to discrimate between the two channels self.legendWidget.show() # the canvas reisze event will trigger a full replot if self.xmax != x[-1]: Logger().push("timeplot : changing x max") self.xmax = x[-1] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if self.xmin != x[0]: Logger().push("timeplot : changing x min") self.xmin = x[0] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if not self.paused: # y_interp = np.interp(self.xscaled, x, y) # y_interp2 = np.interp(self.xscaled, x, y2) # ClassPlot.setdata(self, self.xscaled, y_interp) # self.curve2.setData(self.xscaled, y_interp2) self.curve.setData(x, y) self.curve2.setData(x, y2) self.draw()
def setdata(self, x, y): if self.canvas_width != self.canvasWidget.width(): Logger().push("timeplot : changed canvas width") self.canvas_width = self.canvasWidget.width() self.update_xscale() if self.dual_channel: self.dual_channel = False self.canvasWidget.detach(self.curve2) # disable the legend, useless when one channel is active self.legendWidget.hide() # the canvas reisze event will trigger a full replot if self.xmax != x[-1]: Logger().push("timeplot : changing x max") self.xmax = x[-1] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if self.xmin != x[0]: Logger().push("timeplot : changing x min") self.xmin = x[0] self.settimerange(self.xmin, self.xmax) self.update_xscale() self.needfullreplot = True if not self.paused: y_interp = np.interp(self.xscaled, x, y) self.curve.setData(self.xscaled, y_interp) self.draw()
def timer_toggle(self): if self.display_timer.isActive(): Logger().push("Timer stop") self.display_timer.stop() self.ui.actionStart.setText("Start") AudioBackend().pause() self.dockmanager.pause() else: Logger().push("Timer start") self.display_timer.start() self.ui.actionStart.setText("Stop") AudioBackend().restart() self.dockmanager.restart()
def grow_if_needed(self, length): if length > self.buffer_length: # let the buffer grow according to our needs old_length = self.buffer_length new_length = int(1.5 * length) Logger().push("Ringbuffer: growing buffer for length %d" % (new_length)) # create new buffer newbuffer = zeros((self.buffer.shape[0], 2 * new_length)) # copy existing data so that self.offset does not have to be changed old_offset_mod = self.offset % old_length new_offset_mod = self.offset % new_length shift = new_offset_mod - old_offset_mod #shift can be negative, computing modulo again shift %= new_length # first copy, always complete newbuffer[:, shift:shift + old_length] = self.buffer[:, :old_length] # second copy, can be folded direct = min(old_length, new_length - shift) folded = old_length - direct newbuffer[:, new_length + shift:new_length + shift + direct] = self.buffer[:, :direct] newbuffer[:, :folded] = self.buffer[:, direct:direct + folded] # assign self.butter to the new larger buffer self.buffer = newbuffer self.buffer_length = new_length
def get_readable_devices_list(self): input_devices = self.get_input_devices() raw_devices = sounddevice.query_devices() try: default_input_device = sounddevice.query_devices(kind='input') default_input_device['index'] = raw_devices.index( default_input_device) except sounddevice.PortAudioError as exception: Logger().push("Failed to query the default input device: %s" % (exception)) default_input_device = None devices_list = [] for device in input_devices: api = sounddevice.query_hostapis(device['hostapi'])['name'] if default_input_device is not None and device[ 'index'] == default_input_device['index']: extra_info = ' (default)' else: extra_info = '' nchannels = device['max_input_channels'] desc = "%s (%d channels) (%s) %s" % (device['name'], nchannels, api, extra_info) devices_list += [desc] return devices_list
def get_input_devices(self): devices = sounddevice.query_devices() # early exit if there is no input device. Otherwise query_devices(kind='input') fails input_devices = [ device for device in devices if device['max_input_channels'] > 0 ] if len(input_devices) == 0: return [] try: default_input_device = sounddevice.query_devices(kind='input') except sounddevice.PortAudioError as exception: Logger().push("Failed to query the default input device: %s" % (exception)) default_input_device = None input_devices = [] if default_input_device is not None: # start by the default input device default_input_device['index'] = devices.index(default_input_device) input_devices += [default_input_device] for device in devices: # select only the input devices by looking at the number of input channels if device['max_input_channels'] > 0: device['index'] = devices.index(device) # default input device has already been inserted if default_input_device is not None and device[ 'index'] != default_input_device['index']: input_devices += [device] return input_devices
def setcanvas_width(self, canvas_width): if self.canvas_width != canvas_width: self.canvas_width = canvas_width self.resize(self.canvas_width, self.canvas_height) self.canvasWidthChanged.emit(canvas_width) Logger().push("Spectrogram image: canvas_width changed, now: %d" % (canvas_width))
def open_stream(self, device): Logger().push("Opening the stream for device: " + device['name']) # by default we open the device stream with all the channels # (interleaved in the data buffer) stream = sounddevice.InputStream(samplerate=SAMPLING_RATE, blocksize=FRAMES_PER_BUFFER, device=device['index'], channels=device['max_input_channels'], dtype=int16, callback=self.callback) lat_ms = 1000 * stream.latency Logger().push("Device claims %d ms latency" % (lat_ms)) return stream
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 __init__(self): QtCore.QObject.__init__(self) self.duo_input = False Logger().push("Initializing audio backend") # look for devices self.input_devices = self.get_input_devices() self.output_devices = self.get_output_devices() self.device = None self.first_channel = None self.second_channel = None self.stream = None # we will try to open all the input devices until one # works, starting by the default input device for device in self.input_devices: try: self.stream = self.open_stream(device) self.stream.start() self.device = device Logger().push("Success") break except Exception as exception: Logger().push("Failed to open stream: " + str(exception)) if self.device is not None: self.first_channel = 0 nchannels = self.get_current_device_nchannels() if nchannels == 1: self.second_channel = 0 else: self.second_channel = 1 # counter for the number of input buffer overflows self.xruns = 0 self.chunk_number = 0 self.new_data_available_from_callback.connect(self.handle_new_data) self.devices_with_timing_errors = []
def responsetimechanged(self, index): if index == 0: response_time = 0.025 elif index == 1: response_time = 0.125 elif index == 2: response_time = 0.3 elif index == 3: response_time = 1. Logger().push("responsetimechanged slot %d %d" % (index, response_time)) self.parent().setresponsetime(response_time)
def set_maxfreq(self, maxfreq): if maxfreq != self.maxfreq: self.maxfreq = maxfreq decimation = SAMPLING_RATE / (2 * maxfreq) self.decimation = 2**(floor(log2(decimation))) if self.decimation < 1: self.decimation = 1 self.update_freq_cache() self.update_window() self.update_size() Logger().push("audioproc: will decimate %d times" % self.decimation)
def update_freq_cache(self): if len(self.freq) != self.fft_size / (2 * self.decimation) + 1: Logger().push("audioproc: updating self.freq cache") self.freq = linspace(0, SAMPLING_RATE / (2 * self.decimation), self.fft_size / (2 * self.decimation) + 1) # compute psychoacoustic weighting. See http://en.wikipedia.org/wiki/A-weighting f = self.freq Rc = 12200. ** 2 * f ** 2 / ((f ** 2 + 20.6 ** 2) * (f ** 2 + 12200. ** 2)) Rb = 12200. ** 2 * f ** 3 / ((f ** 2 + 20.6 ** 2) * (f ** 2 + 12200. ** 2) * ((f ** 2 + 158.5 ** 2) ** 0.5)) Ra = 12200. ** 2 * f ** 4 / ((f ** 2 + 20.6 ** 2) * (f ** 2 + 12200. ** 2) * ((f ** 2 + 107.7 ** 2) ** 0.5) * ((f ** 2 + 737.9 ** 2) ** 0.5)) eps = 1e-50 self.C = 0.06 + 20. * log10(Rc + eps) self.B = 0.17 + 20. * log10(Rb + eps) self.A = 2.0 + 20. * log10(Ra + eps)
def is_output_format_supported(self, device, output_format): try: sounddevice.check_output_settings( device=device['index'], channels=device['max_output_channels'], dtype=output_format, samplerate=SAMPLING_RATE) success = True except Exception as exception: Logger().push("Format is not supported: " + str(exception)) success = False return success
def __init__(self, parent): super().__init__(parent) self.setObjectName("tab_log") self.log_scrollarea = QtWidgets.QScrollArea(self) self.log_scrollarea.setWidgetResizable(True) self.log_scrollarea.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) self.log_scrollarea.setObjectName("log_scrollArea") self.log_scrollAreaWidgetContents = QtWidgets.QWidget( self.log_scrollarea) self.log_scrollAreaWidgetContents.setGeometry( QtCore.QRect(0, 0, 87, 220)) self.log_scrollAreaWidgetContents.setStyleSheet( """QWidget { background: white }""") self.log_scrollAreaWidgetContents.setObjectName( "log_scrollAreaWidgetContents") self.log_scrollarea.setWidget(self.log_scrollAreaWidgetContents) self.LabelLog = QtWidgets.QLabel(self.log_scrollAreaWidgetContents) self.LabelLog.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) self.LabelLog.setTextInteractionFlags( QtCore.Qt.LinksAccessibleByKeyboard | QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextBrowserInteraction | QtCore.Qt.TextSelectableByKeyboard | QtCore.Qt.TextSelectableByMouse) self.LabelLog.setObjectName("LabelLog") self.log_layout = QtWidgets.QVBoxLayout( self.log_scrollAreaWidgetContents) self.log_layout.setObjectName("log_layout") self.log_layout.addWidget(self.LabelLog) self.tab_log_layout = QtWidgets.QGridLayout(self) self.tab_log_layout.addWidget(self.log_scrollarea) Logger().logChanged.connect(self.log_changed) self.log_scrollarea.verticalScrollBar().rangeChanged.connect( self.log_scroll_range_changed)
def restoreState(self, settings): if settings.contains("dockNames"): docknames = settings.value("dockNames", []) # list of docks self.docks = [Dock(self.parent(), name) for name in docknames] for dock in self.docks: self.parent().centralLayout.addWidget(dock) settings.beginGroup(dock.objectName()) dock.restoreState(settings) settings.endGroup() else: Logger().push("First launch, display a default set of docks") self.docks = [ Dock(self.parent(), "Dock %d" % (i), widgetId=widget_type) for i, widget_type in enumerate(DEFAULT_DOCKS) ] for dock in self.docks: #self.parent().addDockWidget(QtCore.Qt.TopDockWidgetArea, dock) self.parent().centralLayout.addWidget(dock)
def setcanvas_height(self, canvas_height): if self.canvas_height != canvas_height: self.canvas_height = canvas_height self.resize(self.canvas_width, self.canvas_height) Logger().push("Spectrogram image: canvas_height changed, now: %d" % (canvas_height))
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)
def log_changed(self): self.LabelLog.setText(Logger().text())
def __init__(self): QMainWindow.__init__(self) # 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) # 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() Logger().push("Init finished, entering the main loop")
def update_window(self): N = self.fft_size / self.decimation n = arange(0, N) # Hann window : better frequency resolution than the rectangular window self.window = 0.5 * (1. - cos(2 * pi * n / (N - 1))) Logger().push("audioproc: updating window")
def single_input_type_selected(self, checked): if checked: self.groupBox_second.setEnabled(False) AudioBackend().set_single_input() Logger().push("Switching to single input")
def fftsizechanged(self, index): Logger().push("fft_size_changed slot %d %d %f" % (index, 2 ** index * 32, 150000 / 2 ** index * 32)) fft_size = 2 ** index * 32 self.parent().setfftsize(fft_size)
def main(): print("Platform is %s (%s)" % (platform.system(), sys.platform)) if platform.system() == "Windows": print("Applying Windows-specific setup") # On Windows, redirect stderr to a file import imp import ctypes if (hasattr(sys, "frozen") or # new py2exe hasattr(sys, "importers") or # old py2exe imp.is_frozen("__main__")): # tools/freeze sys.stderr = open(os.path.expanduser("~/friture.exe.log"), "w") # set the App ID for Windows 7 to properly display the icon in the # taskbar. myappid = 'Friture.Friture.Friture.current' # arbitrary string try: ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( myappid) except: print( "Could not set the app model ID. If the plaftorm is older than Windows 7, this is normal." ) app = QApplication(sys.argv) if platform.system() == "Darwin": if hasattr(sys, "frozen"): #py2app sys.stdout = open(os.path.expanduser("~/friture.out.txt"), "w") sys.stderr = open(os.path.expanduser("~/friture.err.txt"), "w") print("Applying Mac OS-specific setup") # help the py2app-packaged application find the Qt plugins (imageformats and platforms) pluginsPath = os.path.normpath( os.path.join(QApplication.applicationDirPath(), os.path.pardir, 'PlugIns')) print("Adding the following to the Library paths: " + pluginsPath) QApplication.addLibraryPath(pluginsPath) # Splash screen pixmap = QPixmap(":/images/splash.png") splash = QSplashScreen(pixmap) splash.show() splash.showMessage("Initializing the audio subsystem") app.processEvents() # Logger class logger = Logger() window = Friture(logger) window.show() splash.finish(window) profile = "no" # "python" or "kcachegrind" or anything else to disable if len(sys.argv) > 1: if sys.argv[1] == "--python": profile = "python" elif sys.argv[1] == "--kcachegrind": profile = "kcachegrind" elif sys.argv[1] == "--no": profile = "no" else: print("command-line arguments (%s) not recognized" % sys.argv[1:]) if profile == "python": import cProfile import pstats cProfile.runctx('app.exec_()', globals(), locals(), filename="friture.cprof") stats = pstats.Stats("friture.cprof") stats.strip_dirs().sort_stats('time').print_stats(20) stats.strip_dirs().sort_stats('cumulative').print_stats(20) sys.exit(0) elif profile == "kcachegrind": import cProfile import lsprofcalltree p = cProfile.Profile() p.run('app.exec_()') k = lsprofcalltree.KCacheGrind(p) with open('cachegrind.out.00000', 'wb') as data: k.output(data) sys.exit(0) else: sys.exit(app.exec_())
def freqscalechanged(self, index): Logger().push("freq_scale slot %d" % index) if index == 1: self.parent().PlotZoneImage.setlog10freqscale() else: self.parent().PlotZoneImage.setlinfreqscale()
def bandsperoctavechanged(self, index): bandsperoctave = 3 * 2**(index - 1) if index >= 1 else 1 Logger().push("bandsperoctavechanged slot %d %d" % (index, bandsperoctave)) self.parent().setbandsperoctave(bandsperoctave)
def main(): if platform.system() == "Windows": print "Running on Windows" # On Windows, redirect stderr to a file import imp, ctypes if (hasattr(sys, "frozen") or # new py2exe hasattr(sys, "importers") or # old py2exe imp.is_frozen("__main__")): # tools/freeze sys.stderr = open(os.path.expanduser("~/friture.exe.log"), "w") # set the App ID for Windows 7 to properly display the icon in the # taskbar. myappid = 'Friture.Friture.Friture.current' # arbitrary string try: ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( myappid) except: print "Could not set the app model ID. If the plaftorm is older than Windows 7, this is normal." app = QApplication(sys.argv) # Splash screen pixmap = QPixmap(":/images/splash.png") splash = QSplashScreen(pixmap) splash.show() splash.showMessage("Initializing the audio subsystem") app.processEvents() # Set the separator stylesheet here # As of Qt 4.6, separator width is not handled correctly # when the stylesheet is applied directly to the QMainWindow instance. # QtCreator workarounds it with a "minisplitter" special class app.setStyleSheet(STYLESHEET) # Logger class logger = Logger() window = Friture(logger) window.show() splash.finish(window) profile = "no" # "python" or "kcachegrind" or anything else to disable if len(sys.argv) > 1: if sys.argv[1] == "--python": profile = "python" #elif sys.argv[1] == "--kcachegrind": #profile = "kcachegrind" elif sys.argv[1] == "--no": profile = "no" else: print "command-line arguments (%s) not recognized" % sys.argv[1:] if profile == "python": import cProfile import pstats cProfile.runctx('app.exec_()', globals(), locals(), filename="friture.cprof") stats = pstats.Stats("friture.cprof") stats.strip_dirs().sort_stats('time').print_stats(20) stats.strip_dirs().sort_stats('cumulative').print_stats(20) sys.exit(0) #elif profile == "kcachegrind": #import cProfile #import lsprofcalltree #p = cProfile.Profile() #p.run('app.exec_()') #k = lsprofcalltree.KCacheGrind(p) #data = open('cachegrind.out.00000', 'wb') #k.output(data) #data.close() ## alternative code with pyprof2calltree instead of lsprofcalltree ##import pyprof2calltree ##pyprof2calltree.convert(p.getstats(), "cachegrind.out.00000") # save #sys.exit(0) else: sys.exit(app.exec_())
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")
def fftsizechanged(self, index): Logger().push("fft_size_changed slot %d %d %f" % (index, 2 ** index * 32, 150000 / 2 ** index * 32)) # FIXME the size should not be found from the index, but attached as item data fft_size = 2 ** index * 32 self.parent().setfftsize(fft_size)