class SensorToolbar(Gtk.Toolbar): ''' The toolbar for specifiying the sensor: sound, resitance, or voltage ''' LOWER = 0.0 UPPER = 1.0 STR_DC_R = \ _("Resistive sensor (connect sensor to pink 'Mic In' on left side \ of XO)" ) + ' ' STR_DC_V = \ _("Voltage sensor (connect sensor to pink 'Mic In' on left side \ of XO)" ) + ' ' STR_AC = _('Sound') + ' ' STR_RESISTANCE = _('Resistance') + ' (' + _('Ohms') + ') ' STR_VOLTAGE = _('Voltage') + ' (' + _('Volts') + ') ' STR_TIME = _('Time Base') + ' ' STR_FREQUENCY = _('Frequency Base') + ' ' STR_INVERT = ' ' + _('Invert') + ' ' STR_XAXIS_TEXT = _('X Axis Scale: 1 division = %(division)s %(unit)s') # TRANSLATORS: This is milli seconds. MS = _('ms') # TRANSLATORS: This is Hertz, so 1/second. HZ = _('Hz') def __init__(self, activity, channels): ''' By default, start with resistance mode ''' super(type(self), self).__init__() self.activity = activity self._channels = channels self._lock_radio_buttons = False self._radio_button_pushed = False self.values = [] for i in range(self._channels): self.values.append('') self.string_for_textbox = '' self.gain = 1.0 self.y_mag = 3.0 self.capture_gain = CAPTURE_GAIN self.mic_boost = MIC_BOOST self.mode = 'sound' # Set up Time-domain Button self.time = RadioToolButton(icon_name='media-audio', group=None) self.insert(self.time, -1) self.time.set_tooltip(_('Sound')) self.time.connect('clicked', self.analog_resistance_voltage_mode_cb, 'sound') # Set up Resistance Button self.resistance = RadioToolButton(icon_name='resistance', group=self.time) if _can_use_dc(self.activity.hw): self.insert(self.resistance, -1) self.resistance.show() self.resistance.set_tooltip(_('Resistance Sensor')) self.resistance.connect('clicked', self.analog_resistance_voltage_mode_cb, 'resistance') # Set up Voltage Button self.voltage = RadioToolButton(icon_name='voltage', group=self.time) if _can_use_dc(self.activity.hw): self.insert(self.voltage, -1) self.voltage.set_tooltip(_('Voltage Sensor')) self.voltage.connect('clicked', self.analog_resistance_voltage_mode_cb, 'voltage') separator = Gtk.SeparatorToolItem() separator.props.draw = True self.insert(separator, -1) self._log_value = LOG_TIMER_VALUES[1] self.log_label = Gtk.Label(self._log_to_string(self._log_value)) toolitem = Gtk.ToolItem() toolitem.add(self.log_label) self.insert(toolitem, -1) self._log_button = ToolButton('timer-10') self._log_button.set_tooltip(_('Select logging interval')) self._log_button.connect('clicked', self._log_selection_cb) self.insert(self._log_button, -1) self._setup_log_palette() # Set up Logging/Stop Logging Button self._record = ToolButton('media-record') self.insert(self._record, -1) self._record.set_tooltip(_('Start logging')) self._record.connect('clicked', self.record_control_cb) separator = Gtk.SeparatorToolItem() separator.props.draw = True self.insert(separator, -1) toolitem = Gtk.ToolItem() self.trigger_label = Gtk.Label(_('Trigger')) toolitem.add(self.trigger_label) self.insert(toolitem, -1) # Set up Trigger Combo box self.trigger_none = RadioToolButton() self.trigger_none.set_icon_name('trigger-none') self.insert(self.trigger_none, -1) self.trigger_none.set_tooltip(_('None')) self.trigger_none.connect('clicked', self.update_trigger_control_cb, self.activity.wave.TRIGGER_NONE) self.trigger_rise = RadioToolButton(group=self.trigger_none) self.trigger_rise.set_icon_name('trigger-rise') self.insert(self.trigger_rise, -1) self.trigger_rise.set_tooltip(_('Rising Edge')) self.trigger_rise.connect('clicked', self.update_trigger_control_cb, self.activity.wave.TRIGGER_POS) self.trigger_fall = RadioToolButton(group=self.trigger_none) self.trigger_fall.set_icon_name('trigger-fall') self.insert(self.trigger_fall, -1) self.trigger_fall.set_tooltip(_('Falling Edge')) self.trigger_fall.connect('clicked', self.update_trigger_control_cb, self.activity.wave.TRIGGER_NEG) self.show_all() def get_log(self): return self._log_value def get_log_idx(self): if self._log_value in LOG_TIMER_VALUES: return LOG_TIMER_VALUES.index(self._log_value) else: return LOG_TIMER_VALUES[0] def set_log_idx(self, idx): self._log_value = LOG_TIMER_VALUES[idx] self.log_label.set_text(self._log_to_string(self._log_value)) if hasattr(self, '_log_button'): self._log_button.set_icon('timer-%d' % (self._log_value)) def _log_selection_cb(self, widget): if self._log_palette: if not self._log_palette.is_up(): self._log_palette.popup(immediate=True) else: self._log_palette.popdown(immediate=True) return def _log_to_seconds(self, tenth_seconds): return tenth_seconds / 10. def _log_to_string(self, tenth_seconds): if tenth_seconds in LOG_TIMER_LABELS: return LOG_TIMER_LABELS[tenth_seconds] else: return _('1 second') def _setup_log_palette(self): self._log_palette = self._log_button.get_palette() for tenth_seconds in LOG_TIMER_VALUES: text = self._log_to_string(tenth_seconds) menu_item = MenuItem(icon_name='timer-%d' % (tenth_seconds), text_label=text) menu_item.connect('activate', self._log_selected_cb, tenth_seconds) self._log_palette.menu.append(menu_item) menu_item.show() def _log_selected_cb(self, button, seconds): self.set_log_idx(LOG_TIMER_VALUES.index(seconds)) def add_frequency_slider(self, toolbox): ''' Either on the Sound toolbar or the Main toolbar ''' self._freq_stepper_up = ToolButton('freq-high') self._freq_stepper_up.set_tooltip(_('Zoom out')) self._freq_stepper_up.connect('clicked', self._freq_stepper_up_cb) self._freq_stepper_up.show() self.activity.adjustmentf = Gtk.Adjustment(0.5, self.LOWER, self.UPPER, 0.01, 0.1, 0) self.activity.adjustmentf.connect('value_changed', self.cb_page_sizef) self._freq_range = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=self.activity.adjustmentf) self._freq_range.set_inverted(True) self._freq_range.set_draw_value(False) self._freq_range.set_size_request(120, 15) self._freq_range.show() self._freq_stepper_down = ToolButton('freq-low') self._freq_stepper_down.set_tooltip(_('Zoom in')) self._freq_stepper_down.connect('clicked', self._freq_stepper_down_cb) self._freq_stepper_down.show() self._freq_range_tool = Gtk.ToolItem() self._freq_range_tool.add(self._freq_range) self._freq_range_tool.show() toolbox.add(self._freq_stepper_up) toolbox.add(self._freq_range_tool) toolbox.add(self._freq_stepper_down) return def update_trigger_control_cb(self, button, value): if button is None: value = self.activity.wave.TRIGGER_NONE if self.activity.wave.get_fft_mode(): self.trigger_none.set_active(True) else: self.activity.wave.set_trigger(value) def analog_resistance_voltage_mode_cb(self, button=None, mode_to_set='sound'): ''' Callback for Analog/Resistance/Voltage Buttons ''' if self._lock_radio_buttons: logging.debug('mode selector locked') self._radio_button_pushed = True return if self.mode == mode_to_set: logging.debug('mode already set to %s' % mode_to_set) return self._lock_radio_buttons = True if self.activity.CONTEXT == 'sound': self.sound_context_off() else: self.sensor_context_off() # Force time domain when switching modes if self.activity.wave.get_fft_mode(): self.activity.timefreq_control() # Turn off logging when switching modes if self.activity.audiograb.we_are_logging: self.record_control_cb() self.set_mode(mode_to_set) if mode_to_set == 'sound': self.set_sound_context() elif mode_to_set == 'resistance': self.set_sensor_context() elif mode_to_set == 'voltage': self.set_sensor_context() self.update_string_for_textbox() return False def unlock_radio_buttons(self): ''' Enable radio button selection ''' logging.debug('unlocking radio buttons') if self._radio_button_pushed: if self.mode == 'sound': self.time.set_active(True) elif self.mode == 'resistance': self.resistance.set_active(True) elif self.mode == 'voltage': self.voltage.set_active(True) self._lock_radio_buttons = False self._radio_button_pushed = False def set_mode(self, mode='sound'): ''' Set the mixer settings to match the current mode. ''' self.mode = mode self.activity.audiograb.set_sensor_type(self.mode) for i in range(self._channels): self.values[i] = 0.0 return def get_mode(self): ''' Get the mixer settings. ''' return self.mode def _freq_stepper_up_cb(self, button=None): ''' Moves the horizontal zoom slider to the left one notch, where one notch is 1/100 of the total range. This correspond to zooming out as a larger number of Hertz or milliseconds will be represented by the same space on the screen. ''' new_value = self._freq_range.get_value() + \ (self.UPPER - self.LOWER) / 100.0 if new_value <= self.UPPER: self._freq_range.set_value(new_value) else: self._freq_range.set_value(self.UPPER) def _freq_stepper_down_cb(self, button=None): ''' Moves the horizontal zoom slider to the right one notch, where one notch is 1/100 of the total range. This corresponds to zooming in. ''' new_value = self._freq_range.get_value() - \ (self.UPPER - self.LOWER) / 100.0 if new_value >= self.LOWER: self._freq_range.set_value(new_value) else: self._freq_range.set_value(self.LOWER) def cb_page_sizef(self, button=None): ''' Callback to scale the frequency range (zoom in and out) ''' if self._update_page_size_id: GObject.source_remove(self._update_page_size_id) self._update_page_size_id =\ GObject.timeout_add(250, self.update_page_size) return True def update_page_size(self): ''' Set up the scaling of the display. ''' self._update_page_size_id = None value = self.activity.adjustmentf.get_value() new_value = round(value * 100.0) / 100.0 if value != new_value: self.activity.adjustmentf.set_value(new_value) return False time_div = 0.001 * max(value, 0.05) freq_div = 1000 * max(value, 0.01) self.activity.wave.set_div(time_div, freq_div) self.update_string_for_textbox() return False def set_sound_context(self): ''' Called when analog sensing is selected ''' self.set_show_hide_windows(mode='sound') GObject.timeout_add(500, self.sound_context_on) self.activity.CONTEXT = 'sound' def set_sensor_context(self): ''' Called when digital sensing is selected ''' self.set_show_hide_windows(mode='sensor') GObject.timeout_add(500, self.sensor_context_on) self.activity.CONTEXT = 'sensor' def set_show_hide_windows(self, mode='sound'): ''' Shows the appropriate window identified by the mode ''' self.activity.wave.set_context_on() for i in range(self._channels): self.activity.side_toolbars[i].set_show_hide(True, mode) def sensor_context_off(self): ''' Called when a DC sensor is no longer selected ''' # self.activity.audiograb.pause_grabbing() self.activity.audiograb.stop_grabbing() def sensor_context_on(self): ''' Called when a DC sensor is selected ''' self.update_string_for_textbox() self.activity.wave.set_trigger(self.activity.wave.TRIGGER_NONE) # self.activity.audiograb.resume_grabbing() self.activity.audiograb.start_grabbing() return False def sound_context_off(self): ''' Called when an analog sensor is no longer selected ''' self.gain, self.y_mag = self.activity.wave.get_mag_params() self.capture_gain = self.activity.audiograb.get_capture_gain() self.mic_boost = self.activity.audiograb.get_mic_boost() self.activity.audiograb.stop_grabbing() def sound_context_on(self): ''' Called when an analog sensor is selected ''' self.activity.wave.set_mag_params(self.gain, self.y_mag) self.update_string_for_textbox() self.update_trigger_control_cb(None, self.activity.wave.TRIGGER_NONE) self.activity.audiograb.start_grabbing() return False def set_sample_value(self, value='', channel=0): ''' Write a sample value to the textbox. ''' self.values[channel] = value self.update_string_for_textbox() return def record_control_cb(self, button=None): ''' Depending upon the selected interval, does either a logging session, or just logs the current buffer. ''' if self.activity.audiograb.we_are_logging: self.activity.audiograb.set_logging_params(start_stop=False) self._record.set_icon_name('media-record') self._record.show() self._record.set_tooltip(_('Start Recording')) else: Xscale = (1.00 / self.activity.audiograb.get_sampling_rate()) Yscale = 0.0 interval = self._log_value / 10. # self.interval_convert() username = self.activity.nick if self.activity.wave.get_fft_mode(): self.activity.data_logger.start_new_session( username, Xscale, Yscale, self._log_to_string(self._log_value), channels=self._channels, mode='frequency') else: self.activity.data_logger.start_new_session( username, Xscale, Yscale, self._log_to_string(self._log_value), channels=self._channels, mode=self.mode) self.activity.audiograb.set_logging_params(start_stop=True, interval=interval) self._record.set_icon_name('record-stop') self._record.show() self._record.set_tooltip(_('Stop Recording')) self.activity.new_recording = True def update_string_for_textbox(self): ''' Update the status field at the bottom of the canvas. ''' if self.mode == 'resistance': string_for_textbox = (self.STR_DC_R + '\n') string_for_textbox += self.STR_RESISTANCE elif self.mode == 'voltage': string_for_textbox = (self.STR_DC_V + '\n') string_for_textbox += self.STR_VOLTAGE else: string_for_textbox = (self.STR_AC + '\t') if self.activity.wave.get_fft_mode(): scalex = self.STR_XAXIS_TEXT % { 'unit': self.HZ, 'division': self.activity.wave.freq_div } string_for_textbox += self.STR_FREQUENCY string_for_textbox += ('\n' + scalex) elif self.mode == 'sound': scalex = self.STR_XAXIS_TEXT % { 'unit': self.MS, 'division': self.activity.wave.time_div * 1000 } string_for_textbox += self.STR_TIME string_for_textbox += ('\n' + scalex) else: for i in range(self._channels): string_for_textbox += '\t(%s)' % (self.values[i]) invert = False for i in range(self._channels): if self.activity.wave.get_invert_state(channel=i): invert = True if invert: string_for_textbox += self.STR_INVERT self.activity.text_box.set_label(string_for_textbox)
class ViewToolbar(Gtk.Toolbar): def __init__(self, activity): GObject.GObject.__init__(self) self._browser = None self._activity = activity self._activity.tray.connect('unmap', self.__unmap_cb) self._activity.tray.connect('map', self.__map_cb) self.zoomout = ToolButton('zoom-out') self.zoomout.set_tooltip(_('Zoom out')) self.zoomout.connect('clicked', self.__zoomout_clicked_cb) self.insert(self.zoomout, -1) self.zoomout.show() self.zoomin = ToolButton('zoom-in') self.zoomin.set_tooltip(_('Zoom in')) self.zoomin.connect('clicked', self.__zoomin_clicked_cb) self.insert(self.zoomin, -1) self.zoomin.show() self.separator = Gtk.SeparatorToolItem() self.separator.set_draw(True) self.insert(self.separator, -1) self.separator.show() self.fullscreen = ToolButton('view-fullscreen') self.fullscreen.set_tooltip(_('Fullscreen')) self.fullscreen.connect('clicked', self.__fullscreen_clicked_cb) self.insert(self.fullscreen, -1) self.fullscreen.show() self.traybutton = ToolButton('tray-hide') self.traybutton.connect('clicked', self.__tray_clicked_cb) self.traybutton.props.sensitive = False self.insert(self.traybutton, -1) self.traybutton.show() tabbed_view = self._activity.get_canvas() if tabbed_view.get_n_pages(): self._connect_to_browser(tabbed_view.props.current_browser) tabbed_view.connect_after('switch-page', self.__switch_page_cb) def __switch_page_cb(self, tabbed_view, page, page_num): self._connect_to_browser(tabbed_view.props.current_browser) def _connect_to_browser(self, browser): self._browser = browser self._update_zoom_buttons() def _update_zoom_buttons(self): is_webkit_browser = isinstance(self._browser, Browser) self.zoomin.set_sensitive(is_webkit_browser) self.zoomout.set_sensitive(is_webkit_browser) def __zoomin_clicked_cb(self, button): tabbed_view = self._activity.get_canvas() tabbed_view.props.current_browser.zoom_in() def __zoomout_clicked_cb(self, button): tabbed_view = self._activity.get_canvas() tabbed_view.props.current_browser.zoom_out() def __fullscreen_clicked_cb(self, button): self._activity.fullscreen() def __tray_clicked_cb(self, button): if self._activity.tray.props.visible is False: self._activity.tray.show() else: self._activity.tray.hide() def __map_cb(self, tray): if len(self._activity.tray.get_children()) > 0: self.tray_set_hide() def __unmap_cb(self, tray): if len(self._activity.tray.get_children()) > 0: self.tray_set_show() def tray_set_show(self): self.traybutton.set_icon('tray-show') self.traybutton.set_tooltip(_('Show Tray')) def tray_set_hide(self): self.traybutton.set_icon('tray-hide') self.traybutton.set_tooltip(_('Hide Tray'))
class TuningToolbar(Gtk.Toolbar): ''' The toolbar for tuning instruments ''' def __init__(self, activity): super(type(self), self).__init__() self.activity = activity self._show_tuning_line = False self._updating_note = True self._tuning_tool = None self._instrument_button = ToolButton('instruments') self._instrument_button.set_tooltip(_('Tune an instrument.')) self._instrument_button.connect('clicked', self._button_selection_cb) self.insert(self._instrument_button, -1) self._setup_instrument_palette() separator = Gtk.SeparatorToolItem() separator.props.draw = True self.insert(separator, -1) self._note = 'A' self._notes_button = ToolButton('notes') self._notes_button.set_tooltip(_('Notes')) self._notes_button.connect('clicked', self._button_selection_cb) self.insert(self._notes_button, -1) self._setup_notes_palette() self._octave = 4 self._octaves_button = ToolButton('octaves') self._octaves_button.set_tooltip(_('Octaves')) self._octaves_button.connect('clicked', self._button_selection_cb) self.insert(self._octaves_button, -1) self._setup_octaves_palette() # The entry is used to display a note or for direct user input self._freq_entry = Gtk.Entry() self._freq_entry.set_text('440') # A self._freq_entry_changed_id = self._freq_entry.connect( 'changed', self._update_freq_entry) if hasattr(self._freq_entry, 'set_tooltip_text'): self._freq_entry.set_tooltip_text( _('Enter a frequency to display.')) self._freq_entry.set_width_chars(8) self._freq_entry.show() toolitem = Gtk.ToolItem() toolitem.add(self._freq_entry) self.insert(toolitem, -1) toolitem.show() self._new_tuning_line = ToolButton('tuning-tools') self._new_tuning_line.show() self.insert(self._new_tuning_line, -1) self._new_tuning_line.set_tooltip(_('Show tuning line.')) self._new_tuning_line.connect('clicked', self.tuning_line_cb) separator = Gtk.SeparatorToolItem() separator.props.draw = True self.insert(separator, -1) self._harmonic = ToolButton('harmonics') self._harmonic.show() self.insert(self._harmonic, -1) self._harmonic.set_tooltip(_('Show harmonics.')) self._harmonic.connect('clicked', self.harmonic_cb) separator = Gtk.SeparatorToolItem() separator.props.draw = True self.insert(separator, -1) self._play_tone = ToolButton('media-playback-start') self._play_tone.show() self.insert(self._play_tone, -1) self._play_tone.set_tooltip(_('Play a note.')) self._play_tone.connect('clicked', self.play_cb) separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) self.insert(separator, -1) self.label = Gtk.Label(label='') self.label.set_use_markup(True) self.label.show() toolitem = Gtk.ToolItem() toolitem.add(self.label) self.insert(toolitem, -1) toolitem.show() self.show_all() def _update_note(self): ''' Calculate the frequency based on note and octave ''' if not hasattr(self, '_freq_entry'): # Still setting up toolbar return i = self._octave * 12 + NOTES.index(self._note) freq = A0 * pow(TWELTHROOT2, i) self._updating_note = True self._freq_entry.set_text('%0.3f' % (freq)) self.label.set_markup( SPAN % (style.COLOR_WHITE.get_html(), self._note + str(self._octave))) if self._show_tuning_line: self.activity.wave.tuning_line = freq return def _update_freq_entry(self, widget): # Calculate a note from a frequency if not self._updating_note: # Only if user types in a freq. try: freq = float(self._freq_entry.get_text()) # Only consider notes in piano range if freq < A0 * 0.97: self.label.set_text('< A0') return if freq > C8 * 1.03: self.label.set_text('> C8') return self.label.set_markup(freq_note(freq, flatsharp=True)) except ValueError: return self._updating_note = False def _button_selection_cb(self, widget): palette = widget.get_palette() if palette: if not palette.is_up(): palette.popup(immediate=True) else: palette.popdown(immediate=True) return def _setup_notes_palette(self): self._notes_palette = self._notes_button.get_palette() for note in NOTES: menu_item = MenuItem(icon_name='', text_label=note) menu_item.connect('activate', self._note_selected_cb, note) self._notes_palette.menu.append(menu_item) menu_item.show() def _note_selected_cb(self, widget, note): self._note = note self._update_note() def _setup_octaves_palette(self): self._octaves_palette = self._octaves_button.get_palette() for octave in range(9): menu_item = MenuItem(icon_name='', text_label=str(octave)) menu_item.connect('activate', self._octave_selected_cb, octave) self._octaves_palette.menu.append(menu_item) menu_item.show() def _octave_selected_cb(self, widget, octave): self._octave = octave self._update_note() def _setup_instrument_palette(self): self.instrument_palette = self._instrument_button.get_palette() self.instrument = [] for k in INSTRUMENT_DICT.keys(): self.instrument.append(k) menu_item = MenuItem(icon_name='', text_label=k) menu_item.connect('activate', self.instrument_selected_cb, k) self.instrument_palette.menu.append(menu_item) menu_item.show() def instrument_selected_cb(self, button, instrument): ''' Callback for instrument control ''' logging.debug(instrument) if self._tuning_tool is not None: self.remove(self._tuning_tool) if instrument == _('None'): self.activity.wave.instrument = None # Remove any previous tuning button if hasattr(self, '_tuning_button'): self._tuning_button.destroy() # Restore the notes, octaves buttons if hasattr(self, '_notes_button'): self.insert(self._notes_button, 2) self.insert(self._octaves_button, 3) return self.remove(self._notes_button) self.remove(self._octaves_button) self.activity.wave.instrument = instrument # If we are not already in freq. base, switch. if not self.activity.wave.get_fft_mode(): self.activity.timefreq_control() # Add a Tuning palette for this instrument self._tuning_button = ToolButton('notes') self._tuning_button.set_tooltip(instrument) self._tuning_button.connect('clicked', self._button_selection_cb) self.insert(self._tuning_button, 1) self._setup_tuning_palette(instrument) def _setup_tuning_palette(self, instrument): self._tuning_palette = self._tuning_button.get_palette() self.tuning = [] self.tuning.append(_('All notes')) menu_item = MenuItem(icon_name='', text_label=_('All notes')) menu_item.connect('activate', self._tuning_selected_cb, instrument, -1) self._tuning_palette.menu.append(menu_item) menu_item.show() for i, f in enumerate(INSTRUMENT_DICT[instrument]): self.tuning.append(freq_note(f)) menu_item = MenuItem(icon_name='', text_label=freq_note(f)) menu_item.connect('activate', self._tuning_selected_cb, instrument, i) self._tuning_palette.menu.append(menu_item) menu_item.show() self.show_all() def _tuning_selected_cb(self, widget, instrument, fidx): ''' Update note ''' if not hasattr(self, '_freq_entry'): # Still setting up toolbar? return if instrument not in INSTRUMENT_DICT: return if fidx == -1: # All notes self.activity.wave.instrument = instrument self.activity.wave.tuning_line = 0.0 self._new_tuning_line.set_icon('tuning-tools') self._new_tuning_line.set_tooltip(_('Show tuning line.')) self._show_tuning_line = False else: freq = INSTRUMENT_DICT[instrument][fidx] self.activity.wave.instrument = None self.activity.wave.tuning_line = freq self._new_tuning_line.set_icon('tuning-tools-off') self._new_tuning_line.set_tooltip(_('Hide tuning line.')) self._show_tuning_line = True self._updating_note = False def harmonic_cb(self, *args): ''' Callback for harmonics control ''' self.activity.wave.harmonics = not self.activity.wave.harmonics if self.activity.wave.harmonics: self._harmonic.set_icon_name('harmonics-off') self._harmonic.set_tooltip(_('Hide harmonics.')) if self.activity.wave.instrument is None and \ self.activity.wave.tuning_line == 0.0: self._load_tuning_line() else: self._harmonic.set_icon_name('harmonics') self._harmonic.set_tooltip(_('Show harmonics.')) def tuning_line_cb(self, *args): ''' Callback for tuning insert ''' if self._show_tuning_line: self.activity.wave.tuning_line = 0.0 self._new_tuning_line.set_icon_name('tuning-tools') self._new_tuning_line.set_tooltip(_('Show tuning line.')) self._show_tuning_line = False else: self._load_tuning_line() def _load_tuning_line(self): ''' Read the freq entry and use value to set tuning line ''' freq = self._freq_entry.get_text() try: self.activity.wave.tuning_line = float(freq) if freq < 0: freq = -freq self._new_tuning_line.set_icon_name('tuning-tools-off') self._new_tuning_line.set_tooltip(_('Hide tuning line.')) self._show_tuning_line = True except ValueError: self.activity.wave.tuning_line = 0.0 self._freq_entry.set_text('0') # If we are not already in freq. base, switch. if not self.activity.wave.get_fft_mode(): self.activity.timefreq_control() def play_cb(self, *args): ''' Save settings, turn off display, and then play a tone at the current frequency ''' channels = [] for c in range(self.activity.audiograb.channels): channels.append(self.activity.wave.get_visibility(channel=c)) self.activity.wave.set_visibility(False, channel=c) wave_status = self.activity.wave.get_active() self.activity.wave.set_context_off() self.activity.wave.set_active(False) if self.activity.hw in [XO4, XO175]: self.activity.audiograb.stop_grabbing() freq = float(self._freq_entry.get_text()) GObject.timeout_add(200, self.play_sound, freq, channels, wave_status) def play_sound(self, freq, channels, wave_status): ''' Play the sound and then restore wave settings ''' self._play_sinewave(freq, 5000, 1) if self.activity.hw in [XO4, XO175]: self.activity.sensor_toolbar.set_mode('sound') self.activity.sensor_toolbar.set_sound_context() self.activity.audiograb.start_grabbing() for c in range(self.activity.audiograb.channels): self.activity.wave.set_visibility(channels[c], channel=c) self.activity.wave.set_context_on() self.activity.wave.set_active(wave_status) def _play_sinewave(self, pitch, amplitude=5000, duration=1): """ Create a Csound score to play a sine wave. """ self.orchlines = [] self.scorelines = [] self.instrlist = [] try: pitch = abs(float(pitch)) amplitude = abs(float(amplitude)) duration = abs(float(duration)) except ValueError: logging.error('bad args to _play_sinewave') return self._prepare_sinewave(pitch, amplitude, duration) path = os.path.join(self.activity.get_activity_root(), 'instance', 'tmp.csd') # Create a csound file from the score. self._audio_write(path) # Play the csound file. check_output(['csound', path], 'call to csound failed?') def _prepare_sinewave(self, pitch, amplitude, duration, starttime=0, pitch_envelope=99, amplitude_envelope=100, instrument=1): pitenv = pitch_envelope ampenv = amplitude_envelope if 1 not in self.instrlist: self.orchlines.append("instr 1\n") self.orchlines.append("kpitenv oscil 1, 1/p3, p6\n") self.orchlines.append("aenv oscil 1, 1/p3, p7\n") self.orchlines.append("asig oscil p5*aenv, p4*kpitenv, p8\n") self.orchlines.append("out asig\n") self.orchlines.append("endin\n\n") self.instrlist.append(1) self.scorelines.append( "i1 %s %s %s %s %s %s %s\n" % (str(starttime), str(duration), str(pitch), str(amplitude), str(pitenv), str(ampenv), str(instrument))) def _audio_write(self, file): """ Compile a .csd file. """ csd = open(file, "w") csd.write("<CsoundSynthesizer>\n\n") csd.write("<CsOptions>\n") csd.write("-+rtaudio=alsa -odevaudio -m0 -d -b256 -B512\n") csd.write("</CsOptions>\n\n") csd.write("<CsInstruments>\n\n") csd.write("sr=16000\n") csd.write("ksmps=50\n") csd.write("nchnls=1\n\n") for line in self.orchlines: csd.write(line) csd.write("\n</CsInstruments>\n\n") csd.write("<CsScore>\n\n") csd.write("f1 0 2048 10 1\n") csd.write("f2 0 2048 10 1 0 .33 0 .2 0 .143 0 .111\n") csd.write("f3 0 2048 10 1 .5 .33 .25 .2 .175 .143 .125 .111 .1\n") csd.write("f10 0 2048 10 1 0 0 .3 0 .2 0 0 .1\n") csd.write("f99 0 2048 7 1 2048 1\n") csd.write("f100 0 2048 7 0. 10 1. 1900 1. 132 0.\n") csd.write(self.scorelines.pop()) csd.write("e\n") csd.write("\n</CsScore>\n") csd.write("\n</CsoundSynthesizer>") csd.close()
class TuningToolbar(Gtk.Toolbar): ''' The toolbar for tuning instruments ''' def __init__(self, activity): super(type(self), self).__init__() self.activity = activity self._show_tuning_line = False self._updating_note = True self._tuning_tool = None self._instrument_button = ToolButton('instruments') self._instrument_button.set_tooltip(_('Tune an instrument.')) self._instrument_button.connect('clicked', self._button_selection_cb) self.insert(self._instrument_button, -1) self._setup_instrument_palette() separator = Gtk.SeparatorToolItem() separator.props.draw = True self.insert(separator, -1) self._note = 'A' self._notes_button = ToolButton('notes') self._notes_button.set_tooltip(_('Notes')) self._notes_button.connect('clicked', self._button_selection_cb) self.insert(self._notes_button, -1) self._setup_notes_palette() self._octave = 4 self._octaves_button = ToolButton('octaves') self._octaves_button.set_tooltip(_('Octaves')) self._octaves_button.connect('clicked', self._button_selection_cb) self.insert(self._octaves_button, -1) self._setup_octaves_palette() # The entry is used to display a note or for direct user input self._freq_entry = Gtk.Entry() self._freq_entry.set_text('440') # A self._freq_entry_changed_id = self._freq_entry.connect( 'changed', self._update_freq_entry) if hasattr(self._freq_entry, 'set_tooltip_text'): self._freq_entry.set_tooltip_text( _('Enter a frequency to display.')) self._freq_entry.set_width_chars(8) self._freq_entry.show() toolitem = Gtk.ToolItem() toolitem.add(self._freq_entry) self.insert(toolitem, -1) toolitem.show() self._new_tuning_line = ToolButton('tuning-tools') self._new_tuning_line.show() self.insert(self._new_tuning_line, -1) self._new_tuning_line.set_tooltip(_('Show tuning line.')) self._new_tuning_line.connect('clicked', self.tuning_line_cb) separator = Gtk.SeparatorToolItem() separator.props.draw = True self.insert(separator, -1) self._harmonic = ToolButton('harmonics') self._harmonic.show() self.insert(self._harmonic, -1) self._harmonic.set_tooltip(_('Show harmonics.')) self._harmonic.connect('clicked', self.harmonic_cb) separator = Gtk.SeparatorToolItem() separator.props.draw = True self.insert(separator, -1) self._play_tone = ToolButton('media-playback-start') self._play_tone.show() self.insert(self._play_tone, -1) self._play_tone.set_tooltip(_('Play a note.')) self._play_tone.connect('clicked', self.play_cb) separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) self.insert(separator, -1) self.label = Gtk.Label(label='') self.label.set_use_markup(True) self.label.show() toolitem = Gtk.ToolItem() toolitem.add(self.label) self.insert(toolitem, -1) toolitem.show() self.show_all() def _update_note(self): ''' Calculate the frequency based on note and octave ''' if not hasattr(self, '_freq_entry'): # Still setting up toolbar return i = self._octave * 12 + NOTES.index(self._note) freq = A0 * pow(TWELTHROOT2, i) self._updating_note = True self._freq_entry.set_text('%0.3f' % (freq)) self.label.set_markup(SPAN % (style.COLOR_WHITE.get_html(), self._note + str(self._octave))) if self._show_tuning_line: self.activity.wave.tuning_line = freq return def _update_freq_entry(self, widget): # Calculate a note from a frequency if not self._updating_note: # Only if user types in a freq. try: freq = float(self._freq_entry.get_text()) # Only consider notes in piano range if freq < A0 * 0.97: self.label.set_text('< A0') return if freq > C8 * 1.03: self.label.set_text('> C8') return self.label.set_markup(freq_note(freq, flatsharp=True)) except ValueError: return self._updating_note = False def _button_selection_cb(self, widget): palette = widget.get_palette() if palette: if not palette.is_up(): palette.popup(immediate=True) else: palette.popdown(immediate=True) return def _setup_notes_palette(self): self._notes_palette = self._notes_button.get_palette() for note in NOTES: menu_item = MenuItem(icon_name='', text_label=note) menu_item.connect('activate', self._note_selected_cb, note) self._notes_palette.menu.append(menu_item) menu_item.show() def _note_selected_cb(self, widget, note): self._note = note self._update_note() def _setup_octaves_palette(self): self._octaves_palette = self._octaves_button.get_palette() for octave in range(9): menu_item = MenuItem(icon_name='', text_label=str(octave)) menu_item.connect('activate', self._octave_selected_cb, octave) self._octaves_palette.menu.append(menu_item) menu_item.show() def _octave_selected_cb(self, widget, octave): self._octave = octave self._update_note() def _setup_instrument_palette(self): self.instrument_palette = self._instrument_button.get_palette() self.instrument = [] for k in INSTRUMENT_DICT.keys(): self.instrument.append(k) menu_item = MenuItem(icon_name='', text_label=k) menu_item.connect('activate', self.instrument_selected_cb, k) self.instrument_palette.menu.append(menu_item) menu_item.show() def instrument_selected_cb(self, button, instrument): ''' Callback for instrument control ''' logging.debug(instrument) if self._tuning_tool is not None: self.remove(self._tuning_tool) if instrument == _('None'): self.activity.wave.instrument = None # Remove any previous tuning button if hasattr(self, '_tuning_button'): self._tuning_button.destroy() # Restore the notes, octaves buttons if hasattr(self, '_notes_button'): self.insert(self._notes_button, 2) self.insert(self._octaves_button, 3) return self.remove(self._notes_button) self.remove(self._octaves_button) self.activity.wave.instrument = instrument # If we are not already in freq. base, switch. if not self.activity.wave.get_fft_mode(): self.activity.timefreq_control() # Add a Tuning palette for this instrument self._tuning_button = ToolButton('notes') self._tuning_button.set_tooltip(instrument) self._tuning_button.connect('clicked', self._button_selection_cb) self.insert(self._tuning_button, 1) self._setup_tuning_palette(instrument) def _setup_tuning_palette(self, instrument): self._tuning_palette = self._tuning_button.get_palette() self.tuning = [] self.tuning.append(_('All notes')) menu_item = MenuItem(icon_name='', text_label=_('All notes')) menu_item.connect('activate', self._tuning_selected_cb, instrument, -1) self._tuning_palette.menu.append(menu_item) menu_item.show() for i, f in enumerate(INSTRUMENT_DICT[instrument]): self.tuning.append(freq_note(f)) menu_item = MenuItem(icon_name='', text_label=freq_note(f)) menu_item.connect('activate', self._tuning_selected_cb, instrument, i) self._tuning_palette.menu.append(menu_item) menu_item.show() self.show_all() def _tuning_selected_cb(self, widget, instrument, fidx): ''' Update note ''' if not hasattr(self, '_freq_entry'): # Still setting up toolbar? return if instrument not in INSTRUMENT_DICT: return if fidx == -1: # All notes self.activity.wave.instrument = instrument self.activity.wave.tuning_line = 0.0 self._new_tuning_line.set_icon('tuning-tools') self._new_tuning_line.set_tooltip(_('Show tuning line.')) self._show_tuning_line = False else: freq = INSTRUMENT_DICT[instrument][fidx] self.activity.wave.instrument = None self.activity.wave.tuning_line = freq self._new_tuning_line.set_icon('tuning-tools-off') self._new_tuning_line.set_tooltip(_('Hide tuning line.')) self._show_tuning_line = True self._updating_note = False def harmonic_cb(self, *args): ''' Callback for harmonics control ''' self.activity.wave.harmonics = not self.activity.wave.harmonics if self.activity.wave.harmonics: self._harmonic.set_icon_name('harmonics-off') self._harmonic.set_tooltip(_('Hide harmonics.')) if self.activity.wave.instrument is None and \ self.activity.wave.tuning_line == 0.0: self._load_tuning_line() else: self._harmonic.set_icon_name('harmonics') self._harmonic.set_tooltip(_('Show harmonics.')) def tuning_line_cb(self, *args): ''' Callback for tuning insert ''' if self._show_tuning_line: self.activity.wave.tuning_line = 0.0 self._new_tuning_line.set_icon_name('tuning-tools') self._new_tuning_line.set_tooltip(_('Show tuning line.')) self._show_tuning_line = False else: self._load_tuning_line() def _load_tuning_line(self): ''' Read the freq entry and use value to set tuning line ''' freq = self._freq_entry.get_text() try: self.activity.wave.tuning_line = float(freq) if freq < 0: freq = -freq self._new_tuning_line.set_icon_name('tuning-tools-off') self._new_tuning_line.set_tooltip(_('Hide tuning line.')) self._show_tuning_line = True except ValueError: self.activity.wave.tuning_line = 0.0 self._freq_entry.set_text('0') # If we are not already in freq. base, switch. if not self.activity.wave.get_fft_mode(): self.activity.timefreq_control() def play_cb(self, *args): ''' Save settings, turn off display, and then play a tone at the current frequency ''' channels = [] for c in range(self.activity.audiograb.channels): channels.append(self.activity.wave.get_visibility(channel=c)) self.activity.wave.set_visibility(False, channel=c) wave_status = self.activity.wave.get_active() self.activity.wave.set_context_off() self.activity.wave.set_active(False) if self.activity.hw in [XO4, XO175]: self.activity.audiograb.stop_grabbing() freq = float(self._freq_entry.get_text()) GLib.timeout_add(200, self.play_sound, freq, channels, wave_status) def play_sound(self, freq, channels, wave_status): ''' Play the sound and then restore wave settings ''' self._play_sinewave(freq, 5000, 1) if self.activity.hw in [XO4, XO175]: self.activity.sensor_toolbar.set_mode('sound') self.activity.sensor_toolbar.set_sound_context() self.activity.audiograb.start_grabbing() for c in range(self.activity.audiograb.channels): self.activity.wave.set_visibility(channels[c], channel=c) self.activity.wave.set_context_on() self.activity.wave.set_active(wave_status) def _play_sinewave(self, pitch, amplitude=5000, duration=1): """ Create a Csound score to play a sine wave. """ self.orchlines = [] self.scorelines = [] self.instrlist = [] try: pitch = abs(float(pitch)) amplitude = abs(float(amplitude)) duration = abs(float(duration)) except ValueError: logging.error('bad args to _play_sinewave') return self._prepare_sinewave(pitch, amplitude, duration) path = os.path.join(self.activity.get_activity_root(), 'instance', 'tmp.csd') # Create a csound file from the score. self._audio_write(path) # Play the csound file. check_output(['csound', path], 'call to csound failed?') def _prepare_sinewave(self, pitch, amplitude, duration, starttime=0, pitch_envelope=99, amplitude_envelope=100, instrument=1): pitenv = pitch_envelope ampenv = amplitude_envelope if 1 not in self.instrlist: self.orchlines.append("instr 1\n") self.orchlines.append("kpitenv oscil 1, 1/p3, p6\n") self.orchlines.append("aenv oscil 1, 1/p3, p7\n") self.orchlines.append("asig oscil p5*aenv, p4*kpitenv, p8\n") self.orchlines.append("out asig\n") self.orchlines.append("endin\n\n") self.instrlist.append(1) self.scorelines.append("i1 %s %s %s %s %s %s %s\n" % (str(starttime), str(duration), str(pitch), str(amplitude), str(pitenv), str(ampenv), str(instrument))) def _audio_write(self, file): """ Compile a .csd file. """ csd = open(file, "w") csd.write("<CsoundSynthesizer>\n\n") csd.write("<CsOptions>\n") csd.write("-+rtaudio=alsa -odevaudio -m0 -d -b256 -B512\n") csd.write("</CsOptions>\n\n") csd.write("<CsInstruments>\n\n") csd.write("sr=16000\n") csd.write("ksmps=50\n") csd.write("nchnls=1\n\n") for line in self.orchlines: csd.write(line) csd.write("\n</CsInstruments>\n\n") csd.write("<CsScore>\n\n") csd.write("f1 0 2048 10 1\n") csd.write("f2 0 2048 10 1 0 .33 0 .2 0 .143 0 .111\n") csd.write("f3 0 2048 10 1 .5 .33 .25 .2 .175 .143 .125 .111 .1\n") csd.write("f10 0 2048 10 1 0 0 .3 0 .2 0 0 .1\n") csd.write("f99 0 2048 7 1 2048 1\n") csd.write("f100 0 2048 7 0. 10 1. 1900 1. 132 0.\n") csd.write(self.scorelines.pop()) csd.write("e\n") csd.write("\n</CsScore>\n") csd.write("\n</CsoundSynthesizer>") csd.close()
class ErikosActivity(activity.Activity): def __init__(self, handle): super(ErikosActivity, self).__init__(handle) toolbar_box = ToolbarBox() # Buttons added to the Activity toolbar activity_button = ActivityToolbarButton(self) toolbar_box.toolbar.insert(activity_button, 0) activity_button.show() # Play Button self.play = ToolButton("media-playback-start") self.play.set_tooltip(_('Play')) self.play.props.sensitive = True self.play.connect('clicked', self._play_cb) toolbar_box.toolbar.insert(self.play, -1) self.play.show() # Sound Toggle Button self.sound = ToolButton("speaker-muted-100") self.sound.set_tooltip(_('Mute')) self.sound.props.sensitive = True self.sound.connect('clicked', self._sound_cb) toolbar_box.toolbar.insert(self.sound, -1) self.sound.show() separator = Gtk.SeparatorToolItem() separator.show() toolbar_box.toolbar.insert(separator, -1) # Label for showing level self.level_label = Gtk.Label("%s %d" % (_("Level"),1)) self.level_label.show() level_toolitem = Gtk.ToolItem() level_toolitem.add(self.level_label) toolbar_box.toolbar.insert(level_toolitem,-1) separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) separator.show() toolbar_box.toolbar.insert(separator, -1) # The ever-present Stop Button stop_button = StopButton(self) stop_button.props.accelerator = '<Ctrl>Q' toolbar_box.toolbar.insert(stop_button, -1) stop_button.show() self.set_toolbar_box(toolbar_box) toolbar_box.show() # Create a canvas canvas = Gtk.DrawingArea() canvas.set_size_request(Gdk.Screen.width(), Gdk.Screen.height()) self.set_canvas(canvas) canvas.show() self.show_all() # Initialize the canvas self.sw = window.new_window(canvas, \ os.path.join(activity.get_bundle_path(), \ 'images/'), \ self) self.sw.activity = self # Read the level from the Journal try: sw.level = int(self.metadata['level']) except: pass def _play_cb(self, button): window.play_the_game(self.sw) return True def _sound_cb(self, button): if self.sw.sound is True: self.sound.set_icon("speaker-muted-000") self.sound.set_tooltip(_('Unmute')) self.sw.sound = False else: self.sound.set_icon("speaker-muted-100") self.sound.set_tooltip(_('Mute')) self.sw.sound = True return True """ Write the slider positions to the Journal """ def write_file(self, file_path): _logger.debug("Write level: " + str(self.sw.level)) self.metadata['level'] = self.sw.level