Example #1
0
    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)
Example #2
0
    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)
Example #3
0
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
Example #4
0
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