def __init__(self, activity): GObject.GObject.__init__(self) self.activity = activity self.instrumentDB = InstrumentDB.getRef() self.noteDB = NoteDB.NoteDB() #-- initial settings ---------------------------------- self.tempo = Config.PLAYER_TEMPO self.beatDuration = 60.0 / self.tempo self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0 self.volume = 0.5 self.csnd = new_csound_client() for i in range(0, 9): self.csnd.setTrackVolume(100, i) # csnd expects a range 0-100 for now self.csnd.setMasterVolume(self.volume * 100) self.csnd.setTempo(self.tempo) self.muted = False #-- Drawing ------------------------------------------- def darken(hex): hexToDec = {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15, "a": 10, "b": 11, "c": 12, "d": 13, "e": 14, "f": 15} r = int(0.7 * (16 * hexToDec[hex[1]] + hexToDec[hex[2]])) g = int(0.7 * (16 * hexToDec[hex[3]] + hexToDec[hex[4]])) b = int(0.7 * (16 * hexToDec[hex[5]] + hexToDec[hex[6]])) return r * 256, g * 256, b * 256 def lighten(hex): hexToDec = {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15, "a": 10, "b": 11, "c": 12, "d": 13, "e": 14, "f": 15} r = 255 - int(0.7 * (255 - ( 16 * hexToDec[hex[1]] + hexToDec[hex[2]]))) g = 255 - int(0.7 * (255 - ( 16 * hexToDec[hex[3]] + hexToDec[hex[4]]))) b = 255 - int(0.7 * (255 - ( 16 * hexToDec[hex[5]] + hexToDec[hex[6]]))) return r * 256, g * 256, b * 256 xoColor = profile.get_color() if not xoColor: xoColorKey = ("#8D8D8D,#FFDDEA") xoColor = XoColor(xoColorKey) # colors in Config and in XoColor are strings, # the colors in style are style.Color, transform all to Gdk.Color self.colors = {"bg": CairoUtil.get_gdk_color(Config.PANEL_BCK_COLOR), "black": style.COLOR_BLACK.get_gdk_color(), #"Picker_Bg": colormap.alloc_color("#404040"), #"Picker_Bg_Inactive": colormap.alloc_color("#808080"), "Picker_Bg": style.COLOR_TOOLBAR_GREY.get_gdk_color(), "Picker_Bg_Inactive": style.COLOR_BUTTON_GREY.get_gdk_color(), "Picker_Fg": style.COLOR_WHITE.get_gdk_color(), "Border_Active": \ CairoUtil.get_gdk_color(xoColor.get_stroke_color()), "Border_Inactive": CairoUtil.get_gdk_color("#8D8D8D"), "Border_Highlight": CairoUtil.get_gdk_color("#FFFFFF"), "Bg_Active": CairoUtil.get_gdk_color(xoColor.get_fill_color()), "Bg_Inactive": CairoUtil.get_gdk_color("#DBDBDB"), "Preview_Note_Fill": CairoUtil.get_gdk_color(Config.BG_COLOR), "Preview_Note_Border": CairoUtil.get_gdk_color(Config.FG_COLOR), "Preview_Note_Selected": style.COLOR_WHITE.get_gdk_color(), # TODO: lighten here can be removed, check if is used in other # places "Note_Fill_Active": Gdk.Color(*lighten("#590000")), # base "Border_Active" "Note_Fill_Inactive": Gdk.Color(*lighten("#8D8D8D")), # base "Border_Inactive" "Beat_Line": CairoUtil.get_gdk_color("#959595")} self.colors["Note_Border_Active"] = self.colors["Border_Active"] self.colors["Note_Border_Inactive"] = self.colors["Border_Inactive"] self.sampleNoteHeight = 7 self.sampleBg = cairo.ImageSurface.create_from_png( imagefile('sampleBG.png')) self.loopPitchOffset = 4 self.loopTickOffset = 13 self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES - 1) / \ (Block.Loop.HEIGHT - 2 * self.loopPitchOffset - \ self.sampleNoteHeight) self.pixelsPerPitch = float(Block.Loop.HEIGHT - \ 2 * self.loopPitchOffset - self.sampleNoteHeight) / \ (Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH) self.pixelsPerTick = Block.Loop.BEAT / float(Config.TICKS_PER_BEAT) self.ticksPerPixel = 1.0 / self.pixelsPerTick #-- Instruments --------------------------------------- self.instrumentImage = {} self.instrumentImageActive = {} for inst in self.instrumentDB.getSet("All"): if not inst.kitStage: self.prepareInstrumentImage(inst.instrumentId, inst.img) self.csnd.load_instrument(inst.name) #-- Loop Images --------------------------------------- self.loopImage = {} # get filled in through updateLoopImage self.loopImageActive = {} #-- Key Images ---------------------------------------- self.keyImage = {} self.keyImageActive = {} # use hardware key codes to work on any keyboard layout (hopefully) self.valid_shortcuts = {18: "9", 19: "0", 20: "-", 21: "=", 32: "O", 33: "P", 34: "[", 35: "]", 47: ";", 48: "'", 51: "\\", 60: ".", 61: "/", None: " "} for key in self.valid_shortcuts.keys(): self.prepareKeyImage(key) #-- Toolbars ------------------------------------------ self.jamToolbar = JamToolbar(self) jam_toolbar_button = ToolbarButton(label=_('Jam'), page=self.jamToolbar, icon_name='voltemp') self.jamToolbar.show() jam_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(jam_toolbar_button, -1) self.beatToolbar = BeatToolbar(self) beat_toolbar_button = ToolbarButton(label=_('Beat'), page=self.beatToolbar, icon_name='heart') self.beatToolbar.show() beat_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(beat_toolbar_button, -1) self.desktopToolbar = DesktopToolbar(self) desktop_toolbar_button = ToolbarButton(label=_('Desktop'), page=self.desktopToolbar, icon_name='jam-presets-list') self.desktopToolbar.show() desktop_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(desktop_toolbar_button, -1) if Config.FEATURES_MIC or Config.FEATURES_NEWSOUNDS: self.recordToolbar = RecordToolbar(self) record_toolbar_button = ToolbarButton(label=_('Record'), page=self.recordToolbar, icon_name='microphone') self.recordToolbar.show() record_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(record_toolbar_button, -1) separator = Gtk.SeparatorToolItem() separator.props.draw = True separator.set_expand(False) self.activity.toolbar_box.toolbar.insert(separator, -1) separator.show() common_playback_buttons(self.activity.toolbar_box.toolbar, self) #-- GUI ----------------------------------------------- if True: # GUI self.modify_bg(Gtk.StateType.NORMAL, self.colors["bg"]) self.GUI = {} self.GUI["mainVBox"] = Gtk.VBox() self.add(self.GUI["mainVBox"]) #-- Desktop ------------------------------------------- self.desktop = self.GUI["desktop"] = Desktop(self) self.GUI["mainVBox"].pack_start(self.GUI["desktop"], True, True, 0) #-- Bank ---------------------------------------------- separator = Gtk.Label(label=" ") separator.set_size_request(-1, style.TOOLBOX_SEPARATOR_HEIGHT) self.GUI["mainVBox"].pack_start(separator, False, True, 0) self.GUI["notebook"] = Gtk.Notebook() self.GUI["notebook"].set_scrollable(True) self.GUI["notebook"].modify_bg(Gtk.StateType.NORMAL, self.colors["Picker_Bg"]) self.GUI["notebook"].modify_bg(Gtk.StateType.ACTIVE, self.colors["Picker_Bg_Inactive"]) # TODO gtk3 no available anymore? #self.GUI["notebook"].props.tab_vborder = style.TOOLBOX_TAB_VBORDER #self.GUI["notebook"].props.tab_hborder = style.TOOLBOX_TAB_HBORDER self.GUI["notebook"].set_size_request(-1, scale(160)) self.GUI["notebook"].connect("switch-page", self.setPicker) self.GUI["mainVBox"].pack_start(self.GUI["notebook"], False, False, 0) self.pickers = {} self.pickerScroll = {} for type in [Picker.Instrument, Picker.Drum, Picker.Loop]: self.pickers[type] = type(self) def prepareLabel(name): label = Gtk.Label(label=Tooltips.categories.get(name) or name) label.set_alignment(0.0, 0.5) label.modify_fg(Gtk.StateType.NORMAL, self.colors["Picker_Fg"]) label.modify_fg(Gtk.StateType.ACTIVE, self.colors["Picker_Fg"]) return label self.GUI["notebook"].append_page(self.pickers[Picker.Drum], prepareLabel(_("Drum Kits"))) self.GUI["notebook"].append_page(self.pickers[Picker.Loop], prepareLabel(_("Loops"))) sets = self.instrumentDB.getLabels()[:] sets.sort() for set in sets: page = Gtk.HBox() page.set = set self.GUI["notebook"].append_page(page, prepareLabel(set)) self.show_all() self.GUI["notebook"].set_current_page(0) #-- Keyboard ------------------------------------------ self.key_dict = {} self.nextTrack = 2 self.keyboardListener = None self.recordingNote = None self.keyMap = {} # default instrument self._updateInstrument( self.instrumentDB.instNamed["kalimba"].instrumentId, 0.5) self.instrumentStack = [] # metronome page = NoteDB.Page(1, local=False) self.metronomePage = self.noteDB.addPage(-1, page) self.metronome = False #-- Drums --------------------------------------------- self.drumLoopId = None # use dummy values for now self.drumFillin = Fillin( 2, 100, self.instrumentDB.instNamed["drum1kit"].instrumentId, 0, 1) #-- Desktops ------------------------------------------ self.curDesktop = None # copy preset desktops path = Config.FILES_DIR + "/Desktops/" filelist = os.listdir(path) for file in filelist: shutil.copyfile(path + file, Config.TMP_DIR + '/' + file) #-- Network ------------------------------------------- self.network = Net.Network() self.network.addWatcher(self.networkStatusWatcher) self.network.connectMessage(Net.HT_SYNC_REPLY, self.processHT_SYNC_REPLY) self.network.connectMessage(Net.HT_TEMPO_UPDATE, self.processHT_TEMPO_UPDATE) self.network.connectMessage(Net.PR_SYNC_QUERY, self.processPR_SYNC_QUERY) self.network.connectMessage(Net.PR_TEMPO_QUERY, self.processPR_TEMPO_QUERY) self.network.connectMessage(Net.PR_REQUEST_TEMPO_CHANGE, self.processPR_REQUEST_TEMPO_CHANGE) # sync self.syncQueryStart = {} self.syncTimeout = None self.heartbeatLoop = self.csnd.loopCreate() self.syncBeats = 4 self.syncTicks = self.syncBeats * Config.TICKS_PER_BEAT self.offsetTicks = 0 # offset from the true heartbeat self.csnd.loopSetNumTicks(self.syncTicks * HEARTBEAT_BUFFER, self.heartbeatLoop) self.heartbeatStart = time.time() self.csnd.loopStart(self.heartbeatLoop) self.curBeat = 0 self.beatWheelTimeout = GObject.timeout_add(100, self.updateBeatWheel) # data packing classes self.packer = xdrlib.Packer() self.unpacker = xdrlib.Unpacker("") # handle forced networking if self.network.isHost(): self.updateSync() self.syncTimeout = GObject.timeout_add(1000, self.updateSync) elif self.network.isPeer(): self.sendTempoQuery() self.syncTimeout = GObject.timeout_add(1000, self.updateSync) self.activity.connect("shared", self.shared) if self.activity.shared_activity: # PEER self.activity.shared_activity.connect("buddy-joined", self.buddy_joined) self.activity.shared_activity.connect("buddy-left", self.buddy_left) self.activity.connect("joined", self.joined) self.network.setMode(Net.MD_WAIT) #-- Final Set Up -------------------------------------- self.setVolume(self.volume) self.setTempo(self.tempo) #self.activity.toolbar_box.set_current_toolbar(1) # JamToolbar self.setDesktop(0, True)
def __init__(self, activity): GObject.GObject.__init__(self) self.activity = activity self.instrumentDB = InstrumentDB.getRef() self.noteDB = NoteDB.NoteDB() #-- initial settings ---------------------------------- self.tempo = Config.PLAYER_TEMPO self.beatDuration = 60.0 / self.tempo self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0 self.volume = 0.5 self.csnd = new_csound_client() for i in range(0, 9): self.csnd.setTrackVolume(100, i) # csnd expects a range 0-100 for now self.csnd.setMasterVolume(self.volume * 100) self.csnd.setTempo(self.tempo) self.muted = False #-- Drawing ------------------------------------------- def darken(hex): hexToDec = { "0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15, "a": 10, "b": 11, "c": 12, "d": 13, "e": 14, "f": 15 } r = int(0.7 * (16 * hexToDec[hex[1]] + hexToDec[hex[2]])) g = int(0.7 * (16 * hexToDec[hex[3]] + hexToDec[hex[4]])) b = int(0.7 * (16 * hexToDec[hex[5]] + hexToDec[hex[6]])) return r * 256, g * 256, b * 256 def lighten(hex): hexToDec = { "0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15, "a": 10, "b": 11, "c": 12, "d": 13, "e": 14, "f": 15 } r = 255 - int(0.7 * (255 - (16 * hexToDec[hex[1]] + hexToDec[hex[2]]))) g = 255 - int(0.7 * (255 - (16 * hexToDec[hex[3]] + hexToDec[hex[4]]))) b = 255 - int(0.7 * (255 - (16 * hexToDec[hex[5]] + hexToDec[hex[6]]))) return r * 256, g * 256, b * 256 xoColor = profile.get_color() if not xoColor: xoColorKey = ("#8D8D8D,#FFDDEA") xoColor = XoColor(xoColorKey) # colors in Config and in XoColor are strings, # the colors in style are style.Color, transform all to Gdk.Color self.colors = {"bg": CairoUtil.get_gdk_color(Config.PANEL_BCK_COLOR), "black": style.COLOR_BLACK.get_gdk_color(), #"Picker_Bg": colormap.alloc_color("#404040"), #"Picker_Bg_Inactive": colormap.alloc_color("#808080"), "Picker_Bg": style.COLOR_TOOLBAR_GREY.get_gdk_color(), "Picker_Bg_Inactive": style.COLOR_BUTTON_GREY.get_gdk_color(), "Picker_Fg": style.COLOR_WHITE.get_gdk_color(), "Border_Active": \ CairoUtil.get_gdk_color(xoColor.get_stroke_color()), "Border_Inactive": CairoUtil.get_gdk_color("#8D8D8D"), "Border_Highlight": CairoUtil.get_gdk_color("#FFFFFF"), "Bg_Active": CairoUtil.get_gdk_color(xoColor.get_fill_color()), "Bg_Inactive": CairoUtil.get_gdk_color("#DBDBDB"), "Preview_Note_Fill": CairoUtil.get_gdk_color(Config.BG_COLOR), "Preview_Note_Border": CairoUtil.get_gdk_color(Config.FG_COLOR), "Preview_Note_Selected": style.COLOR_WHITE.get_gdk_color(), # TODO: lighten here can be removed, check if is used in other # places "Note_Fill_Active": Gdk.Color(*lighten("#590000")), # base "Border_Active" "Note_Fill_Inactive": Gdk.Color(*lighten("#8D8D8D")), # base "Border_Inactive" "Beat_Line": CairoUtil.get_gdk_color("#959595")} self.colors["Note_Border_Active"] = self.colors["Border_Active"] self.colors["Note_Border_Inactive"] = self.colors["Border_Inactive"] self.sampleNoteHeight = 7 self.sampleBg = cairo.ImageSurface.create_from_png( imagefile('sampleBG.png')) self.loopPitchOffset = 4 self.loopTickOffset = 13 self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES - 1) / \ (Block.Loop.HEIGHT - 2 * self.loopPitchOffset - \ self.sampleNoteHeight) self.pixelsPerPitch = float(Block.Loop.HEIGHT - \ 2 * self.loopPitchOffset - self.sampleNoteHeight) / \ (Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH) self.pixelsPerTick = Block.Loop.BEAT / float(Config.TICKS_PER_BEAT) self.ticksPerPixel = 1.0 / self.pixelsPerTick #-- Instruments --------------------------------------- self.instrumentImage = {} self.instrumentImageActive = {} for inst in self.instrumentDB.getSet("All"): if not inst.kitStage: self.prepareInstrumentImage(inst.instrumentId, inst.img) self.csnd.load_instrument(inst.name) #-- Loop Images --------------------------------------- self.loopImage = {} # get filled in through updateLoopImage self.loopImageActive = {} #-- Key Images ---------------------------------------- self.keyImage = {} self.keyImageActive = {} # use hardware key codes to work on any keyboard layout (hopefully) self.valid_shortcuts = { 18: "9", 19: "0", 20: "-", 21: "=", 32: "O", 33: "P", 34: "[", 35: "]", 47: ";", 48: "'", 51: "\\", 60: ".", 61: "/", None: " " } for key in self.valid_shortcuts.keys(): self.prepareKeyImage(key) #-- Toolbars ------------------------------------------ self.jamToolbar = JamToolbar(self) jam_toolbar_button = ToolbarButton(label=_('Jam'), page=self.jamToolbar, icon_name='voltemp') self.jamToolbar.show() jam_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(jam_toolbar_button, -1) self.beatToolbar = BeatToolbar(self) beat_toolbar_button = ToolbarButton(label=_('Beat'), page=self.beatToolbar, icon_name='heart') self.beatToolbar.show() beat_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(beat_toolbar_button, -1) self.desktopToolbar = DesktopToolbar(self) desktop_toolbar_button = ToolbarButton(label=_('Desktop'), page=self.desktopToolbar, icon_name='jam-presets-list') self.desktopToolbar.show() desktop_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(desktop_toolbar_button, -1) if Config.FEATURES_MIC or Config.FEATURES_NEWSOUNDS: self.recordToolbar = RecordToolbar(self) record_toolbar_button = ToolbarButton(label=_('Record'), page=self.recordToolbar, icon_name='microphone') self.recordToolbar.show() record_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(record_toolbar_button, -1) separator = Gtk.SeparatorToolItem() separator.props.draw = True separator.set_expand(False) self.activity.toolbar_box.toolbar.insert(separator, -1) separator.show() common_playback_buttons(self.activity.toolbar_box.toolbar, self) #-- GUI ----------------------------------------------- if True: # GUI self.modify_bg(Gtk.StateType.NORMAL, self.colors["bg"]) self.GUI = {} self.GUI["mainVBox"] = Gtk.VBox() self.add(self.GUI["mainVBox"]) #-- Desktop ------------------------------------------- self.desktop = self.GUI["desktop"] = Desktop(self) self.GUI["mainVBox"].pack_start(self.GUI["desktop"], True, True, 0) #-- Bank ---------------------------------------------- separator = Gtk.Label(label=" ") separator.set_size_request(-1, style.TOOLBOX_SEPARATOR_HEIGHT) self.GUI["mainVBox"].pack_start(separator, False, True, 0) self.GUI["notebook"] = Gtk.Notebook() self.GUI["notebook"].set_scrollable(True) self.GUI["notebook"].modify_bg(Gtk.StateType.NORMAL, self.colors["Picker_Bg"]) self.GUI["notebook"].modify_bg(Gtk.StateType.ACTIVE, self.colors["Picker_Bg_Inactive"]) # TODO gtk3 no available anymore? #self.GUI["notebook"].props.tab_vborder = style.TOOLBOX_TAB_VBORDER #self.GUI["notebook"].props.tab_hborder = style.TOOLBOX_TAB_HBORDER self.GUI["notebook"].set_size_request(-1, scale(160)) self.GUI["notebook"].connect("switch-page", self.setPicker) self.GUI["mainVBox"].pack_start(self.GUI["notebook"], False, False, 0) self.pickers = {} self.pickerScroll = {} for type in [Picker.Instrument, Picker.Drum, Picker.Loop]: self.pickers[type] = type(self) def prepareLabel(name): label = Gtk.Label(label=Tooltips.categories.get(name) or name) label.set_alignment(0.0, 0.5) label.modify_fg(Gtk.StateType.NORMAL, self.colors["Picker_Fg"]) label.modify_fg(Gtk.StateType.ACTIVE, self.colors["Picker_Fg"]) return label self.GUI["notebook"].append_page(self.pickers[Picker.Drum], prepareLabel(_("Drum Kits"))) self.GUI["notebook"].append_page(self.pickers[Picker.Loop], prepareLabel(_("Loops"))) sets = self.instrumentDB.getLabels()[:] sets.sort() for set in sets: page = Gtk.HBox() page.set = set self.GUI["notebook"].append_page(page, prepareLabel(set)) self.show_all() self.GUI["notebook"].set_current_page(0) #-- Keyboard ------------------------------------------ self.key_dict = {} self.nextTrack = 2 self.keyboardListener = None self.recordingNote = None self.keyMap = {} # default instrument self._updateInstrument( self.instrumentDB.instNamed["kalimba"].instrumentId, 0.5) self.instrumentStack = [] # metronome page = NoteDB.Page(1, local=False) self.metronomePage = self.noteDB.addPage(-1, page) self.metronome = False #-- Drums --------------------------------------------- self.drumLoopId = None # use dummy values for now self.drumFillin = Fillin( 2, 100, self.instrumentDB.instNamed["drum1kit"].instrumentId, 0, 1) #-- Desktops ------------------------------------------ self.curDesktop = None # copy preset desktops path = Config.FILES_DIR + "/Desktops/" filelist = os.listdir(path) for file in filelist: shutil.copyfile(path + file, Config.TMP_DIR + '/' + file) #-- Network ------------------------------------------- self.network = Net.Network() self.network.addWatcher(self.networkStatusWatcher) self.network.connectMessage(Net.HT_SYNC_REPLY, self.processHT_SYNC_REPLY) self.network.connectMessage(Net.HT_TEMPO_UPDATE, self.processHT_TEMPO_UPDATE) self.network.connectMessage(Net.PR_SYNC_QUERY, self.processPR_SYNC_QUERY) self.network.connectMessage(Net.PR_TEMPO_QUERY, self.processPR_TEMPO_QUERY) self.network.connectMessage(Net.PR_REQUEST_TEMPO_CHANGE, self.processPR_REQUEST_TEMPO_CHANGE) # sync self.syncQueryStart = {} self.syncTimeout = None self.heartbeatLoop = self.csnd.loopCreate() self.syncBeats = 4 self.syncTicks = self.syncBeats * Config.TICKS_PER_BEAT self.offsetTicks = 0 # offset from the true heartbeat self.csnd.loopSetNumTicks(self.syncTicks * HEARTBEAT_BUFFER, self.heartbeatLoop) self.heartbeatStart = time.time() self.csnd.loopStart(self.heartbeatLoop) self.curBeat = 0 self.beatWheelTimeout = GObject.timeout_add(100, self.updateBeatWheel) # data packing classes self.packer = xdrlib.Packer() self.unpacker = xdrlib.Unpacker("") # handle forced networking if self.network.isHost(): self.updateSync() self.syncTimeout = GObject.timeout_add(1000, self.updateSync) elif self.network.isPeer(): self.sendTempoQuery() self.syncTimeout = GObject.timeout_add(1000, self.updateSync) self.activity.connect("shared", self.shared) if self.activity.shared_activity: # PEER self.activity.shared_activity.connect("buddy-joined", self.buddy_joined) self.activity.shared_activity.connect("buddy-left", self.buddy_left) self.activity.connect("joined", self.joined) self.network.setMode(Net.MD_WAIT) #-- Final Set Up -------------------------------------- self.setVolume(self.volume) self.setTempo(self.tempo) #self.activity.toolbar_box.set_current_toolbar(1) # JamToolbar self.setDesktop(0, True)
class JamMain(Gtk.EventBox): def __init__(self, activity): GObject.GObject.__init__(self) self.activity = activity self.instrumentDB = InstrumentDB.getRef() self.noteDB = NoteDB.NoteDB() #-- initial settings ---------------------------------- self.tempo = Config.PLAYER_TEMPO self.beatDuration = 60.0 / self.tempo self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0 self.volume = 0.5 self.csnd = new_csound_client() for i in range(0, 9): self.csnd.setTrackVolume(100, i) # csnd expects a range 0-100 for now self.csnd.setMasterVolume(self.volume * 100) self.csnd.setTempo(self.tempo) self.muted = False #-- Drawing ------------------------------------------- def darken(hex): hexToDec = {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15, "a": 10, "b": 11, "c": 12, "d": 13, "e": 14, "f": 15} r = int(0.7 * (16 * hexToDec[hex[1]] + hexToDec[hex[2]])) g = int(0.7 * (16 * hexToDec[hex[3]] + hexToDec[hex[4]])) b = int(0.7 * (16 * hexToDec[hex[5]] + hexToDec[hex[6]])) return r * 256, g * 256, b * 256 def lighten(hex): hexToDec = {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15, "a": 10, "b": 11, "c": 12, "d": 13, "e": 14, "f": 15} r = 255 - int(0.7 * (255 - ( 16 * hexToDec[hex[1]] + hexToDec[hex[2]]))) g = 255 - int(0.7 * (255 - ( 16 * hexToDec[hex[3]] + hexToDec[hex[4]]))) b = 255 - int(0.7 * (255 - ( 16 * hexToDec[hex[5]] + hexToDec[hex[6]]))) return r * 256, g * 256, b * 256 xoColor = profile.get_color() if not xoColor: xoColorKey = ("#8D8D8D,#FFDDEA") xoColor = XoColor(xoColorKey) # colors in Config and in XoColor are strings, # the colors in style are style.Color, transform all to Gdk.Color self.colors = {"bg": CairoUtil.get_gdk_color(Config.PANEL_BCK_COLOR), "black": style.COLOR_BLACK.get_gdk_color(), #"Picker_Bg": colormap.alloc_color("#404040"), #"Picker_Bg_Inactive": colormap.alloc_color("#808080"), "Picker_Bg": style.COLOR_TOOLBAR_GREY.get_gdk_color(), "Picker_Bg_Inactive": style.COLOR_BUTTON_GREY.get_gdk_color(), "Picker_Fg": style.COLOR_WHITE.get_gdk_color(), "Border_Active": \ CairoUtil.get_gdk_color(xoColor.get_stroke_color()), "Border_Inactive": CairoUtil.get_gdk_color("#8D8D8D"), "Border_Highlight": CairoUtil.get_gdk_color("#FFFFFF"), "Bg_Active": CairoUtil.get_gdk_color(xoColor.get_fill_color()), "Bg_Inactive": CairoUtil.get_gdk_color("#DBDBDB"), "Preview_Note_Fill": CairoUtil.get_gdk_color(Config.BG_COLOR), "Preview_Note_Border": CairoUtil.get_gdk_color(Config.FG_COLOR), "Preview_Note_Selected": style.COLOR_WHITE.get_gdk_color(), # TODO: lighten here can be removed, check if is used in other # places "Note_Fill_Active": Gdk.Color(*lighten("#590000")), # base "Border_Active" "Note_Fill_Inactive": Gdk.Color(*lighten("#8D8D8D")), # base "Border_Inactive" "Beat_Line": CairoUtil.get_gdk_color("#959595")} self.colors["Note_Border_Active"] = self.colors["Border_Active"] self.colors["Note_Border_Inactive"] = self.colors["Border_Inactive"] self.sampleNoteHeight = 7 self.sampleBg = cairo.ImageSurface.create_from_png( imagefile('sampleBG.png')) self.loopPitchOffset = 4 self.loopTickOffset = 13 self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES - 1) / \ (Block.Loop.HEIGHT - 2 * self.loopPitchOffset - \ self.sampleNoteHeight) self.pixelsPerPitch = float(Block.Loop.HEIGHT - \ 2 * self.loopPitchOffset - self.sampleNoteHeight) / \ (Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH) self.pixelsPerTick = Block.Loop.BEAT / float(Config.TICKS_PER_BEAT) self.ticksPerPixel = 1.0 / self.pixelsPerTick #-- Instruments --------------------------------------- self.instrumentImage = {} self.instrumentImageActive = {} for inst in self.instrumentDB.getSet("All"): if not inst.kitStage: self.prepareInstrumentImage(inst.instrumentId, inst.img) self.csnd.load_instrument(inst.name) #-- Loop Images --------------------------------------- self.loopImage = {} # get filled in through updateLoopImage self.loopImageActive = {} #-- Key Images ---------------------------------------- self.keyImage = {} self.keyImageActive = {} # use hardware key codes to work on any keyboard layout (hopefully) self.valid_shortcuts = {18: "9", 19: "0", 20: "-", 21: "=", 32: "O", 33: "P", 34: "[", 35: "]", 47: ";", 48: "'", 51: "\\", 60: ".", 61: "/", None: " "} for key in self.valid_shortcuts.keys(): self.prepareKeyImage(key) #-- Toolbars ------------------------------------------ self.jamToolbar = JamToolbar(self) jam_toolbar_button = ToolbarButton(label=_('Jam'), page=self.jamToolbar, icon_name='voltemp') self.jamToolbar.show() jam_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(jam_toolbar_button, -1) self.beatToolbar = BeatToolbar(self) beat_toolbar_button = ToolbarButton(label=_('Beat'), page=self.beatToolbar, icon_name='heart') self.beatToolbar.show() beat_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(beat_toolbar_button, -1) self.desktopToolbar = DesktopToolbar(self) desktop_toolbar_button = ToolbarButton(label=_('Desktop'), page=self.desktopToolbar, icon_name='jam-presets-list') self.desktopToolbar.show() desktop_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(desktop_toolbar_button, -1) if Config.FEATURES_MIC or Config.FEATURES_NEWSOUNDS: self.recordToolbar = RecordToolbar(self) record_toolbar_button = ToolbarButton(label=_('Record'), page=self.recordToolbar, icon_name='microphone') self.recordToolbar.show() record_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(record_toolbar_button, -1) separator = Gtk.SeparatorToolItem() separator.props.draw = True separator.set_expand(False) self.activity.toolbar_box.toolbar.insert(separator, -1) separator.show() common_playback_buttons(self.activity.toolbar_box.toolbar, self) #-- GUI ----------------------------------------------- if True: # GUI self.modify_bg(Gtk.StateType.NORMAL, self.colors["bg"]) self.GUI = {} self.GUI["mainVBox"] = Gtk.VBox() self.add(self.GUI["mainVBox"]) #-- Desktop ------------------------------------------- self.desktop = self.GUI["desktop"] = Desktop(self) self.GUI["mainVBox"].pack_start(self.GUI["desktop"], True, True, 0) #-- Bank ---------------------------------------------- separator = Gtk.Label(label=" ") separator.set_size_request(-1, style.TOOLBOX_SEPARATOR_HEIGHT) self.GUI["mainVBox"].pack_start(separator, False, True, 0) self.GUI["notebook"] = Gtk.Notebook() self.GUI["notebook"].set_scrollable(True) self.GUI["notebook"].modify_bg(Gtk.StateType.NORMAL, self.colors["Picker_Bg"]) self.GUI["notebook"].modify_bg(Gtk.StateType.ACTIVE, self.colors["Picker_Bg_Inactive"]) # TODO gtk3 no available anymore? #self.GUI["notebook"].props.tab_vborder = style.TOOLBOX_TAB_VBORDER #self.GUI["notebook"].props.tab_hborder = style.TOOLBOX_TAB_HBORDER self.GUI["notebook"].set_size_request(-1, scale(160)) self.GUI["notebook"].connect("switch-page", self.setPicker) self.GUI["mainVBox"].pack_start(self.GUI["notebook"], False, False, 0) self.pickers = {} self.pickerScroll = {} for type in [Picker.Instrument, Picker.Drum, Picker.Loop]: self.pickers[type] = type(self) def prepareLabel(name): label = Gtk.Label(label=Tooltips.categories.get(name) or name) label.set_alignment(0.0, 0.5) label.modify_fg(Gtk.StateType.NORMAL, self.colors["Picker_Fg"]) label.modify_fg(Gtk.StateType.ACTIVE, self.colors["Picker_Fg"]) return label self.GUI["notebook"].append_page(self.pickers[Picker.Drum], prepareLabel(_("Drum Kits"))) self.GUI["notebook"].append_page(self.pickers[Picker.Loop], prepareLabel(_("Loops"))) sets = self.instrumentDB.getLabels()[:] sets.sort() for set in sets: page = Gtk.HBox() page.set = set self.GUI["notebook"].append_page(page, prepareLabel(set)) self.show_all() self.GUI["notebook"].set_current_page(0) #-- Keyboard ------------------------------------------ self.key_dict = {} self.nextTrack = 2 self.keyboardListener = None self.recordingNote = None self.keyMap = {} # default instrument self._updateInstrument( self.instrumentDB.instNamed["kalimba"].instrumentId, 0.5) self.instrumentStack = [] # metronome page = NoteDB.Page(1, local=False) self.metronomePage = self.noteDB.addPage(-1, page) self.metronome = False #-- Drums --------------------------------------------- self.drumLoopId = None # use dummy values for now self.drumFillin = Fillin( 2, 100, self.instrumentDB.instNamed["drum1kit"].instrumentId, 0, 1) #-- Desktops ------------------------------------------ self.curDesktop = None # copy preset desktops path = Config.FILES_DIR + "/Desktops/" filelist = os.listdir(path) for file in filelist: shutil.copyfile(path + file, Config.TMP_DIR + '/' + file) #-- Network ------------------------------------------- self.network = Net.Network() self.network.addWatcher(self.networkStatusWatcher) self.network.connectMessage(Net.HT_SYNC_REPLY, self.processHT_SYNC_REPLY) self.network.connectMessage(Net.HT_TEMPO_UPDATE, self.processHT_TEMPO_UPDATE) self.network.connectMessage(Net.PR_SYNC_QUERY, self.processPR_SYNC_QUERY) self.network.connectMessage(Net.PR_TEMPO_QUERY, self.processPR_TEMPO_QUERY) self.network.connectMessage(Net.PR_REQUEST_TEMPO_CHANGE, self.processPR_REQUEST_TEMPO_CHANGE) # sync self.syncQueryStart = {} self.syncTimeout = None self.heartbeatLoop = self.csnd.loopCreate() self.syncBeats = 4 self.syncTicks = self.syncBeats * Config.TICKS_PER_BEAT self.offsetTicks = 0 # offset from the true heartbeat self.csnd.loopSetNumTicks(self.syncTicks * HEARTBEAT_BUFFER, self.heartbeatLoop) self.heartbeatStart = time.time() self.csnd.loopStart(self.heartbeatLoop) self.curBeat = 0 self.beatWheelTimeout = GObject.timeout_add(100, self.updateBeatWheel) # data packing classes self.packer = xdrlib.Packer() self.unpacker = xdrlib.Unpacker("") # handle forced networking if self.network.isHost(): self.updateSync() self.syncTimeout = GObject.timeout_add(1000, self.updateSync) elif self.network.isPeer(): self.sendTempoQuery() self.syncTimeout = GObject.timeout_add(1000, self.updateSync) self.activity.connect("shared", self.shared) if self.activity.shared_activity: # PEER self.activity.shared_activity.connect("buddy-joined", self.buddy_joined) self.activity.shared_activity.connect("buddy-left", self.buddy_left) self.activity.connect("joined", self.joined) self.network.setMode(Net.MD_WAIT) #-- Final Set Up -------------------------------------- self.setVolume(self.volume) self.setTempo(self.tempo) #self.activity.toolbar_box.set_current_toolbar(1) # JamToolbar self.setDesktop(0, True) #========================================================== def onActivate(self, arg): pass def onDeactivate(self): pass def onDestroy(self): self.network.shutdown() #clear up scratch folder path = Config.TMP_DIR filelist = os.listdir(path) for file in filelist: os.remove(path + '/' + file) #========================================================== # Playback def onKeyPress(self, widget, event): key = event.hardware_keycode if key in self.keyMap.keys(): activate = True for block in self.keyMap[key]: if block.isActive(): activate = False break if activate: for block in self.keyMap[key]: if not block.isActive(): if block.type == Block.Drum: self.desktop.activateDrum(block) elif block.type == Block.Loop: self.desktop.activateLoop(block) else: for block in self.keyMap[key]: if block.isActive(): if block.type == Block.Drum: self.desktop.deactivateDrum(block) elif block.type == Block.Loop: self.desktop.deactivateLoop(block) return if key in self.key_dict: # repeated press return if key in Config.KEY_MAP_PIANO: pitch = Config.KEY_MAP_PIANO[key] inst = self.instrumentDB.instId[self.instrument["id"]] if inst.kit: # drum kit if pitch in GenerationConstants.DRUMPITCH: pitch = GenerationConstants.DRUMPITCH[pitch] csnote = self._playNote( # trackVol * noteVol key, 36, self.instrument["amplitude"] * 0.5, self.instrument["pan"], 100, self.instrumentDB.instNamed[inst.kit[pitch]].instrumentId, self.instrument["reverb"]) else: if event.get_state() == Gdk.ModifierType.MOD1_MASK: pitch += 5 # Percussions resonance if inst.csoundInstrumentId == Config.INST_PERC: duration = 60 else: duration = -1 csnote = self._playNote( # trackVol * noteVol key, pitch, self.instrument["amplitude"] * 0.5, self.instrument["pan"], duration, self.instrument["id"], self.instrument["reverb"]) if self.keyboardListener: self.keyboardListener.recordNote(csnote.pitch) self.recordingNote = True def onKeyRelease(self, widget, event): key = event.hardware_keycode if key in self.key_dict: self._stopNote(key) if self.recordingNote: if self.keyboardListener: self.keyboardListener.finishNote() self.recordingNote = False def _playNote(self, key, pitch, amplitude, pan, duration, instrumentId, reverb): self.key_dict[key] = CSoundNote( # onset 0, pitch, amplitude, pan, duration, self.nextTrack, instrumentId, reverbSend=reverb, tied=True, mode='mini') self.nextTrack += 1 if self.nextTrack > 8: self.nextTrack = 2 self.csnd.play(self.key_dict[key], 0.3) return self.key_dict[key] def _stopNote(self, key): csnote = self.key_dict[key] if self.instrumentDB.instId[csnote.instrumentId].csoundInstrumentId \ == Config.INST_TIED: csnote.duration = .5 csnote.decay = 0.7 csnote.tied = False self.csnd.play(csnote, 0.3) del self.key_dict[key] def _updateInstrument(self, id, volume, pan=0, reverb=0): self.instrument = {"id": id, "amplitude": volume, "pan": pan, "reverb": reverb} def pushInstrument(self, instrument): self.instrumentStack.append(self.instrument) self.instrument = instrument def popInstrument(self): self.instrument = self.instrumentStack.pop() def _playDrum(self, id, pageId, volume, reverb, beats, regularity, loopId=None, sync=True): oldId = loopId loopId = self.csnd.loopCreate() noteOnsets = [] notePitchs = [] for n in self.noteDB.getNotesByTrack(pageId, 0): n.pushState() noteOnsets.append(n.cs.onset) notePitchs.append(n.cs.pitch) n.cs.instrumentId = id n.cs.amplitude = volume * n.cs.amplitude n.cs.reverbSend = reverb self.csnd.loopPlay(n, 1, loopId=loopId) # add as active n.popState() ticks = self.noteDB.getPage(pageId).ticks self.csnd.loopSetNumTicks(ticks, loopId) self.drumFillin.setLoopId(loopId) self.drumFillin.setProperties( self.tempo, self.instrumentDB.instId[id].name, volume, beats, reverb) self.drumFillin.unavailable(noteOnsets, notePitchs) self.drumFillin.play() if oldId == None: if sync: startTick = self.csnd.loopGetTick(self.heartbeatLoop) \ % self.syncTicks else: startTick = 0 else: # TODO is this really safe? could potentially add several # milliseconds of delay everytime a loop is updated if sync: startTick = self.csnd.loopGetTick(oldId) else: startTick = 0 while startTick > ticks: startTick -= ticks self.csnd.loopSetTick(startTick, loopId) self.csnd.loopStart(loopId) if oldId != None: self.csnd.loopDestroy(oldId) return loopId def _stopDrum(self, loopId): self.drumFillin.stop() self.csnd.loopDestroy(loopId) def _playLoop(self, id, volume, reverb, tune, loopId=None, force=False, sync=True): oldId = loopId loopId = self.csnd.loopCreate() inst = self.instrumentDB.instId[id] ticks = 0 for page in tune: for n in self.noteDB.getNotesByTrack(page, 0): n.pushState() n.cs.instrumentId = id n.cs.amplitude = volume * n.cs.amplitude n.cs.reverbSend = reverb if inst.kit: # drum kit if n.cs.pitch in GenerationConstants.DRUMPITCH: n.cs.pitch = GenerationConstants.DRUMPITCH[n.cs.pitch] n.cs.onset += ticks self.csnd.loopPlay(n, 1, loopId=loopId) n.popState() # metronome track for n in self.noteDB.getNotesByTrack(page, 1): self.csnd.loopPlay(n, 1, loopId=loopId) # record scratch track for n in self.noteDB.getNotesByTrack(page, 2): self.csnd.loopPlay(n, 1, loopId=loopId) ticks += self.noteDB.getPage(page).ticks self.csnd.loopSetNumTicks(ticks, loopId) if oldId == None: if sync: startTick = self.csnd.loopGetTick(self.heartbeatLoop) \ % self.syncTicks else: startTick = 0 else: # TODO is this really safe? could potentially add several # milliseconds of delay everytime a loop is updated if sync: startTick = self.csnd.loopGetTick(oldId) else: startTick = 0 while startTick > ticks: startTick -= ticks self.csnd.loopSetTick(startTick, loopId) self.csnd.loopStart(loopId) if oldId != None: self.csnd.loopDestroy(oldId) return loopId def _stopLoop(self, loopId): self.csnd.loopDestroy(loopId) def addMetronome(self, page, period): self.noteDB.deleteNotesByTrack([page], [1]) baseCS = CSoundNote( 0, # onset 36, # pitch 0.2, # amplitude 0.5, # pan 100, # duration 1, # track self.instrumentDB.instNamed["drum1hatpedal"].instrumentId, reverbSend=0.5, tied=True, mode='mini') stream = [] offset = 0 for b in range(self.noteDB.getPage(page).beats): cs = baseCS.clone() cs.instrumentId = \ self.instrumentDB.instNamed["drum1hatshoulder"].instrumentId cs.amplitude = 0.5 cs.onset += offset stream.append(cs) onset = period while onset < Config.TICKS_PER_BEAT: cs = baseCS.clone() cs.onset = onset + offset stream.append(cs) onset += period offset += Config.TICKS_PER_BEAT self.noteDB.addNotes([page, 1, len(stream)] + stream + [-1]) def removeMetronome(self, page): self.noteDB.deleteNotesByTrack([page], [1]) def handleStopButton(self, widget): self.setStopped() def handleMuteButton(self, widget): if widget.get_active(): self._setMuted(True) else: self._setMuted(False) def setMuted(self, muted): if Config.HAVE_TOOLBOX: toolbar = self.activity.toolbox.toolbar else: toolbar = self.playbackToolbar if toolbar.muteButton.get_active() == muted: return toolbar.muteButton.set_active(muted) toolbar.playbackToolbar.setMuted(muted) def _setMuted(self, muted): if self.muted == muted: return False if self.muted: # unmute self.muted = False self.csnd.setTrackVolume(100, 0) else: # mute self.muted = True self.csnd.setTrackVolume(0, 0) return True def setStopped(self): for drum in list(self.desktop.drums): self.desktop.deactivateDrum(drum) # we copy the list using the list() method for loop in list(self.desktop.loops): self.desktop.deactivateLoop(loop) #========================================================== # Generate def _generateDrumLoop(self, instrumentId, beats, regularity, reverb, pageId=-1): def flatten(ll): rval = [] for l in ll: rval += l return rval notes = flatten(generator( self.instrumentDB.instId[instrumentId].name, beats, 0.8, regularity, reverb)) if pageId == -1: page = Page(beats) pageId = self.noteDB.addPage(-1, page) else: self.noteDB.deleteNotesByTrack([pageId], [0]) if len(notes): self.noteDB.addNotes([pageId, 0, len(notes)] + notes + [-1]) return pageId def _generateTrack(self, instrumentId, page, track, parameters, algorithm): dict = {track: {page: self.noteDB.getCSNotesByTrack(page, track)}} instruments = {page: [self.instrumentDB.instId[instrumentId].name \ for i in range(Config.NUMBER_OF_TRACKS)]} beatsOfPages = {page: self.noteDB.getPage(page).beats} algorithm(parameters, [0.5 for i in range(Config.NUMBER_OF_TRACKS)], instruments, self.tempo, beatsOfPages, [track], [page], dict, 4) # filter & fix input ...WTF!? for track in dict: for page in dict[track]: for note in dict[track][page]: intdur = int(note.duration) note.duration = intdur note.pageId = page note.trackId = track # prepare the new notes newnotes = [] for tid in dict: for pid in dict[tid]: newnotes += dict[tid][pid] # delete the notes and add the new self.noteDB.deleteNotesByTrack([page], [track]) self.noteDB.addNotes([page, track, len(dict[track][page])] \ + dict[track][page] + [-1]) #========================================================== # Mic recording def micRec(self, widget, mic): self.csnd.inputMessage("i5600 0 4") OS.arecord(4, "crop.csd", mic) self.csnd.load_mic_instrument(mic) #========================================================== # Loop Settings def loopSettingsChannel(self, channel, value): self.csnd.setChannel(channel, value) def loopSettingsPlayStop(self, state, loop): if not state: if loop: self.loopSettingsPlaying = True self.csnd.inputMessage(Config.CSOUND_PLAY_LS_NOTE % 5022) else: self.csnd.inputMessage(Config.CSOUND_PLAY_LS_NOTE % 5023) else: if loop: self.loopSettingsPlaying = False self.csnd.inputMessage(Config.CSOUND_STOP_LS_NOTE) def load_ls_instrument(self, soundName): self.csnd.load_ls_instrument(soundName) #========================================================== # Get/Set def getVolume(self): return self.volume def setVolume(self, volume): self.jamToolbar.volumeSlider.set_value(volume) def _setVolume(self, volume): if self.muted: self.setMuted(False) self.volume = volume # csnd expects a range 0-100 for now self.csnd.setMasterVolume(self.volume * 100) def getTempo(self): return self.tempo def setTempo(self, tempo, quiet=False): self.jamToolbar.setTempo(tempo, quiet) def _setTempo(self, tempo, propagate=True): if self.network.isHost() or self.network.isOffline(): t = time.time() elapsedTicks = (t - self.heartbeatStart) * self.ticksPerSecond self.tempo = tempo self.beatDuration = 60.0 / self.tempo self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0 self.csnd.setTempo(self.tempo) if self.network.isHost() or self.network.isOffline(): self.heatbeatStart = t - elapsedTicks * self.beatDuration self.updateSync() self.sendTempoUpdate() def getInstrument(self): return self.instrument def getDesktop(self): return self.desktop def _clearDesktop(self, save=True): if self.curDesktop == None: return if save: self._saveDesktop() self.desktop._clearDesktop() self.curDesktop = None def setDesktop(self, desktop, force=False): radiobtn = self.desktopToolbar.getDesktopButton(desktop) if force and radiobtn.get_active(): self._setDesktop(desktop) else: radiobtn.set_active(True) def _setDesktop(self, desktop): self._clearDesktop() self.curDesktop = desktop TTTable = ControlStream.TamTamTable(self.noteDB, jam=self) filename = self.getDesktopScratchFile(self.curDesktop) try: stream = open(filename, "r") TTTable.parseFile(stream) stream.close() except IOError, (errno, strerror): if Config.DEBUG > 3: print "IOError:: _setDesktop:", errno, strerror
class JamMain(Gtk.EventBox): def __init__(self, activity): GObject.GObject.__init__(self) self.activity = activity self.instrumentDB = InstrumentDB.getRef() self.noteDB = NoteDB.NoteDB() #-- initial settings ---------------------------------- self.tempo = Config.PLAYER_TEMPO self.beatDuration = 60.0 / self.tempo self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0 self.volume = 0.5 self.csnd = new_csound_client() for i in range(0, 9): self.csnd.setTrackVolume(100, i) # csnd expects a range 0-100 for now self.csnd.setMasterVolume(self.volume * 100) self.csnd.setTempo(self.tempo) self.muted = False #-- Drawing ------------------------------------------- def darken(hex): hexToDec = { "0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15, "a": 10, "b": 11, "c": 12, "d": 13, "e": 14, "f": 15 } r = int(0.7 * (16 * hexToDec[hex[1]] + hexToDec[hex[2]])) g = int(0.7 * (16 * hexToDec[hex[3]] + hexToDec[hex[4]])) b = int(0.7 * (16 * hexToDec[hex[5]] + hexToDec[hex[6]])) return r * 256, g * 256, b * 256 def lighten(hex): hexToDec = { "0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15, "a": 10, "b": 11, "c": 12, "d": 13, "e": 14, "f": 15 } r = 255 - int(0.7 * (255 - (16 * hexToDec[hex[1]] + hexToDec[hex[2]]))) g = 255 - int(0.7 * (255 - (16 * hexToDec[hex[3]] + hexToDec[hex[4]]))) b = 255 - int(0.7 * (255 - (16 * hexToDec[hex[5]] + hexToDec[hex[6]]))) return r * 256, g * 256, b * 256 xoColor = profile.get_color() if not xoColor: xoColorKey = ("#8D8D8D,#FFDDEA") xoColor = XoColor(xoColorKey) # colors in Config and in XoColor are strings, # the colors in style are style.Color, transform all to Gdk.Color self.colors = {"bg": CairoUtil.get_gdk_color(Config.PANEL_BCK_COLOR), "black": style.COLOR_BLACK.get_gdk_color(), #"Picker_Bg": colormap.alloc_color("#404040"), #"Picker_Bg_Inactive": colormap.alloc_color("#808080"), "Picker_Bg": style.COLOR_TOOLBAR_GREY.get_gdk_color(), "Picker_Bg_Inactive": style.COLOR_BUTTON_GREY.get_gdk_color(), "Picker_Fg": style.COLOR_WHITE.get_gdk_color(), "Border_Active": \ CairoUtil.get_gdk_color(xoColor.get_stroke_color()), "Border_Inactive": CairoUtil.get_gdk_color("#8D8D8D"), "Border_Highlight": CairoUtil.get_gdk_color("#FFFFFF"), "Bg_Active": CairoUtil.get_gdk_color(xoColor.get_fill_color()), "Bg_Inactive": CairoUtil.get_gdk_color("#DBDBDB"), "Preview_Note_Fill": CairoUtil.get_gdk_color(Config.BG_COLOR), "Preview_Note_Border": CairoUtil.get_gdk_color(Config.FG_COLOR), "Preview_Note_Selected": style.COLOR_WHITE.get_gdk_color(), # TODO: lighten here can be removed, check if is used in other # places "Note_Fill_Active": Gdk.Color(*lighten("#590000")), # base "Border_Active" "Note_Fill_Inactive": Gdk.Color(*lighten("#8D8D8D")), # base "Border_Inactive" "Beat_Line": CairoUtil.get_gdk_color("#959595")} self.colors["Note_Border_Active"] = self.colors["Border_Active"] self.colors["Note_Border_Inactive"] = self.colors["Border_Inactive"] self.sampleNoteHeight = 7 self.sampleBg = cairo.ImageSurface.create_from_png( imagefile('sampleBG.png')) self.loopPitchOffset = 4 self.loopTickOffset = 13 self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES - 1) / \ (Block.Loop.HEIGHT - 2 * self.loopPitchOffset - \ self.sampleNoteHeight) self.pixelsPerPitch = float(Block.Loop.HEIGHT - \ 2 * self.loopPitchOffset - self.sampleNoteHeight) / \ (Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH) self.pixelsPerTick = Block.Loop.BEAT / float(Config.TICKS_PER_BEAT) self.ticksPerPixel = 1.0 / self.pixelsPerTick #-- Instruments --------------------------------------- self.instrumentImage = {} self.instrumentImageActive = {} for inst in self.instrumentDB.getSet("All"): if not inst.kitStage: self.prepareInstrumentImage(inst.instrumentId, inst.img) self.csnd.load_instrument(inst.name) #-- Loop Images --------------------------------------- self.loopImage = {} # get filled in through updateLoopImage self.loopImageActive = {} #-- Key Images ---------------------------------------- self.keyImage = {} self.keyImageActive = {} # use hardware key codes to work on any keyboard layout (hopefully) self.valid_shortcuts = { 18: "9", 19: "0", 20: "-", 21: "=", 32: "O", 33: "P", 34: "[", 35: "]", 47: ";", 48: "'", 51: "\\", 60: ".", 61: "/", None: " " } for key in self.valid_shortcuts.keys(): self.prepareKeyImage(key) #-- Toolbars ------------------------------------------ self.jamToolbar = JamToolbar(self) jam_toolbar_button = ToolbarButton(label=_('Jam'), page=self.jamToolbar, icon_name='voltemp') self.jamToolbar.show() jam_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(jam_toolbar_button, -1) self.beatToolbar = BeatToolbar(self) beat_toolbar_button = ToolbarButton(label=_('Beat'), page=self.beatToolbar, icon_name='heart') self.beatToolbar.show() beat_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(beat_toolbar_button, -1) self.desktopToolbar = DesktopToolbar(self) desktop_toolbar_button = ToolbarButton(label=_('Desktop'), page=self.desktopToolbar, icon_name='jam-presets-list') self.desktopToolbar.show() desktop_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(desktop_toolbar_button, -1) if Config.FEATURES_MIC or Config.FEATURES_NEWSOUNDS: self.recordToolbar = RecordToolbar(self) record_toolbar_button = ToolbarButton(label=_('Record'), page=self.recordToolbar, icon_name='microphone') self.recordToolbar.show() record_toolbar_button.show() self.activity.toolbar_box.toolbar.insert(record_toolbar_button, -1) separator = Gtk.SeparatorToolItem() separator.props.draw = True separator.set_expand(False) self.activity.toolbar_box.toolbar.insert(separator, -1) separator.show() common_playback_buttons(self.activity.toolbar_box.toolbar, self) #-- GUI ----------------------------------------------- if True: # GUI self.modify_bg(Gtk.StateType.NORMAL, self.colors["bg"]) self.GUI = {} self.GUI["mainVBox"] = Gtk.VBox() self.add(self.GUI["mainVBox"]) #-- Desktop ------------------------------------------- self.desktop = self.GUI["desktop"] = Desktop(self) self.GUI["mainVBox"].pack_start(self.GUI["desktop"], True, True, 0) #-- Bank ---------------------------------------------- separator = Gtk.Label(label=" ") separator.set_size_request(-1, style.TOOLBOX_SEPARATOR_HEIGHT) self.GUI["mainVBox"].pack_start(separator, False, True, 0) self.GUI["notebook"] = Gtk.Notebook() self.GUI["notebook"].set_scrollable(True) self.GUI["notebook"].modify_bg(Gtk.StateType.NORMAL, self.colors["Picker_Bg"]) self.GUI["notebook"].modify_bg(Gtk.StateType.ACTIVE, self.colors["Picker_Bg_Inactive"]) # TODO gtk3 no available anymore? #self.GUI["notebook"].props.tab_vborder = style.TOOLBOX_TAB_VBORDER #self.GUI["notebook"].props.tab_hborder = style.TOOLBOX_TAB_HBORDER self.GUI["notebook"].set_size_request(-1, scale(160)) self.GUI["notebook"].connect("switch-page", self.setPicker) self.GUI["mainVBox"].pack_start(self.GUI["notebook"], False, False, 0) self.pickers = {} self.pickerScroll = {} for type in [Picker.Instrument, Picker.Drum, Picker.Loop]: self.pickers[type] = type(self) def prepareLabel(name): label = Gtk.Label(label=Tooltips.categories.get(name) or name) label.set_alignment(0.0, 0.5) label.modify_fg(Gtk.StateType.NORMAL, self.colors["Picker_Fg"]) label.modify_fg(Gtk.StateType.ACTIVE, self.colors["Picker_Fg"]) return label self.GUI["notebook"].append_page(self.pickers[Picker.Drum], prepareLabel(_("Drum Kits"))) self.GUI["notebook"].append_page(self.pickers[Picker.Loop], prepareLabel(_("Loops"))) sets = self.instrumentDB.getLabels()[:] sets.sort() for set in sets: page = Gtk.HBox() page.set = set self.GUI["notebook"].append_page(page, prepareLabel(set)) self.show_all() self.GUI["notebook"].set_current_page(0) #-- Keyboard ------------------------------------------ self.key_dict = {} self.nextTrack = 2 self.keyboardListener = None self.recordingNote = None self.keyMap = {} # default instrument self._updateInstrument( self.instrumentDB.instNamed["kalimba"].instrumentId, 0.5) self.instrumentStack = [] # metronome page = NoteDB.Page(1, local=False) self.metronomePage = self.noteDB.addPage(-1, page) self.metronome = False #-- Drums --------------------------------------------- self.drumLoopId = None # use dummy values for now self.drumFillin = Fillin( 2, 100, self.instrumentDB.instNamed["drum1kit"].instrumentId, 0, 1) #-- Desktops ------------------------------------------ self.curDesktop = None # copy preset desktops path = Config.FILES_DIR + "/Desktops/" filelist = os.listdir(path) for file in filelist: shutil.copyfile(path + file, Config.TMP_DIR + '/' + file) #-- Network ------------------------------------------- self.network = Net.Network() self.network.addWatcher(self.networkStatusWatcher) self.network.connectMessage(Net.HT_SYNC_REPLY, self.processHT_SYNC_REPLY) self.network.connectMessage(Net.HT_TEMPO_UPDATE, self.processHT_TEMPO_UPDATE) self.network.connectMessage(Net.PR_SYNC_QUERY, self.processPR_SYNC_QUERY) self.network.connectMessage(Net.PR_TEMPO_QUERY, self.processPR_TEMPO_QUERY) self.network.connectMessage(Net.PR_REQUEST_TEMPO_CHANGE, self.processPR_REQUEST_TEMPO_CHANGE) # sync self.syncQueryStart = {} self.syncTimeout = None self.heartbeatLoop = self.csnd.loopCreate() self.syncBeats = 4 self.syncTicks = self.syncBeats * Config.TICKS_PER_BEAT self.offsetTicks = 0 # offset from the true heartbeat self.csnd.loopSetNumTicks(self.syncTicks * HEARTBEAT_BUFFER, self.heartbeatLoop) self.heartbeatStart = time.time() self.csnd.loopStart(self.heartbeatLoop) self.curBeat = 0 self.beatWheelTimeout = GObject.timeout_add(100, self.updateBeatWheel) # data packing classes self.packer = xdrlib.Packer() self.unpacker = xdrlib.Unpacker("") # handle forced networking if self.network.isHost(): self.updateSync() self.syncTimeout = GObject.timeout_add(1000, self.updateSync) elif self.network.isPeer(): self.sendTempoQuery() self.syncTimeout = GObject.timeout_add(1000, self.updateSync) self.activity.connect("shared", self.shared) if self.activity.shared_activity: # PEER self.activity.shared_activity.connect("buddy-joined", self.buddy_joined) self.activity.shared_activity.connect("buddy-left", self.buddy_left) self.activity.connect("joined", self.joined) self.network.setMode(Net.MD_WAIT) #-- Final Set Up -------------------------------------- self.setVolume(self.volume) self.setTempo(self.tempo) #self.activity.toolbar_box.set_current_toolbar(1) # JamToolbar self.setDesktop(0, True) #========================================================== def onActivate(self, arg): pass def onDeactivate(self): pass def onDestroy(self): self.network.shutdown() #clear up scratch folder path = Config.TMP_DIR filelist = os.listdir(path) for file in filelist: os.remove(path + '/' + file) #========================================================== # Playback def onKeyPress(self, widget, event): key = event.hardware_keycode if key in self.keyMap.keys(): activate = True for block in self.keyMap[key]: if block.isActive(): activate = False break if activate: for block in self.keyMap[key]: if not block.isActive(): if block.type == Block.Drum: self.desktop.activateDrum(block) elif block.type == Block.Loop: self.desktop.activateLoop(block) else: for block in self.keyMap[key]: if block.isActive(): if block.type == Block.Drum: self.desktop.deactivateDrum(block) elif block.type == Block.Loop: self.desktop.deactivateLoop(block) return if key in self.key_dict: # repeated press return if key in Config.KEY_MAP_PIANO: pitch = Config.KEY_MAP_PIANO[key] inst = self.instrumentDB.instId[self.instrument["id"]] if inst.kit: # drum kit if pitch in GenerationConstants.DRUMPITCH: pitch = GenerationConstants.DRUMPITCH[pitch] csnote = self._playNote( # trackVol * noteVol key, 36, self.instrument["amplitude"] * 0.5, self.instrument["pan"], 100, self.instrumentDB.instNamed[inst.kit[pitch]].instrumentId, self.instrument["reverb"]) else: if event.get_state() == Gdk.ModifierType.MOD1_MASK: pitch += 5 # Percussions resonance if inst.csoundInstrumentId == Config.INST_PERC: duration = 60 else: duration = -1 csnote = self._playNote( # trackVol * noteVol key, pitch, self.instrument["amplitude"] * 0.5, self.instrument["pan"], duration, self.instrument["id"], self.instrument["reverb"]) if self.keyboardListener: self.keyboardListener.recordNote(csnote.pitch) self.recordingNote = True def onKeyRelease(self, widget, event): key = event.hardware_keycode if key in self.key_dict: self._stopNote(key) if self.recordingNote: if self.keyboardListener: self.keyboardListener.finishNote() self.recordingNote = False def _playNote(self, key, pitch, amplitude, pan, duration, instrumentId, reverb): self.key_dict[key] = CSoundNote( # onset 0, pitch, amplitude, pan, duration, self.nextTrack, instrumentId, reverbSend=reverb, tied=True, mode='mini') self.nextTrack += 1 if self.nextTrack > 8: self.nextTrack = 2 self.csnd.play(self.key_dict[key], 0.3) return self.key_dict[key] def _stopNote(self, key): csnote = self.key_dict[key] if self.instrumentDB.instId[csnote.instrumentId].csoundInstrumentId \ == Config.INST_TIED: csnote.duration = .5 csnote.decay = 0.7 csnote.tied = False self.csnd.play(csnote, 0.3) del self.key_dict[key] def _updateInstrument(self, id, volume, pan=0, reverb=0): self.instrument = { "id": id, "amplitude": volume, "pan": pan, "reverb": reverb } def pushInstrument(self, instrument): self.instrumentStack.append(self.instrument) self.instrument = instrument def popInstrument(self): self.instrument = self.instrumentStack.pop() def _playDrum(self, id, pageId, volume, reverb, beats, regularity, loopId=None, sync=True): oldId = loopId loopId = self.csnd.loopCreate() noteOnsets = [] notePitchs = [] for n in self.noteDB.getNotesByTrack(pageId, 0): n.pushState() noteOnsets.append(n.cs.onset) notePitchs.append(n.cs.pitch) n.cs.instrumentId = id n.cs.amplitude = volume * n.cs.amplitude n.cs.reverbSend = reverb self.csnd.loopPlay(n, 1, loopId=loopId) # add as active n.popState() ticks = self.noteDB.getPage(pageId).ticks self.csnd.loopSetNumTicks(ticks, loopId) self.drumFillin.setLoopId(loopId) self.drumFillin.setProperties(self.tempo, self.instrumentDB.instId[id].name, volume, beats, reverb) self.drumFillin.unavailable(noteOnsets, notePitchs) self.drumFillin.play() if oldId == None: if sync: startTick = self.csnd.loopGetTick(self.heartbeatLoop) \ % self.syncTicks else: startTick = 0 else: # TODO is this really safe? could potentially add several # milliseconds of delay everytime a loop is updated if sync: startTick = self.csnd.loopGetTick(oldId) else: startTick = 0 while startTick > ticks: startTick -= ticks self.csnd.loopSetTick(startTick, loopId) self.csnd.loopStart(loopId) if oldId != None: self.csnd.loopDestroy(oldId) return loopId def _stopDrum(self, loopId): self.drumFillin.stop() self.csnd.loopDestroy(loopId) def _playLoop(self, id, volume, reverb, tune, loopId=None, force=False, sync=True): oldId = loopId loopId = self.csnd.loopCreate() inst = self.instrumentDB.instId[id] ticks = 0 for page in tune: for n in self.noteDB.getNotesByTrack(page, 0): n.pushState() n.cs.instrumentId = id n.cs.amplitude = volume * n.cs.amplitude n.cs.reverbSend = reverb if inst.kit: # drum kit if n.cs.pitch in GenerationConstants.DRUMPITCH: n.cs.pitch = GenerationConstants.DRUMPITCH[n.cs.pitch] n.cs.onset += ticks self.csnd.loopPlay(n, 1, loopId=loopId) n.popState() # metronome track for n in self.noteDB.getNotesByTrack(page, 1): self.csnd.loopPlay(n, 1, loopId=loopId) # record scratch track for n in self.noteDB.getNotesByTrack(page, 2): self.csnd.loopPlay(n, 1, loopId=loopId) ticks += self.noteDB.getPage(page).ticks self.csnd.loopSetNumTicks(ticks, loopId) if oldId == None: if sync: startTick = self.csnd.loopGetTick(self.heartbeatLoop) \ % self.syncTicks else: startTick = 0 else: # TODO is this really safe? could potentially add several # milliseconds of delay everytime a loop is updated if sync: startTick = self.csnd.loopGetTick(oldId) else: startTick = 0 while startTick > ticks: startTick -= ticks self.csnd.loopSetTick(startTick, loopId) self.csnd.loopStart(loopId) if oldId != None: self.csnd.loopDestroy(oldId) return loopId def _stopLoop(self, loopId): self.csnd.loopDestroy(loopId) def addMetronome(self, page, period): self.noteDB.deleteNotesByTrack([page], [1]) baseCS = CSoundNote( 0, # onset 36, # pitch 0.2, # amplitude 0.5, # pan 100, # duration 1, # track self.instrumentDB.instNamed["drum1hatpedal"].instrumentId, reverbSend=0.5, tied=True, mode='mini') stream = [] offset = 0 for b in range(self.noteDB.getPage(page).beats): cs = baseCS.clone() cs.instrumentId = \ self.instrumentDB.instNamed["drum1hatshoulder"].instrumentId cs.amplitude = 0.5 cs.onset += offset stream.append(cs) onset = period while onset < Config.TICKS_PER_BEAT: cs = baseCS.clone() cs.onset = onset + offset stream.append(cs) onset += period offset += Config.TICKS_PER_BEAT self.noteDB.addNotes([page, 1, len(stream)] + stream + [-1]) def removeMetronome(self, page): self.noteDB.deleteNotesByTrack([page], [1]) def handleStopButton(self, widget): self.setStopped() def handleMuteButton(self, widget): if widget.get_active(): self._setMuted(True) else: self._setMuted(False) def setMuted(self, muted): if Config.HAVE_TOOLBOX: toolbar = self.activity.toolbox.toolbar else: toolbar = self.playbackToolbar if toolbar.muteButton.get_active() == muted: return toolbar.muteButton.set_active(muted) toolbar.playbackToolbar.setMuted(muted) def _setMuted(self, muted): if self.muted == muted: return False if self.muted: # unmute self.muted = False self.csnd.setTrackVolume(100, 0) else: # mute self.muted = True self.csnd.setTrackVolume(0, 0) return True def setStopped(self): for drum in list(self.desktop.drums): self.desktop.deactivateDrum(drum) # we copy the list using the list() method for loop in list(self.desktop.loops): self.desktop.deactivateLoop(loop) #========================================================== # Generate def _generateDrumLoop(self, instrumentId, beats, regularity, reverb, pageId=-1): def flatten(ll): rval = [] for l in ll: rval += l return rval notes = flatten( generator(self.instrumentDB.instId[instrumentId].name, beats, 0.8, regularity, reverb)) if pageId == -1: page = Page(beats) pageId = self.noteDB.addPage(-1, page) else: self.noteDB.deleteNotesByTrack([pageId], [0]) if len(notes): self.noteDB.addNotes([pageId, 0, len(notes)] + notes + [-1]) return pageId def _generateTrack(self, instrumentId, page, track, parameters, algorithm): dict = {track: {page: self.noteDB.getCSNotesByTrack(page, track)}} instruments = {page: [self.instrumentDB.instId[instrumentId].name \ for i in range(Config.NUMBER_OF_TRACKS)]} beatsOfPages = {page: self.noteDB.getPage(page).beats} algorithm(parameters, [0.5 for i in range(Config.NUMBER_OF_TRACKS)], instruments, self.tempo, beatsOfPages, [track], [page], dict, 4) # filter & fix input ...WTF!? for track in dict: for page in dict[track]: for note in dict[track][page]: intdur = int(note.duration) note.duration = intdur note.pageId = page note.trackId = track # prepare the new notes newnotes = [] for tid in dict: for pid in dict[tid]: newnotes += dict[tid][pid] # delete the notes and add the new self.noteDB.deleteNotesByTrack([page], [track]) self.noteDB.addNotes([page, track, len(dict[track][page])] \ + dict[track][page] + [-1]) #========================================================== # Mic recording def micRec(self, widget, mic): self.csnd.inputMessage("i5600 0 4") OS.arecord(4, "crop.csd", mic) self.csnd.load_mic_instrument(mic) #========================================================== # Loop Settings def loopSettingsChannel(self, channel, value): self.csnd.setChannel(channel, value) def loopSettingsPlayStop(self, state, loop): if not state: if loop: self.loopSettingsPlaying = True self.csnd.inputMessage(Config.CSOUND_PLAY_LS_NOTE % 5022) else: self.csnd.inputMessage(Config.CSOUND_PLAY_LS_NOTE % 5023) else: if loop: self.loopSettingsPlaying = False self.csnd.inputMessage(Config.CSOUND_STOP_LS_NOTE) def load_ls_instrument(self, soundName): self.csnd.load_ls_instrument(soundName) #========================================================== # Get/Set def getVolume(self): return self.volume def setVolume(self, volume): self.jamToolbar.volumeSlider.set_value(volume) def _setVolume(self, volume): if self.muted: self.setMuted(False) self.volume = volume # csnd expects a range 0-100 for now self.csnd.setMasterVolume(self.volume * 100) def getTempo(self): return self.tempo def setTempo(self, tempo, quiet=False): self.jamToolbar.setTempo(tempo, quiet) def _setTempo(self, tempo, propagate=True): if self.network.isHost() or self.network.isOffline(): t = time.time() elapsedTicks = (t - self.heartbeatStart) * self.ticksPerSecond self.tempo = tempo self.beatDuration = 60.0 / self.tempo self.ticksPerSecond = Config.TICKS_PER_BEAT * self.tempo / 60.0 self.csnd.setTempo(self.tempo) if self.network.isHost() or self.network.isOffline(): self.heatbeatStart = t - elapsedTicks * self.beatDuration self.updateSync() self.sendTempoUpdate() def getInstrument(self): return self.instrument def getDesktop(self): return self.desktop def _clearDesktop(self, save=True): if self.curDesktop == None: return if save: self._saveDesktop() self.desktop._clearDesktop() self.curDesktop = None def setDesktop(self, desktop, force=False): radiobtn = self.desktopToolbar.getDesktopButton(desktop) if force and radiobtn.get_active(): self._setDesktop(desktop) else: radiobtn.set_active(True) def _setDesktop(self, desktop): self._clearDesktop() self.curDesktop = desktop TTTable = ControlStream.TamTamTable(self.noteDB, jam=self) filename = self.getDesktopScratchFile(self.curDesktop) try: stream = open(filename, "r") TTTable.parseFile(stream) stream.close() except IOError, (errno, strerror): if Config.DEBUG > 3: print "IOError:: _setDesktop:", errno, strerror