Example #1
0
class Loop(Block):

    HEAD = scale(13)
    BEAT = scale(23)
    TAIL = BEAT + Block.PAD

    WIDTH = [
        HEAD + BEAT * (n - 1) + TAIL for n in range(Config.MAXIMUM_BEATS + 1)
    ]

    BEAT_MUL3 = BEAT * 3

    MASK_START = scale(200)
    MASK_BEAT = MASK_START + HEAD
    MASK_TAIL = MASK_START + HEAD + BEAT * 3

    KEYRECT = [
        HEAD + Block.PAD, Block.HEIGHT - 2 * Block.PAD - Block.KEYSIZE,
        Block.KEYSIZE, Block.KEYSIZE
    ]
    KEYRECT += [KEYRECT[0] + KEYRECT[2], KEYRECT[1] + KEYRECT[3]]

    #::: data format:
    # { "name": name, "id": pageId [, "beats": 2-12, "regularity": 0-1, "key": shortcut ] }
    #:::
    def __init__(self, owner, data):
        Block.__init__(self, owner, data)

        self.type = Loop

        self.canParent = True
        self.canChild = True
        self.canSubstitute = True

        self.parentOffset = Loop.HEAD - 4

        self.data["beats"] = self.owner.noteDB.getPage(self.data["id"]).beats
        self.width = Loop.WIDTH[self.data["beats"]]

        if "regularity" not in self.data.keys():
            self.data["regularity"] = 0.8  #random.random()
        if "key" not in self.data.keys():
            self.data["key"] = None

        self.keyActive = False
        self.keyImage = [
            self.owner.getKeyImage(self.data["key"], False),
            self.owner.getKeyImage(self.data["key"], True)
        ]

        self.img = [
            self.owner.getLoopImage(self.data["id"], False),
            self.owner.getLoopImage(self.data["id"], True)
        ]

    def destroy(self):
        if self.active:
            self.owner.deactivateLoop(self)
        if self.keyActive:
            self.owner.mapKey(None, self, self.data["key"])
        self.owner.noteDB.deletePages([self.data["id"]])
        Block.destroy(self)

    def _updateWidth(self):
        self.invalidateBranch(True)

        oldWidth = self.width

        self.width = Loop.WIDTH[self.data["beats"]]
        self.endX = self.x + self.width

        if self.child:
            self.child.snapToParentLoc(self.getChildAnchor())

        if oldWidth < self.width:  # or block.child:
            self.invalidateBranch(True)

    def updateLoop(self):
        self.updateImage()
        self.invalidate_rect()

        if self.active:
            self.owner.updateLoop(self.getRoot().child)

    def updateImage(self):
        self.owner.updateLoopImage(self.data["id"])
        self.img = [
            self.owner.getLoopImage(self.data["id"], False),
            self.owner.getLoopImage(self.data["id"], True)
        ]

    def setData(self, key, value):

        if key == "beats":
            self.data["beats"] = value
            self.owner.noteDB.updatePage(self.data["id"], PARAMETER.PAGE_BEATS,
                                         value)
            self._updateWidth()
            self.updateLoop()

        elif key == "key":
            oldKey = self.data["key"]
            self.data["key"] = value
            self.keyImage = [
                self.owner.getKeyImage(value, False),
                self.owner.getKeyImage(value, True)
            ]
            self.invalidate_rect()
            if self.keyActive:
                self.owner.mapKey(value, self, oldKey)

        else:
            self.data[key] = value

    def substitute(self, block):
        self.invalidateBranch(True)

        oldWidth = self.width

        newid = self.owner.noteDB.duplicatePages([block.data["id"]
                                                  ])[block.data["id"]]
        self.data["id"] = newid
        self.data["beats"] = self.owner.noteDB.getPage(self.data["id"]).beats

        self.updateImage()
        self._updateWidth()

        if False:  # don't substitute children
            if block.child:
                c = block.child
                after = self
                while c:
                    data = {}
                    for key in c.data.keys():
                        data[key] = c.data[key]

                    newid = self.owner.noteDB.duplicatePages([data["id"]
                                                              ])[data["id"]]
                    self.owner.updateLoopImage(newid)
                    data["id"] = newid

                    copy = Loop(self.owner, self.gc, data)
                    after.addChild(copy)
                    after = copy
                    c = c.child
            elif self.child:
                self.child.snapToParentLoc(self.getChildAnchor())

        if self.active:
            self.owner.updateLoop(self.getRoot().child)

    def testSubstitute(self, block):
        ret = Block.testSubstitute(self, block)
        if ret:
            return ret

        if block.type != Loop:
            return False

        if abs(self.x - block.x) < Block.SNAP and abs(self.y -
                                                      block.y) < Block.SNAP:
            return self

        return False

    def setActive(self, state):
        Block.setActive(self, state)

        if self.child:
            self.child.setActive(state)

    def addChild(self, child):
        Block.addChild(self, child)
        if self.active:
            child.setActive(True)
            self.owner.updateLoop(self.getRoot().child)

    def _addParent(self, parent):
        Block._addParent(self, parent)

        if self.parent.type == Instrument:
            self.keyActive = True
            self.owner.mapKey(self.data["key"], self)
        else:
            root = self.getRoot()
            if root.type == Instrument:
                root = root.child
            if root.getData("key") == None:
                root.setData("key", self.data["key"])
            self.setData("key", None)

    def _removeParent(self):
        if self.active:
            loopRoot = self.getRoot().child
            parent = self.parent
        else:
            loopRoot = None

        self.keyActive = False
        self.owner.mapKey(None, self, self.data["key"])

        Block._removeParent(self)

        if loopRoot == self:
            self.owner.deactivateLoop(loopRoot)
        elif loopRoot != None:
            self.setActive(False)
            parent.child = None  # disconnect us before updating
            self.owner.updateLoop(loopRoot)

    def testMouseOver(self, event):
        ret = self.testWithinKey(event)
        if ret: return ret

        x = event.x - self.x
        y = event.y - self.y

        if 0 <= x <= self.width and 0 <= y <= self.height:
            return -1

        return False

    def testWithinKey(self, event):
        if not self.keyActive:
            return False

        x = event.x - self.x
        y = event.y - self.y

        if Loop.KEYRECT[0] <= x <= Loop.KEYRECT[4] and Loop.KEYRECT[
                1] <= y <= Loop.KEYRECT[5]:
            return self

        return False

    def _doButtonPress(self, event):  # we were hit with a button press
        pass

    def button_release(self, event):
        if not self.dragging:
            if self.active:
                root = self.getRoot()
                self.owner.deactivateLoop(root.child)
            else:
                root = self.getRoot()
                if root.type == Instrument:  # must be attached to an instrument
                    self.owner.activateLoop(root.child)
        Block.button_release(self, event)

    def _doDraw(self, startX, startY, stopX, stopY, pixmap):
        y = max(startY, self.y)
        endY = min(stopY, self.endY)
        height = endY - y

        loop = self.img[self.active]
        if self.active: self.gc.foreground = self.owner.colors["Border_Active"]
        else: self.gc.foreground = self.owner.colors["Border_Inactive"]

        #-- draw head -----------------------------------------

        if self.x + Loop.HEAD > startX:
            x = max(startX, self.x)
            endX = min(stopX, self.x + Loop.HEAD)
            width = endX - x

            # draw border
            self.gc.set_clip_origin(self.x - Loop.MASK_START, self.y)
            pixmap.draw_rectangle(self.gc, True, x, y, width, height)

            # draw block
            self.gc.set_clip_origin(self.x - Loop.MASK_START,
                                    self.y - self.height)
            pixmap.draw_drawable(self.gc, loop, x - self.x, y - self.y, x, y,
                                 width, height)

        #-- draw beats ----------------------------------------

        beats = self.owner.noteDB.getPage(
            self.data["id"]).beats - 1  # last beat is drawn with the tail
        curx = self.x + Loop.HEAD
        while beats > 3:
            if curx >= stopX:
                break
            elif curx + Loop.BEAT_MUL3 > startX:
                x = max(startX, curx)
                endX = min(stopX, curx + Loop.BEAT_MUL3)
                width = endX - x

                # draw border
                self.gc.set_clip_origin(curx - Loop.MASK_BEAT, self.y)
                pixmap.draw_rectangle(self.gc, True, x, y, width, height)

                # draw block
                self.gc.set_clip_origin(curx - Loop.MASK_BEAT,
                                        self.y - self.height)
                pixmap.draw_drawable(self.gc, loop, x - self.x, y - self.y, x,
                                     y, width, height)

            curx += Loop.BEAT_MUL3
            beats -= 3
        if beats and curx < stopX:
            endX = curx + Loop.BEAT * beats
            if endX > startX:
                x = max(startX, curx)
                endX = min(stopX, endX)
                width = endX - x

                # draw border
                self.gc.set_clip_origin(curx - Loop.MASK_BEAT, self.y)
                pixmap.draw_rectangle(self.gc, True, x, y, width, height)

                # draw block
                self.gc.set_clip_origin(curx - Loop.MASK_BEAT,
                                        self.y - self.height)
                pixmap.draw_drawable(self.gc, loop, x - self.x, y - self.y, x,
                                     y, width, height)

            curx += Loop.BEAT * beats

        #-- draw tail -----------------------------------------

        if curx < stopX:
            x = max(startX, curx)
            endX = min(stopX, self.endX)
            width = endX - x

            # draw border
            self.gc.set_clip_origin(curx - Loop.MASK_TAIL, self.y)
            pixmap.draw_rectangle(self.gc, True, x, y, width, height)

            # draw block
            self.gc.set_clip_origin(curx - Loop.MASK_TAIL,
                                    self.y - self.height)
            pixmap.draw_drawable(self.gc, loop, x - self.x, y - self.y, x, y,
                                 width, height)

        #-- draw key ------------------------------------------
        if self.keyActive:
            self.gc.set_clip_origin(
                self.x + Loop.KEYRECT[0] - Block.KEYMASK_START,
                self.y + Loop.KEYRECT[1])
            pixmap.draw_drawable(self.gc, self.keyImage[self.active], 0, 0,
                                 self.x + Loop.KEYRECT[0],
                                 self.y + Loop.KEYRECT[1], Block.KEYSIZE,
                                 Block.KEYSIZE)

    def drawHighlight(self, startX, startY, stopX, stopY, pixmap):
        self.gc.foreground = self.owner.colors["Border_Highlight"]

        #-- draw head -----------------------------------------

        self.gc.set_clip_origin(self.x - Loop.MASK_START, self.y)
        pixmap.draw_rectangle(self.gc, True, self.x, self.y, Loop.HEAD,
                              self.height)

        #-- draw beats ----------------------------------------

        beats = self.owner.noteDB.getPage(
            self.data["id"]).beats - 1  # last beat is drawn with the tail
        x = self.x + Loop.HEAD
        while beats > 3:
            self.gc.set_clip_origin(x - Loop.MASK_BEAT, self.y)
            pixmap.draw_rectangle(self.gc, True, x, self.y, Loop.BEAT_MUL3,
                                  self.height)

            x += Loop.BEAT_MUL3
            beats -= 3
        if beats:
            width = Loop.BEAT * beats

            self.gc.set_clip_origin(x - Loop.MASK_BEAT, self.y)
            pixmap.draw_rectangle(self.gc, True, x, self.y, width, self.height)

            x += width

        #-- draw tail -----------------------------------------

        self.gc.set_clip_origin(x - Loop.MASK_TAIL, self.y)
        pixmap.draw_rectangle(self.gc, True, x, self.y, Loop.TAIL, self.height)

    def drawKeyHighlight(self, pixmap):
        self.gc.foreground = self.owner.colors["Border_Highlight"]
        self.gc.set_clip_origin(self.x + Loop.KEYRECT[0] - Block.KEYMASK_START,
                                self.y + Loop.KEYRECT[1] - Block.KEYSIZE)
        pixmap.draw_rectangle(self.gc, True, self.x + Loop.KEYRECT[0],
                              self.y + Loop.KEYRECT[1], Block.KEYSIZE,
                              Block.KEYSIZE)

    def clear(self):
        self.owner.noteDB.deleteNotesByTrack([self.data["id"]], [0])

        self.updateImage()

        self.invalidate_rect()

        if self.active:
            self.owner.updateLoop(self.getRoot().child)
Example #2
0
class Block:

    WIDTH = scale(100)
    HEIGHT = scale(100)

    SNAP = scale(15)

    PAD = scale(4)

    KEYSIZE = scale(26)
    KEYMASK_START = scale(309)

    def __init__(self, owner, data):
        self.owner = owner
        self.gc = owner.gc

        self.data = {}
        for key in data.keys():
            self.data[key] = data[key]

        self.type = Block

        self.width = Block.WIDTH
        self.height = Block.HEIGHT

        self.parent = None
        self.canChild = False
        self.child = None
        self.canParent = False

        self.canSubstitute = False

        self.parentOffest = 0

        self.dragging = False  # is currently dragging
        self.placed = False  # has been placed on the desktop at least once

        self.firstLoc = True
        self.x = -1
        self.y = -1

        self.active = False

    def dumpToStream(self, ostream, child=False):
        ostream.block_add(ClassToStr[self.type], self.active,
                          self.x + self.width // 2, self.y + self.height // 2,
                          child, self.data)
        if self.child:
            self.child.dumpToStream(ostream, True)

    def destroy(self):
        if self.child:
            self.child.destroy()
            self.child = None
        self.invalidate_rect(not self.dragging)

    def isPlaced(self):
        return self.placed

    def setPlaced(self, placed):
        self.placed = placed

    def getLoc(self):
        return (self.x, self.y)

    def setLoc(self, x, y):
        if x == self.x and y == self.y: return

        if self.firstLoc:
            self.firstLoc = False
        else:
            self.invalidate_rect(not self.dragging)

        self.x = int(x)
        self.y = int(y)
        self.endX = self.x + self.width
        self.endY = self.y + self.height

        self.invalidate_rect(not self.dragging)

        if self.child:
            self.child.snapToParentLoc(self.getChildAnchor())

    def resetLoc(self):
        if self.oldParent != None:
            self.oldParent.addChild(self)
            return False
        else:
            self.setLoc(self.oldLoc[0], self.oldLoc[1])
            return True

    def getParentAnchor(self):
        return (self.x + self.parentOffset, self.y)

    def getChildAnchor(self):
        return (self.endX, self.y)

    def snapToParentLoc(self, loc):
        self.setLoc(loc[0] - self.parentOffset, loc[1])

    def substitute(self, block):
        pass  # override in subclasses

    def testSubstitute(self, block):
        if self.child:
            return self.child.testSubstitute(block)

    def testChild(self, loc):

        if not self.canParent:
            return False

        if self.child:
            return self.child.testChild(loc)
        elif abs(self.endX - loc[0]) < Block.SNAP and abs(self.y -
                                                          loc[1]) < Block.SNAP:
            return self

        return False

    def addChild(self, child):
        c = self.child
        if self.child:
            self.removeChild()

        self.child = child
        child._addParent(self)
        child.snapToParentLoc(self.getChildAnchor())

        if c:
            child.addChild(c)

    def removeChild(self):
        self.child._removeParent()
        self.child = None

    def _addParent(self, parent):
        self.parent = parent

    def _removeParent(self):
        self.parent = None

    def getRoot(self):
        if self.parent: return self.parent.getRoot()
        return self

    def isActive(self):
        return self.active

    def setActive(self, state):
        self.active = state
        self.invalidate_rect(not self.dragging)

    def getData(self, key):
        return self.data[key]

    def setData(self, key, value):
        self.data[key] = value

    def testMouseOver(self, event):
        if self.child:
            ret = self.child.testMouseOver(event)
            if ret: return ret

        x = event.x - self.x
        y = event.y - self.y

        if 0 <= x <= self.width and 0 <= y <= self.height:
            return -1

        return False

    def button_press(self, event):

        if event.y < self.y or event.y > self.endY:
            return False

        return self._button_pressB(event)

    def _button_pressB(self, event):

        if event.x < self.x:
            return False

        if event.x > self.endX:
            if self.child:
                return self.child._button_pressB(event)
            else:
                return False

        self.oldParent = self.parent
        self.oldLoc = (self.x, self.y)
        self.dragOffset = (event.x - self.x, event.y - self.y)

        self._doButtonPress(event)

        return self

    def _doButtonPress(self, event):
        pass  # override in subclasses

    def button_release(self, event):
        if self.dragging:
            self.dragging = False
            self.placed = True
            self.invalidateBranch()

    def motion_notify(self, event):

        removeFromBlocks = not self.dragging and not self.parent

        if not self.dragging:
            self.dragging = True
            self.invalidate_rect()

        if self.parent:
            self.parent.removeChild()

        self.setLoc(event.x - self.dragOffset[0], event.y - self.dragOffset[1])

        return removeFromBlocks

    def _beginDrag(self):
        self.dragging = True
        self.dragOffset = (self.width // 2, self.height // 2)

    def invalidateBranch(self, base=True):
        self.invalidate_rect(base)
        if self.child:
            self.child.invalidateBranch(base)

    def invalidate_rect(self, base=True):
        self.owner.invalidate_rect(self.x, self.y, self.width, self.height,
                                   base)

    def draw(self, startX, startY, stopX, stopY, pixmap):
        if stopY <= self.y or startY >= self.endY:
            return False

        self._drawB(startX, startY, stopX, stopY, pixmap)

    def _drawB(self, startX, startY, stopX, stopY, pixmap):

        if stopX <= self.x:
            return False

        if self.child:
            self.child._drawB(startX, startY, stopX, stopY, pixmap)

        if startX >= self.endX:
            return False

        self._doDraw(startX, startY, stopX, stopY, pixmap)

        return True

    def _doDraw(self, startX, startY, stopX, stopY, pixmap):
        pass  # override in subclasses

    def drawHighlight(self, startX, startY, stopX, stopY, pixmap):
        pass  # override in subclasses
Example #3
0
class Drum(Block):

    MASK_START = scale(100)

    KEYRECT = [
        Block.PAD - 1, Block.HEIGHT + 1 - Block.PAD - Block.KEYSIZE,
        Block.KEYSIZE, Block.KEYSIZE
    ]
    KEYRECT += [KEYRECT[0] + KEYRECT[2], KEYRECT[1] + KEYRECT[3]]

    #::: data format:
    # { "name": name, "id": instrumentId [ , "page": pageId, "volume": 0-1, "reverb": 0-1, "beats": 2-12, "regularity": 0-1, "key": shortcut ] }
    #:::
    def __init__(self, owner, data):
        Block.__init__(self, owner, data)
        self.instrumentDB = InstrumentDB.getRef()
        self.type = Drum

        self.canSubstitute = True

        if not "page" in self.data.keys():
            self.data["page"] = -1
        if not "volume" in self.data.keys():
            self.data["volume"] = 0.5
        if not "reverb" in self.data.keys():
            self.data["reverb"] = 0.0
        if not "beats" in self.data.keys():
            self.data["beats"] = 4  #random.randint(2, 12)
        if not "regularity" in self.data.keys():
            self.data["regularity"] = 0.8  #random.random()
        if "key" not in self.data.keys():
            self.data["key"] = None

        self.owner.mapKey(self.data["key"], self)
        self.keyImage = [
            self.owner.getKeyImage(self.data["key"], False),
            self.owner.getKeyImage(self.data["key"], True)
        ]

        self.img = [
            self.owner.getInstrumentImage(self.data["id"], False),
            self.owner.getInstrumentImage(self.data["id"], True)
        ]

        if self.data["page"] == -1:
            self.regenerate()

    def destroy(self):
        self.owner.mapKey(None, self, self.data["key"])
        self.owner.noteDB.deletePages([self.data["page"]])
        Block.destroy(self)

    def setData(self, key, value):
        if key == "beats":
            self.data["beats"] = value
            self.owner.noteDB.updatePage(self.data["page"],
                                         PARAMETER.PAGE_BEATS, value)

        elif key == "key":
            oldKey = self.data["key"]
            self.data["key"] = value
            self.keyImage = [
                self.owner.getKeyImage(value, False),
                self.owner.getKeyImage(value, True)
            ]
            self.invalidate_rect()
            self.owner.mapKey(value, self, oldKey)

        else:
            self.data[key] = value

        if self.active:
            self.owner.updateDrum(self)

    def substitute(self, block):
        self.setData("name", block.data["name"])
        self.setData("id", block.data["id"])

        self.img = [
            self.owner.getInstrumentImage(self.data["id"], False),
            self.owner.getInstrumentImage(self.data["id"], True)
        ]

        self.invalidate_rect(True)

        if self.active:
            self.owner.updateDrum(self)

    def testSubstitute(self, block):
        ret = Block.testSubstitute(self, block)
        if ret:
            return ret

        if block.type == Loop:
            return False

        if self.instrumentDB.instId[block.data["id"]].kit == None:
            return False

        if abs(self.x - block.x) < Block.SNAP and abs(self.y -
                                                      block.y) < Block.SNAP:
            return self

        return False

    def testMouseOver(self, event):
        ret = self.testWithinKey(event)
        if ret: return ret

        x = event.x - self.x
        y = event.y - self.y

        if 0 <= x <= self.width and 0 <= y <= self.height:
            return -1

        return False

    def testWithinKey(self, event):
        x = event.x - self.x
        y = event.y - self.y

        if Drum.KEYRECT[0] <= x <= Drum.KEYRECT[4] and Drum.KEYRECT[
                1] <= y <= Drum.KEYRECT[5]:
            return self

        return False

    def _doButtonPress(self, event):  # we were hit with a button press
        pass

    def button_release(self, event):
        if not self.dragging:
            if self.active:
                self.owner.deactivateDrum(self)
            else:
                self.owner.activateDrum(self)
        Block.button_release(self, event)

    def _doDraw(self, startX, startY, stopX, stopY, pixmap):
        x = max(startX, self.x)
        y = max(startY, self.y)
        endX = min(stopX, self.endX)
        endY = min(stopY, self.endY)
        width = endX - x
        height = endY - y

        # draw border
        if self.active: self.gc.foreground = self.owner.colors["Border_Active"]
        else: self.gc.foreground = self.owner.colors["Border_Inactive"]
        self.gc.set_clip_origin(self.x - Drum.MASK_START, self.y)
        pixmap.draw_rectangle(self.gc, True, x, y, width, height)

        # draw block
        self.gc.set_clip_origin(self.x - Drum.MASK_START, self.y - self.height)
        pixmap.draw_drawable(self.gc, self.img[self.active], x - self.x,
                             y - self.y, x, y, width, height)

        # draw key
        self.gc.set_clip_origin(self.x + Drum.KEYRECT[0] - Block.KEYMASK_START,
                                self.y + Drum.KEYRECT[1])
        pixmap.draw_drawable(self.gc, self.keyImage[self.active], 0, 0,
                             self.x + Drum.KEYRECT[0],
                             self.y + Drum.KEYRECT[1], Block.KEYSIZE,
                             Block.KEYSIZE)

    def drawHighlight(self, startX, startY, stopX, stopY, pixmap):
        self.gc.foreground = self.owner.colors["Border_Highlight"]
        self.gc.set_clip_origin(self.x - Drum.MASK_START, self.y)
        pixmap.draw_rectangle(self.gc, True, self.x, self.y, self.width,
                              self.height)

    def drawKeyHighlight(self, pixmap):
        self.gc.foreground = self.owner.colors["Border_Highlight"]
        self.gc.set_clip_origin(self.x + Drum.KEYRECT[0] - Block.KEYMASK_START,
                                self.y + Drum.KEYRECT[1] - Block.KEYSIZE)
        pixmap.draw_rectangle(self.gc, True, self.x + Drum.KEYRECT[0],
                              self.y + Drum.KEYRECT[1], Block.KEYSIZE,
                              Block.KEYSIZE)

    def regenerate(self):
        self.data["page"] = self.owner.owner._generateDrumLoop(
            self.data["id"], self.data["beats"], self.data["regularity"],
            self.data["reverb"], self.data["page"])
        if self.active:
            self.owner.updateDrum(self)

    def clear(self):
        self.owner.noteDB.deleteNotesByTrack([self.data["page"]], [0])
Example #4
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 #5
0
class Loop(Block):

    HEAD = scale(13)
    BEAT = scale(23)
    TAIL = BEAT + Block.PAD

    WIDTH = [
        HEAD + BEAT * (n - 1) + TAIL for n in range(Config.MAXIMUM_BEATS + 1)
    ]

    BEAT_MUL3 = BEAT * 3

    MASK_START = scale(200)
    MASK_BEAT = MASK_START + HEAD
    MASK_TAIL = MASK_START + HEAD + BEAT * 3

    KEYRECT = [
        HEAD + Block.PAD, Block.HEIGHT - 2 * Block.PAD - Block.KEYSIZE,
        Block.KEYSIZE, Block.KEYSIZE
    ]
    KEYRECT += [KEYRECT[0] + KEYRECT[2], KEYRECT[1] + KEYRECT[3]]

    #::: data format:
    # { "name": name, "id": pageId [, "beats": 2-12, "regularity": 0-1,
    # "key": shortcut ] }
    #:::
    def __init__(self, owner, data):
        Block.__init__(self, owner, data)

        self.type = Loop

        self.canParent = True
        self.canChild = True
        self.canSubstitute = True

        self.parentOffset = Loop.HEAD - 4

        self.data["beats"] = self.owner.noteDB.getPage(self.data["id"]).beats
        self.width = Loop.WIDTH[self.data["beats"]]

        if "regularity" not in self.data.keys():
            self.data["regularity"] = 0.8  # random.random()
        if "key" not in self.data.keys():
            self.data["key"] = None

        self.keyActive = False
        self.keyImage = [
            self.owner.getKeyImage(self.data["key"], False),
            self.owner.getKeyImage(self.data["key"], True)
        ]

        self.img = [
            self.owner.getLoopImage(self.data["id"], False),
            self.owner.getLoopImage(self.data["id"], True)
        ]

    def destroy(self):
        if self.active:
            self.owner.deactivateLoop(self)
        if self.keyActive:
            self.owner.mapKey(None, self, self.data["key"])
        self.owner.noteDB.deletePages([self.data["id"]])
        Block.destroy(self)

    def _updateWidth(self):
        self.invalidateBranch(True)

        oldWidth = self.width

        self.width = Loop.WIDTH[self.data["beats"]]
        self.endX = self.x + self.width

        if self.child:
            self.child.snapToParentLoc(self.getChildAnchor())

        if oldWidth < self.width:  # or block.child:
            self.invalidateBranch(True)

    def updateLoop(self):
        self.updateImage()
        self.invalidate_rect()

        if self.active:
            self.owner.updateLoop(self.getRoot().child)

    def updateImage(self):
        self.owner.updateLoopImage(self.data["id"])
        self.img = [
            self.owner.getLoopImage(self.data["id"], False),
            self.owner.getLoopImage(self.data["id"], True)
        ]

    def setData(self, key, value):

        if key == "beats":
            self.data["beats"] = value
            self.owner.noteDB.updatePage(self.data["id"], PARAMETER.PAGE_BEATS,
                                         value)
            self._updateWidth()
            self.updateLoop()

        elif key == "key":
            oldKey = self.data["key"]
            self.data["key"] = value
            self.keyImage = [
                self.owner.getKeyImage(value, False),
                self.owner.getKeyImage(value, True)
            ]
            self.invalidate_rect()
            if self.keyActive:
                self.owner.mapKey(value, self, oldKey)

        else:
            self.data[key] = value

    def substitute(self, block):
        self.invalidateBranch(True)

        oldWidth = self.width

        noteDB = self.owner.noteDB
        newid = noteDB.duplicatePages([block.data["id"]])[block.data["id"]]
        self.data["id"] = newid
        self.data["beats"] = noteDB.getPage(self.data["id"]).beats

        self.updateImage()
        self._updateWidth()

        if False:  # don't substitute children
            if block.child:
                c = block.child
                after = self
                while c:
                    data = {}
                    for key in c.data.keys():
                        data[key] = c.data[key]

                    newid = noteDB.duplicatePages([data["id"]])[data["id"]]
                    self.owner.updateLoopImage(newid)
                    data["id"] = newid

                    copy = Loop(self.owner, self.gc, data)
                    after.addChild(copy)
                    after = copy
                    c = c.child
            elif self.child:
                self.child.snapToParentLoc(self.getChildAnchor())

        if self.active:
            self.owner.updateLoop(self.getRoot().child)

    def testSubstitute(self, block):
        ret = Block.testSubstitute(self, block)
        if ret:
            return ret

        if block.type != Loop:
            return False

        if abs(self.x - block.x) < Block.SNAP and \
                abs(self.y - block.y) < Block.SNAP:
            return self

        return False

    def setActive(self, state):
        Block.setActive(self, state)

        if self.child:
            self.child.setActive(state)

    def addChild(self, child):
        Block.addChild(self, child)
        if self.active:
            child.setActive(True)
            self.owner.updateLoop(self.getRoot().child)

    def _addParent(self, parent):
        Block._addParent(self, parent)

        if self.parent.type == Instrument:
            self.keyActive = True
            self.owner.mapKey(self.data["key"], self)
        else:
            root = self.getRoot()
            if root.type == Instrument:
                root = root.child
            if root.getData("key") is None:
                root.setData("key", self.data["key"])
            self.setData("key", None)

    def _removeParent(self):
        if self.active:
            loopRoot = self.getRoot().child
            parent = self.parent
        else:
            loopRoot = None

        self.keyActive = False
        self.owner.mapKey(None, self, self.data["key"])

        Block._removeParent(self)

        if loopRoot == self:
            self.owner.deactivateLoop(loopRoot)
        elif loopRoot is not None:
            self.setActive(False)
            parent.child = None  # disconnect us before updating
            self.owner.updateLoop(loopRoot)

    def testMouseOver(self, event):
        ret = self.testWithinKey(event)
        if ret:
            return ret

        x = event.x - self.x
        y = event.y - self.y

        if 0 <= x <= self.width and 0 <= y <= self.height:
            return -1

        return False

    def testWithinKey(self, event):
        if not self.keyActive:
            return False

        x = event.x - self.x
        y = event.y - self.y

        if Loop.KEYRECT[0] <= x <= Loop.KEYRECT[4] and \
                Loop.KEYRECT[1] <= y <= Loop.KEYRECT[5]:
            return self

        return False

    def _doButtonPress(self, event):  # we were hit with a button press
        pass

    def button_release(self, event):
        if not self.dragging:
            if self.active:
                root = self.getRoot()
                self.owner.deactivateLoop(root.child)
            else:
                root = self.getRoot()
                # must be attached to an instrument
                if root.type == Instrument:
                    self.owner.activateLoop(root.child)
        Block.button_release(self, event)

    def _doDraw(self,
                startX,
                startY,
                stopX,
                stopY,
                ctx,
                highlight=False,
                key_highlight=False):
        x = max(startX, self.x)
        y = max(startY, self.y)

        loop = self.img[self.active]
        width = loop.get_width()
        height = loop.get_height()

        ctx.save()

        CairoUtil.draw_loop_mask(ctx, x, y, width, height)

        ctx.set_line_width(3)
        if self.active:
            ctx.set_source_rgb(
                *CairoUtil.gdk_color_to_cairo(self.owner.colors["Bg_Active"]))
        else:
            ctx.set_source_rgb(*CairoUtil.gdk_color_to_cairo(
                self.owner.colors["Bg_Inactive"]))
        ctx.fill_preserve()
        if self.active:
            ctx.set_source_rgb(*CairoUtil.gdk_color_to_cairo(
                self.owner.colors["Border_Active"]))
        else:
            ctx.set_source_rgb(*CairoUtil.gdk_color_to_cairo(
                self.owner.colors["Border_Inactive"]))

        if highlight:
            ctx.set_source_rgb(*CairoUtil.gdk_color_to_cairo(
                self.owner.colors["Border_Highlight"]))

        ctx.stroke()

        ctx.save()
        # draw block
        ctx.translate(x, y)
        ctx.set_source_surface(loop)
        ctx.paint()
        ctx.restore()

        #draw key
        if self.keyActive:
            ctx.save()
            ctx.translate(x + Loop.KEYRECT[0], y + Loop.KEYRECT[1])
            ctx.set_source_surface(self.keyImage[self.active])
            ctx.paint()
            ctx.restore()
        if key_highlight:
            ctx.save()
            ctx.set_source_rgb(*CairoUtil.gdk_color_to_cairo(
                self.owner.colors["Border_Highlight"]))
            ctx.translate(x + Loop.KEYRECT[0] - 1, y + Loop.KEYRECT[1] - 1)
            CairoUtil.draw_round_rect(ctx,
                                      0,
                                      0,
                                      Block.KEYSIZE + 2,
                                      Block.KEYSIZE + 2,
                                      radio=5)
            ctx.stroke()
            ctx.restore()

        ctx.restore()

    def drawHighlight(self, startX, startY, stopX, stopY, ctx):
        self._doDraw(startX, startY, stopX, stopY, ctx, highlight=True)

    def drawKeyHighlight(self, ctx):
        self._doDraw(self.x, self.y, self.x, self.y, ctx, key_highlight=True)

    def clear(self):
        self.owner.noteDB.deleteNotesByTrack([self.data["id"]], [0])

        self.updateImage()

        self.invalidate_rect()

        if self.active:
            self.owner.updateLoop(self.getRoot().child)
Example #6
0
class Drum(Block):

    MASK_START = scale(100)

    KEYRECT = [
        Block.PAD - 1, Block.HEIGHT + 1 - Block.PAD - Block.KEYSIZE,
        Block.KEYSIZE, Block.KEYSIZE
    ]
    KEYRECT += [KEYRECT[0] + KEYRECT[2], KEYRECT[1] + KEYRECT[3]]

    #::: data format:
    # { "name": name, "id": instrumentId [ , "page": pageId, "volume": 0-1,
    # "reverb": 0-1, "beats": 2-12, "regularity": 0-1, "key": shortcut ] }
    #:::
    def __init__(self, owner, data):
        Block.__init__(self, owner, data)
        self.instrumentDB = InstrumentDB.getRef()
        self.type = Drum

        self.canSubstitute = True

        if not "page" in self.data.keys():
            self.data["page"] = -1
        if not "volume" in self.data.keys():
            self.data["volume"] = 0.5
        if not "reverb" in self.data.keys():
            self.data["reverb"] = 0.0
        if not "beats" in self.data.keys():
            self.data["beats"] = 4  # random.randint(2, 12)
        if not "regularity" in self.data.keys():
            self.data["regularity"] = 0.8  # random.random()
        if "key" not in self.data.keys():
            self.data["key"] = None

        self.owner.mapKey(self.data["key"], self)
        self.keyImage = [
            self.owner.getKeyImage(self.data["key"], False),
            self.owner.getKeyImage(self.data["key"], True)
        ]

        self.img = [
            self.owner.getInstrumentImage(self.data["id"], False),
            self.owner.getInstrumentImage(self.data["id"], True)
        ]

        if self.data["page"] == -1:
            self.regenerate()

    def destroy(self):
        self.owner.mapKey(None, self, self.data["key"])
        self.owner.noteDB.deletePages([self.data["page"]])
        Block.destroy(self)

    def setData(self, key, value):
        if key == "beats":
            self.data["beats"] = value
            self.owner.noteDB.updatePage(self.data["page"],
                                         PARAMETER.PAGE_BEATS, value)

        elif key == "key":
            oldKey = self.data["key"]
            self.data["key"] = value
            self.keyImage = [
                self.owner.getKeyImage(value, False),
                self.owner.getKeyImage(value, True)
            ]
            self.invalidate_rect()
            self.owner.mapKey(value, self, oldKey)

        else:
            self.data[key] = value

        if self.active:
            self.owner.updateDrum(self)

    def substitute(self, block):
        self.setData("name", block.data["name"])
        self.setData("id", block.data["id"])

        self.img = [
            self.owner.getInstrumentImage(self.data["id"], False),
            self.owner.getInstrumentImage(self.data["id"], True)
        ]

        self.invalidate_rect(True)

        if self.active:
            self.owner.updateDrum(self)

    def testSubstitute(self, block):
        ret = Block.testSubstitute(self, block)
        if ret:
            return ret

        if block.type == Loop:
            return False

        if self.instrumentDB.instId[block.data["id"]].kit is None:
            return False

        if abs(self.x - block.x) < Block.SNAP and \
                abs(self.y - block.y) < Block.SNAP:
            return self

        return False

    def testMouseOver(self, event):
        ret = self.testWithinKey(event)
        if ret:
            return ret

        x = event.x - self.x
        y = event.y - self.y

        if 0 <= x <= self.width and 0 <= y <= self.height:
            return -1

        return False

    def testWithinKey(self, event):
        x = event.x - self.x
        y = event.y - self.y

        if Drum.KEYRECT[0] <= x <= Drum.KEYRECT[4] and \
                Drum.KEYRECT[1] <= y <= Drum.KEYRECT[5]:
            return self

        return False

    def _doButtonPress(self, event):  # we were hit with a button press
        pass

    def button_release(self, event):
        if not self.dragging:
            if self.active:
                self.owner.deactivateDrum(self)
            else:
                self.owner.activateDrum(self)
        Block.button_release(self, event)

    def _doDraw(self,
                startX,
                startY,
                stopX,
                stopY,
                ctx,
                highlight=False,
                key_highlight=False):
        x = max(startX, self.x)
        y = max(startY, self.y)
        endX = min(stopX, self.endX)
        endY = min(stopY, self.endY)
        width = endX - x
        height = endY - y
        ctx.save()
        # draw border
        CairoUtil.draw_drum_mask(ctx, x, y, width)

        ctx.set_line_width(3)
        if self.active:
            ctx.set_source_rgb(
                *CairoUtil.gdk_color_to_cairo(self.owner.colors["Bg_Active"]))
        else:
            ctx.set_source_rgb(*CairoUtil.gdk_color_to_cairo(
                self.owner.colors["Bg_Inactive"]))
        ctx.fill_preserve()
        if self.active:
            ctx.set_source_rgb(*CairoUtil.gdk_color_to_cairo(
                self.owner.colors["Border_Active"]))
        else:
            ctx.set_source_rgb(*CairoUtil.gdk_color_to_cairo(
                self.owner.colors["Border_Inactive"]))
        if highlight:
            ctx.set_source_rgb(*CairoUtil.gdk_color_to_cairo(
                self.owner.colors["Border_Highlight"]))

        ctx.stroke()

        # draw block
        ctx.save()
        ctx.translate(x, y)
        ctx.set_source_surface(self.img[self.active])
        ctx.paint()
        ctx.restore()

        # draw key
        ctx.save()
        ctx.translate(self.x + Drum.KEYRECT[0], self.y + Drum.KEYRECT[1])
        ctx.set_source_surface(self.keyImage[self.active])
        ctx.paint()
        ctx.restore()
        ctx.restore()

        if key_highlight:
            ctx.save()
            ctx.set_source_rgb(*CairoUtil.gdk_color_to_cairo(
                self.owner.colors["Border_Highlight"]))
            ctx.translate(x + Drum.KEYRECT[0] - 1, y + Drum.KEYRECT[1] - 1)
            CairoUtil.draw_round_rect(ctx,
                                      0,
                                      0,
                                      Block.KEYSIZE + 2,
                                      Block.KEYSIZE + 2,
                                      radio=5)
            ctx.stroke()
            ctx.restore()

    def drawHighlight(self, startX, startY, stopX, stopY, ctx):
        self._doDraw(startX, startY, stopX, stopY, ctx, highlight=True)

    def drawKeyHighlight(self, ctx):
        self._doDraw(self.x, self.y, self.x, self.y, ctx, key_highlight=True)

    def regenerate(self):
        self.data["page"] = self.owner.owner._generateDrumLoop(
            self.data["id"], self.data["beats"], self.data["regularity"],
            self.data["reverb"], self.data["page"])
        if self.active:
            self.owner.updateDrum(self)

    def clear(self):
        self.owner.noteDB.deleteNotesByTrack([self.data["page"]], [0])
Example #7
0
class SynthLabConstants:

    PIC_SIZE = scale(80)
    HALF_SIZE = PIC_SIZE // 2
    PIC_SIZE_HIGHLIGHT = PIC_SIZE + 4
    HALF_SIZE_HIGHLIGHT = PIC_SIZE + 2

    GT_CONTROL_OUTPUT = 0
    GT_CONTROL_INPUT = 1
    GT_SOUND_OUTPUT = 2
    GT_SOUND_INPUT = 3

    # GATE_POINT[objecttype][gatetype][gatenum] = (x,y)
    # relative to object center
    GATE_POINT = [
            [
                [(scale(-1), scale(33))],
                ],
            [
                [],
                [(scale(-24), scale(-34)), (scale(-9), scale(-34)),
                    (scale(8), scale(-34)), (scale(24), scale(-34))],
                [(scale(-1), scale(33))],
                ],
            [
                [],
                [(scale(31), scale(-20)), (scale(31), scale(-6)),
                    (scale(31), scale(6)), (scale(31), scale(19))],
                [(scale(-3), scale(33))],
                [(scale(-3), scale(-34))],
                ],
            [
                [],
                [],
                [],
                [(scale(2), scale(-35))],
                ],
            ]

    # GATE_MAP[objecttype][gatetype][gatenum] = [sx, sy, ex, ey, (wireX,wireY)]
    # gate locations relative to object center
    GATE_MAP = [
            [
                [[scale(-7), scale(26), scale(4), scale(39)]],
                ],
            [
                [],
                [[scale(-30), scale(-40), scale(-19), scale(-28)],
                    [scale(-15), scale(-40), scale(-3), scale(-28)],
                    [scale(3), scale(-40), scale(14), scale(-28)],
                    [scale(19), scale(-40), scale(28), scale(-28)],
                    ],
                [[scale(-6), scale(28), scale(5), scale(40)]],
                ],
            [
                [],
                [[scale(25), scale(-25), scale(37), scale(-14)],
                    [scale(25), scale(-12), scale(37), scale(-1)],
                    [scale(25), scale(1), scale(37), scale(12)],
                    [scale(25), scale(13), scale(37), scale(25)],
                    ],
                [[scale(-8), scale(27), scale(3), scale(40)]],
                [[scale(-8), scale(-40), scale(3), scale(-27)]],
                ],
            [
                [],
                [],
                [],
                [[scale(-4), scale(-40), scale(7), scale(-29)]],
                ],
            ]

    # insert wire locations into map
    GATE_OFFSET = scale(15)
    for oT in GATE_MAP:
        for gT in oT:
            for m in gT:
                x = (m[2]+m[0])//2
                y = (m[3]+m[1])//2
                # snap to edges
                if x < -HALF_SIZE+GATE_OFFSET: x = m[0]
                elif x > HALF_SIZE-GATE_OFFSET: x = m[2]
                if y < -HALF_SIZE+GATE_OFFSET: y = m[1]
                elif y > HALF_SIZE-GATE_OFFSET: y = m[3]
                m.append( ( x, y ) )

    OBJ_Y_LOC = scale(710)
    INIT_LOCATIONS = [
            [scale(450), OBJ_Y_LOC], [scale(450), OBJ_Y_LOC],
            [scale(450), OBJ_Y_LOC], [scale(450), OBJ_Y_LOC],
            [scale(225), OBJ_Y_LOC], [scale(225), OBJ_Y_LOC],
            [scale(225), OBJ_Y_LOC], [scale(225), OBJ_Y_LOC],
            [scale(675), OBJ_Y_LOC], [scale(675) ,OBJ_Y_LOC],
            [scale(675), OBJ_Y_LOC], [scale(675), OBJ_Y_LOC],
            [scale(450), scale(625)],
            ]

    FLOAT1 = [.1, 1]
    FLOAT = [.01, 2]
    INTEGER = [1, 0]

    # s1 s2 s3 s4 s1min s1max s2min s2max s3min s3max s4min s4max [s1step s1digits] [s2step s2digits] [s3step s3digits]
    TYPES = {   'lfo': [.1, 5, 0, 1, 0, 1, 0, 50, 0, 5, 0, 1,  FLOAT, FLOAT, INTEGER],
                'rand': [.5, 1.5, 2, 0, 0, 2, 0, 2, 0, 50, 0, 1, FLOAT, FLOAT, FLOAT],
                'adsr': [.02, .05, .8, .1, 0, 1, 0, 1, 0, 1, 0, 1, FLOAT, FLOAT, FLOAT],
                'trackpadX': [0, 1, 0, 0, -1, 1, 0, 2, 0, 1, 0, 1, FLOAT, FLOAT, INTEGER],
                'trackpadY': [0, 1, 0, 0, -1, 1, 0, 2, 0, 1, 0, 1, FLOAT, FLOAT, INTEGER],
                'fm': [1, .5, 5, 1, 0, 2, 0, 2, 0, 10, 0, 2, FLOAT, FLOAT, FLOAT],
                'buzz': [1, 30, .85, 1, 0, 2, 0, 40, 0, 1, 0, 2, FLOAT, INTEGER, FLOAT],
                'vco': [1, 1, .2, 1, 0, 2, 0, 2, 0, .5, 0, 2, FLOAT, INTEGER, FLOAT],
                'pluck': [1, 5000, 0, 1, 0, 2, 100, 8000, 0, 8, 0, 2, FLOAT, INTEGER, FLOAT],
                'noise': [0, 3000, 4000, 1, 0, 2, 0, 8000, 0, 6000, 0, 2, INTEGER, INTEGER,INTEGER],
                'sample': [1, 15, 5000, 1, 0, 2, 0, 99, 100, 8000, 0, 2, FLOAT, INTEGER, INTEGER],
                'voice': [1, 3, 5, 1, 0, 2, 0, 8, 1, 10, 0, 2, FLOAT, INTEGER, FLOAT],
                'grain': [1, 15, 1, 1, 0, 2, 0, 99, 0, 1, 0, 2, FLOAT, INTEGER, FLOAT],
                'addSynth': [1, .005, 5, 1, 0, 2, 0, 20, 0, 9, 0, 2, FLOAT, FLOAT, INTEGER],
                'mic': [5, 1, 1, 1, 1, 10, 1, 4, 0, 4, 0, 2, FLOAT, INTEGER, FLOAT],
                'wguide': [100, 3000, .8, 1, 0, 200, 100, 5000, 0, 1, 0, 2, FLOAT1, INTEGER, FLOAT],
                'distort': [800, .7, .7, 1, 0, 1000, 0, 1, 0, 1, 0, 2, INTEGER, FLOAT, FLOAT],
                'filter': [1000, .6, 0, 1, 200, 5000, 0, 1, 0, 2, 0, 2, INTEGER, FLOAT, INTEGER],
                'ring': [500, 1, 0, 1, 0, 1000, 0, 1, 0, 5, 0, 2, INTEGER, FLOAT, INTEGER],
                'reverb': [1.5, 3000, .5, 1, 0, 4, 100, 7000, 0, 1, 0, 2, FLOAT, INTEGER, FLOAT],
                'harmon': [1.25, .04, .5, 1, 0, 2, 0, 1, 0, 1, 0, 2, FLOAT, FLOAT, FLOAT],
                'eq4band': [1., 1., 1., 1., 0, 2, 0, 2, 0, 2, 0, 2, FLOAT, FLOAT, FLOAT],
                'chorus': [.5, 1., 5., .5, 0, 3, 0, 10, 0, 30, 0, 1, FLOAT, FLOAT, FLOAT]}

    CONTROL_TYPES = ['lfo', 'rand', 'adsr', 'trackpadX', 'trackpadY']
    CONTROL_TYPES_PLUS = [type + '+' for type in CONTROL_TYPES]
    SOURCE_TYPES = ['fm', 'buzz', 'vco', 'pluck', 'noise', 'sample', 'voice', 'grain', 'addSynth']
    if Config.FEATURES_MIC: SOURCE_TYPES += ['mic']
    SOURCE_TYPES_PLUS = [type + '+' for type in SOURCE_TYPES]
    FX_TYPES = ['wguide', 'distort','filter', 'ring', 'reverb', 'harmon', 'eq4band', 'chorus']
    FX_TYPES_PLUS = [type + '+' for type in FX_TYPES]
    OUTPUT_TYPE = ['adsr']
    OUTPUT_TYPE_SEL = ['adsrsel']
    CHOOSE_TYPE = [CONTROL_TYPES, SOURCE_TYPES, FX_TYPES, OUTPUT_TYPE]
    CHOOSE_TYPE_PLUS = [CONTROL_TYPES_PLUS, SOURCE_TYPES_PLUS, FX_TYPES_PLUS]

# SynthLab Tooltips
    SOURCE = _('Source')
    EFFECT = _('Effect')
    CONTROL = _('Control')
    SOUNDOUT = _('Sound Output')

    #Controls
    # TRANS: http://en.wikipedia.org/wiki/Low_frequency_oscillator
    LFO = _('LFO')
    AMP = _('Amplitude')
    FREQ = _('Frequency')
    WAVEFORM = _('Waveform')
    LFO_WAVEFORMS = [_('Sine'), _('Triangle'), _('Bi-Square'), _('Uni-Square'), _('Sawtooth'), _('Sawtooth-down')]
    OFFSET = _('Offset')
    LFO_INFO = _('A low frequency oscillation (LFO) is an inaudible, pulsing wave used to change another sound.')
    LFO_PARA1 = _('The volume of the LFO wave. More volume means more effect.')
    LFO_PARA2 = _('The speed of the wave.')
    LFO_PARA3 = _('The type of wave that will be used for the LFO.')
    LFO_PARA4 = _('The value added to the amplitude of the LFO.')

    RANDOM = _('Random')
    MIN = _('Minimum')
    MAX = _('Maximum')
    FREQ = FREQ
    SEED = _('Seed')
    RANDOM_INFO = _('A sequence of numbers without repetition chosen by the computer.')
    RANDOM_PARA1 = _('The smallest number allowed')
    RANDOM_PARA2 = _('The biggest number allowed.')
    RANDOM_PARA3 = _('The speed of the sequence.')
    RANDOM_PARA4 = _('The number to initialize the number generator')

    ADSR = _('Envelope')
    ATTACK = _('Attack')
    DECAY = _('Decay')
    SUSTAIN = _('Sustain')
    RELEASE = _('Release')
    ADSR_INFO = _("An ADSR envelope is the shape of the sound's volume over time.")
    ADSR_PARA1 = _('(A) how quickly the sound reaches full volume.')
    ADSR_PARA2 = _('(D) how quickly the sound drops after the attack.')
    ADSR_PARA3= _('(S) the volume of the sound until the note is released.')
    ADSR_PARA4 = _('(R) how quickly the sound goes away.')

    TRACKPADX = _('Trackpad X')
    MIN = MIN
    MAX = MAX
    SCALING = _('Scaling')
    SCALING_TYPES =    [_('Linear'), _('Logarithmic')]
    POLL = _('Poll time')
    TRACKPADX_INFO = _('The trackpad can be used to modify the sound. This is from left to right.')
    TRACKPADX_PARA1 = _('The minimum value the trackpad will send.')
    TRACKPADX_PARA2 = _('The maximum value the trackpad will send.')
    TRACKPADX_PARA3 = _('The shape of the value repartition. In a straight line (linear) or a curved line (logarithmic).')
    TRACKPADX_PARA4= _('The time interval between each event coming from the trackpad.')

    TRACKPADY = _('Trackpad Y')
    MIN = MIN
    MAX = MAX
    SCALING = SCALING
    SCALING_TYPES = SCALING_TYPES
    POLL = POLL
    TRACKPADY_INFO = _('The trackpad can be used to modify the sound. This is from top to bottom.')
    TRACKPADY_PARA1 = _('The minimum value the trackpad will send.')
    TRACKPADY_PARA2 = _('The maximum value the trackpad will send.')
    TRACKPADY_PARA3 = _('The shape of the value repartition. In a straight line (linear) or a curved line (logarithmic).')
    TRACKPADY_PARA4 = _('The time interval between each event coming from the trackpad.')

    #Source
    # TRANS: http://en.wikipedia.org/wiki/Frequency_modulation_synthesis
    FM = _('FM')
    # TRANS: http://en.wikipedia.org/wiki/Carrier_wave
    CAR = _('Carrier Frequency')
    MOD = _('Modulator Frequency')
    INDEX = _('Index')
    GAIN = _('Gain')
    FM_INFO = _('Frequency modulation synthesis (FM) creates an electronic sound by combining the frequency of two waves (the carrier and the modulator).')
    FM_PARA1 = _('The main wave frequency.')
    FM_PARA2 = _('The frequency of the wave that will modify the Carrier wave.')
    FM_PARA3 = _('The variation in frequency of the Carrier wave.')
    FM_PARA4 = _('The volume of the sound.')

    BUZZ = _('Buzz')
    FREQ = FREQ
    NHARM = _('Number of harmonics')
    FSLOPE = _('Filter Slope')
    GAIN = GAIN
    BUZZ_INFO = _('A buzz is a very bright sound with many harmonics.')
    BUZZ_PARA1 = _('The pitch of the buzz sound.')
    BUZZ_PARA2 = _('The harmonic thickness of the sound.')
    BUZZ_PARA3 = _('The brightness of the sound.')
    BUZZ_PARA4 = _('The volume of the sound.')

    # TRANS: http://en.wikipedia.org/wiki/Voltage-controlled_oscillator
    VCO = _('VCO')
    FREQ = FREQ
    WAVEFORM = WAVEFORM
    VCO_WAVEFORMS = [_('Sawtooth'), _('Square'), _('Triangle')]
    FSLOPE = FSLOPE
    GAIN = GAIN
    VCO_INFO = _('A voltage-controlled oscillator (VCO) creates an electronic sound by combining the shape of two waves.')
    VCO_PARA1 = _('The wave that will be modified by the VCO.')
    VCO_PARA2 = _("The shape of the VCO's wave.")
    VCO_PARA3 = _('The brightness of the sound.')
    VCO_PARA4 = _('The volume of the sound.')

    PLUCK = _('Pluck')
    FREQ = FREQ
    LFILTER = Tooltips.PROP['filterTypeLowButton']
    # TRANS: http://en.wikipedia.org/wiki/Vibrato
    VIBRATO = _('Vibrato')
    GAIN = GAIN
    PLUCK_INFO = _('An electronic string instrument (like a guitar).')
    PLUCK_PARA1 = _('The pitch of the instrument.')
    PLUCK_PARA2 = _('The brightness of the sound.')
    PLUCK_PARA3 = _('The speed of the wave.')
    PLUCK_PARA4 = _('The volume of the sound.')

    NOISE = _('Noise')
    NOISETYPE = _('Type')
    NOISE_TYPES = [_('White'), _('Pink'), _('Gauss')]
    FREQ = FREQ
    BANDWITH = _('Bandwidth')
    GAIN = GAIN
    NOISE_INFO = _('Noise is a sound with energy on every frequency.')
    NOISE_PARA1 = _('The shape of noise to be used (white = bright, pink = dark, gauss = colored).')
    NOISE_PARA2 = _('The brightness of the sound.')
    NOISE_PARA3 = _('The thickness of the sound.')
    NOISE_PARA4 = _('The volume of the sound.')

    SAMPLE = _('Sound Sample')
    FREQ = FREQ
    SAMPLEN = _('Sample Number')
    SAMPLE_NAMES = _('Sample name')
    LFILTER = LFILTER
    GAIN = GAIN
    SAMPLE_INFO = _("A sample is a real sound that has been recorded and that can be played back.")
    SAMPLE_PARA1 = _('The pitch of the sample.')
    SAMPLE_PARA2 = _('The sample to be used.')
    SAMPLE_PARA3 = _('The brightness of the sound.')
    SAMPLE_PARA4 = _('The volume of the sound.')

    VOICE = _('Voice')
    FREQ = FREQ
    VOWEL = _('Vowel')
    VOWEL_TYPES = ['i', 'e', 'ee', 'a', 'u', 'o1', 'o2', 'oa', 'oe']
    VIBRATO = VIBRATO
    GAIN = GAIN
    VOICE_INFO = _('An electronic voice.')
    VOICE_PARA1 = _('The pitch of the sound.')
    VOICE_PARA2 = _('The shape of the sound based on vowels.')
    VOICE_PARA3 = _('The speed of the wave.')
    VOICE_PARA4 = _('The volume of the sound.')

    GRAIN = _('Grain')
    FREQ = FREQ
    SAMPLEN = SAMPLEN
    INDEX = _('Index')
    GAIN = GAIN
    GRAIN_INFO = _('The grain effect splits the sound in tiny bits which can be rearranged differently.')
    GRAIN_PARA1 = _('The pitch of grains.')
    GRAIN_PARA2 = _('The sample to be used.')
    GRAIN_PARA3 = _('The variation in pitch of grains.')
    GRAIN_PARA4 = _('The volume of the sound.')

    ADDSYNTH = _('Additive Synthesis')
    FREQ = FREQ
    SPREAD = _('Spread')
    WAVE = _('Waveform')
    GAIN = GAIN
    ADDSYNTH_INFO = _('Additive synthesis creates musical timbre by combining different waves.')
    ADDSYNTH_PARA1 = _('The pitch of the sound.')
    ADDSYNTH_PARA2 = _('The separation between the different waves.')
    ADDSYNTH_PARA3 = _('The shape of the wave.')
    ADDSYNTH_PARA4 = _('The volume of the sound.')

    MIC = _('Microphone input')
    DURATION = _('Length of the memory')
    BIN = _('memory number')
    SPEED = _('Playback speed')
    GAIN = GAIN
    MIC_INFO = _('Microphone input is record into a buffer for playback (right-click on the object to record sound)')
    MIC_PARA1 = _('Length of the memory')
    MIC_PARA2 = _('This parameter can not be modified')
    MIC_PARA3 = _('Speed playback changes duration and pitch of the sound')
    MIC_PARA4 = _('The volume of the sound.')

    #Effects
    DELAY = _('Delay')
    FREQ = FREQ
    LFILTER = LFILTER
    FEEDBACK = _('Feedback')
    GAIN = GAIN
    DELAY_INFO = _('Delay is an audio effect that repeats the sound over and over.')
    DELAY_PARA1 = _('The speed of the delay.')
    DELAY_PARA2 = _('The brightness of the sound.')
    DELAY_PARA3 = _('The time it takes for the sound to go away.')
    DELAY_PARA4 = _('The volume of the sound.')

    DIST = _('Distortion')
    FREQ = FREQ
    RESON = _('Resonance')
    DISTL = _('Distortion Level')
    GAIN = GAIN
    DIST_INFO = _("Distortion is the deformation of a wave which creates harsh sounds.")
    DIST_PARA1 = _('The pitch of the distorted sound.')
    DIST_PARA2 = _('The amount of vibration the instrument has against itself.')
    DIST_PARA3 = _('The volume of the distorted sound.')
    DIST_PARA4 = _('The volume of the sound.')

    FILTER = _('Filter')
    FREQ = FREQ
    FSLOPE = FSLOPE
    FTYPE = _('Type')
    FILTER_TYPES = [_('Lowpass'), _('Highpass'), _('Bandpass')]
    GAIN = GAIN
    FILTER_INFO = _('An audio filter is designed to brighten, darken or color a sound.')
    FILTER_PARA1 = _('The point in the sound to be filtered.')
    FILTER_PARA2 = _('The size of the region affected by the filter.')
    FILTER_PARA3 = _('The type of filter used: lowpass = dark, highpass = bright, bandpass = colored.')
    FILTER_PARA4 = _('The volume of the sound.')

    # TRANS: http://en.wikipedia.org/wiki/Ring_modulation
    RINGMOD = _('Ring Modulator')
    FREQ = FREQ
    MIX = _('Mix')
    WAVEFORM = WAVEFORM
    LFO_WAVEFORMS = LFO_WAVEFORMS
    GAIN = GAIN
    RINGMOD_INFO = _('Ring modulation is an audio effect that creates metallic sounds.')
    RINGMOD_PARA1 = _('The pitch of the ring modulator.')
    RINGMOD_PARA2 = _('The volume of the modulated sound.')
    RINGMOD_PARA3 = _('The shape of the wave used for modulation.')
    RINGMOD_PARA4 = _('The volume of the sound.')

    REVERB = _('Reverb')
    REVERBD = _('Length')
    REVERBF = Tooltips.PROP['filterTypeLowButton']
    REVERBL = _('Reverb Level')
    GAIN = GAIN
    REVERB_INFO = _('Reverberation is the length a sound stays in a room.')
    REVERB_PARA1 = _('The size of the room.')
    REVERB_PARA2 = _('The brightness of the reverberated sound.')
    REVERB_PARA3 = _('The amount of reverb to be applied.')
    REVERB_PARA4 = _('The volume of the sound.')

    HARMON = _('Harmonizer')
    FREQ = FREQ
    DRYDELAY = _('Dry delay')
    MIX = MIX
    GAIN = GAIN
    HARMON_INFO = _('A harmonizer doubles the sound musically.')
    HARMON_PARA1 = _('The pitch of the doubled sound.')
    HARMON_PARA2 = _('The start time of the original sound.')
    HARMON_PARA3 = _('The balance between the original and the doubled sound.')
    HARMON_PARA4 = _('The volume of the sound.')

    EQ4BAND = _('Equalizer 4 bands')
    FREQ1 = _('Band one gain')
    FREQ2 = _('Band two gain')
    FREQ3 = _('Band three gain')
    FREQ4 = _('Band four gain')
    EQ4BAND_INFO = _('A 4 band equalizer chooses slices (bands) in the sound and makes them louder or softer.')
    EQ4BAND_PARA1 = _('The volume of band 1 (low).')
    EQ4BAND_PARA2 = _('The volume of band 2 (mid-low).')
    EQ4BAND_PARA3 = _('The volume of band 3 (mid-high).')
    EQ4BAND_PARA4 = _('The volume of band 4 (high).')

    # TRANS: http://en.wikipedia.org/wiki/Chorus_effect
    CHORUS = _('Chorus')
    LFODEPTH = _('LFO Depth')
    LFOFREQ = _('LFO Frequency')
    DELAY = _('Delay')
    FEEDBACK = FEEDBACK
    CHORUS_INFO = _('The chorus effect plays copies of the same sound with a slight variation.')
    CHORUS_PARA1 = _('The volume of the LFO signal.')
    CHORUS_PARA2 = _('The pitch of the LFO signal.')
    CHORUS_PARA3 = _('The amount of delay between the two signals.')
    CHORUS_PARA4 = _('The volume of the sound.')

    SYNTHTYPES = [[LFO, RANDOM, ADSR, TRACKPADX, TRACKPADY],
                    [FM, BUZZ, VCO, PLUCK, NOISE, SAMPLE, VOICE, GRAIN, ADDSYNTH, MIC],
                    [DELAY, DIST, FILTER, RINGMOD, REVERB, HARMON, EQ4BAND, CHORUS], [ADSR]]

    SYNTHPARA = {	'lfo': [AMP, FREQ, WAVEFORM, OFFSET, LFO_INFO, LFO_PARA1, LFO_PARA2, LFO_PARA3, LFO_PARA4],
                    'rand': [MIN, MAX, FREQ, SEED, RANDOM_INFO, RANDOM_PARA1, RANDOM_PARA2, RANDOM_PARA3, RANDOM_PARA4],
                    'adsr': [ATTACK, DECAY, SUSTAIN, RELEASE, ADSR_INFO, ADSR_PARA1, ADSR_PARA2, ADSR_PARA3, ADSR_PARA4],
                    'trackpadX': [MIN, MAX, SCALING, POLL, TRACKPADX_INFO, TRACKPADX_PARA1, TRACKPADX_PARA2, TRACKPADX_PARA3, TRACKPADX_PARA4],
                    'trackpadY': [MIN, MAX, SCALING, POLL, TRACKPADY_INFO, TRACKPADY_PARA1, TRACKPADY_PARA2, TRACKPADY_PARA3, TRACKPADY_PARA4],
                    'fm': [CAR, MOD, INDEX, GAIN, FM_INFO, FM_PARA1, FM_PARA2, FM_PARA3, FM_PARA4],
                    'buzz': [FREQ, NHARM, FSLOPE, GAIN, BUZZ_INFO, BUZZ_PARA1, BUZZ_PARA2, BUZZ_PARA3, BUZZ_PARA4],
                    'vco': [FREQ, WAVEFORM, FSLOPE, GAIN, VCO_INFO, VCO_PARA1, VCO_PARA2, VCO_PARA3, VCO_PARA4],
                    'pluck': [FREQ, LFILTER, VIBRATO, GAIN, PLUCK_INFO, PLUCK_PARA1, PLUCK_PARA2, PLUCK_PARA3, PLUCK_PARA4],
                    'noise': [NOISETYPE, FREQ, BANDWITH, GAIN, NOISE_INFO, NOISE_PARA1, NOISE_PARA2, NOISE_PARA3, NOISE_PARA4],
                    'sample': [FREQ, SAMPLEN, LFILTER, GAIN, SAMPLE_INFO, SAMPLE_PARA1, SAMPLE_PARA2, SAMPLE_PARA3, SAMPLE_PARA4],
                    'voice': [FREQ, VOWEL, VIBRATO, GAIN, VOICE_INFO, VOICE_PARA1, VOICE_PARA2, VOICE_PARA3, VOICE_PARA4],
                    'grain': [FREQ, SAMPLEN, INDEX, GAIN, GRAIN_INFO, GRAIN_PARA1, GRAIN_PARA2, GRAIN_PARA3, GRAIN_PARA4],
                    'addSynth': [FREQ, SPREAD, WAVE, GAIN, ADDSYNTH_INFO, ADDSYNTH_PARA1, ADDSYNTH_PARA2, ADDSYNTH_PARA3, ADDSYNTH_PARA4],
                    'mic': [DURATION, BIN, SPEED, GAIN, MIC_INFO, MIC_PARA1, MIC_PARA2, MIC_PARA3, MIC_PARA4],
                    'wguide': [FREQ, LFILTER, FEEDBACK, GAIN, DELAY_INFO, DELAY_PARA1, DELAY_PARA2, DELAY_PARA3, DELAY_PARA4],
                    'distort': [FREQ, RESON, DISTL, GAIN, DIST_INFO, DIST_PARA1, DIST_PARA2, DIST_PARA3, DIST_PARA4],
                    'filter': [FREQ, FSLOPE, FTYPE, GAIN, FILTER_INFO, FILTER_PARA1, FILTER_PARA2, FILTER_PARA3, FILTER_PARA4],
                    'ring': [FREQ, MIX, WAVEFORM, GAIN, RINGMOD_INFO, RINGMOD_PARA1, RINGMOD_PARA2, RINGMOD_PARA3, RINGMOD_PARA4],
                    'reverb': [REVERBD, REVERBF, REVERBL, GAIN, REVERB_INFO, REVERB_PARA1, REVERB_PARA2, REVERB_PARA3, REVERB_PARA4],
                    'harmon': [FREQ, DRYDELAY, MIX, GAIN, HARMON_INFO, HARMON_PARA1, HARMON_PARA2, HARMON_PARA3, HARMON_PARA4],
                    'eq4band': [FREQ1, FREQ2, FREQ3, FREQ4, EQ4BAND_INFO, EQ4BAND_PARA1, EQ4BAND_PARA2, EQ4BAND_PARA3, EQ4BAND_PARA4],
                    'chorus': [LFODEPTH, LFOFREQ, DELAY, FEEDBACK, CHORUS_INFO, CHORUS_PARA1, CHORUS_PARA2, CHORUS_PARA3, CHORUS_PARA4]}
Example #8
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)