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
148 lines
6.0 KiB
#! /usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Copyright 2022, 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 <http://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
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
|
|
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
|
|
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):
|
|
self.cursorWasMovedAfterChoosingPitchViaMidi = True
|
|
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,
|
|
"trackId" : id(trackState.track),
|
|
"trackName" : trackState.track.name,
|
|
"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" : block.name,
|
|
"localCursorIndex" : block.localCursorIndex,
|
|
"itemId" : id(item),
|
|
"item" : item,
|
|
"tempoUnitsPerMinute" : tempoItem.unitsPerMinute,
|
|
"tempoReferenceTicks" : tempoItem.referenceTicks,
|
|
"tempoGraphType" : tempoItem.graphType,
|
|
}
|
|
|