#! /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
" 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 ,
}