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'))
Exemple #3
0
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()
Exemple #4
0
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