Beispiel #1
0
class SimplePianoActivity(activity.Activity):
    """SimplePianoActivity class as specified in activity.info"""
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.close)
        Gst.init(None)

        self._what_list = []

        self.play_recording_thread = None

        self.playing_recording = False
        self.firstTime = False
        self.playing = False
        self.regularity = 0.7
        self._drums_store = []
        self.recording = False
        self.recorded_keys = []
        self.is_valid_recording = False

        # we do not have collaboration features
        # make the share option insensitive
        self.max_participants = 1
        self.csnd = new_csound_client()
        self.rythmInstrument = 'drum1kick'
        # toolbar with the new toolbar redesign
        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        toolbar_box.toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)

        self.play_index = 0

        self.play_recording_button = ToolButton(
            icon_name='media-playback-start')
        self.play_recording_button.set_property('can-default', True)
        self.play_recording_button.show()
        self.record_button = ToggleToolButton(icon_name='media-record')
        self.record_button.set_property('can-default', True)
        self.record_button.show()
        self.play_recording_button.set_sensitive(False)

        self.record_button.connect('clicked', self.__record_button_click_cb)

        self.play_recording_button.connect('clicked',
                                           self.handlePlayRecordingButton)

        toolbar_box.toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)

        # TODO: disabe until is implemented with csnd6
        # self.createPercussionToolbar(toolbar_box)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        keybord_labels = RadioToolButton()
        keybord_labels.props.icon_name = 'q_key'
        keybord_labels.props.group = keybord_labels
        keybord_labels.connect('clicked', self.set_keyboard_labels_cb)
        toolbar_box.toolbar.insert(keybord_labels, -1)

        notes_labels = RadioToolButton()
        notes_labels.props.icon_name = 'do_key'
        notes_labels.props.group = keybord_labels
        notes_labels.connect('clicked', self.set_notes_labels_cb)
        toolbar_box.toolbar.insert(notes_labels, -1)

        ti_notes_labels = RadioToolButton()
        ti_notes_labels.props.icon_name = 'ti_key'
        ti_notes_labels.props.group = keybord_labels
        ti_notes_labels.connect('clicked', self.set_ti_notes_labels_cb)
        toolbar_box.toolbar.insert(ti_notes_labels, -1)

        german_labels = RadioToolButton()
        german_labels.props.icon_name = 'c_key'
        german_labels.props.group = keybord_labels
        german_labels.connect('clicked', self.set_german_labels_cb)
        toolbar_box.toolbar.insert(german_labels, -1)

        no_labels = RadioToolButton()
        no_labels.props.icon_name = 'edit-clear'
        no_labels.props.group = keybord_labels
        no_labels.connect('clicked', self.set_keyboard_no_labels_cb)
        toolbar_box.toolbar.insert(no_labels, -1)
        self._what_widget = Gtk.ToolItem()
        self._what_search_button = FilterToolItem(_('Select Instrument'),
                                                  'view-type', _('Piano'),
                                                  self._what_widget)
        self._what_widget.show()
        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(self._what_search_button, -1)
        self._what_search_button.show()
        self._what_search_button.set_is_important(True)
        self._what_widget_contents = None
        self._what_drum_widget_contents = None

        separator = Gtk.SeparatorToolItem()
        toolbar_box.toolbar.insert(separator, -1)

        toolbar_box.toolbar.insert(self.record_button, -1)
        toolbar_box.toolbar.insert(self.play_recording_button, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self._save_as_audio_bt = ToolButton(icon_name='save-as-audio')
        self._save_as_audio_bt.props.tooltip = _('Save as audio')
        self._save_as_audio_bt.connect('clicked', self._save_ogg_cb)
        self._save_as_audio_bt.show()
        self._save_as_audio_bt.set_sensitive(False)
        activity_button.page.insert(self._save_as_audio_bt, -1)

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()

        self.keyboard_letters = ['ZSXDCVGBHNJM', 'Q2W3ER5T6Y7U', 'I']

        notes = [
            'DO', ['DO#', 'REb'], 'RE', ['RE#', 'MIb'], 'MI', 'FA',
            ['FA#', 'SOLb'], 'SOL', ['SOL#', 'LAb'], 'LA', ['LA#', 'SIb'], 'SI'
        ]
        self.notes_labels = [notes, notes, ['DO']]

        # some countries use TI instead of SI
        ti_notes = [
            'DO', ['DO#', 'REb'], 'RE', ['RE#', 'MIb'], 'MI', 'FA',
            ['FA#', 'SOLb'], 'SOL', ['SOL#', 'LAb'], 'LA', ['LA#', 'TIb'], 'TI'
        ]
        self.ti_notes_labels = [ti_notes, ti_notes, ['DO']]

        german_notes = [
            'C', ['C#', 'Db'], 'D', ['D#', 'Eb'], 'E', 'F', ['F#', 'Gb'], 'G',
            ['G#', 'Ab'], 'A', ['A#', 'Bb'], 'B'
        ]

        self.german_labels = [german_notes, german_notes, ['C']]

        self.piano = PianoKeyboard(octaves=2,
                                   add_c=True,
                                   labels=self.keyboard_letters)

        # init csound
        self.instrumentDB = InstrumentDB.getRef()
        self.timeout_ms = 50
        self.instVolume = 50
        self.drumVolume = 0.5
        self.instrument = 'piano'
        self.beat = 4
        self.reverb = 0.1
        self.tempo = PLAYER_TEMPO
        self.beatDuration = 60.0 / self.tempo
        self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0

        self.sequencer = MiniSequencer(self.recordStateButton,
                                       self.recordOverSensitivity)
        self.loop = Loop(self.beat, math.sqrt(self.instVolume * 0.01))

        self.drumFillin = Fillin(self.beat, self.tempo, self.rythmInstrument,
                                 self.reverb, self.drumVolume)

        self.muteInst = False
        self.csnd.setTempo(self.tempo)
        self.noteList = []
        for i in range(21):
            self.csnd.setTrackVolume(100, i)

        # TODO commented because apparently are not used in the activity
        # for i in range(10):
        #     self.csnd.load_instrument('guidice' + str(i + 1))

        self.volume = 100
        self.csnd.setMasterVolume(self.volume)

        self.enableKeyboard()
        self.setInstrument(self.instrument)

        self.connect('key-press-event', self.onKeyPress)
        self.connect('key-release-event', self.onKeyRelease)

        self.piano.connect('key_pressed', self.__key_pressed_cb)
        self.piano.connect('key_released', self.__key_released_cb)
        vbox = Gtk.VBox()
        vbox.set_homogeneous(False)
        self.load_instruments()
        self._event_box = Gtk.EventBox()
        self._event_box.modify_bg(Gtk.StateType.NORMAL,
                                  style.COLOR_WHITE.get_gdk_color())
        vbox.pack_start(self._event_box, False, False, 0)
        vbox.pack_end(self.piano, True, True, 0)
        vbox.show_all()
        self.set_canvas(vbox)
        piano_height = Gdk.Screen.width() / 2
        self._event_box.set_size_request(
            -1,
            Gdk.Screen.height() - piano_height - style.GRID_CELL_SIZE)
        self.connect('size-allocate', self.__allocate_cb)

        # TODO: disabe until is implemented with csnd6
        # GLib.idle_add(self.initializePercussion)

    def createPercussionToolbar(self, toolbar_box):

        self.beats_pm_button = IntensitySelector(range(2, 13), 4,
                                                 imagefile('beat3.svg'))
        self.tempo_button = \
            IntensitySelector(range(PLAYER_TEMPO_LOWER,
                                    PLAYER_TEMPO_UPPER + 1, PLAYER_TEMPO_STEP),
                              PLAYER_TEMPO, imagefile('tempo5.png'))

        self.complexity_button = IntensitySelector(xfrange(0, 1, 0.1),
                                                   self.regularity,
                                                   imagefile('complex6.svg'))

        self._play_percussion_btn = ToolButton(
            icon_name='media-playback-start')
        self._play_percussion_btn.set_property('can-default', True)
        self._play_percussion_btn.show()
        self._play_percussion_btn.connect('clicked', self.handlePlayButton)

        beats_toolbar = ToolbarBox()
        beats_toolbar.toolbar.insert(self._play_percussion_btn, -1)

        self._what_drum_widget = Gtk.ToolItem()
        self._what_drum_search_button = FilterToolItem(_('Select Drum'),
                                                       'view-type',
                                                       _('Jazz / Rock Kit'),
                                                       self._what_drum_widget)
        self._what_drum_search_button.set_widget_icon(
            file_name=imagefile("drum1kit.svg"))

        self._what_drum_widget.show()
        beats_toolbar.toolbar.insert(self._what_drum_search_button, -1)
        self._what_drum_search_button.show()
        self._what_drum_search_button.set_is_important(True)

        beats_toolbar.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        beats_toolbar.toolbar.insert(self.complexity_button, -1)
        beats_toolbar.toolbar.insert(self.beats_pm_button, -1)
        beats_toolbar.toolbar.insert(self.tempo_button, -1)

        beats_toolbar_button = ToolbarButton(icon_name='toolbar-drums',
                                             page=beats_toolbar)
        beats_toolbar_button.show()

        toolbar_box.toolbar.insert(beats_toolbar_button, 1)

        self.beats_pm_button.set_tooltip(_("Beats per bar"))
        self.beats_pm_button.show()
        self.beats_pm_button.connect('changed', self.beatSliderChange, True)
        self.tempo_button.connect('changed', self.tempoSliderChange, True)
        self.complexity_button.connect('changed', self.handleComplexityChange,
                                       True)
        self.complexity_button.set_tooltip(_("Beat complexity"))
        self.tempo_button.show()
        self.tempo_button.set_tooltip(_('Tempo'))
        self.complexity_button.show()

    def initializePercussion(self):
        self.rythmInstrument = 'drum1kit'
        self.csnd.load_drumkit(self.rythmInstrument)
        self.csnd.setTempo(self.tempo)
        self.beatPickup = False

        def flatten(ll):
            rval = []
            for l in ll:
                rval += l
            return rval

        noteOnsets = []
        notePitchs = []

        i = 0
        self.noteList = []
        self.csnd.loopClear()
        for x in flatten(
                generator(self.rythmInstrument, self.beat, 0.8,
                          self.regularity, self.reverb)):
            x.amplitude = x.amplitude * self.drumVolume
            noteOnsets.append(x.onset)
            notePitchs.append(x.pitch)
            n = Note(0, x.trackId, i, x)
            self.noteList.append((x.onset, n))
            i = i + 1
            self.csnd.loopPlay(n, 1)  # add as active

        self.csnd.loopSetNumTicks(self.beat * Config.TICKS_PER_BEAT)
        self.drumFillin.unavailable(noteOnsets, notePitchs)

        if self.playing:
            self.csnd.loopStart()

    def __allocate_cb(self, widget, rect):
        GLib.idle_add(self.resize, rect.width, rect.height)
        return False

    def resize(self, width, height):
        logging.debug('activity.py resize......')
        piano_height = width / 2
        self._event_box.set_size_request(
            -1,
            Gdk.Screen.height() - piano_height - style.GRID_CELL_SIZE)
        return False

    def load_instruments(self):
        self._instruments_store = []

        # load the images
        images_path = os.path.join(activity.get_bundle_path(), 'instruments')
        logging.debug('Loading instrument images from %s', images_path)
        for file_name in os.listdir(images_path):
            image_file_name = os.path.join(images_path, file_name)
            pxb = GdkPixbuf.Pixbuf.new_from_file_at_size(
                image_file_name, 75, 75)
            # instrument_name = image_file_name[image_file_name.rfind('/'):]
            instrument_name = image_file_name[image_file_name.rfind('/') + 1:]
            instrument_name = instrument_name[:instrument_name.find('.')]
            instrument_desc = \
                self.instrumentDB.instNamed[instrument_name].nameTooltip

            file_path = os.path.join(images_path, file_name)

            # set the default icon
            if (instrument_name == 'piano'):
                self._what_search_button.set_widget_icon(file_name=file_path)

            self._instruments_store.append({
                "instrument_name":
                instrument_name,
                "pxb":
                pxb,
                "instrument_desc":
                instrument_desc,
                "file_name":
                file_path,
                "callback":
                self.__instrument_iconview_activated_cb
            })

        self._what_widget_contents = set_palette_list(self._instruments_store)
        self._what_widget.add(self._what_widget_contents)
        self._what_widget_contents.show()

        # TODO: disabe until is implemented with csnd6
        """
        for drum_number in range(0, DRUMCOUNT):
            drum_name = 'drum%dkit' % (drum_number + 1)
            self._drums_store.append({
                "instrument_name": drum_name,
                "file_name": imagefile(drum_name + '.svg'),
                "instrument_desc":
                    self.instrumentDB.instNamed[drum_name].nameTooltip,
                "callback": self.__drum_iconview_activated_cb
            })

        self._what_drum_widget_contents = set_palette_list(self._drums_store)
        self._what_drum_widget.add(self._what_drum_widget_contents)
        self._what_drum_widget_contents.show()
        """

    def __drum_iconview_activated_cb(self, widget, event, item):
        data = item['instrument_name']
        self.rythmInstrument = data
        self.csnd.load_drumkit(data)
        instrumentId = self.instrumentDB.instNamed[data].instrumentId
        for (o, n) in self.noteList:
            self.csnd.loopUpdate(n, NoteDB.PARAMETER.INSTRUMENT, instrumentId,
                                 -1)
        self.drumFillin.setInstrument(self.rythmInstrument)
        self._what_drum_search_button.set_widget_label(
            label=item['instrument_desc'])
        self._what_drum_search_button.set_widget_icon(
            file_name=item['file_name'])

    def __instrument_iconview_activated_cb(self, widget, event, item):
        self.setInstrument(item['instrument_name'])
        self._what_search_button.set_widget_icon(file_name=item['file_name'])
        self._what_search_button.set_widget_label(
            label=item['instrument_desc'])

    def set_notes_labels_cb(self, widget):
        self.piano.font_size = 16
        self.piano.set_labels(self.notes_labels)

    def set_ti_notes_labels_cb(self, widget):
        self.piano.font_size = 16
        self.piano.set_labels(self.ti_notes_labels)

    def set_keyboard_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.keyboard_letters)

    def set_german_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.german_labels)

    def beatSliderChange(self, widget, event):
        self.beat = int(self.beats_pm_button.get_value())
        self.sequencer.beat = self.beat
        self.loop.beat = self.beat
        self.drumFillin.setBeats(self.beat)
        img = int(self.scale(self.beat, 2, 12, 1, 11))
        self.beats_pm_button.set_image(imagefile('beat' + str(img) + '.svg'))
        self.beatPickup = False
        self.regenerate()
        self.beatPickup = True

    def regenerate(self):
        def flatten(ll):
            rval = []
            for l in ll:
                rval += l
            return rval

        noteOnsets = []
        notePitchs = []
        i = 0
        self.noteList = []
        self.csnd.loopClear()
        for x in flatten(
                generator(self.rythmInstrument, self.beat, 0.8,
                          self.regularity, self.reverb)):
            x.amplitude = x.amplitude * self.drumVolume
            noteOnsets.append(x.onset)
            notePitchs.append(x.pitch)
            n = Note(0, x.trackId, i, x)
            self.noteList.append((x.onset, n))
            i = i + 1
            self.csnd.loopPlay(n, 1)  # add as active
        self.csnd.loopSetNumTicks(self.beat * Config.TICKS_PER_BEAT)
        self.drumFillin.unavailable(noteOnsets, notePitchs)
        self.recordOverSensitivity(False)
        if self.playing:
            self.csnd.loopStart()

    def handlePlayRecordingButton(self, val):
        if not self.playing_recording:
            self.playing_recording = True
            self.play_recording_button.props.icon_name = 'media-playback-stop'
            self.play_recording_thread = \
                GLib.timeout_add(100, self._play_recorded_keys)
        else:
            self.playing_recording = False
            self.play_recording_button.props.icon_name = 'media-playback-start'

    def _save_ogg_cb(self, widget):
        self._wav_tempfile = tempfile.NamedTemporaryFile(mode='w+b',
                                                         suffix='.wav',
                                                         dir='/tmp/')
        self.csnd.inputMessage(Config.CSOUND_RECORD_PERF %
                               self._wav_tempfile.name)

        self.playing_recording = True
        self.play_recording_thread = \
            GLib.timeout_add(100, self._play_recorded_keys,
                             self._save_ogg_end_cb)

    def _save_ogg_end_cb(self):
        self.csnd.inputMessage(Config.CSOUND_STOP_RECORD_PERF %
                               self._wav_tempfile.name)

        self._ogg_tempfile = tempfile.NamedTemporaryFile(mode='w+b',
                                                         suffix='.ogg',
                                                         dir='/tmp/')

        line = 'filesrc location=%s ! ' \
            'wavparse ! audioconvert ! vorbisenc ! oggmux ! ' \
            'filesink location=%s' % (self._wav_tempfile.name,
                                      self._ogg_tempfile.name)
        pipe = Gst.parse_launch(line)
        pipe.get_bus().add_signal_watch()
        pipe.get_bus().connect('message::eos', self._save_ogg_eos_cb, pipe)
        pipe.set_state(Gst.State.PLAYING)

    def _save_ogg_eos_cb(self, bus, message, pipe):
        bus.remove_signal_watch()
        pipe.set_state(Gst.State.NULL)

        title = '%s saved as audio' % self.metadata['title']

        jobject = datastore.create()
        jobject.metadata['title'] = title
        jobject.metadata['keep'] = '0'
        jobject.metadata['mime_type'] = 'audio/ogg'
        jobject.file_path = self._ogg_tempfile.name
        datastore.write(jobject)

        self._wav_tempfile.close()
        self._ogg_tempfile.close()

        alert = NotifyAlert(10)
        alert.props.title = _('Audio recorded')
        alert.props.msg = _('The audio file was saved in the Journal')
        alert.connect('response', self.__alert_response_cb)
        self.add_alert(alert)

        return False

    def __alert_response_cb(self, alert, result):
        self.remove_alert(alert)

    def __record_button_click_cb(self, button):
        if not self.recording:
            self.play_recording_button.set_sensitive(False)
            self._save_as_audio_bt.set_sensitive(False)
            self.recorded_keys = []
            self.recording = True
            icon = Icon(icon_name='media-record', fill_color='#ff0000')
            icon.show()
            self.record_button.set_icon_widget(icon)
        else:
            self.recording = False
            icon = Icon(icon_name='media-record', fill_color='#ffffff')
            icon.show()
            self.record_button.set_icon_widget(icon)
            if len(self.recorded_keys) != 0:
                self.play_recording_button.set_sensitive(True)
                self._save_as_audio_bt.set_sensitive(True)

    def tempoSliderChange(self, widget, event):
        self._updateTempo(self.tempo_button.get_value())
        img = int(
            self.scale(self.tempo, PLAYER_TEMPO_LOWER, PLAYER_TEMPO_UPPER, 1,
                       9))
        self.tempo_button.set_image(imagefile('tempo' + str(img) + '.png'))

    def _updateTempo(self, val):
        self.tempo = val
        self.beatDuration = 60.0 / self.tempo
        self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0
        self.csnd.setTempo(self.tempo)
        self.sequencer.tempo = self.tempo
        self.drumFillin.setTempo(self.tempo)

    def handlePlayButton(self, val):
        if not self.playing:
            if not self.firstTime:
                self.regenerate()
                self.firstTime = True
            self.drumFillin.play()
            self.csnd.loopSetTick(0)
            self.csnd.loopStart()
            self.playing = True
            self._play_percussion_btn.props.icon_name = 'media-playback-stop'
        else:
            self.drumFillin.stop()
            self.sequencer.stopPlayback()
            self.csnd.loopPause()
            self.playing = False
            self._play_percussion_btn.props.icon_name = 'media-playback-start'

    def scale(self, input, input_min, input_max, output_min, output_max):
        range_input = input_max - input_min
        range_output = output_max - output_min
        result = (input - input_min) * range_output / range_input + output_min

        if (input_min > input_max and output_min > output_max) or \
           (output_min > output_max and input_min < input_max):
            if result > output_min:
                return output_min
            elif result < output_max:
                return output_max
            else:
                return result

        if (input_min < input_max and output_min < output_max) or \
           (output_min < output_max and input_min > input_max):
            if result > output_max:
                return output_max
            elif result < output_min:
                return output_min
            else:
                return result

    def handleComplexityChange(self, widget, event):
        self.regularity = self.complexity_button.get_value()
        img = int(self.complexity_button.get_value() * 7) + 1
        self.complexity_button.set_image(
            imagefile('complex' + str(img) + '.svg'))
        self.beatPickup = False
        self.regenerate()
        self.beatPickup = True

    """
    def handleBalanceSlider(self, adj):
        self.instVolume = int(adj.get_value())
        self.drumVolume = sqrt( (100-self.instVolume)*0.01 )
        self.adjustDrumVolume()
        self.drumFillin.setVolume(self.drumVolume)
        instrumentVolume = sqrt( self.instVolume*0.01 )
        self.loop.adjustLoopVolume(instrumentVolume)
        self.sequencer.adjustSequencerVolume(instrumentVolume)
        img = int(self.scale(self.instVolume,100,0,0,4.9))
        self._playToolbar.balanceSliderImgLeft.set_from_file(
                imagefile('dru' + str(img) + '.png'))
        img2 = int(self.scale(self.instVolume,0,100,0,4.9))
        self._playToolbar.balanceSliderImgRight.set_from_file(
                imagefile('instr' + str(img2) + '.png'))

    def handleReverbSlider(self, adj):
        self.reverb = adj.get_value()
        self.drumFillin.setReverb( self.reverb )
        img = int(self.scale(self.reverb,0,1,0,4))
        self._playToolbar.reverbSliderImgRight.set_from_file(
                imagefile('reverb' + str(img) + '.png'))
        self.keyboardStandAlone.setReverb(self.reverb)
    """

    def set_keyboard_no_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(None)

    def enableKeyboard(self):
        self.keyboardStandAlone = KeyboardStandAlone(
            self.sequencer.recording, self.sequencer.adjustDuration,
            self.csnd.loopGetTick, self.sequencer.getPlayState, self.loop)
        self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)

    def setInstrument(self, instrument):
        logging.debug("Set Instrument: %s" % instrument)
        self.instrument = instrument
        self.keyboardStandAlone.setInstrument(instrument)
        self.csnd.load_instrument(instrument)

    def recordStateButton(self, button, state):
        pass
#        if button == 1:
#            self._recordToolbar.keyboardRecButton.set_active( state )
#        else:
#            self._recordToolbar.keyboardRecOverButton.set_active( state )

    def recordOverSensitivity(self, state):
        pass
        # self._recordToolbar.keyboardRecOverButton.set_sensitive( state )

    def _play_recorded_keys(self, end_cb=None):
        GLib.source_remove(self.play_recording_thread)
        letter = self.recorded_keys[self.play_index]
        time_difference = 0
        if self.play_index == len(self.recorded_keys) - 1:
            time_difference = \
                self.recorded_keys[self.play_index][0] - \
                self.recorded_keys[self.play_index - 1][0]
        else:
            next_time = self.recorded_keys[self.play_index + 1][0]
            time_difference = next_time - letter[0]

        if not self.playing_recording:
            self.play_recording_button.props.icon_name = 'media-playback-start'
            return

        if letter[-1] == 1:
            self.keyboardStandAlone.do_key_release(
                LETTERS_TO_KEY_CODES[letter[3]])
            GLib.idle_add(self.piano.physical_key_changed,
                          LETTERS_TO_KEY_CODES[letter[3]], False)
        else:
            self.keyboardStandAlone.do_key_press(
                LETTERS_TO_KEY_CODES[letter[3]], None,
                math.sqrt(self.instVolume * 0.01))
            GLib.idle_add(self.piano.physical_key_changed,
                          LETTERS_TO_KEY_CODES[letter[3]], True)

        if self.play_index == len(self.recorded_keys) - 1:
            self.play_index = 0
            self.play_recording_button.props.icon_name = 'media-playback-start'
            self.playing_recording = False
            if end_cb is not None:
                end_cb()
        else:
            self.play_index += 1
            self.play_recording_thread = \
                GLib.timeout_add(int((time_difference) * 1000),
                                 self._play_recorded_keys, end_cb)

    def __key_pressed_cb(self, widget, octave_clicked, key_clicked, letter,
                         physicallKey):
        logging.debug('Pressed Octave: %d Key: %d Letter: %s' %
                      (octave_clicked, key_clicked, letter))
        if letter in LETTERS_TO_KEY_CODES.keys():
            if self.recording:
                self.recorded_keys.append(
                    [time.time(), octave_clicked, key_clicked, letter])
            if not physicallKey:
                self.keyboardStandAlone.do_key_press(
                    LETTERS_TO_KEY_CODES[letter], None,
                    math.sqrt(self.instVolume * 0.01))

    def __key_released_cb(self, widget, octave_clicked, key_clicked, letter,
                          physicallKey):
        if self.recording:
            self.recorded_keys.append(
                [time.time(), octave_clicked, key_clicked, letter, 1])
        if not physicallKey:
            if letter in LETTERS_TO_KEY_CODES.keys():
                self.keyboardStandAlone.do_key_release(
                    LETTERS_TO_KEY_CODES[letter])

    def onKeyPress(self, widget, event):
        if event.state & Gdk.ModifierType.CONTROL_MASK:
            return
        if event.hardware_keycode == 37:
            if self.muteInst:
                self.muteInst = False
            else:
                self.muteInst = True
        self.piano.physical_key_changed(event.hardware_keycode, True)
        self.keyboardStandAlone.onKeyPress(widget, event,
                                           math.sqrt(self.instVolume * 0.01))

    def onKeyRelease(self, widget, event):
        self.keyboardStandAlone.onKeyRelease(widget, event)
        self.piano.physical_key_changed(event.hardware_keycode, False)

    def write_file(self, file_path):
        f = open(file_path, 'w')
        # substract the initial time to all the saved values
        if len(self.recorded_keys) > 0:
            initial_time = self.recorded_keys[0][0]
            for key in self.recorded_keys:
                key[0] = key[0] - initial_time

        f.write(json.dumps(self.recorded_keys))
        f.close()

    def read_file(self, file_path):
        f = open(file_path, 'r')
        contents = f.read().strip()

        self.recorded_keys = json.loads(contents)
        if len(self.recorded_keys) != 0:
            self.play_recording_button.set_sensitive(True)
            self._save_as_audio_bt.set_sensitive(True)
        f.close()

    def close(self, skip_save=False):
        self.csnd.stop()  # without which Csound will segfault
        activity.Activity.close(self, skip_save=skip_save)
Beispiel #2
0
class SimplePianoActivity(activity.Activity):
    """SimplePianoActivity class as specified in activity.info"""

    def __init__(self, handle):
        """Set up the HelloWorld activity."""
        activity.Activity.__init__(self, handle)

        # we do not have collaboration features
        # make the share option insensitive
        self.max_participants = 1

        # toolbar with the new toolbar redesign
        toolbar_box = ToolbarBox()

        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        keybord_labels = RadioToolButton()
        keybord_labels.props.icon_name = 'q_key'
        keybord_labels.props.group = keybord_labels
        keybord_labels.connect('clicked', self.set_keyboard_labels_cb)
        toolbar_box.toolbar.insert(keybord_labels, -1)

        notes_labels = RadioToolButton()
        notes_labels.props.icon_name = 'do_key'
        notes_labels.props.group = keybord_labels
        notes_labels.connect('clicked', self.set_notes_labels_cb)
        toolbar_box.toolbar.insert(notes_labels, -1)

        german_labels = RadioToolButton()
        german_labels.props.icon_name = 'c_key'
        german_labels.props.group = keybord_labels
        german_labels.connect('clicked', self.set_german_labels_cb)
        toolbar_box.toolbar.insert(german_labels, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()

        self.keyboard_letters = ['ZSXDCVGBHNJM', 'Q2W3ER5T6Y7U', 'I']

        notes = ['DO', 'DO#', 'RE', 'RE#', 'MI', 'FA', 'FA#', 'SOL',
                 'SOL#', 'LA', 'LA#', 'SI']
        self.notes_labels = [notes, notes, ['DO']]

        german_notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G',
                        'G#', 'A', 'A#', 'B']

        self.german_labels = [german_notes, german_notes, ['C']]

        self.piano = PianoKeyboard(octaves=2, add_c=True,
                                   labels=self.keyboard_letters)

        # init csound
        self.instrumentDB = InstrumentDB.getRef()
        self.firstTime = False
        self.playing = False
        self.csnd = new_csound_client()
        self.timeout_ms = 50
        self.instVolume = 50
        self.drumVolume = 0.5
        self.instrument = 'piano'
        self.regularity = 0.75
        self.beat = 4
        self.reverb = 0.1
        self.tempo = Config.PLAYER_TEMPO
        self.beatDuration = 60.0 / self.tempo
        self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0
        #self.rythmInstrument = 'drum1kit'
        #self.csnd.load_drumkit(self.rythmInstrument)
        self.sequencer = MiniSequencer(self.recordStateButton,
                                       self.recordOverSensitivity)
        self.loop = Loop(self.beat, math.sqrt(self.instVolume * 0.01))

        self.muteInst = False
        self.csnd.setTempo(self.tempo)
        self.noteList = []
        time.sleep(0.001)  # why?
        for i in range(21):
            self.csnd.setTrackVolume(100, i)

        for i in range(10):
            r = str(i + 1)
            self.csnd.load_instrument('guidice' + r)

        self.volume = 100
        self.csnd.setMasterVolume(self.volume)

        self.enableKeyboard()
        self.setInstrument(self.instrument)

        self.connect('key-press-event', self.onKeyPress)
        self.connect('key-release-event', self.onKeyRelease)
        # finish csount init

        self.piano.connect('key_pressed', self.__key_pressed_cb)
        self.piano.connect('key_released', self.__key_released_cb)
        vbox = Gtk.VBox()
        vbox.set_homogeneous(False)
        self.load_instruments()
        vbox.pack_end(self.piano, True, True, 0)
        self.scrolled = Gtk.ScrolledWindow()
        vbox.pack_start(self.scrolled, False, False, 0)
        self.scrolled.add(self.instruments_iconview)
        vbox.show_all()
        self.set_canvas(vbox)
        piano_height = Gdk.Screen.width() / 2
        self.scrolled.set_size_request(
            -1, Gdk.Screen.height() - piano_height - style.GRID_CELL_SIZE)
        self.connect('size-allocate', self.__allocate_cb)

    def __allocate_cb(self, widget, rect):
        GLib.idle_add(self.resize, rect.width, rect.height)
        return False

    def resize(self, width, height):
        piano_height = width / 2
        self.scrolled.set_size_request(
            -1, height - piano_height - style.GRID_CELL_SIZE)
        return False

    def load_instruments(self):
        self._instruments_store = Gtk.ListStore(str, GdkPixbuf.Pixbuf, str)
        self._instruments_store.set_sort_column_id(0, Gtk.SortType.ASCENDING)
        self.instruments_iconview = Gtk.IconView(self._instruments_store)
        self.instruments_iconview.set_text_column(2)
        self.instruments_iconview.set_pixbuf_column(1)

        # load the images
        images_path = os.path.join(activity.get_bundle_path(),
                                   'instruments')
        logging.error('Loading instrument images from %s', images_path)
        for file_name in os.listdir(images_path):
            image_file_name = os.path.join(images_path, file_name)
            logging.error('Adding %s', image_file_name)
            pxb = GdkPixbuf.Pixbuf.new_from_file_at_size(
                image_file_name, 75, 75)
            #instrument_name = image_file_name[image_file_name.rfind('/'):]
            instrument_name = image_file_name[image_file_name.rfind('/') + 1:]
            instrument_name = instrument_name[:instrument_name.find('.')]
            instrument_desc = \
                self.instrumentDB.instNamed[instrument_name].nameTooltip
            self._instruments_store.append([instrument_name, pxb,
                                           instrument_desc])
        self.instruments_iconview.connect(
            'selection-changed', self.__instrument_iconview_activated_cb)

    def __instrument_iconview_activated_cb(self, widget):
        item = widget.get_selected_items()[0]
        model = widget.get_model()
        instrument_name = model[item][0]
        self.setInstrument(instrument_name)

    def set_notes_labels_cb(self, widget):
        self.piano.font_size = 16
        self.piano.set_labels(self.notes_labels)

    def set_keyboard_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.keyboard_letters)

    def set_german_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.german_labels)

    def enableKeyboard(self):
        self.keyboardStandAlone = KeyboardStandAlone(
            self.sequencer.recording, self.sequencer.adjustDuration,
            self.csnd.loopGetTick, self.sequencer.getPlayState, self.loop)
        self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)

    def setInstrument(self, instrument):
        self.instrument = instrument
        self.keyboardStandAlone.setInstrument(instrument)
        self.csnd.load_instrument(instrument)

    def recordStateButton(self, button, state):
        pass
#        if button == 1:
#            self._recordToolbar.keyboardRecButton.set_active( state )
#        else:
#            self._recordToolbar.keyboardRecOverButton.set_active( state )

    def recordOverSensitivity(self, state):
        pass
        #self._recordToolbar.keyboardRecOverButton.set_sensitive( state )

    def __key_pressed_cb(self, widget, octave_clicked, key_clicked, letter):
        logging.debug(
            'Pressed Octave: %d Key: %d Letter: %s' %
            (octave_clicked, key_clicked, letter))
        if letter in LETTERS_TO_KEY_CODES.keys():
            self.keyboardStandAlone.do_key_press(
                LETTERS_TO_KEY_CODES[letter], None,
                math.sqrt(self.instVolume * 0.01))

    def __key_released_cb(self, widget, octave_clicked, key_clicked, letter):
        if letter in LETTERS_TO_KEY_CODES.keys():
            self.keyboardStandAlone.do_key_release(
                LETTERS_TO_KEY_CODES[letter])

    def onKeyPress(self, widget, event):

        if event.hardware_keycode == 37:
            if self.muteInst:
                self.muteInst = False
            else:
                self.muteInst = True
        self.piano.physical_key_changed(event.hardware_keycode, True)
        self.keyboardStandAlone.onKeyPress(
            widget, event, math.sqrt(self.instVolume * 0.01))

    def onKeyRelease(self, widget, event):
        self.keyboardStandAlone.onKeyRelease(widget, event)
        self.piano.physical_key_changed(event.hardware_keycode, False)
class SimplePianoActivity(activity.Activity):
    """SimplePianoActivity class as specified in activity.info"""

    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.close)
        Gst.init(None)

        self._what_list = []

        self.play_recording_thread = None

        self.playing_recording = False
        self.firstTime = False
        self.playing = False
        self.regularity = 0.7
        self._drums_store = []
        self.recording = False
        self.recorded_keys = []
        self.is_valid_recording = False

        # we do not have collaboration features
        # make the share option insensitive
        self.max_participants = 1
        self.csnd = new_csound_client()
        self.rythmInstrument = 'drum1kick'
        # toolbar with the new toolbar redesign
        toolbar_box = ToolbarBox()
        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        toolbar_box.toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)

        self.play_index = 0

        self.play_recording_button = ToolButton(
            icon_name='media-playback-start')
        self.play_recording_button.set_property('can-default', True)
        self.play_recording_button.show()
        self.record_button = ToggleToolButton(icon_name='media-record')
        self.record_button.set_property('can-default', True)
        self.record_button.show()
        self.play_recording_button.set_sensitive(False)

        self.record_button.connect('clicked', self.__record_button_click_cb)

        self.play_recording_button.connect('clicked',
                                           self.handlePlayRecordingButton)

        toolbar_box.toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)

        # TODO: disabe until is implemented with csnd6
        # self.createPercussionToolbar(toolbar_box)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        keybord_labels = RadioToolButton()
        keybord_labels.props.icon_name = 'q_key'
        keybord_labels.props.group = keybord_labels
        keybord_labels.connect('clicked', self.set_keyboard_labels_cb)
        toolbar_box.toolbar.insert(keybord_labels, -1)

        notes_labels = RadioToolButton()
        notes_labels.props.icon_name = 'do_key'
        notes_labels.props.group = keybord_labels
        notes_labels.connect('clicked', self.set_notes_labels_cb)
        toolbar_box.toolbar.insert(notes_labels, -1)

        ti_notes_labels = RadioToolButton()
        ti_notes_labels.props.icon_name = 'ti_key'
        ti_notes_labels.props.group = keybord_labels
        ti_notes_labels.connect('clicked', self.set_ti_notes_labels_cb)
        toolbar_box.toolbar.insert(ti_notes_labels, -1)

        german_labels = RadioToolButton()
        german_labels.props.icon_name = 'c_key'
        german_labels.props.group = keybord_labels
        german_labels.connect('clicked', self.set_german_labels_cb)
        toolbar_box.toolbar.insert(german_labels, -1)

        no_labels = RadioToolButton()
        no_labels.props.icon_name = 'edit-clear'
        no_labels.props.group = keybord_labels
        no_labels.connect('clicked', self.set_keyboard_no_labels_cb)
        toolbar_box.toolbar.insert(no_labels, -1)
        self._what_widget = Gtk.ToolItem()
        self._what_search_button = FilterToolItem(_('Select Instrument'),
                                                  'view-type',
                                                  _('Piano'),
                                                  self._what_widget)
        self._what_widget.show()
        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        toolbar_box.toolbar.insert(self._what_search_button, -1)
        self._what_search_button.show()
        self._what_search_button.set_is_important(True)
        self._what_widget_contents = None
        self._what_drum_widget_contents = None

        separator = Gtk.SeparatorToolItem()
        toolbar_box.toolbar.insert(separator, -1)

        toolbar_box.toolbar.insert(self.record_button, -1)
        toolbar_box.toolbar.insert(self.play_recording_button, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self._save_as_audio_bt = ToolButton(icon_name='save-as-audio')
        self._save_as_audio_bt.props.tooltip = _('Save as audio')
        self._save_as_audio_bt.connect('clicked', self._save_ogg_cb)
        self._save_as_audio_bt.show()
        self._save_as_audio_bt.set_sensitive(False)
        activity_button.page.insert(self._save_as_audio_bt, -1)

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()

        self.keyboard_letters = ['ZSXDCVGBHNJM', 'Q2W3ER5T6Y7U', 'I']

        notes = ['DO', ['DO#', 'REb'], 'RE', ['RE#', 'MIb'], 'MI', 'FA',
                 ['FA#', 'SOLb'], 'SOL',
                 ['SOL#', 'LAb'], 'LA', ['LA#', 'SIb'], 'SI']
        self.notes_labels = [notes, notes, ['DO']]

        # some countries use TI instead of SI
        ti_notes = ['DO', ['DO#', 'REb'], 'RE', ['RE#', 'MIb'], 'MI', 'FA',
                    ['FA#', 'SOLb'], 'SOL',
                    ['SOL#', 'LAb'], 'LA', ['LA#', 'TIb'], 'TI']
        self.ti_notes_labels = [ti_notes, ti_notes, ['DO']]

        german_notes = ['C', ['C#', 'Db'], 'D', ['D#', 'Eb'], 'E', 'F',
                        ['F#', 'Gb'], 'G',
                        ['G#', 'Ab'], 'A', ['A#', 'Bb'], 'B']

        self.german_labels = [german_notes, german_notes, ['C']]

        self.piano = PianoKeyboard(octaves=2, add_c=True,
                                   labels=self.keyboard_letters)

        # init csound
        self.instrumentDB = InstrumentDB.getRef()
        self.timeout_ms = 50
        self.instVolume = 50
        self.drumVolume = 0.5
        self.instrument = 'piano'
        self.beat = 4
        self.reverb = 0.1
        self.tempo = PLAYER_TEMPO
        self.beatDuration = 60.0 / self.tempo
        self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0

        self.sequencer = MiniSequencer(self.recordStateButton,
                                       self.recordOverSensitivity)
        self.loop = Loop(self.beat, math.sqrt(self.instVolume * 0.01))

        self.drumFillin = Fillin(self.beat,
                                 self.tempo,
                                 self.rythmInstrument,
                                 self.reverb,
                                 self.drumVolume)

        self.muteInst = False
        self.csnd.setTempo(self.tempo)
        self.noteList = []
        for i in range(21):
            self.csnd.setTrackVolume(100, i)

        # TODO commented because apparently are not used in the activity
        # for i in range(10):
        #     self.csnd.load_instrument('guidice' + str(i + 1))

        self.volume = 100
        self.csnd.setMasterVolume(self.volume)

        self.enableKeyboard()
        self.setInstrument(self.instrument)

        self.connect('key-press-event', self.onKeyPress)
        self.connect('key-release-event', self.onKeyRelease)

        self.piano.connect('key_pressed', self.__key_pressed_cb)
        self.piano.connect('key_released', self.__key_released_cb)
        vbox = Gtk.VBox()
        vbox.set_homogeneous(False)
        self.load_instruments()
        self._event_box = Gtk.EventBox()
        self._event_box.modify_bg(
            Gtk.StateType.NORMAL, style.COLOR_WHITE.get_gdk_color())
        vbox.pack_start(self._event_box, False, False, 0)
        vbox.pack_end(self.piano, True, True, 0)
        vbox.show_all()
        self.set_canvas(vbox)
        piano_height = Gdk.Screen.width() / 2
        self._event_box.set_size_request(
            -1, Gdk.Screen.height() - piano_height - style.GRID_CELL_SIZE)
        self.connect('size-allocate', self.__allocate_cb)

        # TODO: disabe until is implemented with csnd6
        # GLib.idle_add(self.initializePercussion)

    def createPercussionToolbar(self, toolbar_box):

        self.beats_pm_button = IntensitySelector(range(2, 13),
                                                 4,
                                                 imagefile('beat3.svg'))
        self.tempo_button = \
            IntensitySelector(range(PLAYER_TEMPO_LOWER,
                                    PLAYER_TEMPO_UPPER + 1, PLAYER_TEMPO_STEP),
                              PLAYER_TEMPO, imagefile('tempo5.png'))

        self.complexity_button = IntensitySelector(xfrange(0, 1, 0.1),
                                                   self.regularity,
                                                   imagefile('complex6.svg'))

        self._play_percussion_btn = ToolButton(
            icon_name='media-playback-start')
        self._play_percussion_btn.set_property('can-default', True)
        self._play_percussion_btn.show()
        self._play_percussion_btn.connect('clicked', self.handlePlayButton)

        beats_toolbar = ToolbarBox()
        beats_toolbar.toolbar.insert(self._play_percussion_btn, -1)

        self._what_drum_widget = Gtk.ToolItem()
        self._what_drum_search_button = FilterToolItem(
            _('Select Drum'), 'view-type', _('Jazz / Rock Kit'),
            self._what_drum_widget)
        self._what_drum_search_button.set_widget_icon(
            file_name=imagefile("drum1kit.svg"))

        self._what_drum_widget.show()
        beats_toolbar.toolbar.insert(self._what_drum_search_button, -1)
        self._what_drum_search_button.show()
        self._what_drum_search_button.set_is_important(True)

        beats_toolbar.toolbar.insert(Gtk.SeparatorToolItem(), -1)
        beats_toolbar.toolbar.insert(self.complexity_button, -1)
        beats_toolbar.toolbar.insert(self.beats_pm_button, -1)
        beats_toolbar.toolbar.insert(self.tempo_button, -1)

        beats_toolbar_button = ToolbarButton(icon_name='toolbar-drums',
                                             page=beats_toolbar)
        beats_toolbar_button.show()

        toolbar_box.toolbar.insert(beats_toolbar_button, 1)

        self.beats_pm_button.set_tooltip(_("Beats per bar"))
        self.beats_pm_button.show()
        self.beats_pm_button.connect('changed', self.beatSliderChange, True)
        self.tempo_button.connect('changed', self.tempoSliderChange, True)
        self.complexity_button.connect('changed',
                                       self.handleComplexityChange,
                                       True)
        self.complexity_button.set_tooltip(_("Beat complexity"))
        self.tempo_button.show()
        self.tempo_button.set_tooltip(_('Tempo'))
        self.complexity_button.show()

    def initializePercussion(self):
        self.rythmInstrument = 'drum1kit'
        self.csnd.load_drumkit(self.rythmInstrument)
        self.csnd.setTempo(self.tempo)
        self.beatPickup = False

        def flatten(ll):
            rval = []
            for l in ll:
                rval += l
            return rval

        noteOnsets = []
        notePitchs = []

        i = 0
        self.noteList = []
        self.csnd.loopClear()
        for x in flatten(
            generator(self.rythmInstrument, self.beat,
                      0.8, self.regularity, self.reverb)):
            x.amplitude = x.amplitude * self.drumVolume
            noteOnsets.append(x.onset)
            notePitchs.append(x.pitch)
            n = Note(0, x.trackId, i, x)
            self.noteList.append((x.onset, n))
            i = i + 1
            self.csnd.loopPlay(n, 1)                    # add as active

        self.csnd.loopSetNumTicks(self.beat * Config.TICKS_PER_BEAT)
        self.drumFillin.unavailable(noteOnsets, notePitchs)

        if self.playing:
            self.csnd.loopStart()

    def __allocate_cb(self, widget, rect):
        GLib.idle_add(self.resize, rect.width, rect.height)
        return False

    def resize(self, width, height):
        logging.debug('activity.py resize......')
        piano_height = width / 2
        self._event_box.set_size_request(
            -1, Gdk.Screen.height() - piano_height - style.GRID_CELL_SIZE)
        return False

    def load_instruments(self):
        self._instruments_store = []

        # load the images
        images_path = os.path.join(activity.get_bundle_path(),
                                   'instruments')
        logging.debug('Loading instrument images from %s', images_path)
        for file_name in os.listdir(images_path):
            image_file_name = os.path.join(images_path, file_name)
            pxb = GdkPixbuf.Pixbuf.new_from_file_at_size(
                image_file_name, 75, 75)
            # instrument_name = image_file_name[image_file_name.rfind('/'):]
            instrument_name = image_file_name[image_file_name.rfind('/') + 1:]
            instrument_name = instrument_name[:instrument_name.find('.')]
            instrument_desc = \
                self.instrumentDB.instNamed[instrument_name].nameTooltip

            file_path = os.path.join(images_path, file_name)

            # set the default icon
            if (instrument_name == 'piano'):
                self._what_search_button.set_widget_icon(
                    file_name=file_path)

            self._instruments_store.append(
                {"instrument_name": instrument_name,
                 "pxb": pxb,
                 "instrument_desc": instrument_desc,
                 "file_name": file_path,
                 "callback": self.__instrument_iconview_activated_cb})

        self._what_widget_contents = set_palette_list(self._instruments_store)
        self._what_widget.add(self._what_widget_contents)
        self._what_widget_contents.show()

        # TODO: disabe until is implemented with csnd6
        """
        for drum_number in range(0, DRUMCOUNT):
            drum_name = 'drum%dkit' % (drum_number + 1)
            self._drums_store.append({
                "instrument_name": drum_name,
                "file_name": imagefile(drum_name + '.svg'),
                "instrument_desc":
                    self.instrumentDB.instNamed[drum_name].nameTooltip,
                "callback": self.__drum_iconview_activated_cb
            })

        self._what_drum_widget_contents = set_palette_list(self._drums_store)
        self._what_drum_widget.add(self._what_drum_widget_contents)
        self._what_drum_widget_contents.show()
        """

    def __drum_iconview_activated_cb(self, widget, event, item):
        data = item['instrument_name']
        self.rythmInstrument = data
        self.csnd.load_drumkit(data)
        instrumentId = self.instrumentDB.instNamed[data].instrumentId
        for (o, n) in self.noteList:
            self.csnd.loopUpdate(n, NoteDB.PARAMETER.INSTRUMENT,
                                 instrumentId, -1)
        self.drumFillin.setInstrument(self.rythmInstrument)
        self._what_drum_search_button.set_widget_label(
            label=item['instrument_desc'])
        self._what_drum_search_button.set_widget_icon(
            file_name=item['file_name'])

    def __instrument_iconview_activated_cb(self, widget, event, item):
        self.setInstrument(item['instrument_name'])
        self._what_search_button.set_widget_icon(file_name=item['file_name'])
        self._what_search_button.set_widget_label(
            label=item['instrument_desc'])

    def set_notes_labels_cb(self, widget):
        self.piano.font_size = 16
        self.piano.set_labels(self.notes_labels)

    def set_ti_notes_labels_cb(self, widget):
        self.piano.font_size = 16
        self.piano.set_labels(self.ti_notes_labels)

    def set_keyboard_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.keyboard_letters)

    def set_german_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.german_labels)

    def beatSliderChange(self, widget, event):
        self.beat = int(self.beats_pm_button.get_value())
        self.sequencer.beat = self.beat
        self.loop.beat = self.beat
        self.drumFillin.setBeats(self.beat)
        img = int(self.scale(self.beat, 2, 12, 1, 11))
        self.beats_pm_button.set_image(imagefile('beat' + str(img) + '.svg'))
        self.beatPickup = False
        self.regenerate()
        self.beatPickup = True

    def regenerate(self):
        def flatten(ll):
            rval = []
            for l in ll:
                rval += l
            return rval
        noteOnsets = []
        notePitchs = []
        i = 0
        self.noteList = []
        self.csnd.loopClear()
        for x in flatten(
            generator(self.rythmInstrument,
                      self.beat, 0.8, self.regularity,
                      self.reverb)):
            x.amplitude = x.amplitude * self.drumVolume
            noteOnsets.append(x.onset)
            notePitchs.append(x.pitch)
            n = Note(0, x.trackId, i, x)
            self.noteList.append((x.onset, n))
            i = i + 1
            self.csnd.loopPlay(n, 1)                    # add as active
        self.csnd.loopSetNumTicks(self.beat * Config.TICKS_PER_BEAT)
        self.drumFillin.unavailable(noteOnsets, notePitchs)
        self.recordOverSensitivity(False)
        if self.playing:
            self.csnd.loopStart()

    def handlePlayRecordingButton(self, val):
        if not self.playing_recording:
            self.playing_recording = True
            self.play_recording_button.props.icon_name = 'media-playback-stop'
            self.play_recording_thread = \
                GLib.timeout_add(100, self._play_recorded_keys)
        else:
            self.playing_recording = False
            self.play_recording_button.props.icon_name = 'media-playback-start'

    def _save_ogg_cb(self, widget):
        self._wav_tempfile = tempfile.NamedTemporaryFile(
            mode='w+b', suffix='.wav', dir='/tmp/')
        self.csnd.inputMessage(Config.CSOUND_RECORD_PERF %
                               self._wav_tempfile.name)

        self.playing_recording = True
        self.play_recording_thread = \
            GLib.timeout_add(100, self._play_recorded_keys,
                             self._save_ogg_end_cb)

    def _save_ogg_end_cb(self):
        self.csnd.inputMessage(Config.CSOUND_STOP_RECORD_PERF %
                               self._wav_tempfile.name)

        self._ogg_tempfile = tempfile.NamedTemporaryFile(
            mode='w+b', suffix='.ogg', dir='/tmp/')

        line = 'filesrc location=%s ! ' \
            'wavparse ! audioconvert ! vorbisenc ! oggmux ! ' \
            'filesink location=%s' % (self._wav_tempfile.name,
                                      self._ogg_tempfile.name)
        pipe = Gst.parse_launch(line)
        pipe.get_bus().add_signal_watch()
        pipe.get_bus().connect('message::eos', self._save_ogg_eos_cb, pipe)
        pipe.set_state(Gst.State.PLAYING)

    def _save_ogg_eos_cb(self, bus, message, pipe):
        bus.remove_signal_watch()
        pipe.set_state(Gst.State.NULL)

        title = '%s saved as audio' % self.metadata['title']

        jobject = datastore.create()
        jobject.metadata['title'] = title
        jobject.metadata['keep'] = '0'
        jobject.metadata['mime_type'] = 'audio/ogg'
        jobject.file_path = self._ogg_tempfile.name
        datastore.write(jobject)

        self._wav_tempfile.close()
        self._ogg_tempfile.close()

        alert = NotifyAlert(10)
        alert.props.title = _('Audio recorded')
        alert.props.msg = _('The audio file was saved in the Journal')
        alert.connect('response', self.__alert_response_cb)
        self.add_alert(alert)

        return False

    def __alert_response_cb(self, alert, result):
        self.remove_alert(alert)

    def __record_button_click_cb(self, button):
        if not self.recording:
            self.play_recording_button.set_sensitive(False)
            self._save_as_audio_bt.set_sensitive(False)
            self.recorded_keys = []
            self.recording = True
            icon = Icon(icon_name='media-record', fill_color='#ff0000')
            icon.show()
            self.record_button.set_icon_widget(icon)
        else:
            self.recording = False
            icon = Icon(icon_name='media-record', fill_color='#ffffff')
            icon.show()
            self.record_button.set_icon_widget(icon)
            if len(self.recorded_keys) != 0:
                self.play_recording_button.set_sensitive(True)
                self._save_as_audio_bt.set_sensitive(True)

    def tempoSliderChange(self, widget, event):
        self._updateTempo(self.tempo_button.get_value())
        img = int(self.scale(self.tempo, PLAYER_TEMPO_LOWER,
                             PLAYER_TEMPO_UPPER, 1, 9))
        self.tempo_button.set_image(imagefile('tempo' + str(img) + '.png'))

    def _updateTempo(self, val):
        self.tempo = val
        self.beatDuration = 60.0 / self.tempo
        self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0
        self.csnd.setTempo(self.tempo)
        self.sequencer.tempo = self.tempo
        self.drumFillin.setTempo(self.tempo)

    def handlePlayButton(self, val):
        if not self.playing:
            if not self.firstTime:
                self.regenerate()
                self.firstTime = True
            self.drumFillin.play()
            self.csnd.loopSetTick(0)
            self.csnd.loopStart()
            self.playing = True
            self._play_percussion_btn.props.icon_name = 'media-playback-stop'
        else:
            self.drumFillin.stop()
            self.sequencer.stopPlayback()
            self.csnd.loopPause()
            self.playing = False
            self._play_percussion_btn.props.icon_name = 'media-playback-start'

    def scale(self, input, input_min, input_max,
              output_min, output_max):
        range_input = input_max - input_min
        range_output = output_max - output_min
        result = (input - input_min) * range_output / range_input + output_min

        if (input_min > input_max and output_min > output_max) or \
           (output_min > output_max and input_min < input_max):
            if result > output_min:
                return output_min
            elif result < output_max:
                return output_max
            else:
                return result

        if (input_min < input_max and output_min < output_max) or \
           (output_min < output_max and input_min > input_max):
            if result > output_max:
                return output_max
            elif result < output_min:
                return output_min
            else:
                return result

    def handleComplexityChange(self, widget, event):
        self.regularity = self.complexity_button.get_value()
        img = int(self.complexity_button.get_value() * 7) + 1
        self.complexity_button.set_image(
            imagefile('complex' + str(img) + '.svg'))
        self.beatPickup = False
        self.regenerate()
        self.beatPickup = True

    """
    def handleBalanceSlider(self, adj):
        self.instVolume = int(adj.get_value())
        self.drumVolume = sqrt( (100-self.instVolume)*0.01 )
        self.adjustDrumVolume()
        self.drumFillin.setVolume(self.drumVolume)
        instrumentVolume = sqrt( self.instVolume*0.01 )
        self.loop.adjustLoopVolume(instrumentVolume)
        self.sequencer.adjustSequencerVolume(instrumentVolume)
        img = int(self.scale(self.instVolume,100,0,0,4.9))
        self._playToolbar.balanceSliderImgLeft.set_from_file(
                imagefile('dru' + str(img) + '.png'))
        img2 = int(self.scale(self.instVolume,0,100,0,4.9))
        self._playToolbar.balanceSliderImgRight.set_from_file(
                imagefile('instr' + str(img2) + '.png'))

    def handleReverbSlider(self, adj):
        self.reverb = adj.get_value()
        self.drumFillin.setReverb( self.reverb )
        img = int(self.scale(self.reverb,0,1,0,4))
        self._playToolbar.reverbSliderImgRight.set_from_file(
                imagefile('reverb' + str(img) + '.png'))
        self.keyboardStandAlone.setReverb(self.reverb)
    """
    def set_keyboard_no_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(None)

    def enableKeyboard(self):
        self.keyboardStandAlone = KeyboardStandAlone(
            self.sequencer.recording, self.sequencer.adjustDuration,
            self.csnd.loopGetTick, self.sequencer.getPlayState, self.loop)
        self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)

    def setInstrument(self, instrument):
        logging.debug("Set Instrument: %s" % instrument)
        self.instrument = instrument
        self.keyboardStandAlone.setInstrument(instrument)
        self.csnd.load_instrument(instrument)

    def recordStateButton(self, button, state):
        pass
#        if button == 1:
#            self._recordToolbar.keyboardRecButton.set_active( state )
#        else:
#            self._recordToolbar.keyboardRecOverButton.set_active( state )

    def recordOverSensitivity(self, state):
        pass
        # self._recordToolbar.keyboardRecOverButton.set_sensitive( state )

    def _play_recorded_keys(self, end_cb=None):
        GLib.source_remove(self.play_recording_thread)
        letter = self.recorded_keys[self.play_index]
        time_difference = 0
        if self.play_index == len(self.recorded_keys) - 1:
            time_difference = \
                self.recorded_keys[self.play_index][0] - \
                self.recorded_keys[self.play_index - 1][0]
        else:
            next_time = self.recorded_keys[self.play_index + 1][0]
            time_difference = next_time - letter[0]

        if not self.playing_recording:
            self.play_recording_button.props.icon_name = 'media-playback-start'
            return

        if letter[-1] == 1:
            self.keyboardStandAlone.do_key_release(
                LETTERS_TO_KEY_CODES[letter[3]])
            GLib.idle_add(self.piano.physical_key_changed,
                          LETTERS_TO_KEY_CODES[letter[3]], False)
        else:
            self.keyboardStandAlone.do_key_press(
                LETTERS_TO_KEY_CODES[letter[3]], None,
                math.sqrt(self.instVolume * 0.01))
            GLib.idle_add(self.piano.physical_key_changed,
                          LETTERS_TO_KEY_CODES[letter[3]], True)

        if self.play_index == len(self.recorded_keys) - 1:
            self.play_index = 0
            self.play_recording_button.props.icon_name = 'media-playback-start'
            self.playing_recording = False
            if end_cb is not None:
                end_cb()
        else:
            self.play_index += 1
            self.play_recording_thread = \
                GLib.timeout_add(int((time_difference) * 1000),
                                 self._play_recorded_keys, end_cb)

    def __key_pressed_cb(self, widget, octave_clicked, key_clicked, letter,
                         physicallKey):
        logging.debug(
            'Pressed Octave: %d Key: %d Letter: %s' %
            (octave_clicked, key_clicked, letter))
        if letter in LETTERS_TO_KEY_CODES.keys():
            if self.recording:
                self.recorded_keys.append(
                    [time.time(), octave_clicked, key_clicked, letter])
            if not physicallKey:
                self.keyboardStandAlone.do_key_press(
                    LETTERS_TO_KEY_CODES[letter], None,
                    math.sqrt(self.instVolume * 0.01))

    def __key_released_cb(self, widget, octave_clicked, key_clicked, letter,
                          physicallKey):
        if self.recording:
            self.recorded_keys.append(
                [time.time(), octave_clicked, key_clicked, letter, 1])
        if not physicallKey:
            if letter in LETTERS_TO_KEY_CODES.keys():
                self.keyboardStandAlone.do_key_release(
                    LETTERS_TO_KEY_CODES[letter])

    def onKeyPress(self, widget, event):
        if event.state & Gdk.ModifierType.CONTROL_MASK:
            return
        if event.hardware_keycode == 37:
            if self.muteInst:
                self.muteInst = False
            else:
                self.muteInst = True
        self.piano.physical_key_changed(event.hardware_keycode, True)
        self.keyboardStandAlone.onKeyPress(
            widget, event, math.sqrt(self.instVolume * 0.01))

    def onKeyRelease(self, widget, event):
        self.keyboardStandAlone.onKeyRelease(widget, event)
        self.piano.physical_key_changed(event.hardware_keycode, False)

    def write_file(self, file_path):
        f = open(file_path, 'w')
        # substract the initial time to all the saved values
        if len(self.recorded_keys) > 0:
            initial_time = self.recorded_keys[0][0]
            for key in self.recorded_keys:
                key[0] = key[0] - initial_time

        f.write(json.dumps(self.recorded_keys))
        f.close()

    def read_file(self, file_path):
        f = open(file_path, 'r')
        contents = f.read().strip()

        self.recorded_keys = json.loads(contents)
        if len(self.recorded_keys) != 0:
            self.play_recording_button.set_sensitive(True)
            self._save_as_audio_bt.set_sensitive(True)
        f.close()

    def close(self, skip_save=False):
        self.csnd.stop()  # without which Csound will segfault
        activity.Activity.close(self, skip_save=skip_save)
Beispiel #4
0
class SimplePianoActivity(activity.Activity):
    """SimplePianoActivity class as specified in activity.info"""
    def __init__(self, handle):
        """Set up the HelloWorld activity."""
        activity.Activity.__init__(self, handle)

        # we do not have collaboration features
        # make the share option insensitive
        self.max_participants = 1

        # toolbar with the new toolbar redesign
        toolbar_box = ToolbarBox()

        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        keybord_labels = RadioToolButton()
        keybord_labels.props.icon_name = 'q_key'
        keybord_labels.props.group = keybord_labels
        keybord_labels.connect('clicked', self.set_keyboard_labels_cb)
        toolbar_box.toolbar.insert(keybord_labels, -1)

        notes_labels = RadioToolButton()
        notes_labels.props.icon_name = 'do_key'
        notes_labels.props.group = keybord_labels
        notes_labels.connect('clicked', self.set_notes_labels_cb)
        toolbar_box.toolbar.insert(notes_labels, -1)

        ti_notes_labels = RadioToolButton()
        ti_notes_labels.props.icon_name = 'ti_key'
        ti_notes_labels.props.group = keybord_labels
        ti_notes_labels.connect('clicked', self.set_ti_notes_labels_cb)
        toolbar_box.toolbar.insert(ti_notes_labels, -1)

        german_labels = RadioToolButton()
        german_labels.props.icon_name = 'c_key'
        german_labels.props.group = keybord_labels
        german_labels.connect('clicked', self.set_german_labels_cb)
        toolbar_box.toolbar.insert(german_labels, -1)

        no_labels = RadioToolButton()
        no_labels.props.icon_name = 'edit-clear'
        no_labels.props.group = keybord_labels
        no_labels.connect('clicked', self.set_keyboard_no_labels_cb)
        toolbar_box.toolbar.insert(no_labels, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show_all()

        self.keyboard_letters = ['ZSXDCVGBHNJM', 'Q2W3ER5T6Y7U', 'I']

        notes = [
            'DO', ['DO#', 'REb'], 'RE', ['RE#', 'MIb'], 'MI', 'FA',
            ['FA#', 'SOLb'], 'SOL', ['SOL#', 'LAb'], 'LA', ['LA#', 'SIb'], 'SI'
        ]
        self.notes_labels = [notes, notes, ['DO']]

        # some countries use TI instead of SI
        ti_notes = [
            'DO', ['DO#', 'REb'], 'RE', ['RE#', 'MIb'], 'MI', 'FA',
            ['FA#', 'SOLb'], 'SOL', ['SOL#', 'LAb'], 'LA', ['LA#', 'TIb'], 'TI'
        ]
        self.ti_notes_labels = [ti_notes, ti_notes, ['DO']]

        german_notes = [
            'C', ['C#', 'Db'], 'D', ['D#', 'Eb'], 'E', 'F', ['F#', 'Gb'], 'G',
            ['G#', 'Ab'], 'A', ['A#', 'Bb'], 'B'
        ]

        self.german_labels = [german_notes, german_notes, ['C']]

        self.piano = PianoKeyboard(octaves=2,
                                   add_c=True,
                                   labels=self.keyboard_letters)

        # init csound
        self.instrumentDB = InstrumentDB.getRef()
        self.firstTime = False
        self.playing = False
        self.csnd = new_csound_client()
        self.timeout_ms = 50
        self.instVolume = 50
        self.drumVolume = 0.5
        self.instrument = 'piano'
        self.regularity = 0.75
        self.beat = 4
        self.reverb = 0.1
        self.tempo = Config.PLAYER_TEMPO
        self.beatDuration = 60.0 / self.tempo
        self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0
        #self.rythmInstrument = 'drum1kit'
        #self.csnd.load_drumkit(self.rythmInstrument)
        self.sequencer = MiniSequencer(self.recordStateButton,
                                       self.recordOverSensitivity)
        self.loop = Loop(self.beat, math.sqrt(self.instVolume * 0.01))

        self.muteInst = False
        self.csnd.setTempo(self.tempo)
        self.noteList = []
        time.sleep(0.001)  # why?
        for i in range(21):
            self.csnd.setTrackVolume(100, i)

        for i in range(10):
            r = str(i + 1)
            self.csnd.load_instrument('guidice' + r)

        self.volume = 100
        self.csnd.setMasterVolume(self.volume)

        self.enableKeyboard()
        self.setInstrument(self.instrument)

        self.connect('key-press-event', self.onKeyPress)
        self.connect('key-release-event', self.onKeyRelease)
        # finish csount init

        self.piano.connect('key_pressed', self.__key_pressed_cb)
        self.piano.connect('key_released', self.__key_released_cb)
        vbox = Gtk.VBox()
        vbox.set_homogeneous(False)
        self.load_instruments()
        vbox.pack_end(self.piano, True, True, 0)
        self.scrolled = Gtk.ScrolledWindow()
        vbox.pack_start(self.scrolled, False, False, 0)
        self.scrolled.add(self.instruments_iconview)
        vbox.show_all()
        self.set_canvas(vbox)
        piano_height = Gdk.Screen.width() / 2
        self.scrolled.set_size_request(
            -1,
            Gdk.Screen.height() - piano_height - style.GRID_CELL_SIZE)
        self.connect('size-allocate', self.__allocate_cb)

    def __allocate_cb(self, widget, rect):
        GLib.idle_add(self.resize, rect.width, rect.height)
        return False

    def resize(self, width, height):
        piano_height = width / 2
        self.scrolled.set_size_request(
            -1, height - piano_height - style.GRID_CELL_SIZE)
        return False

    def load_instruments(self):
        self._instruments_store = Gtk.ListStore(str, GdkPixbuf.Pixbuf, str)
        self._instruments_store.set_sort_column_id(0, Gtk.SortType.ASCENDING)
        self.instruments_iconview = Gtk.IconView(self._instruments_store)
        self.instruments_iconview.set_text_column(2)
        self.instruments_iconview.set_pixbuf_column(1)

        # load the images
        images_path = os.path.join(activity.get_bundle_path(), 'instruments')
        logging.error('Loading instrument images from %s', images_path)
        for file_name in os.listdir(images_path):
            image_file_name = os.path.join(images_path, file_name)
            logging.error('Adding %s', image_file_name)
            pxb = GdkPixbuf.Pixbuf.new_from_file_at_size(
                image_file_name, 75, 75)
            #instrument_name = image_file_name[image_file_name.rfind('/'):]
            instrument_name = image_file_name[image_file_name.rfind('/') + 1:]
            instrument_name = instrument_name[:instrument_name.find('.')]
            instrument_desc = \
                self.instrumentDB.instNamed[instrument_name].nameTooltip
            self._instruments_store.append(
                [instrument_name, pxb, instrument_desc])
        self.instruments_iconview.connect(
            'selection-changed', self.__instrument_iconview_activated_cb)

    def __instrument_iconview_activated_cb(self, widget):
        item = widget.get_selected_items()[0]
        model = widget.get_model()
        instrument_name = model[item][0]
        self.setInstrument(instrument_name)

    def set_notes_labels_cb(self, widget):
        self.piano.font_size = 16
        self.piano.set_labels(self.notes_labels)

    def set_ti_notes_labels_cb(self, widget):
        self.piano.font_size = 16
        self.piano.set_labels(self.ti_notes_labels)

    def set_keyboard_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.keyboard_letters)

    def set_german_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(self.german_labels)

    def set_keyboard_no_labels_cb(self, widget):
        self.piano.font_size = 25
        self.piano.set_labels(None)

    def enableKeyboard(self):
        self.keyboardStandAlone = KeyboardStandAlone(
            self.sequencer.recording, self.sequencer.adjustDuration,
            self.csnd.loopGetTick, self.sequencer.getPlayState, self.loop)
        self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)

    def setInstrument(self, instrument):
        self.instrument = instrument
        self.keyboardStandAlone.setInstrument(instrument)
        self.csnd.load_instrument(instrument)

    def recordStateButton(self, button, state):
        pass
#        if button == 1:
#            self._recordToolbar.keyboardRecButton.set_active( state )
#        else:
#            self._recordToolbar.keyboardRecOverButton.set_active( state )

    def recordOverSensitivity(self, state):
        pass
        #self._recordToolbar.keyboardRecOverButton.set_sensitive( state )

    def __key_pressed_cb(self, widget, octave_clicked, key_clicked, letter):
        logging.debug('Pressed Octave: %d Key: %d Letter: %s' %
                      (octave_clicked, key_clicked, letter))
        if letter in LETTERS_TO_KEY_CODES.keys():
            self.keyboardStandAlone.do_key_press(
                LETTERS_TO_KEY_CODES[letter], None,
                math.sqrt(self.instVolume * 0.01))

    def __key_released_cb(self, widget, octave_clicked, key_clicked, letter):
        if letter in LETTERS_TO_KEY_CODES.keys():
            self.keyboardStandAlone.do_key_release(
                LETTERS_TO_KEY_CODES[letter])

    def onKeyPress(self, widget, event):

        if event.hardware_keycode == 37:
            if self.muteInst:
                self.muteInst = False
            else:
                self.muteInst = True
        self.piano.physical_key_changed(event.hardware_keycode, True)
        self.keyboardStandAlone.onKeyPress(widget, event,
                                           math.sqrt(self.instVolume * 0.01))

    def onKeyRelease(self, widget, event):
        self.keyboardStandAlone.onKeyRelease(widget, event)
        self.piano.physical_key_changed(event.hardware_keycode, False)