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)
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)
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)