Merge branch 'master' of gitlab.com:hugvey/hugvey

This commit is contained in:
Ruben van de Ven 2019-05-11 18:14:20 +02:00
commit 0aafc3209f
9 changed files with 383 additions and 370 deletions

View file

@ -93,7 +93,7 @@ chown=pi:pi
## Deploy / usefull commands ## Deploy / usefull commands
```bash ```bash
for i in {1..6}; do rsync -av ~/hugvey/ pi@hugvey$i.local:/home/pi/hugvey/ --exclude=www --exclude=venv --exclude=local --exclude=*.pyc --exclude=.git; done for i in {1..26}; do echo $i; rsync -av ~/hugvey/ pi@hugvey$i.local:/home/pi/hugvey/ --exclude=www --exclude=venv --exclude=local --exclude=*.pyc --exclude=.git --exclude=recordings --exclude=/voice* --exclude=/pd; done
``` ```
```bash ```bash
@ -204,4 +204,4 @@ times occured/only on n-th instance: determines the order of diversions of the s
## 4G Modem ## 4G Modem
Visit 192.168.5.1 Visit 192.168.5.1
The password is at the bottom of the device. The password is at the bottom of the device.

View file

@ -5,7 +5,7 @@ voice:
input_rate: 44100 input_rate: 44100
target_rate: 16000 target_rate: 16000
port: 4444 port: 4444
input_name: 'AK5371' input_name: 'USB Audio Device'
output_name: 'USB Audio Device' output_name: 'USB Audio Device'
input_mixer: 'Mic' input_mixer: 'Mic'
output_mixer: 'PCM' output_mixer: 'PCM'
@ -13,6 +13,3 @@ voice:
output_volume: 30 output_volume: 30
file_address: "http://hugveycmd.local:8888" file_address: "http://hugveycmd.local:8888"
output_driver: pulseaudio output_driver: pulseaudio

View file

@ -24,6 +24,7 @@ from hugvey.voice import VoiceStorage
import multiprocessing import multiprocessing
from hugvey.speech.recorder import Recorder from hugvey.speech.recorder import Recorder
from pythonosc import udp_client from pythonosc import udp_client
import copy
mainLogger = logging.getLogger("hugvey") mainLogger = logging.getLogger("hugvey")
@ -67,7 +68,7 @@ class CentralCommand(object):
self.languageFiles = {} self.languageFiles = {}
self.languageConfig = {} self.languageConfig = {}
self.args = args # cli args self.args = args # cli args
eventLogger.addHandler(logging.handlers.QueueHandler(self.logQueue)) eventLogger.addHandler(logging.handlers.QueueHandler(self.logQueue))
def loadConfig(self, filename): def loadConfig(self, filename):
@ -85,14 +86,14 @@ class CentralCommand(object):
self.hugvey_ids = [i + 1 for i in range(self.config['hugveys'])] self.hugvey_ids = [i + 1 for i in range(self.config['hugveys'])]
self.loadLanguages() self.loadLanguages()
voice_dir = os.path.join(self.config['web']['files_dir'], 'voices') voice_dir = os.path.join(self.config['web']['files_dir'], 'voices')
self.voiceStorage = VoiceStorage(voice_dir, self.languageConfig) self.voiceStorage = VoiceStorage(voice_dir, self.languageConfig)
self.panopticon = Panopticon(self, self.config, self.voiceStorage) self.panopticon = Panopticon(self, self.config, self.voiceStorage)
def loadLanguages(self): def loadLanguages(self):
logger.debug('load language files') logger.debug('load language files')
self.languages = {} self.languages = {}
@ -116,7 +117,7 @@ class CentralCommand(object):
# if not hv.story: # if not hv.story:
# status['status'] = 'off' # status['status'] = 'off'
# return status # return status
status['status'] = hv.getStatus() status['status'] = hv.getStatus()
status['language'] = hv.language_code status['language'] = hv.language_code
status['light_id'] = hv.lightId status['light_id'] = hv.lightId
@ -126,7 +127,7 @@ class CentralCommand(object):
# status['history'] = hv.story.getLogSummary() # disabled as it is a bit slow. We now have eventLog # status['history'] = hv.story.getLogSummary() # disabled as it is a bit slow. We now have eventLog
# status['counts'] = {t: len(a) for t, a in status['history'].items() if t != 'directions' } # status['counts'] = {t: len(a) for t, a in status['history'].items() if t != 'directions' }
status['counts'] = {} if not hv.story else hv.story.getLogCounts() status['counts'] = {} if not hv.story else hv.story.getLogCounts()
status['duration'] = 0 if not hv.story else hv.story.timer.getElapsed() status['duration'] = 0 if not hv.story else hv.story.timer.getElapsed()
return status return status
@ -139,10 +140,10 @@ class CentralCommand(object):
'logbookId': None, 'logbookId': None,
'logbook': [], 'logbook': [],
} }
#use this to test if any threads stay open #use this to test if any threads stay open
# eg. after killing/dying of a hugvey # eg. after killing/dying of a hugvey
# print(threading.enumerate()) # print(threading.enumerate())
for hv_id in self.hugvey_ids: for hv_id in self.hugvey_ids:
status['hugveys'].append(self.getHugveyStatus(hv_id, selected_id == hv_id)) status['hugveys'].append(self.getHugveyStatus(hv_id, selected_id == hv_id))
@ -151,7 +152,7 @@ class CentralCommand(object):
if self.hugveys[selected_id].recorder: if self.hugveys[selected_id].recorder:
status['logbook'] = self.hugveys[selected_id].recorder.currentLog status['logbook'] = self.hugveys[selected_id].recorder.currentLog
status['logbookId'] = selected_id status['logbookId'] = selected_id
return status return status
def commandHugvey(self, hv_id, msg): def commandHugvey(self, hv_id, msg):
@ -169,8 +170,8 @@ class CentralCommand(object):
def _queueCommand(self, hv_id, msg): def _queueCommand(self, hv_id, msg):
self.commandQueue.put_nowait((hv_id, msg)) self.commandQueue.put_nowait((hv_id, msg))
def commandLight(self, route, data): def commandLight(self, route, data):
""" """
Buffer light commands Buffer light commands
@ -215,13 +216,13 @@ class CentralCommand(object):
logger.warn('Stopping command sender') logger.warn('Stopping command sender')
s.close() s.close()
async def lightSender(self): async def lightSender(self):
lightConn = udp_client.SimpleUDPClient( lightConn = udp_client.SimpleUDPClient(
self.config['light']['ip'], self.config['light']['ip'],
self.config['light']['port']) self.config['light']['port'])
logger.info(f"Ready to send light commands to: {self.config['light']['ip']}:{self.config['light']['port']}") logger.info(f"Ready to send light commands to: {self.config['light']['ip']}:{self.config['light']['port']}")
while self.isRunning.is_set(): while self.isRunning.is_set():
@ -232,7 +233,7 @@ class CentralCommand(object):
logger.warn('Stopping light sender') logger.warn('Stopping light sender')
lightConn._sock.close() lightConn._sock.close()
async def redLightController(self): async def redLightController(self):
""" """
Every second, check if no hugveys are available. If so, the red light should be Every second, check if no hugveys are available. If so, the red light should be
@ -264,7 +265,7 @@ class CentralCommand(object):
thread = threading.Thread( thread = threading.Thread(
target=self.hugveyStateRunner, args=(hugvey_id,), name=f"hugvey#{hugvey_id}") target=self.hugveyStateRunner, args=(hugvey_id,), name=f"hugvey#{hugvey_id}")
thread.start() thread.start()
def hugveyStateRunner(self, hugvey_id): def hugveyStateRunner(self, hugvey_id):
while self.isRunning.is_set(): while self.isRunning.is_set():
logger.info(f'Instantiate hugvey #{hugvey_id}') logger.info(f'Instantiate hugvey #{hugvey_id}')
@ -278,7 +279,7 @@ class CentralCommand(object):
return return
logger.critical(f'Hugvey stopped (crashed?). Reinstantiate after 5 sec') logger.critical(f'Hugvey stopped (crashed?). Reinstantiate after 5 sec')
time.sleep(5) time.sleep(5)
async def timerEmitter(self): async def timerEmitter(self):
""" """
This is fixed: a one hour loop with a collective moment 10-15 minutes, This is fixed: a one hour loop with a collective moment 10-15 minutes,
@ -288,25 +289,25 @@ class CentralCommand(object):
intervals = [ intervals = [
{ {
'start_time': 10*60, 'start_time': 10*60,
'duration': 5 * 60, 'duration': 5 * 60,
}, },
{ {
'start_time': 30*60, 'start_time': 30*60,
'duration': 5 * 60, 'duration': 5 * 60,
}, },
{ {
'start_time': 50*60, 'start_time': 50*60,
'duration': 5 * 60, 'duration': 5 * 60,
} }
] ]
self.start_time = time.time() self.start_time = time.time()
# TODO: emit start event # TODO: emit start event
while self.isRunning.is_set(): while self.isRunning.is_set():
pass pass
async def eventListener(self): async def eventListener(self):
s = self.ctx.socket(zmq.SUB) s = self.ctx.socket(zmq.SUB)
s.bind(self.config['events']['listen_address']) s.bind(self.config['events']['listen_address'])
@ -319,7 +320,7 @@ class CentralCommand(object):
while self.isRunning.is_set(): while self.isRunning.is_set():
try: try:
hugvey_id, msg = await zmqReceive(s) hugvey_id, msg = await zmqReceive(s)
if hugvey_id not in self.hugvey_ids: if hugvey_id not in self.hugvey_ids:
logger.critical( logger.critical(
"Message from alien Hugvey: {}".format(hugvey_id)) "Message from alien Hugvey: {}".format(hugvey_id))
@ -355,7 +356,7 @@ class CentralCommand(object):
fn = await self.voiceStorage.requestFile(hv.language_code, text, isVariable) fn = await self.voiceStorage.requestFile(hv.language_code, text, isVariable)
if fn is None: if fn is None:
eventLogger.getChild(f"{hugvey_id}").critical("error: No voice file fetched, check logs.") eventLogger.getChild(f"{hugvey_id}").critical("error: No voice file fetched, check logs.")
fn = 'local/crash.wav' fn = 'local/crash.wav'
# TODO: trigger a repeat/crash event. # TODO: trigger a repeat/crash event.
await s.send_string(fn) await s.send_string(fn)
except Exception as e: except Exception as e:
@ -376,7 +377,7 @@ class CentralCommand(object):
self.catchException(self.lightSender())) self.catchException(self.lightSender()))
self.tasks['redLightController'] = self.loop.create_task( self.tasks['redLightController'] = self.loop.create_task(
self.catchException(self.redLightController())) self.catchException(self.redLightController()))
for hid in self.hugvey_ids: for hid in self.hugvey_ids:
self.tasks['voiceListener'] = self.loop.create_task( self.tasks['voiceListener'] = self.loop.create_task(
self.catchException(self.voiceListener(hid))) self.catchException(self.voiceListener(hid)))
@ -386,12 +387,12 @@ class CentralCommand(object):
self.panopticon_thread = threading.Thread( self.panopticon_thread = threading.Thread(
target=self.panopticon.start, name="Panopticon") target=self.panopticon.start, name="Panopticon")
self.panopticon_thread.start() self.panopticon_thread.start()
self.loop.run_forever() self.loop.run_forever()
def stop(self): def stop(self):
self.isRunning.clear() self.isRunning.clear()
async def catchException(self, awaitable): async def catchException(self, awaitable):
try: try:
# print(awaitable) # print(awaitable)
@ -405,7 +406,7 @@ class HugveyState(object):
"""Represents the state of a Hugvey client on the server. """Represents the state of a Hugvey client on the server.
Manages server connections & voice parsing etc. Manages server connections & voice parsing etc.
""" """
# all statusses can only go up or down, except for gone, which is an error state: # all statusses can only go up or down, except for gone, which is an error state:
# off <-> blocked <-> available <-> running <-> paused # off <-> blocked <-> available <-> running <-> paused
STATE_OFF = "off" STATE_OFF = "off"
@ -424,7 +425,7 @@ class HugveyState(object):
self.isConfigured = None self.isConfigured = None
self.isRunning = asyncio.Event(loop=self.loop) self.isRunning = asyncio.Event(loop=self.loop)
self.isRunning.clear() self.isRunning.clear()
self.eventQueue = None self.eventQueue = None
self.language_code = 'en-GB' self.language_code = 'en-GB'
self.story = None self.story = None
@ -435,24 +436,24 @@ class HugveyState(object):
self.notShuttingDown = True # TODO: allow shutdown of object self.notShuttingDown = True # TODO: allow shutdown of object
self.startMsgId = None self.startMsgId = None
self.eventLogger = eventLogger.getChild(f"{self.id}") self.eventLogger = eventLogger.getChild(f"{self.id}")
self.setStatus(self.STATE_GONE) self.setStatus(self.STATE_GONE)
self.requireRestartAfterStop = None self.requireRestartAfterStop = None
def __del__(self): def __del__(self):
self.logger.warn("Destroying hugvey object") self.logger.warn("Destroying hugvey object")
def getStatus(self): def getStatus(self):
return self.status return self.status
def setStatus(self, status): def setStatus(self, status):
self.status = status self.status = status
lightOn = status in [self.STATE_AVAILABLE, self.STATE_PAUSE] lightOn = status in [self.STATE_AVAILABLE, self.STATE_PAUSE]
self.setLightStatus(lightOn) self.setLightStatus(lightOn)
self.eventLogger.info(f"status: {self.status}") self.eventLogger.info(f"status: {self.status}")
def config(self, hostname, ip): def config(self, hostname, ip):
self.ip = ip self.ip = ip
self.hostname = hostname self.hostname = hostname
@ -463,7 +464,7 @@ class HugveyState(object):
else: else:
self.logger.info( self.logger.info(
f"Hugvey {self.id} at {self.ip}, host: {self.hostname}") f"Hugvey {self.id} at {self.ip}, host: {self.hostname}")
if self.status == self.STATE_GONE: if self.status == self.STATE_GONE:
# turn on :-) # turn on :-)
self.setStatus(self.STATE_BLOCKED) self.setStatus(self.STATE_BLOCKED)
@ -496,7 +497,7 @@ class HugveyState(object):
self.logger.exception(e) self.logger.exception(e)
self.logger.critical(f"Hugvey crash") self.logger.critical(f"Hugvey crash")
self.eventLogger.critical(f"error: {e}") self.eventLogger.critical(f"error: {e}")
# restart # restart
# TODO: test proper functioning # TODO: test proper functioning
self.shutdown() self.shutdown()
@ -510,18 +511,18 @@ class HugveyState(object):
else: else:
# Allow for both the Hugvey Command, or the Story handle the event. # Allow for both the Hugvey Command, or the Story handle the event.
self.loop.call_soon_threadsafe(self._queueEvent, msg) self.loop.call_soon_threadsafe(self._queueEvent, msg)
def _queueEvent(self, msg): def _queueEvent(self, msg):
""" """
Put event in both the event loop for the story as well as the Hugvey State handler Put event in both the event loop for the story as well as the Hugvey State handler
""" """
self.logger.debug(f"Queue event in hugvey loop: {msg}") self.logger.debug(f"Queue event in hugvey loop: {msg}")
self.eventQueue.put_nowait(msg) self.eventQueue.put_nowait(msg)
# connection events don't need to go to the story # connection events don't need to go to the story
if msg['event'] == 'connection': if msg['event'] == 'connection':
return return
if self.story: if self.story:
self.story.events.append(msg) self.story.events.append(msg)
else: else:
@ -539,7 +540,7 @@ class HugveyState(object):
# self.gone() # self.gone()
self.shutdown() self.shutdown()
continue continue
self.logger.debug("Received: {}".format(event)) self.logger.debug("Received: {}".format(event))
if event['event'] == 'connection': if event['event'] == 'connection':
# 'event': 'connection', # 'event': 'connection',
@ -547,11 +548,11 @@ class HugveyState(object):
# 'host': socket.gethostname(), # 'host': socket.gethostname(),
# 'ip': self.getIp(), # 'ip': self.getIp(),
self.config(event['host'], event['ip']) self.config(event['host'], event['ip'])
if event['event'] == 'language': if event['event'] == 'language':
self.setLanguage(event['code']) self.setLanguage(event['code'])
if event['event'] == 'pause': if event['event'] == 'pause':
self.pause() self.pause()
if event['event'] == 'block': if event['event'] == 'block':
@ -564,7 +565,7 @@ class HugveyState(object):
self.story._finish() # finish story AND hugvey state self.story._finish() # finish story AND hugvey state
if event['event'] == 'resume': if event['event'] == 'resume':
self.resume() self.resume()
if event['event'] == 'change_language': if event['event'] == 'change_language':
self.setLanguage(event['lang_code']) self.setLanguage(event['lang_code'])
if event['event'] == 'change_light': if event['event'] == 'change_light':
@ -578,7 +579,7 @@ class HugveyState(object):
# self.restart() # self.restart()
if self.story is None: if self.story is None:
return return
self.startMsgId = event['msg_id'] self.startMsgId = event['msg_id']
self.logger.debug(f"Restart from {self.startMsgId}") self.logger.debug(f"Restart from {self.startMsgId}")
self.restart() self.restart()
@ -588,18 +589,18 @@ class HugveyState(object):
def setLanguage(self, language_code): def setLanguage(self, language_code):
if language_code not in self.command.languages: if language_code not in self.command.languages:
raise Exception("Invalid language {}".format(language_code)) raise Exception("Invalid language {}".format(language_code))
self.logger.info(f"set language: {language_code}") self.logger.info(f"set language: {language_code}")
self.language_code = language_code self.language_code = language_code
if self.google: if self.google:
self.google.setLanguage(language_code) self.google.setLanguage(language_code)
if self.isRunning.is_set(): if self.isRunning.is_set():
self.restart() self.restart()
# self.story.reset() # self.story.reset()
# self.story.setStoryData(self.command.languages[language_code]) # self.story.setStoryData(self.command.languages[language_code])
def pause(self): def pause(self):
self.logger.info('Pause') self.logger.info('Pause')
if self.google: if self.google:
@ -608,7 +609,7 @@ class HugveyState(object):
self.story.pause() self.story.pause()
self.isRunning.clear() self.isRunning.clear()
self.setStatus(self.STATE_PAUSE) self.setStatus(self.STATE_PAUSE)
def resume(self): def resume(self):
""" Start playing without reset""" """ Start playing without reset"""
self.logger.info('Resume') self.logger.info('Resume')
@ -618,14 +619,14 @@ class HugveyState(object):
self.story.resume() self.story.resume()
self.isRunning.set() self.isRunning.set()
self.setStatus(self.STATE_RUNNING) self.setStatus(self.STATE_RUNNING)
def restart(self): def restart(self):
"""Start playing with reset""" """Start playing with reset"""
self.logger.info('Restart') self.logger.info('Restart')
if self.story: if self.story:
self.story.stop() self.story.stop()
self.resume() self.resume()
def block(self): def block(self):
"""Block a hugvey""" """Block a hugvey"""
self.logger.info('block') self.logger.info('block')
@ -635,37 +636,37 @@ class HugveyState(object):
self.story.finish() self.story.finish()
self.isRunning.clear() self.isRunning.clear()
self.setStatus(self.STATE_BLOCKED) self.setStatus(self.STATE_BLOCKED)
def available(self): def available(self):
"""Put in available mode""" """Put in available mode"""
self.logger.info('Finish/Await') self.logger.info('Finish/Await')
self.pause() self.pause()
self.setStatus(self.STATE_AVAILABLE) self.setStatus(self.STATE_AVAILABLE)
def setLightStatus(self, on): def setLightStatus(self, on):
status = 1 if on else 0 status = 1 if on else 0
self.logger.log(LOG_BS, f"Send /hugvey {status}") self.logger.log(LOG_BS, f"Send /hugvey {status}")
self.command.commandLight('/hugvey', [self.lightId, status]) self.command.commandLight('/hugvey', [self.lightId, status])
def setLightId(self, id): def setLightId(self, id):
""" """
Connect hugvey to another light Connect hugvey to another light
""" """
self.lightId = id self.lightId = id
def gone(self): def gone(self):
'''Status to 'gone' as in, shutdown/crashed/whatever '''Status to 'gone' as in, shutdown/crashed/whatever
''' '''
self.pause() self.pause()
if self.story: if self.story:
self.story.stop() self.story.stop()
self.logger.warn('Gone') self.logger.warn('Gone')
self.eventLogger.warn("Gone") self.eventLogger.warn("Gone")
self.isConfigured = None self.isConfigured = None
self.setStatus(self.STATE_GONE) self.setStatus(self.STATE_GONE)
def shutdown(self, definitive = False): def shutdown(self, definitive = False):
self.logger.info(f"Start shutdown sequence {definitive}") self.logger.info(f"Start shutdown sequence {definitive}")
self.eventLogger.critical(f"error: shutting down") self.eventLogger.critical(f"error: shutting down")
@ -674,7 +675,7 @@ class HugveyState(object):
if self.story: if self.story:
self.story.shutdown() self.story.shutdown()
self.story = None self.story = None
# shutdown for stream consumers already ran. Only clear references # shutdown for stream consumers already ran. Only clear references
if self.google: if self.google:
self.google = None self.google = None
@ -682,14 +683,14 @@ class HugveyState(object):
self.player = None self.player = None
if self.recorder: if self.recorder:
self.recorder = None self.recorder = None
if self.requireRestartAfterStop is None: if self.requireRestartAfterStop is None:
# prevent double setting of the same variable # prevent double setting of the same variable
# first call sometimes triggers second # first call sometimes triggers second
self.requireRestartAfterStop = not definitive self.requireRestartAfterStop = not definitive
self.notShuttingDown = False self.notShuttingDown = False
async def playStory(self): async def playStory(self):
while self.notShuttingDown: while self.notShuttingDown:
@ -707,13 +708,13 @@ class HugveyState(object):
self.logger.warn(f"Starting from {startMsgId}") self.logger.warn(f"Starting from {startMsgId}")
if not self.streamer: if not self.streamer:
await asyncio.sleep(1) await asyncio.sleep(1)
self.streamer.triggerStart() self.streamer.triggerStart()
self.story.setStoryData(self.command.languages[self.language_code]) self.story.setStoryData(copy.deepcopy(self.command.languages[self.language_code]))
self.setLightStatus(False) self.setLightStatus(False)
await self.story.run(startMsgId) await self.story.run(startMsgId)
# self.story = None # self.story = None
def getStreamer(self): def getStreamer(self):
if not self.streamer: if not self.streamer:
self.streamer = AudioStreamer( self.streamer = AudioStreamer(
@ -721,19 +722,19 @@ class HugveyState(object):
self.ip, self.ip,
int(self.command.config['voice']['port']) + self.id, int(self.command.config['voice']['port']) + self.id,
self.id) self.id)
if self.command.config['voyeur']: if self.command.config['voyeur']:
self.logger.warn("Debug on: Connecting Audio player") self.logger.warn("Debug on: Connecting Audio player")
self.player = Player( self.player = Player(
self.command.config['voice']['src_rate'], self.command.config['voice']['out_rate']) self.command.config['voice']['src_rate'], self.command.config['voice']['out_rate'])
self.streamer.addConsumer(self.player) self.streamer.addConsumer(self.player)
if self.command.config['voice']['record_dir']: if self.command.config['voice']['record_dir']:
self.logger.warn("Record Audio of conversation") self.logger.warn("Record Audio of conversation")
self.recorder = Recorder( self.id, self.recorder = Recorder( self.id,
self.command.config['voice']['src_rate'], self.command.config['voice']['record_dir']) self.command.config['voice']['src_rate'], self.command.config['voice']['record_dir'])
self.streamer.addConsumer(self.recorder) self.streamer.addConsumer(self.recorder)
self.logger.debug("Start Speech") self.logger.debug("Start Speech")
self.google = GoogleVoiceClient( self.google = GoogleVoiceClient(
hugvey=self, hugvey=self,
@ -748,7 +749,7 @@ class HugveyState(object):
''' '''
Start the audio streamer service Start the audio streamer service
''' '''
self.logger.debug("Start audio loop") self.logger.debug("Start audio loop")
while self.notShuttingDown: while self.notShuttingDown:

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
apt-get update apt-get update
apt-get install -y munin-node bc supervisor libsox-fmt-pulse apt-get install -y munin-node bc supervisor
cp installation/rpi-internal-temp /usr/share/munin/plugins cp installation/rpi-internal-temp /usr/share/munin/plugins
ln -sf /usr/share/munin/plugins/rpi-internal-temp /etc/munin/plugins/rpi-internal-temp ln -sf /usr/share/munin/plugins/rpi-internal-temp /etc/munin/plugins/rpi-internal-temp
rm /etc/munin/plugins/irqstats rm /etc/munin/plugins/irqstats

View file

@ -1,34 +1,51 @@
#N canvas 223 291 679 478 10; #N canvas 200 136 660 592 10;
#X obj 155 392 dac~; #X obj 131 277 dac~;
#X obj 155 342 readsf~; #X obj 131 227 readsf~;
#X msg 81 297 0; #X obj 209 209 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144
#X obj 233 324 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144
-1 -1; -1 -1;
#X text 252 322 (re-)start loop; #X text 229 206 (re-)start loop;
#X obj 239 60 oscparse; #X msg 132 170 open /home/a/projects/pd-play/testaudio.wav \, 1;
#X obj 239 85 list trim; #X obj 208 459 netsend -u -b;
#X obj 239 38 netreceive -u -b 5555; #X obj 208 481 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
#X obj 339 163 route 1; 1;
#X obj 425 162 route 0; #X msg 355 464 disconnect;
#X obj 339 119 route trigger; #X obj 208 393 list prepend send;
#X text 69 276 stop loop; #X obj 208 415 list trim;
#X obj 469 216 print "starting loop"; #X msg 357 434 connect localhost 5555;
#X obj 470 239 print "stopping loop"; #X obj 208 345 oscformat /trigger;
#X msg 155 244 open testaudio.wav \, 1; #X msg 51 193 0;
#X text 237 17 listen to osc at port 5555; #X obj 227 83 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
#X text 155 219 change audiofile HERE; -1;
#X text 40 173 stop loop;
#X msg 210 292 1;
#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
1;
#X text 309 61 !STOP!;
#X text 422 60 Playing indicator;
#X msg 261 118 1;
#X msg 330 117 0;
#X connect 1 0 0 0; #X connect 1 0 0 0;
#X connect 1 0 0 1; #X connect 1 0 0 1;
#X connect 1 1 3 0; #X connect 1 1 2 0;
#X connect 2 0 1 0; #X connect 2 0 4 0;
#X connect 3 0 14 0; #X connect 2 0 15 0;
#X connect 4 0 1 0;
#X connect 5 0 6 0; #X connect 5 0 6 0;
#X connect 6 0 10 0;
#X connect 7 0 5 0; #X connect 7 0 5 0;
#X connect 8 0 3 0; #X connect 8 0 9 0;
#X connect 8 0 12 0; #X connect 9 0 5 0;
#X connect 9 0 2 0; #X connect 10 0 5 0;
#X connect 9 0 13 0; #X connect 11 0 8 0;
#X connect 10 0 8 0; #X connect 12 0 1 0;
#X connect 10 0 9 0; #X connect 13 0 10 0;
#X connect 14 0 1 0; #X connect 13 0 2 0;
#X connect 13 0 21 0;
#X connect 15 0 11 0;
#X connect 16 0 12 0;
#X connect 16 0 7 0;
#X connect 16 0 22 0;
#X connect 21 0 18 0;
#X connect 22 0 18 0;

View file

@ -25,7 +25,7 @@ while(1):
print("sending start to {0} on port {1}".format(args.ip, args.port)) print("sending start to {0} on port {1}".format(args.ip, args.port))
client.send_message("/trigger", 1) client.send_message("/trigger", 1)
time.sleep(2) time.sleep(2)
print("sending stop to {0} on port {1}".format(args.ip, args.port)) print("sending start to {0} on port {1}".format(args.ip, args.port))
client.send_message("/trigger", 0) client.send_message("/trigger", 0)
time.sleep(2) time.sleep(2)

View file

@ -7,7 +7,7 @@ voice:
port: 4444 port: 4444
chunk: 2972 chunk: 2972
google_credentials: "../test_googlespeech/My First Project-0c7833e0d5fa.json" google_credentials: "../test_googlespeech/My First Project-0c7833e0d5fa.json"
hugveys: 25 hugveys: 26
languages: languages:
- code: en-GB - code: en-GB
file: story_en.json file: story_en.json
@ -28,7 +28,7 @@ languages:
ms_lang: "fr-FR" ms_lang: "fr-FR"
web: web:
port: 8888 port: 8888
files_dir: "local/" files_dir: "local/"
light: light:
ip: "192.168.178.15" ip: "192.168.178.15"
port: 7400 port: 7400

View file

@ -18,60 +18,60 @@ class Timeline{
{content: '.', start: new Date(), type: 'point', group: 1} {content: '.', start: new Date(), type: 'point', group: 1}
]); ]);
console.log('init timeline'); console.log('init timeline');
let groups = []; let groups = [];
for(let hid = 1; hid<=this.count; hid++) { for(let hid = 1; hid<=this.count; hid++) {
groups.push({id: parseInt(hid), content: 'Hugvey #'+hid}); groups.push({id: parseInt(hid), content: 'Hugvey #'+hid});
this.eventDataSet.add({content: 'initiate', start: new Date(), type: 'point', group: parseInt(hid)}) this.eventDataSet.add({content: 'initiate', start: new Date(), type: 'point', group: parseInt(hid)})
} }
let dataGroups = new vis.DataSet(groups); let dataGroups = new vis.DataSet(groups);
let options = { let options = {
// 'rollingMode': {'follow': true, 'offset': .8 } // 'rollingMode': {'follow': true, 'offset': .8 }
}; };
console.log('groups', dataGroups, groups, options); console.log('groups', dataGroups, groups, options);
this.timeline = new vis.Timeline(this.el, this.eventDataSet, dataGroups, options); this.timeline = new vis.Timeline(this.el, this.eventDataSet, dataGroups, options);
let tl = this.timeline; let tl = this.timeline;
let startDate = new Date(); let startDate = new Date();
startDate.setMinutes(startDate.getMinutes()-1); startDate.setMinutes(startDate.getMinutes()-1);
let endDate = new Date(); let endDate = new Date();
endDate.setMinutes(endDate.getMinutes()+20); endDate.setMinutes(endDate.getMinutes()+20);
setTimeout(function(){ setTimeout(function(){
tl.setWindow(startDate, endDate); tl.setWindow(startDate, endDate);
}, 500); }, 500);
this.moveInterval = setInterval(function(){ this.moveInterval = setInterval(function(){
// skip movement if not visible // skip movement if not visible
tl.moveTo(new Date()); tl.moveTo(new Date());
}, 1000); }, 1000);
ws.addEventListener( 'message', this); ws.addEventListener( 'message', this);
} }
handleEvent(e) { handleEvent(e) {
console.log('handle', e, this); console.log('handle', e, this);
if(e.type == 'message') { if(e.type == 'message') {
this.wsOnMessage(e) this.wsOnMessage(e)
} }
} }
wsOnMessage(e) { wsOnMessage(e) {
let msg = JSON.parse( e.data ); let msg = JSON.parse( e.data );
if ( typeof msg['action'] === 'undefined' ) { if ( typeof msg['action'] === 'undefined' ) {
console.error( "not a valid message: " + e.data ); console.error( "not a valid message: " + e.data );
return; return;
} }
if(msg['action'] != 'log') { if(msg['action'] != 'log') {
return; return;
} }
console.debug(msg, this); console.debug(msg, this);
let hv_id = parseInt(msg['id']); let hv_id = parseInt(msg['id']);
// {'action': 'log', 'id':hugvey_id, 'type': items[0], 'info', 'args'} // {'action': 'log', 'id':hugvey_id, 'type': items[0], 'info', 'args'}
let d, parts; let d, parts;
@ -91,7 +91,7 @@ class Timeline{
this.eventDataSet.update(d); this.eventDataSet.update(d);
console.log('update', d); console.log('update', d);
} else { } else {
this.eventDataSet.add({id: mId, content: msgContent, title: `${msgContent} (${msgId})`, start: new Date(), group: hv_id, 'className': 'message'}); this.eventDataSet.add({id: mId, content: msgContent, title: `${msgContent} (${msgId})`, start: new Date(), group: hv_id, 'className': 'message'});
} }
break; break;
case 'speaking': case 'speaking':
@ -101,7 +101,7 @@ class Timeline{
let id = parts.shift(); let id = parts.shift();
let content = parts.join(' '); let content = parts.join(' ');
let scId = 'sc-'+id+'-'+hv_id; let scId = 'sc-'+id+'-'+hv_id;
if(info.startsWith('start')){ if(info.startsWith('start')){
this.eventDataSet.add({content: info, start: new Date(), type: 'point', group: hv_id, 'className': 'speech'}); this.eventDataSet.add({content: info, start: new Date(), type: 'point', group: hv_id, 'className': 'speech'});
} }
@ -115,7 +115,7 @@ class Timeline{
this.eventDataSet.update(d); this.eventDataSet.update(d);
} else { } else {
console.log('add'); console.log('add');
this.eventDataSet.add({id: scId, content: content, title: content, start: new Date(), group: hv_id, 'className': 'speech'}); this.eventDataSet.add({id: scId, content: content, title: content, start: new Date(), group: hv_id, 'className': 'speech'});
} }
} }
if(info.startsWith('end')){ if(info.startsWith('end')){
@ -125,7 +125,7 @@ class Timeline{
this.eventDataSet.update(d); this.eventDataSet.update(d);
} }
} }
break; break;
case 'story': case 'story':
// 'info': 'start'/'finished' // 'info': 'start'/'finished'
@ -147,4 +147,4 @@ class Timeline{
} }
} }
var tl = new Timeline(ws, document.getElementById('line'), 25); var tl = new Timeline(ws, document.getElementById('line'), 26);