#! /usr/bin/env python3 # -*- coding: utf-8 -*- """ Copyright 2021, Nils Hilbricht, Germany ( https://www.hilbricht.net ) This file is part of the Laborejo Software Suite ( https://www.laborejo.org ), 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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__); logger.info("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 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 self.correctPitchindex() 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 self.correctPitchindex() def upOctave(self): """Move the cursor/pitchindex up, +350. Call with Cursor.upOctave(). Class method""" self.pitchindex += pitch.OCTAVE self.correctPitchindex() def downOctave(self): """Move the cursor/pitchindex down, -350. Call with Cursor.downOctave(). Class method""" self.pitchindex -= pitch.OCTAVE self.correctPitchindex() def correctPitchindex(self): if self.pitchindex >= pitch.MAX: self.pitchindex = pitch.MAX elif self.pitchindex <= pitch.MIN: self.pitchindex = pitch.MIN else: 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 else: return 1 else: if self.oneTimePrevailingDotInverter: self.oneTimePrevailingDotInverter = not self.oneTimePrevailingDotInverter return 1 else: 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 saved. """ 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, "cboxMidiOutUuid" : trackState.track.sequencerInterface.cboxMidiOutUuid, #used for midi throught. Step midi shall produce sound through the current track. "midiChannel" : trackState.midiChannel(), #zero based "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, "localCursorIndex" : block.localCursorIndex, "itemId" : id(item), "item" : item, "tempoUnitsPerMinute" : tempoItem.unitsPerMinute, "tempoReferenceTicks" : tempoItem.referenceTicks, "tempoGraphType" : tempoItem.graphType, }