Pitch modifier for story configuration

This commit is contained in:
Ruben van de Ven 2020-01-13 08:23:30 +01:00
parent 7c7fcf071a
commit ac11645dd0
2 changed files with 112 additions and 92 deletions

View file

@ -25,8 +25,10 @@ from .communication import LOG_BS
mainLogger = logging.getLogger("hugvey") mainLogger = logging.getLogger("hugvey")
logger = mainLogger.getChild("narrative") logger = mainLogger.getChild("narrative")
class Utterance(object): class Utterance(object):
"""Part of a reply""" """Part of a reply"""
def __init__(self, startTime): def __init__(self, startTime):
self.startTime = startTime self.startTime = startTime
self.endTime = None self.endTime = None
@ -34,9 +36,9 @@ class Utterance(object):
self.lastUpdateTime = startTime self.lastUpdateTime = startTime
def setText(self, text, now): def setText(self, text, now):
self.text = text.lower() # always lowercase self.text = text.lower() # always lowercase
self.lastUpdateTime = now self.lastUpdateTime = now
def hasText(self): def hasText(self):
return len(self.text) > 0 return len(self.text) > 0
@ -77,7 +79,7 @@ class Message(object):
self.lightChange = None self.lightChange = None
self.didRepeat = False self.didRepeat = False
self.fileError = False self.fileError = False
# Used by diversions, autogenerated directions should link to next chapter mark instead of the given msgTo # Used by diversions, autogenerated directions should link to next chapter mark instead of the given msgTo
self.generatedDirectionsJumpToChapter = False self.generatedDirectionsJumpToChapter = False
@ -151,7 +153,7 @@ class Message(object):
# if not None in self.variableValues.values(): # if not None in self.variableValues.values():
# self.logger.warn(f"now fetch {name} for {self.id}") # self.logger.warn(f"now fetch {name} for {self.id}")
# asyncio.get_event_loop().create_task(self.getAudioFilePath()) # asyncio.get_event_loop().create_task(self.getAudioFilePath())
def getVariableValue(self, var): def getVariableValue(self, var):
return self.variableValues[var] if (self.variableValues[var] is not None) else self.story.configuration.nothing_text #TODO: translate nothing to each language return self.variableValues[var] if (self.variableValues[var] is not None) else self.story.configuration.nothing_text #TODO: translate nothing to each language
@ -173,7 +175,7 @@ class Message(object):
if self.label and len(self.label): if self.label and len(self.label):
return self.label return self.label
return self.getText() return self.getText()
def getTextLabel(self): def getTextLabel(self):
""" """
A combination of getText and getLabel for maximum verbosity A combination of getText and getLabel for maximum verbosity
@ -236,8 +238,8 @@ class Message(object):
await s.send_json(info) await s.send_json(info)
filename = await s.recv_string() filename = await s.recv_string()
s.close() s.close()
# TODO: should this go trough the event Queue? risking a too long delay though # TODO: should this go trough the event Queue? risking a too long delay though
if filename == 'local/crash.wav' or len(filename) < 1: if filename == 'local/crash.wav' or len(filename) < 1:
self.logger.warning("Noting crash") self.logger.warning("Noting crash")
self.fileError = True self.fileError = True
@ -246,7 +248,7 @@ class Message(object):
self.logger.debug(f"Fetched audio for {textlabel}: {filename}") self.logger.debug(f"Fetched audio for {textlabel}: {filename}")
return filename return filename
class Reply(object): class Reply(object):
def __init__(self, message: Message): def __init__(self, message: Message):
@ -373,7 +375,7 @@ class Condition(object):
if 'vars' in data: if 'vars' in data:
condition.vars = data['vars'] condition.vars = data['vars']
if 'regex' in condition.vars: if 'regex' in condition.vars:
condition.vars['regex'] = condition.vars['regex'].rstrip() condition.vars['regex'] = condition.vars['regex'].rstrip()
@ -435,7 +437,7 @@ class Condition(object):
self.vars['variable'] self.vars['variable']
) )
return r return r
def _hasTimer(self, story) -> bool: def _hasTimer(self, story) -> bool:
if not story.lastMsgFinishTime: if not story.lastMsgFinishTime:
return False return False
@ -443,7 +445,7 @@ class Condition(object):
loopTime = story.hugvey.command.timer.getElapsed() % 3600 loopTime = story.hugvey.command.timer.getElapsed() % 3600
ltTime = int(self.vars['less_than']) ltTime = int(self.vars['less_than'])
gtTime = int(self.vars['more_than']) gtTime = int(self.vars['more_than'])
if not ltTime and not gtTime: if not ltTime and not gtTime:
# ignore invalid times # ignore invalid times
return return
@ -455,21 +457,21 @@ class Condition(object):
r = True r = True
else: else:
r = False r = False
if 'inverseMatch' in self.vars and self.vars['inverseMatch']: if 'inverseMatch' in self.vars and self.vars['inverseMatch']:
r = not r r = not r
self.logInfo = "Looptime is {} {} < {} < {}".format( self.logInfo = "Looptime is {} {} < {} < {}".format(
'' if r else 'not', '' if r else 'not',
f'{gtTime}' if gtTime else '-', f'{gtTime}' if gtTime else '-',
loopTime, loopTime,
f'{ltTime}' if ltTime else '-', f'{ltTime}' if ltTime else '-',
) )
return r return r
def _variableEquals(self, story) -> bool: def _variableEquals(self, story) -> bool:
v1 = story.variableValues[self.vars['variable1']] if story.hasVariableSet(self.vars['variable1']) else None v1 = story.variableValues[self.vars['variable1']] if story.hasVariableSet(self.vars['variable1']) else None
v2 = story.variableValues[self.vars['variable2']] if story.hasVariableSet(self.vars['variable2']) else None v2 = story.variableValues[self.vars['variable2']] if story.hasVariableSet(self.vars['variable2']) else None
@ -483,10 +485,10 @@ class Condition(object):
r = (v1 != v2) r = (v1 != v2)
else: else:
r = (v1 == v2) r = (v1 == v2)
story.logger.info("'{}' {} '{}' ({})".format(v1, '==' if v1 == v2 else '!=', v2, r)) story.logger.info("'{}' {} '{}' ({})".format(v1, '==' if v1 == v2 else '!=', v2, r))
return r return r
def _hasDiverged(self, story) -> bool: def _hasDiverged(self, story) -> bool:
if not story.lastMsgFinishTime: if not story.lastMsgFinishTime:
return False return False
@ -509,15 +511,15 @@ class Condition(object):
) )
return r return r
def _hasAudioError(self, story) -> bool: def _hasAudioError(self, story) -> bool:
if not story.currentMessage or not story.currentMessage.fileError: if not story.currentMessage or not story.currentMessage.fileError:
return False return False
self.logInfo = f"Has error loading audio file for {story.currentMessage.id}" self.logInfo = f"Has error loading audio file for {story.currentMessage.id}"
return True return True
def _hasPlayed(self, story) -> bool: def _hasPlayed(self, story) -> bool:
if not story.lastMsgFinishTime: if not story.lastMsgFinishTime:
return False return False
@ -547,12 +549,12 @@ class Condition(object):
) )
return r return r
def _hasVariableStorage(self, story) -> bool: def _hasVariableStorage(self, story) -> bool:
if not story.lastMsgFinishTime: if not story.lastMsgFinishTime:
return False return False
if self.hasRan: if self.hasRan:
# Prevent multiple runs of the same query within eg. waiting for a timeout. # Prevent multiple runs of the same query within eg. waiting for a timeout.
return False return False
@ -561,11 +563,11 @@ class Condition(object):
unique = bool(self.vars['unique']) if 'unique' in self.vars else False unique = bool(self.vars['unique']) if 'unique' in self.vars else False
varValues = story.hugvey.command.variableStore.getLastOfName(self.vars['var_name'], story.language_code, number, unique) varValues = story.hugvey.command.variableStore.getLastOfName(self.vars['var_name'], story.language_code, number, unique)
self.hasRan = True self.hasRan = True
if len(varValues) < number: if len(varValues) < number:
story.logger.warn(f"{self.id}: Too few instances of {self.vars['var_name']}, only {len(varValues)} in store") story.logger.warn(f"{self.id}: Too few instances of {self.vars['var_name']}, only {len(varValues)} in store")
return False return False
for i in range(number): for i in range(number):
story.setVariableValue( story.setVariableValue(
f"stored_{self.vars['var_name']}_{i+1}", f"stored_{self.vars['var_name']}_{i+1}",
@ -574,7 +576,7 @@ class Condition(object):
) )
return True return True
def _hasMetReplyContains(self, story) -> bool: def _hasMetReplyContains(self, story) -> bool:
""" """
Check the reply for specific characteristics: Check the reply for specific characteristics:
@ -789,15 +791,15 @@ class Diversion(object):
if self.type != 'repeat' and self.type !='interrupt': if self.type != 'repeat' and self.type !='interrupt':
# repeat diversion should be usable infinte times # repeat diversion should be usable infinte times
self.hasHit = True self.hasHit = True
story.addToLog(self) story.addToLog(self)
story.hugvey.eventLogger.info(f"diverge {self.id}") story.hugvey.eventLogger.info(f"diverge {self.id}")
except Exception as e: except Exception as e:
story.logger.critical("Exception when attempting diversion") story.logger.critical("Exception when attempting diversion")
story.logger.exception(e) story.logger.exception(e)
return False return False
return r return r
def createReturnDirectionsTo(self, story, startMsg, returnMsg, originalDirection = None, inheritTiming = True, timeoutDuration = .5, replyContainsDurations = None): def createReturnDirectionsTo(self, story, startMsg, returnMsg, originalDirection = None, inheritTiming = True, timeoutDuration = .5, replyContainsDurations = None):
@ -840,11 +842,11 @@ class Diversion(object):
msg = story.get(msgId) msg = story.get(msgId)
if not msg: if not msg:
continue continue
usedReturnMessage = returnMsg usedReturnMessage = returnMsg
if msg.generatedDirectionsJumpToChapter: if msg.generatedDirectionsJumpToChapter:
usedReturnMessage = story.getNextChapterForMsg(returnMsg, canIncludeSelf=True) usedReturnMessage = story.getNextChapterForMsg(returnMsg, canIncludeSelf=True)
if not usedReturnMessage: if not usedReturnMessage:
# in case of a diversion in the last bit of the story, it can be there there is no return message. # in case of a diversion in the last bit of the story, it can be there there is no return message.
raise Exception(f"No return message found for {msg.id}") raise Exception(f"No return message found for {msg.id}")
@ -885,7 +887,7 @@ class Diversion(object):
story.add(condition2) story.add(condition2)
direction.isDiversionReturn = True # will clear the currentDiversion on story direction.isDiversionReturn = True # will clear the currentDiversion on story
story.diversionDirections.append(direction) story.diversionDirections.append(direction)
story.logger.info(f"Created direction: {direction.id} ({msg.id} -> {usedReturnMessage.id}) {condition.id} with timeout {finalTimeoutDuration}s") story.logger.info(f"Created direction: {direction.id} ({msg.id} -> {usedReturnMessage.id}) {condition.id} with timeout {finalTimeoutDuration}s")
story.add(condition) story.add(condition)
story.add(direction) story.add(direction)
@ -962,7 +964,7 @@ class Diversion(object):
if not direction: if not direction:
# ignore the direction argument, and only check if the current message has a valid default # ignore the direction argument, and only check if the current message has a valid default
return return
waitTime = story.applyTimeFactor(1.8 if 'waitTime' not in self.params else float(self.params['waitTime'])) waitTime = story.applyTimeFactor(1.8 if 'waitTime' not in self.params else float(self.params['waitTime']))
timeSince = story.currentReply.getTimeSinceLastUtterance() timeSince = story.currentReply.getTimeSinceLastUtterance()
if timeSince < waitTime: if timeSince < waitTime:
@ -986,7 +988,7 @@ class Diversion(object):
story.logger.critical(f"Not a valid message id for diversion: {self.params['msgId']}") story.logger.critical(f"Not a valid message id for diversion: {self.params['msgId']}")
return return
if 'nextChapterOnReturn' in self.params and self.params['nextChapterOnReturn']: if 'nextChapterOnReturn' in self.params and self.params['nextChapterOnReturn']:
msgTo = story.getNextChapterForMsg(story.currentMessage, False) or direction.msgTo msgTo = story.getNextChapterForMsg(story.currentMessage, False) or direction.msgTo
returnInheritTiming = False returnInheritTiming = False
@ -1014,33 +1016,33 @@ class Diversion(object):
#: :var story: Story #: :var story: Story
if story.currentDiversion or not msgFrom or not msgTo: if story.currentDiversion or not msgFrom or not msgTo:
return False return False
if not msgTo.chapterStart: if not msgTo.chapterStart:
# only when changing chapter # only when changing chapter
return return
window_open_second = float(self.params['start_second']) window_open_second = float(self.params['start_second'])
window_close_second = window_open_second + float(self.params['window']) window_close_second = window_open_second + float(self.params['window'])
# Only keep a 1h loop # Only keep a 1h loop
now = story.hugvey.command.timer.getElapsed() % 3600 now = story.hugvey.command.timer.getElapsed() % 3600
if now < window_open_second or now > window_close_second: if now < window_open_second or now > window_close_second:
return return
#open! #open!
msg = story.get(self.params['msgId']) msg = story.get(self.params['msgId'])
if msg is None: if msg is None:
story.logger.critical(f"Not a valid message id for diversion: {self.id} {self.params['msgId']}") story.logger.critical(f"Not a valid message id for diversion: {self.id} {self.params['msgId']}")
return return
self.returnMessage = msgTo self.returnMessage = msgTo
self.createReturnDirectionsTo(story, msg, msgTo, direction, inheritTiming=True) self.createReturnDirectionsTo(story, msg, msgTo, direction, inheritTiming=True)
await story.setCurrentMessage(msg) await story.setCurrentMessage(msg)
story.currentDiversion = self story.currentDiversion = self
return True return True
async def _divergeIfRepeatRequest(self, story, msgFrom, msgTo, direction): async def _divergeIfRepeatRequest(self, story, msgFrom, msgTo, direction):
""" """
Participant asks if message can be repeated. Participant asks if message can be repeated.
@ -1052,7 +1054,7 @@ class Diversion(object):
# Perhaps set isFinished when matching condition. # Perhaps set isFinished when matching condition.
if story.currentReply is None or story.currentReply.getTimeSinceLastUtterance() < story.applyTimeFactor(1.8): if story.currentReply is None or story.currentReply.getTimeSinceLastUtterance() < story.applyTimeFactor(1.8):
return return
if story.currentMessage.didRepeat: if story.currentMessage.didRepeat:
# repeat only once # repeat only once
return return
@ -1151,7 +1153,6 @@ class Diversion(object):
async def _returnAfterTimeout(self, story): async def _returnAfterTimeout(self, story):
story.logger.info(f"Finalise diversion: {self.id}") story.logger.info(f"Finalise diversion: {self.id}")
async def _divergeIfInterrupted(self, story, msgFrom, msgTo, direction): async def _divergeIfInterrupted(self, story, msgFrom, msgTo, direction):
""" """
This is here as a placeholder for the interruption diversion. This is here as a placeholder for the interruption diversion.
@ -1177,14 +1178,16 @@ class Diversion(object):
return True return True
class Configuration(object): class Configuration(object):
id = 'configuration' id = 'configuration'
volume = 1 # Volume multiplier for 'play' command volume = 1 # Volume multiplier for 'play' command
nothing_text = "nothing" # When variable is not set, but used in sentence, replace it with this word. nothing_text = "nothing" # When variable is not set, but used in sentence, replace it with this word.
time_factor = 1 time_factor = 1 # time is multiplied to timeouts etc. (not playback)
tempo_factor = 1 tempo_factor = 1 # tempo is multiplied (playback)
pitch_modifier = 1 # pitch is added (playback)
light0_intensity = 0 light0_intensity = 0
light0_fade = 30. # fade duration in seconds light0_fade = 30. # fade duration in seconds
light0_isSophie = False light0_isSophie = False
light1_intensity = 150 light1_intensity = 150
light1_fade = 10. light1_fade = 10.
@ -1210,7 +1213,7 @@ class Configuration(object):
l = [] l = []
for i in range(5): for i in range(5):
l.append({ l.append({
'intensity': int(c[f"light{i}_intensity"]), 'intensity': int(c[f"light{i}_intensity"]),
'fade': float(c[f"light{i}_fade"]), 'fade': float(c[f"light{i}_fade"]),
'isSophie': float(c[f"light{i}_isSophie"]) 'isSophie': float(c[f"light{i}_isSophie"])
}) })
@ -1396,13 +1399,13 @@ class Story(object):
def setVariableValue(self, name, value, store=True): def setVariableValue(self, name, value, store=True):
if name not in self.variables: if name not in self.variables:
self.logger.warn(f"Set variable that is not needed in the story: {name}") self.logger.warn(f"Set variable that is not needed in the story: {name}")
if name in self.variableValues and self.variableValues[name] == value: if name in self.variableValues and self.variableValues[name] == value:
self.logger.debug(f"Skip double setting of variable {name} to {value}") self.logger.debug(f"Skip double setting of variable {name} to {value}")
return return
self.logger.debug(f"Set variable {name} to {value}") self.logger.debug(f"Set variable {name} to {value}")
self.variableValues[name] = value self.variableValues[name] = value
if store: if store:
self.hugvey.command.variableStore.addVariable(self.runId, name, value, self.hugvey.id, self.language_code) self.hugvey.command.variableStore.addVariable(self.runId, name, value, self.hugvey.id, self.language_code)
@ -1604,7 +1607,7 @@ class Story(object):
if len(e['transcript'].strip()) < 1: if len(e['transcript'].strip()) < 1:
self.logger.warning(f'ignore empty transcription {e}') self.logger.warning(f'ignore empty transcription {e}')
continue continue
# participants speaks, reset counter # participants speaks, reset counter
self.stats['consecutiveSilentTimeouts'] = 0 self.stats['consecutiveSilentTimeouts'] = 0
@ -1641,7 +1644,7 @@ class Story(object):
utterance.setText(e['transcript'], utterance.lastUpdateTime) utterance.setText(e['transcript'], utterance.lastUpdateTime)
else: else:
utterance.setText(e['transcript'], now) utterance.setText(e['transcript'], now)
self.hugvey.eventLogger.debug("speaking: content {} \"{}\"".format(id(utterance), e['transcript'])) self.hugvey.eventLogger.debug("speaking: content {} \"{}\"".format(id(utterance), e['transcript']))
if not self.timer.hasMark('first_speech'): if not self.timer.hasMark('first_speech'):
self.timer.setMark('first_speech') self.timer.setMark('first_speech')
@ -1678,7 +1681,7 @@ class Story(object):
# back to a previous point in time. # back to a previous point in time.
# self.logger.warn("Skipping double direction for diversion") # self.logger.warn("Skipping double direction for diversion")
continue continue
condition = self._processDirection(direction) condition = self._processDirection(direction)
if not condition: if not condition:
continue continue
@ -1688,7 +1691,7 @@ class Story(object):
chosenDirection = direction chosenDirection = direction
metCondition = condition metCondition = condition
break break
isDiverging = await self._processDiversions(chosenDirection) isDiverging = await self._processDiversions(chosenDirection)
@ -1706,12 +1709,12 @@ class Story(object):
if direction.isDiversionReturn and not direction.diversionHasReturned: if direction.isDiversionReturn and not direction.diversionHasReturned:
self.logger.info(f"Mark diversion as returned for return direction {direction.id}") self.logger.info(f"Mark diversion as returned for return direction {direction.id}")
direction.diversionHasReturned = True direction.diversionHasReturned = True
# chosenDirection.diversionHasReturned = True # chosenDirection.diversionHasReturned = True
await self.currentDiversion.finalise(self) await self.currentDiversion.finalise(self)
await self.setCurrentMessage(chosenDirection.msgTo, allowReplyInterrupt=allowReplyInterrupt) await self.setCurrentMessage(chosenDirection.msgTo, allowReplyInterrupt=allowReplyInterrupt)
return chosenDirection return chosenDirection
@ -1782,22 +1785,22 @@ class Story(object):
def logHasMsg(self, node): def logHasMsg(self, node):
return node in self.msgLog return node in self.msgLog
def checkIfGone(self): def checkIfGone(self):
''' '''
Make a guestimation if the audience has left... just really a simple timer check. Make a guestimation if the audience has left... just really a simple timer check.
If we do think so, give an error and stop the conversation If we do think so, give an error and stop the conversation
''' '''
if not self.lastMsgFinishTime: if not self.lastMsgFinishTime:
# don't do it when hugvey is speaking # don't do it when hugvey is speaking
return return
if self.timer.hasMark('last_speech') and self.timer.getElapsed('last_speech') > 30*60: if self.timer.hasMark('last_speech') and self.timer.getElapsed('last_speech') > 30*60:
self.hugvey.eventLogger.warning("Audience is quiet for too long...stopping") self.hugvey.eventLogger.warning("Audience is quiet for too long...stopping")
self.logger.warning("Audience is quiet, force END!") self.logger.warning("Audience is quiet, force END!")
self._finish() self._finish()
def checkIfHanging(self): def checkIfHanging(self):
''' '''
Make a guestimation if the story is hanging at a message. Raise exception once. Make a guestimation if the story is hanging at a message. Raise exception once.
@ -1807,9 +1810,9 @@ class Story(object):
# or when it already gave the error for this message # or when it already gave the error for this message
return return
diff = self.timer.getElapsed() - self.lastMsgFinishTime diff = self.timer.getElapsed() - self.lastMsgFinishTime
safeDiff = self.hugvey.command.config['story']['hugvey_critical_silence'] if 'hugvey_critical_silence' in self.hugvey.command.config['story'] else 90 safeDiff = self.hugvey.command.config['story']['hugvey_critical_silence'] if 'hugvey_critical_silence' in self.hugvey.command.config['story'] else 90
if diff > safeDiff: if diff > safeDiff:
self.hugvey.eventLogger.warning("Hugvey is quiet for very long!") self.hugvey.eventLogger.warning("Hugvey is quiet for very long!")
self.logger.critical("Hugvey is quiet for very long!") # critical messages are forwarded to telegram self.logger.critical("Hugvey is quiet for very long!") # critical messages are forwarded to telegram
@ -1896,7 +1899,7 @@ class Story(object):
message.id, message.getTextLabel())) message.id, message.getTextLabel()))
if message.id != self.startMessage.id: if message.id != self.startMessage.id:
self.addToLog(message) self.addToLog(message)
self.hugvey.eventLogger.info(f"message: {message.id} {message.uuid} start \"{message.getLabel()}\"") self.hugvey.eventLogger.info(f"message: {message.id} {message.uuid} start \"{message.getLabel()}\"")
# TODO: prep events & timer etc. # TODO: prep events & timer etc.
@ -1916,9 +1919,12 @@ class Story(object):
params['vol'] = "{:.4f}".format(params['vol']) params['vol'] = "{:.4f}".format(params['vol'])
params['tempo'] = (float(params['tempo']) if 'tempo' in params else 1) * (float(self.configuration.tempo_factor) if hasattr(self.configuration, 'tempo_factor') else 1) params['tempo'] = (float(params['tempo']) if 'tempo' in params else 1) * (float(self.configuration.tempo_factor) if hasattr(self.configuration, 'tempo_factor') else 1)
duration = float(duration) / params['tempo'] duration = float(duration) / params['tempo']
params['tempo'] = "{:.4f}".format(params['tempo']) params['tempo'] = "{:.4f}".format(params['tempo'])
params['pitch'] = (float(params['pitch']) if 'pitch' in params else 0)\
+ (float(self.configuration.pitch_modifier) if hasattr(self.configuration, 'pitch_modifier') else 0)
params['pitch'] = "{:.4f}".format(params['pitch'])
# self.hugvey.google.pause() # pause STT to avoid text events while decision is made # self.hugvey.google.pause() # pause STT to avoid text events while decision is made
self.hugvey.sendCommand({ self.hugvey.sendCommand({
'action': 'play', 'action': 'play',
@ -1927,7 +1933,7 @@ class Story(object):
'params': params, 'params': params,
'duration': duration 'duration': duration
}) })
if message.lightChange is not None: if message.lightChange is not None:
self.fadeLightPreset(message.lightChange) self.fadeLightPreset(message.lightChange)
# self.hugvey.setLightStatus(message.lightChange) # self.hugvey.setLightStatus(message.lightChange)
@ -1944,18 +1950,18 @@ class Story(object):
logmsg += "\n- {0} -> {1} (when: {2}) ".format(direction.msgFrom.id, direction.msgTo.id, conditions) logmsg += "\n- {0} -> {1} (when: {2}) ".format(direction.msgFrom.id, direction.msgTo.id, conditions)
self.logger.log(LOG_BS,logmsg) self.logger.log(LOG_BS,logmsg)
# if message.id != self.startMessage.id: # if message.id != self.startMessage.id:
# self.storeState() # self.storeState()
def fadeLightPreset(self, presetNr: int): def fadeLightPreset(self, presetNr: int):
if presetNr < 0 or presetNr > 4: if presetNr < 0 or presetNr > 4:
self.logger.critical(f"Error parsing light fade preset code '{presetNr}'") self.logger.critical(f"Error parsing light fade preset code '{presetNr}'")
return return
preset = self.configuration.getLightPresets()[presetNr] preset = self.configuration.getLightPresets()[presetNr]
self.currentLightPresetNr = presetNr self.currentLightPresetNr = presetNr
self.hugvey.transitionLight(preset['intensity'], preset['fade'], preset['isSophie']) self.hugvey.transitionLight(preset['intensity'], preset['fade'], preset['isSophie'])
def getCurrentDirections(self): def getCurrentDirections(self):
@ -2039,7 +2045,7 @@ class Story(object):
if msgId in checked: if msgId in checked:
# self.logger.log(LOG_BS, f"Finish for {msgId} already checked") # self.logger.log(LOG_BS, f"Finish for {msgId} already checked")
return [] return []
checked.append(msgId) checked.append(msgId)
if not msgId in self.directionsPerMsg or len(self.directionsPerMsg[msgId]) < 1: if not msgId in self.directionsPerMsg or len(self.directionsPerMsg[msgId]) < 1:
@ -2085,7 +2091,7 @@ class Story(object):
return self.strands[msg.id] return self.strands[msg.id]
return self.calculateFinishesForMsg(msg.id, checked=[]) return self.calculateFinishesForMsg(msg.id, checked=[])
def applyTimeFactor(self, time) -> float: def applyTimeFactor(self, time) -> float:
""" """
Apply the particularities of the configuration.time_factor Apply the particularities of the configuration.time_factor

View file

@ -190,18 +190,18 @@ class Panopticon {
if(this.hugveys.selectedId) { if(this.hugveys.selectedId) {
this.updateSelectedHugvey(); this.updateSelectedHugvey();
} }
let avail = 0; let avail = 0;
let blocked = 0; let blocked = 0;
for(let hv of this.hugveys.hugveys) { for(let hv of this.hugveys.hugveys) {
if(hv.status =='available') avail ++; if(hv.status =='available') avail ++;
if(hv.status =='blocked') blocked ++; if(hv.status =='blocked') blocked ++;
} }
this.hugveys.blockedHugveys = blocked; this.hugveys.blockedHugveys = blocked;
this.hugveys.availableHugveys = avail; this.hugveys.availableHugveys = avail;
break; break;
case 'log': case 'log':
@ -209,12 +209,12 @@ class Panopticon {
} }
} ); } );
} }
selectHugvey(hv_id) { selectHugvey(hv_id) {
this.hugveys.selectedId = hv_id; this.hugveys.selectedId = hv_id;
this.send({ action: 'selection', selected_id: hv_id }); this.send({ action: 'selection', selected_id: hv_id });
} }
change_loop_time(newTime) { change_loop_time(newTime) {
console.log('update', newTime); console.log('update', newTime);
this.send({ action: 'loop_time', time: newTime }); this.send({ action: 'loop_time', time: newTime });
@ -288,7 +288,7 @@ class Panopticon {
} }
} }
this.hugveys.selectedLang = code; this.hugveys.selectedLang = code;
let req = new XMLHttpRequest(); let req = new XMLHttpRequest();
let graph = this.graph; let graph = this.graph;
req.addEventListener( "load", function( e ) { req.addEventListener( "load", function( e ) {
@ -392,7 +392,7 @@ class Graph {
graph.saveJson(); graph.saveJson();
el.classList.remove('loading'); el.classList.remove('loading');
}, 100); }, 100);
} ); } );
document.getElementById( 'btn-addMsg' ).addEventListener( 'click', function( e ) { graph.createMsg(); } ); document.getElementById( 'btn-addMsg' ).addEventListener( 'click', function( e ) { graph.createMsg(); } );
document.getElementById( 'btn-diversions' ).addEventListener( 'click', function( e ) { graph.showDiversions(); } ); document.getElementById( 'btn-diversions' ).addEventListener( 'click', function( e ) { graph.showDiversions(); } );
@ -497,7 +497,7 @@ class Graph {
} }
else if(type == 'repeat') { else if(type == 'repeat') {
div['params']['regex'] = "can you repeat that\\?"; div['params']['regex'] = "can you repeat that\\?";
} }
else if(type == 'collective_moment') { else if(type == 'collective_moment') {
div['params']['start_second'] = 20 * 60; // second to start div['params']['start_second'] = 20 * 60; // second to start
div['params']['window'] = 60; // how long to wait, in seconds div['params']['window'] = 60; // how long to wait, in seconds
@ -1122,6 +1122,20 @@ class Graph {
'step': 0.01 'step': 0.01
}) })
), ),
crel(
'label',
"Playback pitch modifier: (< 0 is lower, >0 is higher)",
crel('input', {
'type': 'number',
'on': {
'change': function(e){
panopticon.graph.configuration['pitch_modifier'] = parseFloat(e.target.value)
}
},
'value': this.configuration.hasOwnProperty('pitch_modifier') ? this.configuration.pitch_modifier : 0,
'step': 1
})
),
crel('hr'), crel('hr'),
crel('h2', 'Light fade setting #0'), crel('h2', 'Light fade setting #0'),
crel( crel(
@ -1444,7 +1458,7 @@ class Graph {
"uploaded" "uploaded"
) : 'Auto-generated') ) : 'Auto-generated')
); );
let lightOptions = [ let lightOptions = [
crel("option", {'value': null}, "Do nothing") crel("option", {'value': null}, "Do nothing")
]; ];
@ -1463,18 +1477,18 @@ class Graph {
} }
lightOptions.push(crel("option", l, `Fade preset #${i} (${intensity} in ${duration}s)`)); lightOptions.push(crel("option", l, `Fade preset #${i} (${intensity} in ${duration}s)`));
} }
// let lightOptionNone = {'value': null} // let lightOptionNone = {'value': null}
// //
// let lightOptionOn = {'value': 1} // let lightOptionOn = {'value': 1}
// let lightOptionOff = {'value': 0} // let lightOptionOff = {'value': 0}
// //
// if(msg.hasOwnProperty('light')) { // if(msg.hasOwnProperty('light')) {
// if(msg['light'] === 1) lightOptionOn['selected'] = 'selected'; // if(msg['light'] === 1) lightOptionOn['selected'] = 'selected';
// if(msg['light'] === 0) lightOptionOff['selected'] = 'selected'; // if(msg['light'] === 0) lightOptionOff['selected'] = 'selected';
// if(msg['light'] === null) lightOptionNone['selected'] = 'selected'; // if(msg['light'] === null) lightOptionNone['selected'] = 'selected';
// } // }
let msgInfoEl = crel( 'div', { 'class': 'msg__info' }, let msgInfoEl = crel( 'div', { 'class': 'msg__info' },
crel('div', { crel('div', {
'class':'btn btn--delete btn--delete-msg', 'class':'btn btn--delete btn--delete-msg',
@ -1893,7 +1907,7 @@ class Graph {
if(attr.hasOwnProperty('description')) { if(attr.hasOwnProperty('description')) {
inputs.push(crel('div', {'class':'description'}, attr['description'])); inputs.push(crel('div', {'class':'description'}, attr['description']));
} }
} }
return inputs; return inputs;
} }