Compare commits

...

10 Commits

Author SHA1 Message Date
NATURESPEAK
e2f841465c fix endings etc. 2022-04-11 13:09:01 +02:00
NATURESPEAK
9673a3d4f0 dansbox 2022-04-10 15:39:46 +02:00
Gauthier
19f7b8d30d speak simple 2022-04-10 15:21:22 +02:00
NATURESPEAK
d70f643836 new osc, tempo, etc. 2022-04-10 15:20:13 +02:00
NATURESPEAK
5857da6e00 osc server localhost 2022-04-09 18:01:15 +02:00
NATURESPEAK
35caa1e0ad revert temp 2022-04-04 10:48:02 +02:00
NATURESPEAK
b2cc817911 jaccard, temp, etc. 2022-04-04 09:12:49 +02:00
gauthiier
c33d577bb6 debug, threading, etc 2022-03-20 16:06:57 +01:00
Gauthier
e91d3012cb two way comm 2022-03-20 10:45:12 +01:00
Gauthier
72b984bf48 terminal out 2022-03-17 15:29:44 +01:00
9 changed files with 908 additions and 74 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
#macos #macos
.DS_store .DS_store
venv venv
sandox
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/

2
run.sh Executable file
View File

@ -0,0 +1,2 @@
. venv/bin/activate
python speak_broadcast.py -c ../assets/voice.config.json

View File

@ -1,11 +1,13 @@
import argparse, json, sys, time, random import argparse, json, sys, time, random
import utterance.voice import utterance.voice
import utterance.utils import utterance.utils
UTTERANCE_LEN = 64 #<--------------- these should be in config
def main() -> int: def main() -> int:
p = argparse.ArgumentParser() p = argparse.ArgumentParser()
p.add_argument("-c", "--config", type=str, default="config.json", help="configuratin file") p.add_argument("-c", "--config", type=str, default="voice.config.json", help="configuratin file")
p.add_argument("-i", "--iterations", type=int, default=10, help="number of iterations") p.add_argument("-i", "--iterations", type=int, default=10, help="number of iterations")
args = p.parse_args() args = p.parse_args()
@ -16,25 +18,23 @@ def main() -> int:
voices = [] voices = []
for v in conf['voices']: for v in conf['voices']:
voice = utterance.voice.Voice(name=v["name"].upper(), model=v['model_dir'], tokenizer=v['tokeniser_file'], temp=float(v["temperature"]), lenght=32) model = v['model']
voice = utterance.voice.Voice(name=v["name"].upper(), model=model['model_dir'], tokenizer=model['tokeniser_file'], temp=float(model["temperature"]), length=UTTERANCE_LEN)
voices.append(voice) voices.append(voice)
nbr_voices = len(voices) current_voice_name = ""
current_voice = ""
for i in range(args.iterations): for i in range(args.iterations):
rindex = random.randint(0, nbr_voices - 1) v = random.sample(voices, 1)[0]
v = voices[rindex] if v.name != current_voice_name:
if v.name != current_voice: current_voice_name = v.name
print("==========") print("==========")
print(v.name + ":") print(v.name + ":")
current_voice = v.name t = v.utter_one()
t = v.utter_one()
if t != None:
t = utterance.utils.clean(t) t = utterance.utils.clean(t)
t = utterance.utils.format(t) t = utterance.utils.format(t)
print(t) print(t)
time.sleep(4) # time.sleep(1)
if __name__ == '__main__': if __name__ == '__main__':

359
speak_broadcast-backup.py Normal file
View File

@ -0,0 +1,359 @@
import argparse, json, sys, time, random, logging, signal, threading
import utterance.voice
import utterance.utils
import utterance.osc
import examine.metric
logging.basicConfig(level=logging.INFO)
UTTERANCE_LEN = 64 #<--------------- these should be in config
NUM_METRIC_GEN = 75
NUM_SAMPLE_VOICES = 3
RANDOM_SEED_TIMER_MIN = 2
STATE_TRANSITION_TIMER_MIN = 1.5
broadcast = None
metric = None
exit = False
terminal = False
debug = False
state = "METRIC"
def format_str(text) -> str:
t = utterance.utils.clean(text)
return utterance.utils.format(t)
def tokenise_str(text):
return utterance.utils.tokenise(text)
def utter_one(v, temp=None, length=None) -> str:
u = v.utter_one(temp=temp, length=length)
return format_str(u)
def prompt_one(v, pinput: str, temp=None, length=None) -> str:
u = v.prompt(pinput=pinput, temp=None, length=length)
return format_str(u)
def utter_one_vectorise(v, temp=None, length=None):
global metric
uv = utter_one(v, temp, length)
uv_vec = metric.vector(uv)
return uv, uv_vec
def prompt_one_vectorise(v, pinput: str, temp=None, length=None):
global metric
uv = prompt_one(v, pinput, temp, length)
uv_vec = metric.vector(uv)
return uv, uv_vec
def utter_n_vectorise_distance(v, n, vec, temp=None, length=None):
global metric
results = []
texts = v.utter_n(n=n, temp=temp, length=length)
for t in texts:
t = format_str(t)
t_vec = metric.vector(t)
d = examine.metric.cos_dist(vec, t_vec)
results.append([d, t, v])
return results
def terminal_utterance(utterance):
if terminal:
print(utterance, end="")
def broadcast_utterance(v, utterance):
global broadcast, exit, debug
# Send all text to server to calculate bounds in advance
broadcast.utterance(utterance, v.calculate)
text = ""
broadcast.utterance(text, v.channel)
terminal_utterance(text)
time.sleep(2)
frags = v.fragments(utterance)
for f in frags:
text += f
broadcast.utterance(text, v.channel)
terminal_utterance(f)
# sleep_time = 2
# toks = tokenise_str(f)
toks = f.split()
sleep_time = len(toks)
if sleep_time <= 2:
sleep_time += 1
time.sleep(sleep_time)
if exit:
return
broadcast.command('clear')
print("==========")
time.sleep(3)
def find_candidates(v, uv_vec, voices, results):
logging.info(f"LOOP::finding candidates")
start = time.time()
candidates = random.sample(voices, NUM_SAMPLE_VOICES)
for c in candidates:
if exit:
break
if c == v:
continue
results += utter_n_vectorise_distance(c, NUM_METRIC_GEN, uv_vec)
results.sort(key=lambda t: t[0], reverse=True)
lapse = time.time() - start
logging.info(f"LOOP::done - {lapse} secs")
def update():
global exit
while not exit:
try:
utterance.osc.update()
except Exception as e:
logging.error(e)
pass
time.sleep(0.2)
def signal_terminate(signum, frame):
global exit
logging.warning("::SIGNAL TERMINATE::")
exit = True
def main() -> int:
global broadcast, metric, terminal, debug, state
p = argparse.ArgumentParser()
p.add_argument("-c", "--config", type=str, default="voice.config.json", help="configuratin file")
p.add_argument("-i", "--iterations", type=int, default=10, help="number of iterations")
p.add_argument("-t", "--terminal", action='store_true', help="print to terminal")
args = p.parse_args()
logging.info(f"INIT::loading config file - {args.config}")
with open(args.config) as f:
conf = json.load(f)
logging.info(conf)
terminal = args.terminal
#--------------------#
# VOICES
#--------------------#
logging.info(f"INIT::creating voices")
voices = []
for v in conf['voices']:
model = v['model']
voice = utterance.voice.Voice(name=v["name"].upper(), model=model['model_dir'], tokenizer=model['tokeniser_file'], temp=float(model["temperature"]), length=UTTERANCE_LEN)
voice.set_channel(v['osc_channel']['root'], v['osc_channel']['utterance'])
voice.set_calculate(v['osc_channel']['root'], v['osc_channel']['calculate'])
voice.set_temperature(v['osc_channel']['root'], v['osc_channel']['temperature'])
voices.append(voice)
#--------------------#
# QUESTION
#--------------------#
logging.info(f"INIT::setting up question")
questions = conf['questions']
questions_array = questions.copy()
random.shuffle(questions_array)
#--------------------#
# NET
#--------------------#
logging.info(f"INIT::setting up OSC")
utterance.osc.start_osc()
broadcast = utterance.osc.OscBroadcaster(name="osc_broadcast", host=conf['host_voicemachine'], port=conf['port_voicemachine'], command_channel=conf['command_osc_channel'])
def receiver_cb(temp, name):
global debug
if type(temp) == str and temp == "DEBUG":
debug = name
logging.info(f"DEBUG MODE: {debug}")
else: # temperature
for v in voices:
if v.name == name:
print(f'{name} - {temp}')
v.temp = temp
# broadcast.temperature(temp, v.temperature) # <-- doesn works because deadlocks in osc_process...
receiver = utterance.osc.OscReceiver(name="osc_receiver", host=conf['host_machinespeak'], port=conf['port_machinespeak'], callback_fn=receiver_cb)
#--------------------#
# METRIC
#--------------------#
logging.info(f"INIT::loading doc2vec metrics")
metric = examine.metric.Metric(model_input='data/models/doc2vec.model')
#--------------------#
# RANDOM
#--------------------#
def random_seed(seconds):
global t_random_seed, exit
i = 0
while i < seconds:
i += 1
time.sleep(1)
if exit:
return
logging.info("RANDOM::SEEDING RANDOM")
random.seed(time.time())
if not exit:
t_random_seed = threading.Thread(target=random_seed, args=(random.randint(60, 60 * RANDOM_SEED_TIMER_MIN), ))
t_random_seed.start()
t_random_seed = threading.Thread(target=random_seed, args=(random.randint(60, 60 * RANDOM_SEED_TIMER_MIN), ))
t_random_seed.start()
#--------------------#
# STATE TRANSITION
#--------------------#
# STATES = ["METRIC", "QUESTION", "RANDOM"]
def state_transition(seconds):
global t_state_transition, exit, state
i = 0
while i < seconds:
i += 1
time.sleep(1)
if exit:
return
logging.info("STATE::STATE TRANSITION")
state = "QUESTION"
if not exit:
t_state_transition = threading.Thread(target=state_transition, args=(random.randint(60, 60 * STATE_TRANSITION_TIMER_MIN), ))
t_state_transition.start()
t_state_transition = threading.Thread(target=state_transition, args=(random.randint(60, 60 * STATE_TRANSITION_TIMER_MIN), ))
t_state_transition.start()
#--------------------#
# A
#--------------------#
logging.info(f"INIT::generating first utterance")
v = random.choice(voices)
uv, uv_vec = utter_one_vectorise(v)
# -- this only updates OSC --
# -- might not need this in production
t_update = threading.Thread(target=update)
t_update.start()
while not exit:
if state == "METRIC":
logging.info(f"- state METRIC")
results = []
t = threading.Thread(target=find_candidates, args=[v, uv_vec, voices, results])
t.start()
logging.info(f"METRIC::broadcasting {v.name}")
broadcast_utterance(v, uv)
t.join()
# ok here we need to randomise maybe...?!
# ([d, t, v])
choice = results[0]
# makse sure we don't say the same thing over and over again
for r in results:
v = r[2]
u = r[1]
if v.select(u):
choice = r
break
else:
logging.info(f"METRIC::reduncancy {v.name}")
v = choice[2]
uv = choice[1]
uv_vec = metric.vector(uv)
logging.info(f"METRIC::next {v.name}")
elif state == "QUESTION":
logging.info(f"- state QUESTION")
if len(questions_array) <= 0:
questions_array = questions.copy()
random.shuffle(questions_array)
# random question
q = questions_array.pop(0)
# random voice
v = random.choice(voices)
# random voice asks random question
logging.info(f"QUESTION::{v.name} : {q['question']}")
broadcast.utterance(q['question'], v.calculate)
broadcast.utterance(q['question'], v.channel)
time.sleep(15)
# answer
v = [e for e in voices if e.name == q['voice']]
if len(v) == 1:
v = v[0]
logging.info(f"QUESTION::answer - {v.name}")
uv, uv_vec = prompt_one_vectorise(v, q['prompt'])
v.remember(uv)
# broadcast_utterance(v, uv) <-- this is broadcasted as part of METRIC STATE
state = "METRIC"
elif state == "RANDOM":
logging.info(f"- state RANDOM")
v = random.choice(voices)
l = random.randint(5, UTTERANCE_LEN)
uv, uv_vec = utter_one_vectorise(v, length=l)
broadcast_utterance(v, uv)
t_update.join()
logging.info(f"TERMINATE::terminating OSC")
utterance.osc.terminate_osc()
# if t_random_seed:
logging.info(f"TERMINATE::random seed")
t_random_seed.join()
logging.info(f"TERMINATE::state transition")
t_state_transition.join()
logging.info(f"FIN")
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_terminate)
sys.exit(main())

View File

@ -1,34 +1,56 @@
import argparse, json, sys, time, random, logging, signal, threading import argparse, json, sys, time, random, logging, signal, threading, string
import utterance.voice import utterance.voice
import utterance.utils import utterance.utils
import utterance.osc import utterance.oscosc
import examine.metric import examine.metric
UTTERANCE_LEN = 16 #<--------------- these should be in config logging.basicConfig(level=logging.INFO)
NUM_METRIC_GEN = 50
UTTERANCE_LEN = 64 #<--------------- these should be in config
NUM_METRIC_GEN = 75
NUM_SAMPLE_VOICES = 3 NUM_SAMPLE_VOICES = 3
RANDOM_SEED_TIMER_MIN = 2
STATE_TRANSITION_TIMER_MIN = 10
broadcast = None broadcast = None
metric = None metric = None
exit = False exit = False
terminal = False
debug = False
state = "METRIC"
B_SKIP = []
B_SWAPS = {}
def format_str(text) -> str: def format_str(text) -> str:
t = utterance.utils.clean(text) t = utterance.utils.clean(text)
return utterance.utils.format(t) return utterance.utils.format(t)
def utter_one(v) -> str: def tokenise_str(text):
u = v.utter_one() return utterance.utils.tokenise(text)
def utter_one(v, temp=None, length=None) -> str:
u = v.utter_one(temp=temp, length=length)
return format_str(u) return format_str(u)
def utter_one_vectorise(v): def prompt_one(v, pinput: str, temp=None, length=None) -> str:
u = v.prompt(pinput=pinput, temp=None, length=length)
return format_str(u)
def utter_one_vectorise(v, temp=None, length=None):
global metric global metric
uv = utter_one(v) uv = utter_one(v, temp, length)
uv_vec = metric.vector(uv) uv_vec = metric.vector(uv)
return uv, uv_vec return uv, uv_vec
def utter_n_vectorise_distance(v, n, vec): def prompt_one_vectorise(v, pinput: str, temp=None, length=None):
global metric
uv = prompt_one(v, pinput, temp, length)
uv_vec = metric.vector(uv)
return uv, uv_vec
def utter_n_vectorise_distance(v, n, vec, temp=None, length=None):
global metric global metric
results = [] results = []
texts = v.utter_n(n=n) texts = v.utter_n(n=n, temp=temp, length=length)
for t in texts: for t in texts:
t = format_str(t) t = format_str(t)
t_vec = metric.vector(t) t_vec = metric.vector(t)
@ -36,26 +58,98 @@ def utter_n_vectorise_distance(v, n, vec):
results.append([d, t, v]) results.append([d, t, v])
return results return results
def terminal_utterance(utterance):
if terminal:
print(utterance, end="")
def fix_ending(frags):
result = frags.copy()
end = result[-1]
end = end.translate(str.maketrans('', '', string.punctuation))
fix = utterance.utils.fix_sentence(end)
if fix is None or len(fix) == 0:
result = result[:-1]
# result[-1] = result[-1]
else:
result[-1] = fix
result[-1] = utterance.utils.fix_punctuation(result[-1])
print(result)
return result
def fix_beginning(frags):
global B_SKIP, B_SWAPS
result = frags.copy()
beginnig = result[0]
toks = beginnig.split()
if len(toks) > 0:
f = toks[0].lower()
if f[0] in string.punctuation:
f = f[1:]
if f in B_SKIP:
if len(toks) > 2:
result[0] = " ".join(toks[1:]).capitalize() + "\n"
else:
if result[1][0] == ' ':
result[1] = result[1][1:]
result[1] = result[1].capitalize()
return result[1:]
elif toks[0] in B_SWAPS:
result[0] = result[0].replace(toks[0], B_SWAPS[toks[0]])
return result
def broadcast_utterance(v, utterance): def broadcast_utterance(v, utterance):
global broadcast, exit print(utterance)
text = v.name.upper() + ":\n" global broadcast, exit, debug
# Send all text to server to calculate bounds in advance
broadcast.utterance(utterance, v.calculate)
text = ""
broadcast.utterance(text, v.channel) broadcast.utterance(text, v.channel)
terminal_utterance(text)
time.sleep(2) time.sleep(2)
frags = v.fragments(utterance) frags = v.fragments(utterance)
frags = fix_beginning(frags)
frags = fix_ending(frags)
for f in frags: for f in frags:
terminal_utterance(f)
text += f text += f
broadcast.utterance(text, v.channel) broadcast.utterance(text, v.channel)
time.sleep(2)
# sleep_time = 2
# toks = tokenise_str(f)
toks = f.split()
sleep_time = len(toks)
if sleep_time <= 2:
sleep_time += 1
time.sleep(sleep_time)
if exit: if exit:
return return
broadcast.command('clear') broadcast.command('clear')
time.sleep(2) print("==========")
time.sleep(3)
def find_candidates(v, uv_vec, voices, results): def find_candidates(v, uv_vec, voices, results):
logging.info(f"LOOP::finding candidates") logging.info(f"LOOP::finding candidates")
@ -74,7 +168,16 @@ def find_candidates(v, uv_vec, voices, results):
lapse = time.time() - start lapse = time.time() - start
logging.info(f"LOOP::done - {lapse} secs") logging.info(f"LOOP::done - {lapse} secs")
# def update():
# global exit
# while not exit:
# try:
# utterance.osc.update()
# except Exception as e:
# logging.error(e)
# pass
# time.sleep(0.2)
def signal_terminate(signum, frame): def signal_terminate(signum, frame):
global exit global exit
@ -83,14 +186,13 @@ def signal_terminate(signum, frame):
def main() -> int: def main() -> int:
global broadcast, metric global broadcast, metric, terminal, debug, state, UTTERANCE_LEN, NUM_METRIC_GEN, NUM_SAMPLE_VOICES, RANDOM_SEED_TIMER_MIN, STATE_TRANSITION_TIMER_MIN, B_SKIP, B_SWAPS
p = argparse.ArgumentParser() p = argparse.ArgumentParser()
p.add_argument("-c", "--config", type=str, default="voice.config.json", help="configuratin file") p.add_argument("-c", "--config", type=str, default="voice.config.json", help="configuratin file")
p.add_argument("-i", "--iterations", type=int, default=10, help="number of iterations") p.add_argument("-i", "--iterations", type=int, default=10, help="number of iterations")
args = p.parse_args() p.add_argument("-t", "--terminal", action='store_true', help="print to terminal")
args = p.parse_args()
logging.basicConfig(level=logging.INFO)
logging.info(f"INIT::loading config file - {args.config}") logging.info(f"INIT::loading config file - {args.config}")
@ -99,6 +201,22 @@ def main() -> int:
logging.info(conf) logging.info(conf)
terminal = args.terminal
#--------------------#
# CONFIGS
#--------------------#
u_conf = conf['utterance_configuration']
UTTERANCE_LEN = u_conf['UTTERANCE_LEN']
NUM_METRIC_GEN = u_conf['NUM_METRIC_GEN']
NUM_SAMPLE_VOICES = u_conf['NUM_SAMPLE_VOICES']
RANDOM_SEED_TIMER_MIN = u_conf['RANDOM_SEED_TIMER_MIN']
STATE_TRANSITION_TIMER_MIN = u_conf['STATE_TRANSITION_TIMER_MIN']
B_SKIP = u_conf['b_skip']
B_SWAPS = u_conf['b_swaps']
#--------------------# #--------------------#
# VOICES # VOICES
#--------------------# #--------------------#
@ -107,17 +225,45 @@ def main() -> int:
voices = [] voices = []
for v in conf['voices']: for v in conf['voices']:
model = v['model'] model = v['model']
voice = utterance.voice.Voice(name=v["name"].upper(), model=model['model_dir'], tokenizer=model['tokeniser_file'], temp=float(model["temperature"]), lenght=UTTERANCE_LEN) voice = utterance.voice.Voice(name=v["name"].upper(), model=model['model_dir'], tokenizer=model['tokeniser_file'], temp=float(model["temperature"]), length=UTTERANCE_LEN)
voice.set_channel(v['osc_channel']['root'], v['osc_channel']['utterance']) voice.set_channel(v['osc_channel']['root'], v['osc_channel']['utterance'])
voice.set_calculate(v['osc_channel']['root'], v['osc_channel']['calculate'])
voice.set_temperature(v['osc_channel']['root'], v['osc_channel']['temperature'])
voices.append(voice) voices.append(voice)
#--------------------#
# QUESTION
#--------------------#
logging.info(f"INIT::setting up question")
questions = conf['questions']
questions_array = questions.copy()
random.shuffle(questions_array)
#--------------------# #--------------------#
# NET # NET
#--------------------# #--------------------#
logging.info(f"INIT::setting up OSC") logging.info(f"INIT::setting up OSC")
broadcast = utterance.osc.OscBroadcaster(name="osc_broadcast", host=conf['host'], port=conf['port'], command_channel=conf['command_osc_channel'])
broadcast.start_osc() broadcast = utterance.oscosc.OscBroadcaster(name="osc_broadcast", host=conf['host_voicemachine'], port=conf['port_voicemachine'], command_channel=conf['command_osc_channel'])
# def receiver_cb_temp(unused_addr, args, temp, name):
# for v in voices:
# if v.name == name:
# print(f'{name} - {temp}')
# v.temp = temp
# # broadcast.temperature(temp, v.temperature) # <-- doesn works because deadlocks in osc_process...
# def receiver_cb_command(unused_addr, args, cmd):
# global debug
# debug = name
# logging.info(f"DEBUG MODE: {debug}")
# receiver = utterance.oscosc.OscReceiver(name="osc_receiver", host=conf['host_machinespeak'], port=conf['port_machinespeak'], callback_fn_command=receiver_cb_command, callback_fn_temp=receiver_cb_temp)
# t_osc_receiver = threading.Thread(target=receiver.server.serve_forever)
# t_osc_receiver.start()
#--------------------# #--------------------#
# METRIC # METRIC
@ -125,36 +271,163 @@ def main() -> int:
logging.info(f"INIT::loading doc2vec metrics") logging.info(f"INIT::loading doc2vec metrics")
metric = examine.metric.Metric(model_input='data/models/doc2vec.model') metric = examine.metric.Metric(model_input='data/models/doc2vec.model')
#--------------------#
# RANDOM
#--------------------#
def random_seed(seconds):
global t_random_seed, exit
i = 0
while i < seconds:
i += 1
time.sleep(1)
if exit:
return
logging.info("RANDOM::SEEDING RANDOM")
random.seed(time.time())
if not exit:
t_random_seed = threading.Thread(target=random_seed, args=(random.randint(60, 60 * RANDOM_SEED_TIMER_MIN), ))
t_random_seed.start()
t_random_seed = threading.Thread(target=random_seed, args=(random.randint(60, 60 * RANDOM_SEED_TIMER_MIN), ))
t_random_seed.start()
#--------------------#
# STATE TRANSITION
#--------------------#
# STATES = ["METRIC", "QUESTION", "RANDOM"]
def state_transition(seconds):
global t_state_transition, exit, state
i = 0
while i < seconds:
i += 1
time.sleep(1)
if exit:
return
logging.info("STATE::STATE TRANSITION")
state = "QUESTION"
if not exit:
t_state_transition = threading.Thread(target=state_transition, args=(random.randint(60, 60 * STATE_TRANSITION_TIMER_MIN), ))
t_state_transition.start()
t_state_transition = threading.Thread(target=state_transition, args=(random.randint(60, 60 * STATE_TRANSITION_TIMER_MIN), ))
t_state_transition.start()
#--------------------# #--------------------#
# A # A
#--------------------# #--------------------#
logging.info(f"INIT::generating first utterance") logging.info(f"INIT::generating first utterance")
random.seed(time.time())
v = random.choice(voices) v = random.choice(voices)
uv, uv_vec = utter_one_vectorise(v) uv, uv_vec = utter_one_vectorise(v)
# -- this only updates OSC --
# -- might not need this in production
# t_update = threading.Thread(target=update)
# t_update.start()
while not exit: while not exit:
results = [] if state == "METRIC":
t = threading.Thread(target=find_candidates, args=[v, uv_vec, voices, results])
t.start()
logging.info(f"LOOP::broadcasting {v.name}") logging.info(f"- state METRIC")
broadcast_utterance(v, uv)
t.join()
# ok here we need to randomise maybe...?! results = []
# ([d, t, v]) t = threading.Thread(target=find_candidates, args=[v, uv_vec, voices, results])
choice = results[0] t.start()
v = choice[2]
uv = choice[1] logging.info(f"METRIC::broadcasting {v.name}")
uv_vec = metric.vector(uv)
logging.info(f"LOOP::next {v.name}") try:
broadcast_utterance(v, uv)
except Exception as e:
logging.error(e)
pass
t.join()
# ok here we need to randomise maybe...?!
# ([d, t, v])
choice = results[0]
# makse sure we don't say the same thing over and over again
for r in results:
v = r[2]
u = r[1]
if v.select(u):
choice = r
break
else:
logging.info(f"METRIC::reduncancy {v.name}")
v = choice[2]
uv = choice[1]
uv_vec = metric.vector(uv)
logging.info(f"METRIC::next {v.name}")
elif state == "QUESTION":
logging.info(f"- state QUESTION")
if len(questions_array) <= 0:
questions_array = questions.copy()
random.shuffle(questions_array)
# random question
q = questions_array.pop(0)
# random voice
v = random.choice(voices)
# random voice asks random question
logging.info(f"QUESTION::{v.name} : {q['question']}")
broadcast.utterance(q['question'], v.calculate)
broadcast.utterance(q['question'], v.channel)
time.sleep(15)
# answer
v = [e for e in voices if e.name == q['voice']]
if len(v) == 1:
v = v[0]
logging.info(f"QUESTION::answer - {v.name}")
uv, uv_vec = prompt_one_vectorise(v, q['prompt'])
v.remember(uv)
# broadcast_utterance(v, uv) <-- this is broadcasted as part of METRIC STATE
state = "METRIC"
logging.info(f"TERMINATE::terminating OSC") elif state == "RANDOM":
broadcast.terminate_osc()
logging.info(f"- state RANDOM")
v = random.choice(voices)
l = random.randint(5, UTTERANCE_LEN)
uv, uv_vec = utter_one_vectorise(v, length=l)
broadcast_utterance(v, uv)
# t_update.join()
# logging.info(f"TERMINATE::terminating OSC")
# t_osc_receiver.stop()
# t_osc_receiver.join()
# if t_random_seed:
logging.info(f"TERMINATE::random seed")
t_random_seed.join()
logging.info(f"TERMINATE::state transition")
t_state_transition.join()
logging.info(f"FIN") logging.info(f"FIN")

View File

@ -1,5 +1,24 @@
from osc4py3.as_eventloop import * from osc4py3.as_eventloop import *
from osc4py3 import oscbuildparse from osc4py3 import oscbuildparse
from threading import Lock
mutex = Lock()
temp_mutex = Lock()
def start_osc():
osc_startup()
def terminate_osc():
osc_terminate()
def temperature(temp, name):
print(f'{temp} - {name}')
def update():
global mutex
with mutex:
osc_process()
class OscBroadcaster: class OscBroadcaster:
@ -8,27 +27,38 @@ class OscBroadcaster:
self.host = host self.host = host
self.port = port self.port = port
self.cmd = command_channel self.cmd = command_channel
osc_udp_client(self.host, self.port, self.name) osc_udp_client(self.host, self.port, self.name)
def utterance(self, utterance: str, channel: str): def utterance(self, utterance: str, channel: str):
msg = oscbuildparse.OSCMessage(channel, None, [utterance]) msg = oscbuildparse.OSCMessage(channel, None, [utterance])
osc_send(msg, self.name) osc_send(msg, self.name)
osc_process() update()
def command(self, command: str): def command(self, command: str):
msg = oscbuildparse.OSCMessage(self.cmd, None, [command]) msg = oscbuildparse.OSCMessage(self.cmd, None, [command])
osc_send(msg, self.name) osc_send(msg, self.name)
osc_process() update()
@staticmethod
def start_osc():
osc_startup()
@staticmethod
def terminate_osc():
osc_terminate()
def temperature(self, temp: float, channel: str):
global temp_mutex
with temp_mutex:
msg = oscbuildparse.OSCMessage(channel, None, [temp])
osc_send(msg, self.name)
osc_process()
class OscReceiver:
def __init__(self, name: str, host: str, port: str, callback_fn=None):
self.name = name
self.host = host
self.port = port
# osc_udp_server(self.host, self.port, self.name)
osc_udp_server("0.0.0.0", self.port, self.name)
if callback_fn:
osc_method('/', callback_fn)
else:
osc_method('/', temperature)

56
utterance/oscosc.py Normal file
View File

@ -0,0 +1,56 @@
from pythonosc import udp_client
from pythonosc import osc_server
from pythonosc import dispatcher
from threading import Lock
temp_mutex = Lock()
class OscBroadcaster:
def __init__(self, name: str, host: str, port: str, command_channel: str):
self.name = name
self.host = host
self.port = int(port)
self.cmd = command_channel
self.client = udp_client.SimpleUDPClient(self.host, self.port)
def utterance(self, utterance: str, channel: str):
self.client.send_message(channel, utterance)
def command(self, command: str):
self.client.send_message(self.cmd, command)
def temperature(self, temp: float, channel: str):
global temp_mutex
with temp_mutex:
self.client.send_message(self.cmd, command)
class OscReceiver:
def __init__(self, name: str, host: str, port: str, callback_fn_command=None, callback_fn_temp=None):
self.dispatcher = dispatcher.Dispatcher()
if callback_fn_command != None:
self.dispatcher.map('/command', callback_fn_command)
if callback_fn_temp != None:
self.dispatcher.map('/temperature', callback_fn_command)
self.name = name
self.host = "127.0.0.1"
self.port = int(port)
self.server = osc_server.ThreadingOSCUDPServer((self.host, self.port), self.dispatcher)
# # osc_udp_server(self.host, self.port, self.name)
# osc_udp_server("127.0.0.1", self.port, self.name)
# if callback_fn:
# osc_method('/', callback_fn)
# else:
# osc_method('/', temperature)

View File

@ -1,4 +1,6 @@
import string, regex import string, regex
from gensim.utils import tokenize
import nltk
def clean(text: str) -> str: def clean(text: str) -> str:
@ -16,13 +18,15 @@ def clean(text: str) -> str:
def format(text: str) -> str: def format(text: str) -> str:
return text.replace('\r\n', '\n').replace('\n\n', '\n') return text.replace('\r\n', '\n').replace('\n\n', '\n').replace('', "'").replace("", "'")
def fragments(utterance: str): def fragments(utterance: str):
frags = [] frags = []
sentences = utterance.splitlines() sentences = utterance.splitlines()
PUNCT_RE = regex.compile(r'(\p{Punctuation})') PUNCT_RE = regex.compile(r'(\p{Punctuation})')
skip_punctuation = ["'"]
for s in sentences: for s in sentences:
sf = PUNCT_RE.split(s) sf = PUNCT_RE.split(s)
@ -36,13 +40,74 @@ def fragments(utterance: str):
cum += k cum += k
else: else:
cum += k cum += k
frags.append(cum) if k in skip_punctuation:
cum = "" continue
else:
# if cum[0] not in [' ', '\n']:
# cum = ' ' + cum
frags.append(cum)
cum = ""
cum += '\n' cum += '\n'
frags.append(cum) frags.append(cum)
# get rid of newline (2x)
if len(frags) > 0:
if frags[-1] == '\n':
frags = frags[:-1]
if len(frags) > 0:
if frags[-1] == '\n':
frags = frags[:-1]
return frags return frags
def tokenise(utterance: str):
return list(tokenize(utterance, lower=True))
def fix_sentence(s: str) -> str:
if len(s.strip()) <= 1:
return None
text = nltk.word_tokenize(s)
tags = nltk.pos_tag(text)
if len(tags) == 0:
return None
elif len(tags) == 1 and tags[0][1] in ['DT', 'WDT', 'IN']:
return None
elif len(tags) == 1 and len(tags[0][0]) == 1 and tags[0][1] in ['NN']:
return None
if tags[-1][1] in ['DT', 'WDT', 'IN', 'CC']:
tags = tags[:-1]
return " ".join([x[0] for x in tags])
elif tags[-1][1] in ['NN'] and len(tags[-1][0]) == 1:
tags = tags[:-1]
return " ".join([x[0] for x in tags])
return s
def fix_punctuation(s: str) -> str:
if len(s.strip()) == 0:
return ""
if len(s) == 1 and s in string.punctuation:
if s != ',':
return s
else:
return ""
e = s.rstrip()
if e[-1] in string.punctuation:
if e[-1] in [',', ':']:
s = e[:-1] + '.'
else:
s = e + '.'
return s

View File

@ -1,22 +1,58 @@
from aitextgen import aitextgen from aitextgen import aitextgen
import utterance.utils import utterance.utils
import regex, string import gensim, regex, string, time
UTTERANCE_MEMORY_LEN = 15
UTTERANCE_MEMORY_MIN_DIST = 0.85
class Voice: class Voice:
def __init__(self, name: str, model: str, tokenizer: str, temp: int, lenght: int): def __init__(self, name: str, model: str, tokenizer: str, temp: int, length: int):
self.name = name self.name = name
self.temp = temp self.temp = temp
self.lenght = lenght self.length = length
self.v = aitextgen(model_folder=model, tokenizer_file=tokenizer) self.v = aitextgen(model_folder=model, tokenizer_file=tokenizer)
self.utterances = []
def utter_n(self, n: int, temp: float = None, lenght: int = None): def utter_n(self, n: int, temp: float = None, length: int = None):
t = self.temp if temp != None else temp t = self.temp if temp == None else temp
l = self.lenght if lenght != None else lenght l = self.length if length == None else length
return self.v.generate(n=n, max_lenght=l, temperature=t, return_as_list=True) return self.v.generate(n=n, max_length=l, temperature=t, seed=int(time.time()), return_as_list=True)
def utter_one(self, temp: float = None, length: int = None) -> str:
t = self.temp if temp == None else temp
l = self.length if length == None else length
return self.utter_n(n=1, temp=temp, length=length)[0]
def prompt(self, pinput: str, temp: float = None, length: int = None) -> str:
t = self.temp if temp == None else temp
l = self.length if length == None else length
return self.v.generate(prompt=pinput, n=1, max_length=l, temperature=t, seed=int(time.time()), return_as_list=True)[0]
def select(self, utterance: str) -> bool:
# fuction making sure to not say the same thing
toks = set(gensim.utils.tokenize(utterance))
i = 0
for u in self.utterances:
d = gensim.matutils.jaccard_distance(toks, u)
print(f"{i} - d: {d}")
if d < UTTERANCE_MEMORY_MIN_DIST:
return False
self.utterances.append(toks)
if len(self.utterances) > UTTERANCE_MEMORY_LEN:
self.utterances.pop(0)
return True
def remember(self, utterance: str):
toks = set(gensim.utils.tokenize(utterance))
self.utterances.append(toks)
def utter_one(self, temp: int = None, lenght: float = None) -> str:
return self.utter_n(n=1, temp=temp, lenght=lenght)[0]
def set_channel(self, root: str, endpoint: str): def set_channel(self, root: str, endpoint: str):
self.channel = root + endpoint self.channel = root + endpoint
@ -24,6 +60,18 @@ class Voice:
def channel(self): def channel(self):
return self.channel return self.channel
def set_calculate(self, root: str, endpoint: str):
self.calculate = root + endpoint
def calculate(self):
return self.calculate
def set_temperature(self, root: str, endpoint: str):
self.temperature = root + endpoint
def calculate(self):
return self.temperature
def fragments(self, utt: str): def fragments(self, utt: str):
self.utterance = utt self.utterance = utt
self.utterance_fragments = utterance.utils.fragments(utt) self.utterance_fragments = utterance.utils.fragments(utt)