From b8b912a10a9e4d8b999ddb61764554fe9db3e45d Mon Sep 17 00:00:00 2001 From: Hugvey Central Command Date: Sun, 12 May 2019 19:51:54 +0200 Subject: [PATCH] Change for working version --- client_config.yml | 2 +- hugvey/story.py | 98 ++++++++++++++++++++++++----------------------- pd/loopaudio.pd | 49 ++++++++++++------------ 3 files changed, 76 insertions(+), 73 deletions(-) diff --git a/client_config.yml b/client_config.yml index 5ec33d3..c9d6af9 100644 --- a/client_config.yml +++ b/client_config.yml @@ -12,4 +12,4 @@ voice: input_volume: 100 output_volume: 30 file_address: "http://hugveycmd.local:8888" - output_driver: pulseaudio + output_driver: null diff --git a/hugvey/story.py b/hugvey/story.py index 766319b..08332e0 100644 --- a/hugvey/story.py +++ b/hugvey/story.py @@ -40,8 +40,8 @@ class Utterance(object): def isFinished(self): return self.endTime is not None - - + + def __getstate__(self): # print(f'get utterance {self}') state = self.__dict__.copy() @@ -68,7 +68,7 @@ class Message(object): self.parseForVariables() self.uuid = None # Have a unique id each time the message is played back. self.color = None - + def __getstate__(self): # Copy the object's state from self.__dict__ which contains # all our instance attributes. Always use the dict.copy() @@ -78,7 +78,7 @@ class Message(object): # Remove the unpicklable entries. del state['filenameFetchLock'] return state - + def __setstate__(self, state): self.__dict__.update(state) self.filenameFetchLock = asyncio.Lock() @@ -103,9 +103,9 @@ class Message(object): if not 'vol' in msg.params: # prevent clipping on some Lyrebird tracks msg.params['vol'] = .8 - + msg.params['vol'] = float(msg.params['vol']) - + return msg def parseForVariables(self): @@ -215,8 +215,8 @@ class Reply(object): self.forMessage = None self.utterances = [] self.setForMessage(message) - - + + def __getstate__(self): # print(f'get reply {self}') state = self.__dict__.copy() @@ -297,8 +297,8 @@ class Condition(object): self.logInfo = None self.originalJsonString = None self.usedContainsDuration = None - - + + def __getstate__(self): # print(f'get condition {self.id}') state = self.__dict__.copy() @@ -500,8 +500,8 @@ class Direction(object): self.conditions = [] self.conditionMet = None self.isDiversionReturn = False - - + + def __getstate__(self): # print(f'get direction {self.id}') state = self.__dict__.copy() @@ -568,8 +568,8 @@ class Diversion(object): if not self.method: raise Exception("No valid type given for diversion") - - + + def __getstate__(self): # print(f'get diversion {self.id}') state = self.__dict__.copy() @@ -628,7 +628,7 @@ class Diversion(object): }] """ self.counter +=1 - + # story.logger.warn(f"CREATING DIRECTIONS FOR {startMsg.id}") finishMessageIds = story.getFinishesForMsg(startMsg) finalTimeoutDuration = timeoutDuration finalContainsDurations = replyContainsDurations @@ -647,6 +647,7 @@ class Diversion(object): finalContainsDurations = json.loads(condition.originalJsonString)['vars']['delays'] i = 0 + # story.logger.warn(f"FINISHES: {finishMessageIds}") for msgId in finishMessageIds: # Some very ugly hack to add a direction & condition i+=1 @@ -693,6 +694,7 @@ class Diversion(object): story.logger.info(f"Created direction: {direction.id} {condition.id} with timeout {finalTimeoutDuration}s") story.add(condition) story.add(direction) + # story.logger.warn(f"ADDED DIRECTION {direction.id}") @@ -943,7 +945,7 @@ class Diversion(object): class Configuration(object): id = 'configuration' volume = 1 - + @classmethod def initFromJson(configClass, data, story): config = Configuration() @@ -1006,22 +1008,22 @@ class Stopwatch(object): def clearMark(self, name): if name in self.marks: self.marks.pop(name) - + def __getstate__(self): # print(f'get stopwatch') state = self.__dict__.copy() state['isRunning'] = self.isRunning.is_set() return state - + def __setstate__(self, state): self.__dict__.update(state) - + self.isRunning = asyncio.Event() if 'isRunning' in state and state['isRunning']: self.isRunning.set() else: self.isRunning.clear() - + class StoryState(object): """ @@ -1064,7 +1066,7 @@ class StoryState(object): def __init__(self): pass -# +# class Story(object): """Story represents and manages a story/narrative flow""" @@ -1484,7 +1486,7 @@ class Story(object): # TODO create timer event # self.commands.append({'msg':'TEST!'}) - + # Test stability of Central Command with deliberate crash # if self.timer.getElapsed() > 10: # raise Exception("Test exception") @@ -1552,10 +1554,10 @@ class Story(object): self.logger.critical(f"error: crash when reading wave file: {fn}") self.logger.exception(e) duration = 10 # some default duration to have something to fall back to - + params = message.getParams().copy() params['vol'] = params['vol'] * self.configuration.volume if 'vol' in params else self.configuration.volume - + # self.hugvey.google.pause() # pause STT to avoid text events while decision is made self.hugvey.sendCommand({ 'action': 'play', @@ -1620,7 +1622,7 @@ class Story(object): self.isRunning = True if not self.lastMsgFinishTime and self.currentMessage: await self.setCurrentMessage(self.currentMessage) - + await self._renderer() def isFinished(self): @@ -1648,16 +1650,16 @@ class Story(object): self.timer.pause() def calculateFinishesForMsg(self, msgId, depth = 0, checked = []): - if msgId in checked: - return [] - - checked.append(msgId) - + # if msgId in checked: + # return [] + # + # checked.append(msgId) + if not msgId in self.directionsPerMsg or len(self.directionsPerMsg[msgId]) < 1: # is finish return [msgId] - if depth > 200: + if depth > 100: return [] finishes = [] @@ -1690,6 +1692,7 @@ class Story(object): returns message ids """ + print(msg.id, self.strands) if msg.id in self.strands: return self.strands[msg.id] @@ -1709,22 +1712,22 @@ class Story(object): # TODO: should the direction have at least a timeout condition set, or not perse? return self.directionsPerMsg[msg.id][0] - + @classmethod def getStateDir(self): return "/tmp" # day = time.strftime("%Y%m%d") # t = time.strftime("%H:%M:%S") -# +# # self.out_folder = os.path.join(self.main_folder, day, f"{self.hv_id}", t) # if not os.path.exists(self.out_folder): # self.logger.debug(f"Create directory {self.out_folder}") # self.target_folder = os.makedirs(self.out_folder, exist_ok=True) - - @classmethod + + @classmethod def getStateFilename(cls, hv_id): return os.path.join(cls.getStateDir(), f"hugvey{hv_id}") - + def storeState(self): # TODO: stop stopwatch fn = self.getStateFilename(self.hugvey.id) @@ -1734,49 +1737,48 @@ class Story(object): pickle.dump(self, fp) # write atomic to disk: flush, close, rename fp.flush() - os.fsync(fp.fileno()) - + os.fsync(fp.fileno()) + os.rename(tmpfn, fn) self.logger.debug(f"saved state to {fn}") - + def hasSavedState(self): return self.hugveyHasSavedState(self.hugvey.id) - + @classmethod def hugveyHasSavedState(cls, hv_id): return os.path.exists(cls.getStateFilename(hv_id)) - + @classmethod def loadStoryFromState(cls, hugvey_state): # restart stopwatch with open(cls.getStateFilename(hugvey_state.id), 'rb') as fp: story = pickle.load(fp) - + story.hugvey = hugvey_state story.logger = mainLogger.getChild(f"{story.hugvey.id}").getChild("story") return story # TODO: take running state etc. - + @classmethod def clearSavedState(cls, hv_id): fn = cls.getStateFilename(hv_id) if os.path.exists(fn): os.unlink(fn) mainLogger.info(f"Removed state: {fn}") -# +# def __getstate__(self): # Copy the object's state from self.__dict__ which contains # all our instance attributes. Always use the dict.copy() # method to avoid modifying the original state. state = self.__dict__.copy() - + # Remove the unpicklable entries. del state['hugvey'] del state['logger'] # del state['isRunning'] - + return state - + def __setstate__(self, state): self.__dict__.update(state) - diff --git a/pd/loopaudio.pd b/pd/loopaudio.pd index 5c10c88..226d149 100644 --- a/pd/loopaudio.pd +++ b/pd/loopaudio.pd @@ -1,12 +1,11 @@ -#N canvas 200 136 660 592 10; +#N canvas 976 211 660 592 10; #X obj 131 277 dac~; #X obj 131 227 readsf~; #X obj 209 209 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X text 229 206 (re-)start loop; -#X msg 132 170 open /home/a/projects/pd-play/testaudio.wav \, 1; #X obj 208 459 netsend -u -b; -#X obj 208 481 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +#X obj 208 481 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #X msg 355 464 disconnect; #X obj 208 393 list prepend send; @@ -19,33 +18,35 @@ #X obj 318 84 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X text 217 60 START; -#X obj 436 84 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +#X obj 436 84 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #X text 309 61 !STOP!; #X text 422 60 Playing indicator; #X msg 261 118 1; #X msg 330 117 0; #X obj 208 345 oscformat /loop; -#X msg 356 434 connect 192.168.178.15 7400; +#X msg 356 434 connect 192.168.1.174 7400; +#X msg 132 170 open /mnt/stash/hugvey/score38_loop_40s_extra.wav \, +1; #X connect 1 0 0 0; #X connect 1 0 0 1; #X connect 1 1 2 0; -#X connect 2 0 4 0; -#X connect 2 0 13 0; -#X connect 4 0 1 0; -#X connect 5 0 6 0; -#X connect 7 0 5 0; -#X connect 8 0 9 0; -#X connect 9 0 5 0; -#X connect 10 0 1 0; -#X connect 11 0 22 0; -#X connect 11 0 2 0; -#X connect 11 0 19 0; -#X connect 13 0 21 0; -#X connect 14 0 10 0; -#X connect 14 0 7 0; -#X connect 14 0 20 0; -#X connect 19 0 16 0; -#X connect 20 0 16 0; -#X connect 21 0 8 0; -#X connect 22 0 5 0; +#X connect 2 0 22 0; +#X connect 2 0 12 0; +#X connect 4 0 5 0; +#X connect 6 0 4 0; +#X connect 7 0 8 0; +#X connect 8 0 4 0; +#X connect 9 0 1 0; +#X connect 10 0 21 0; +#X connect 10 0 2 0; +#X connect 10 0 18 0; +#X connect 12 0 20 0; +#X connect 13 0 9 0; +#X connect 13 0 6 0; +#X connect 13 0 19 0; +#X connect 18 0 15 0; +#X connect 19 0 15 0; +#X connect 20 0 7 0; +#X connect 21 0 4 0; +#X connect 22 0 1 0;