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.
 
 

157 lines
4.7 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 ),
This application 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/>.
"""
"""
This file handles various durations and their conversions.
"""
import logging; logger = logging.getLogger(__name__); logger.info("import")
from typing import Union
from engine.config import METADATA
#Suggested import in other files:
#from template.engine.duration import DB, DL, D1, D2, D4, D8, D16, D32, D64, D128
#import template.engine.duration as duration
MAXIMUM_TICK_DURATION = 2**31-1
D4:int = METADATA["quarterNoteInTicks"] #type: ignore
D8 = int(D4 / 2)
D16 = int(D8 / 2)
D32 = int(D16 / 2)
D64 = int(D32 / 2)
D128 = int(D64 / 2)
D256 = int(D128 / 2)
D512 = int(D256 / 2)
D1024 = int(D512 / 2) # set this to a number with many factors, like 210. According to http://homes.sice.indiana.edu/donbyrd/CMNExtremes.htm this is the real world limit.
if not int(D1024) == D1024:
logger.error(f"Warning: Lowest duration D0124 has decimal places: {D1024} but should be a plain integer. Your D4 value: {D4} has not enough 2^n factors. ")
D2 = D4 *2
D1 = D2 *2
DB = D1 * 2 #Breve
DL = DB * 2 #Longa
DM = DL * 2 #Maxima
if not int(D128) == D128:
raise ValueError(f"Durations must be integers (or .0). Yours: {D128}")
D_BASE_DURATIONS = (D1024, D512, D256, D64, D32, D16, D8, D4, D2, D1, DB, DL, DM)
#Duration Keywords
D_DEFAULT = 0
D_STACCATO = 1
D_TENUTO = 2
D_TIE = 3
def _baseDurationToTraditionalNumber(baseDuration:int)->int:
"""4 = Quarter, 8 = Eighth, 0 = Brevis, -1= Longa
Created in the loop below on startup"""
if baseDuration == D4:
return 4
elif baseDuration == D8:
return 8
elif baseDuration == D16:
return 16
elif baseDuration == D2:
return 2
elif baseDuration == D1:
return 1
elif baseDuration == DB:
return 0
elif baseDuration == DL:
return -1
elif baseDuration == DM:
return -2
elif baseDuration == D32:
return 32
elif baseDuration == D64:
return 64
elif baseDuration == D128:
return 128
elif baseDuration == D256:
return 256
elif baseDuration == D512:
return 512
elif baseDuration == D1024:
return 1024
elif baseDuration == 0: #for KeySignatures and chords on init etc.
return 0
else:
raise ValueError("baseDuration does not match any traditioanl note duration value")
baseDurations = (0, D1024, D512, D256, D128, D64, D32, D16, D8, D4, D2, D1, DB, DL, DM) #from constants.py
baseDurationToTraditionalNumber = {}
for baseDuration in baseDurations:
baseDurationToTraditionalNumber[baseDuration] = _baseDurationToTraditionalNumber(baseDuration)
traditionalNumberToBaseDuration = {}
for baseDuration in baseDurations:
traditionalNumberToBaseDuration[_baseDurationToTraditionalNumber(baseDuration)] = baseDuration
def jackBBTicksToDuration(beatTypeAsTraditionalNumber:int, jackTicks:int, jackBeatTicks:int)->Union[float, None]:
if None in (beatTypeAsTraditionalNumber, jackTicks, jackBeatTicks):
return None
else:
if not beatTypeAsTraditionalNumber in traditionalNumberToBaseDuration:
raise ValueError(f"{beatTypeAsTraditionalNumber} must be one of {traditionalNumberToBaseDuration}")
beatTypeDuration = traditionalNumberToBaseDuration[beatTypeAsTraditionalNumber]
#print (beatTypeAsTraditionalNumber, beatTypeDuration, jackTicks, jackBeatTicks)
factor = beatTypeDuration / jackBeatTicks
return jackTicks * factor
def lyDurationLogToTicks(durationLog:int)->int:
return 2**(8 - durationLog) * 6
ticksToLyDurationLogDict = {
DM: -3, # Maxima
DL: -2, # Longa
DB: -1, # Breve
D1: 0, # Whole
D2: 1, #Half
D4: 2, #Quarter
D8: 3, #Eighth
D16: 4, #Sixteenth
D32: 5, #1/32
D64: 6, #1/64
D128: 7, #1/128
D256: 8, #1/256
}
def ticksToLilypond(ticks:int)->int:
if ticks in ticksToLyDurationLogDict:
return ticksToLyDurationLogDict[ticks]
else:
raise ValueError(f"{ticks} not in duration.py ticksToLyDurationLogDict")