You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
6.0 KiB

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
Copyright 2022, Nils Hilbricht, Germany ( )
This file is part of the Laborejo Software Suite ( ),
Laborejo2 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <>.
import logging; logger = logging.getLogger(__name__);"import")
#Standard Library Modules
#Third Party Modules
#Template Modules
import template.engine.pitch as pitch
import template.engine.duration as duration
#Our modules
class Cursor:
"""There is only one cursor in the program.
Each track has its own state with its own gap, tickindex etc.
but the cursor is unique.
Used for pitch and states which apply to all tracks, like
"insert next note with dot?"
The cursor moves 'on stafflines' not through the keysig. Adaption to the keysig is done
by the api like in insertCursorCode or insertKeysignature
def __init__(self, score):
self.score = score
self.pitchindex = pitch.ly2pitch["c'"]
self.prevailingBaseDuration = duration.D4
self.persistentPrevailingDot = False #For the sake of simplicity this is only a bool. No "two prevailing dots"
self.oneTimePrevailingDotInverter = False #effects persistentPrevailingDot
self.cursorWasMovedAfterChoosingPitchViaMidi = False #this flag enables the api to check if the cursor was set with cursor commands (=arrow keys) after it was set by midi. If not the api can insert the literal midi pitch
def up(self):
"""Move the cursor/pitchindex up, +50.
The cursor always goes up and down the c major scale.
Call with Cursor.up(). Class method """
self.pitchindex += pitch.STEP
def down(self):
"""Move the cursor/pitchindex down, -50.
The cursor always goes up and down the c major scale.
Call with Cursor.down(). Class method"""
self.pitchindex -= pitch.STEP
def upOctave(self):
"""Move the cursor/pitchindex up, +350.
Call with Cursor.upOctave(). Class method"""
self.pitchindex += pitch.OCTAVE
def downOctave(self):
"""Move the cursor/pitchindex down, -350.
Call with Cursor.downOctave(). Class method"""
self.pitchindex -= pitch.OCTAVE
def correctPitchindex(self):
self.cursorWasMovedAfterChoosingPitchViaMidi = True
if self.pitchindex >= pitch.MAX:
self.pitchindex = pitch.MAX
elif self.pitchindex <= pitch.MIN:
self.pitchindex = pitch.MIN
self.pitchindex = pitch.toWhite[self.pitchindex]
def getPrevailingDot(self):
"""0 or 1.
Also toggles oneTimePrevailingDotInverter"""
if self.persistentPrevailingDot:
if self.oneTimePrevailingDotInverter:
self.oneTimePrevailingDotInverter = not self.oneTimePrevailingDotInverter
return 0
return 1
if self.oneTimePrevailingDotInverter:
self.oneTimePrevailingDotInverter = not self.oneTimePrevailingDotInverter
return 1
return 0
def distanceInDiatonicStepsFromTrebleB(self):
return pitch.distanceInDiatonicSteps(pitch.ly2pitch["b'"], self.pitchindex) #offset from the middle line in treble clef h', which is 0. c'' is -1, a' is +1
def asDotOnLine(self, clef):
return self.distanceInDiatonicStepsFromTrebleB() + clef.asDotOnLineOffset
def exportObject(self, trackState):
"""Every time the cursor moves this will be called.
return a dict which is a good layout
export basis. For example for GUI frontends. They don't have to
parse and calculate their own values in slow pure Python then.
This is for GUIs etc. not for saving to JSOn. The cursor is not
tempoItem = trackState.track.parentData.tempoTrack.tempoAtTickPosition(trackState.tickindex) #TODO: maybe get that from a cached version.
item = trackState.track.currentItem()
block = trackState.track.currentBlock()
return {
"type": "Cursor",
"trackIndex": trackState.index(),
"track" : trackState.track,
"trackId" : id(trackState.track),
"trackName" :,
"cboxMidiOutUuid" : trackState.track.sequencerInterface.cboxMidiOutUuid, #used for midi throught. Step midi shall produce sound through the current track.
"midiChannel" : trackState.midiChannel(), #zero based
"solo" : trackState.track.solo,
"trackId" : id(trackState.track),
"position" : trackState.position(),
"tickindex" : trackState.tickindex,
"dotOnLine": self.asDotOnLine(trackState.clef()),
"pitch" : self.pitchindex,
"lilypondPitch" : pitch.pitch2ly[self.pitchindex],
"appending" : trackState.isAppending(),
"blockindex" : trackState.blockindex,
"block" : block,
"blockName" :,
"localCursorIndex" : block.localCursorIndex,
"itemId" : id(item),
"item" : item,
"tempoUnitsPerMinute" : tempoItem.unitsPerMinute,
"tempoReferenceTicks" : tempoItem.referenceTicks,
"tempoGraphType" : tempoItem.graphType,