Compare commits
10 Commits
c76875a08d
...
e2f841465c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2f841465c | ||
|
|
9673a3d4f0 | ||
|
|
19f7b8d30d | ||
|
|
d70f643836 | ||
|
|
5857da6e00 | ||
|
|
35caa1e0ad | ||
|
|
b2cc817911 | ||
|
|
c33d577bb6 | ||
|
|
e91d3012cb | ||
|
|
72b984bf48 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
#macos
|
||||
.DS_store
|
||||
venv
|
||||
sandox
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
||||
2
run.sh
Executable file
2
run.sh
Executable file
@ -0,0 +1,2 @@
|
||||
. venv/bin/activate
|
||||
python speak_broadcast.py -c ../assets/voice.config.json
|
||||
20
speak.py
20
speak.py
@ -2,10 +2,12 @@ import argparse, json, sys, time, random
|
||||
import utterance.voice
|
||||
import utterance.utils
|
||||
|
||||
UTTERANCE_LEN = 64 #<--------------- these should be in config
|
||||
|
||||
def main() -> int:
|
||||
|
||||
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")
|
||||
args = p.parse_args()
|
||||
|
||||
@ -16,25 +18,23 @@ def main() -> int:
|
||||
|
||||
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)
|
||||
|
||||
nbr_voices = len(voices)
|
||||
current_voice = ""
|
||||
current_voice_name = ""
|
||||
for i in range(args.iterations):
|
||||
rindex = random.randint(0, nbr_voices - 1)
|
||||
v = voices[rindex]
|
||||
if v.name != current_voice:
|
||||
v = random.sample(voices, 1)[0]
|
||||
if v.name != current_voice_name:
|
||||
current_voice_name = v.name
|
||||
print("==========")
|
||||
print(v.name + ":")
|
||||
current_voice = v.name
|
||||
t = v.utter_one()
|
||||
if t != None:
|
||||
t = utterance.utils.clean(t)
|
||||
t = utterance.utils.format(t)
|
||||
print(t)
|
||||
|
||||
time.sleep(4)
|
||||
# time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
359
speak_broadcast-backup.py
Normal file
359
speak_broadcast-backup.py
Normal 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())
|
||||
@ -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.utils
|
||||
import utterance.osc
|
||||
import utterance.oscosc
|
||||
import examine.metric
|
||||
|
||||
UTTERANCE_LEN = 16 #<--------------- these should be in config
|
||||
NUM_METRIC_GEN = 50
|
||||
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 = 10
|
||||
broadcast = None
|
||||
metric = None
|
||||
exit = False
|
||||
terminal = False
|
||||
debug = False
|
||||
state = "METRIC"
|
||||
B_SKIP = []
|
||||
B_SWAPS = {}
|
||||
|
||||
def format_str(text) -> str:
|
||||
t = utterance.utils.clean(text)
|
||||
return utterance.utils.format(t)
|
||||
|
||||
def utter_one(v) -> str:
|
||||
u = v.utter_one()
|
||||
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 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
|
||||
uv = utter_one(v)
|
||||
uv = utter_one(v, temp, length)
|
||||
uv_vec = metric.vector(uv)
|
||||
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
|
||||
results = []
|
||||
texts = v.utter_n(n=n)
|
||||
texts = v.utter_n(n=n, temp=temp, length=length)
|
||||
for t in texts:
|
||||
t = format_str(t)
|
||||
t_vec = metric.vector(t)
|
||||
@ -36,26 +58,98 @@ def utter_n_vectorise_distance(v, n, vec):
|
||||
results.append([d, t, v])
|
||||
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):
|
||||
|
||||
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)
|
||||
terminal_utterance(text)
|
||||
time.sleep(2)
|
||||
|
||||
frags = v.fragments(utterance)
|
||||
|
||||
frags = fix_beginning(frags)
|
||||
frags = fix_ending(frags)
|
||||
|
||||
for f in frags:
|
||||
|
||||
terminal_utterance(f)
|
||||
|
||||
text += f
|
||||
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:
|
||||
return
|
||||
|
||||
broadcast.command('clear')
|
||||
time.sleep(2)
|
||||
print("==========")
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
def find_candidates(v, uv_vec, voices, results):
|
||||
logging.info(f"LOOP::finding candidates")
|
||||
@ -74,7 +168,16 @@ def find_candidates(v, uv_vec, voices, results):
|
||||
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
|
||||
@ -83,15 +186,14 @@ def signal_terminate(signum, frame):
|
||||
|
||||
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.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.basicConfig(level=logging.INFO)
|
||||
|
||||
logging.info(f"INIT::loading config file - {args.config}")
|
||||
|
||||
with open(args.config) as f:
|
||||
@ -99,6 +201,22 @@ def main() -> int:
|
||||
|
||||
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
|
||||
#--------------------#
|
||||
@ -107,17 +225,45 @@ def main() -> int:
|
||||
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"]), 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_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")
|
||||
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
|
||||
@ -125,36 +271,163 @@ def main() -> int:
|
||||
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")
|
||||
random.seed(time.time())
|
||||
|
||||
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"LOOP::broadcasting {v.name}")
|
||||
logging.info(f"METRIC::broadcasting {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"LOOP::next {v.name}")
|
||||
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")
|
||||
broadcast.terminate_osc()
|
||||
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")
|
||||
# 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")
|
||||
|
||||
|
||||
@ -1,5 +1,24 @@
|
||||
from osc4py3.as_eventloop import *
|
||||
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:
|
||||
|
||||
@ -13,22 +32,33 @@ class OscBroadcaster:
|
||||
def utterance(self, utterance: str, channel: str):
|
||||
msg = oscbuildparse.OSCMessage(channel, None, [utterance])
|
||||
osc_send(msg, self.name)
|
||||
osc_process()
|
||||
update()
|
||||
|
||||
def command(self, command: str):
|
||||
msg = oscbuildparse.OSCMessage(self.cmd, None, [command])
|
||||
osc_send(msg, self.name)
|
||||
update()
|
||||
|
||||
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()
|
||||
|
||||
@staticmethod
|
||||
def start_osc():
|
||||
osc_startup()
|
||||
|
||||
@staticmethod
|
||||
def terminate_osc():
|
||||
osc_terminate()
|
||||
|
||||
|
||||
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
56
utterance/oscosc.py
Normal 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)
|
||||
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import string, regex
|
||||
from gensim.utils import tokenize
|
||||
import nltk
|
||||
|
||||
def clean(text: str) -> str:
|
||||
|
||||
@ -16,7 +18,7 @@ def clean(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):
|
||||
frags = []
|
||||
@ -24,6 +26,8 @@ def fragments(utterance: str):
|
||||
|
||||
PUNCT_RE = regex.compile(r'(\p{Punctuation})')
|
||||
|
||||
skip_punctuation = ["'"]
|
||||
|
||||
for s in sentences:
|
||||
sf = PUNCT_RE.split(s)
|
||||
cum = ""
|
||||
@ -36,13 +40,74 @@ def fragments(utterance: str):
|
||||
cum += k
|
||||
else:
|
||||
cum += k
|
||||
if k in skip_punctuation:
|
||||
continue
|
||||
else:
|
||||
# if cum[0] not in [' ', '\n']:
|
||||
# cum = ' ' + cum
|
||||
frags.append(cum)
|
||||
cum = ""
|
||||
cum += '\n'
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,22 +1,58 @@
|
||||
from aitextgen import aitextgen
|
||||
import utterance.utils
|
||||
import regex, string
|
||||
import gensim, regex, string, time
|
||||
|
||||
UTTERANCE_MEMORY_LEN = 15
|
||||
UTTERANCE_MEMORY_MIN_DIST = 0.85
|
||||
|
||||
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.temp = temp
|
||||
self.lenght = lenght
|
||||
self.length = length
|
||||
self.v = aitextgen(model_folder=model, tokenizer_file=tokenizer)
|
||||
self.utterances = []
|
||||
|
||||
def utter_n(self, n: int, temp: float = None, lenght: int = None):
|
||||
t = self.temp if temp != None else temp
|
||||
l = self.lenght if lenght != None else lenght
|
||||
return self.v.generate(n=n, max_lenght=l, temperature=t, return_as_list=True)
|
||||
def utter_n(self, n: int, temp: float = None, length: int = None):
|
||||
t = self.temp if temp == None else temp
|
||||
l = self.length if length == None else length
|
||||
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):
|
||||
self.channel = root + endpoint
|
||||
@ -24,6 +60,18 @@ class Voice:
|
||||
def channel(self):
|
||||
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):
|
||||
self.utterance = utt
|
||||
self.utterance_fragments = utterance.utils.fragments(utt)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user