First client version

This commit is contained in:
Ruben van de Ven 2019-01-15 23:34:59 +01:00
parent bf8649c5fe
commit ad9a4b8f13
5 changed files with 98 additions and 57 deletions

8
client_config.yml Normal file
View File

@ -0,0 +1,8 @@
events:
cmd_address: "tcp://127.0.0.1:5555"
publish_address: "tcp://0.0.0.0:5556"
voice:
input_rate: 44100
target_rate: 16000
port: 4444
input_name: null

View File

@ -8,13 +8,15 @@ import time
import zmq import zmq
import asyncio import asyncio
from zmq.asyncio import Context from zmq.asyncio import Context
import yaml
import re
from .communication import zmqReceive, zmqSend, getTopic from .communication import zmqReceive, zmqSend, getTopic
logger = logging.getLogger("client") logger = logging.getLogger("client")
class VoiceServer(object): class VoiceServer(object):
"""A UDP server, providing mic data at 16 kHz""" """A UDP server, providing mic data at 16 kHz"""
def __init__(self, voice_port, input_rate, input_name = None, target_rate = 16000): def __init__(self, voice_port: int, input_rate: int, input_name: str = None, target_rate: int = 16000):
self.voice_port = voice_port self.voice_port = voice_port
self.input_rate = input_rate self.input_rate = input_rate
self.target_rate = target_rate self.target_rate = target_rate
@ -54,7 +56,7 @@ class VoiceServer(object):
pass pass
return (None, pyaudio.paContinue) return (None, pyaudio.paContinue)
async def start(self): def start(self):
FORMAT = pyaudio.paInt16 FORMAT = pyaudio.paInt16
CHANNELS = 1 CHANNELS = 1
CHUNK = 4096 CHUNK = 4096
@ -99,14 +101,21 @@ class VoiceServer(object):
def stop(self): def stop(self):
self.stopped = True self.stopped = True
async def asyncStart(self, loop):
future = loop.run_in_executor(None, self.start)
r = await future
# await self.start()
class CommandHandler(object): class CommandHandler(object):
def __init__(self, hugvey_id, event_address = "tcp://127.0.0.1:5555"): def __init__(self, hugvey_id, cmd_address = "tcp://127.0.0.1:5555", publish_address = "tcp://0.0.0.0:5555"):
self.eventQueue = [] self.eventQueue = []
self.ctx = Context.instance() self.ctx = Context.instance()
self.hugvey_id = hugvey_id self.hugvey_id = hugvey_id
self.event_address = event_address self.cmd_address = cmd_address
self.publish_address = publish_address
def handle(self, cmd): def handle(self, cmd):
# self.sendMessage({'reply':'test'})
if not 'action' in cmd: if not 'action' in cmd:
logger.critical("Invalid command: {}".format(cmd)) logger.critical("Invalid command: {}".format(cmd))
return return
@ -117,9 +126,11 @@ class CommandHandler(object):
def cmdPlay(self, msgId, msgText): def cmdPlay(self, msgId, msgText):
# espeak(msgText) # espeak(msgText)
logger.inof("Play: {}".format(msgText)) # TODO kill if playing & play wave file
# preferably a cat (local)/curl (remote) pipe into player
logger.info("Play: {}".format(msgText))
time.sleep(2) time.sleep(2)
sendMessage({ self.sendMessage({
'event': 'playbackFinish', 'event': 'playbackFinish',
'msgId': msgId 'msgId': msgId
}) })
@ -129,26 +140,25 @@ class CommandHandler(object):
async def command_listener(self): async def command_listener(self):
s = self.ctx.socket(zmq.SUB) s = self.ctx.socket(zmq.SUB)
s.connect(self.event_address) s.connect(self.cmd_address)
queueName = 'hv{}'.format(self.hugvey_id)
s.subscribe(queueName)
logger.info("Subscribed to commands on {}".format(queueName))
while True:
hugvey_id, msg = await zmqReceive(s)
# topic, msg = await s.recv_multipart()
print('received', msg)
s.close()
async def event_sender(port):
s = self.ctx.socket(zmq.PUB)
s.connect(self.event_send_address)
topic = getTopic(self.hugvey_id) topic = getTopic(self.hugvey_id)
s.subscribe(topic) s.subscribe(topic)
logger.info("Subscribed to commands for {} on {}".format(topic, self.cmd_address))
while True:
hugvey_id, cmd = await zmqReceive(s)
self.handle(cmd)
# topic, msg = await s.recv_multipart()
# print('received', msg, time.time())
s.close()
logger.info("Subscribed to commands on {}".format(topic)) async def event_sender(self):
s = self.ctx.socket(zmq.PUB)
# TODO: see if we can connect() here. So we can PUSH(??) the ip
s.bind(self.publish_address)
logger.info("Publish on: {}".format(self.publish_address))
while True: while True:
for i in range(len(self.eventQueue)): for i in range(len(self.eventQueue)):
hugvey_id, msg = await zmqSend(s, self.hugvey_id, self.eventQueue.pop(0)) await zmqSend(s, self.hugvey_id, self.eventQueue.pop(0))
if len(self.eventQueue) == 0: if len(self.eventQueue) == 0:
await asyncio.sleep(0.05) await asyncio.sleep(0.05)
@ -158,21 +168,41 @@ class Hugvey(object):
"""The Hugvey client, to be ran on the Raspberry Pi's """The Hugvey client, to be ran on the Raspberry Pi's
""" """
def __init__(self): def __init__(self):
self.id = self.getId()
pass pass
def getId(self) -> int:
"""Get Hugvey ID from hostname"""
h = socket.gethostname()
return int(re.findall('\d+', h )[0])
def loadConfig(self, filename): def loadConfig(self, filename):
# filename with open(filename, 'r') as fp:
pass logger.debug('Load config from {}'.format(filename))
self.config = yaml.safe_load(fp)
async def startCommandListener(): async def startCommandListener():
return await self.cmd_server.command_listener() return await self.cmd_server.command_listener()
def start(self): def start(self):
self.voice_server = VoiceServer(4444, 44100) self.voice_server = VoiceServer(
self.cmd_server = CommandHandler(1) voice_port = int(self.config['voice']['port']),
input_rate = int(self.config['voice']['input_rate']),
input_name = self.config['voice']['input_name'],
target_rate = int(self.config['voice']['target_rate']),
)
self.cmd_server = CommandHandler(
hugvey_id = self.id,
cmd_address = self.config['events']['cmd_address'],
publish_address = self.config['events']['publish_address'],
)
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
logger.info('start') logger.info('start')
loop.run_until_complete(self.voice_server.start()) # self.voice_server.asyncStart(loop)
loop.run_until_complete(self.cmd_server.command_listener()) # loop.run_until_complete(self.voice_server.start())
loop.run_until_complete(self.cmd_server.command_listener()) asyncio.ensure_future(self.voice_server.asyncStart(loop))
asyncio.ensure_future(self.cmd_server.command_listener())
asyncio.ensure_future(self.cmd_server.event_sender())
loop.run_forever()
logger.info('done') logger.info('done')

View File

@ -7,11 +7,11 @@ def getTopic(hugvey_id):
return "hv{}".format(hugvey_id) return "hv{}".format(hugvey_id)
def zmqSend(socket, hugvey_id, msg): async def zmqSend(socket, hugvey_id, msg):
msgData = json.dumps(msg) msgData = json.dumps(msg)
topic = getTopic(hugvey_id) topic = getTopic(hugvey_id)
logger.info("Send 0mq to {} containing {}".format(topic, msg)) logger.info("Send 0mq to {} containing {}".format(topic, msg))
return socket.send_multipart([topic.encode(), msgData.encode()]) await socket.send_multipart([topic.encode(), msgData.encode()])
async def zmqReceive(socket): async def zmqReceive(socket):
topic, msg = await socket.recv_multipart() topic, msg = await socket.recv_multipart()

View File

@ -2,34 +2,15 @@ from hugvey.client import Hugvey
import coloredlogs, logging import coloredlogs, logging
import argparse import argparse
if __name__ == '__main__': if __name__ == '__main__':
argParser = argparse.ArgumentParser(description='Start up a Hugvey pillow. Mic stream becomes available on TCP Socket, and starts listening + emitting events') argParser = argparse.ArgumentParser(description='Start up a Hugvey pillow. Mic stream becomes available on TCP Socket, and starts listening + emitting events')
# argParser.add_argument( argParser.add_argument(
# '--voice-port', '--config',
# required=True, '-c',
# type=int, required=True,
# help='The port on which to listen for TCP connections (listens on 0.0.0.0) for audio receivers' type=str,
# ) help='The yaml config file to load'
# argParser.add_argument( )
# '--event-address',
# type=str,
# default="127.0.0.1",
# help='The ip to which to set up the TCP connection for sending events. Can also be an existing unix file socket.'
# )
# argParser.add_argument(
# '--event-port',
# type=int,
# help='The port on which to set up the TCP connection for sending events. Ignored if --event-address points to a file socket'
# )
# argParser.add_argument(
# '--language-code',
# default="en-US",
# type=str,
# help='Language code for Speech to Text (BCP-47 language tag)'
# )
argParser.add_argument( argParser.add_argument(
'--verbose', '--verbose',
'-v', '-v',
@ -42,6 +23,6 @@ if __name__ == '__main__':
level=logging.DEBUG if args.verbose else logging.INFO, level=logging.DEBUG if args.verbose else logging.INFO,
) )
# server = VoiceServer(voice_port=4444, input_rate=44100)
hv = Hugvey() hv = Hugvey()
hv.loadConfig(args.config)
hv.start() hv.start()

22
test_pub.py Normal file
View File

@ -0,0 +1,22 @@
import zmq
import random
import sys
import time
import hugvey.communication
port = "5555"
if len(sys.argv) > 1:
port = sys.argv[1]
int(port)
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:%s" % port)
while True:
# topic = random.randrange(9999,10005)
# topic = "hv" + str(random.randrange(1,3))
messagedata = str(time.time())
# print ("{} {}".format(topic, messagedata))
hugvey.communication.zmqSend(socket, random.randrange(1,3), messagedata)
time.sleep(1)