From ffa298a1fdf2b4f5dc22f1936241d03bdc9ef5e5 Mon Sep 17 00:00:00 2001 From: gauthiier Date: Sun, 13 Mar 2022 17:09:05 +0100 Subject: [PATCH] moving forward --- config.json | 23 +++--- speak_broadcast.py | 113 +++++++++++++++++++++++++++ speak_metric.py | 32 +++----- speak_metric_broadcast.py | 159 ++++++++++++++++++++++++++++++++++++++ utterance/osc.py | 34 ++++++++ utterance/utils.py | 31 +++++++- utterance/voice.py | 15 ++++ voice.config.json | 107 +++++++++++++++++++++++++ 8 files changed, 483 insertions(+), 31 deletions(-) create mode 100644 speak_broadcast.py create mode 100644 speak_metric_broadcast.py create mode 100644 utterance/osc.py create mode 100644 voice.config.json diff --git a/config.json b/config.json index b22ca73..acb5183 100644 --- a/config.json +++ b/config.json @@ -1,29 +1,34 @@ { "voices": [ { - "name": "Ralph", + "name": "ETHER", "model_dir": "data/tokens+models/Emerson-Nature.txt_bs=64_ns=8000_vs=5000", "tokeniser_file": "data/tokens+models/Emerson-Nature.txt_bs=64_ns=8000_vs=5000/Emerson-Nature.txt_ns=5000.tokenizer.json", "temperature": "0.9" }, { - "name": "Jean", - "model_dir": "data/tokens+models/Lafontaine-Fables[english].txt_bs=64_ns=8000_vs=5000", - "tokeniser_file": "data/tokens+models/Lafontaine-Fables[english].txt_bs=64_ns=8000_vs=5000/Lafontaine-Fables[english].txt_ns=5000.tokenizer.json", + "name": "CHAOS", + "model_dir": "data/tokens+models/Lafontaine-Fables[english].txt_bs=64_ns=3000_vs=5000", + "tokeniser_file": "data/tokens+models/Lafontaine-Fables[english].txt_bs=64_ns=3000_vs=5000/Lafontaine-Fables[english].txt_ns=5000.tokenizer.json", "temperature": "1.2" }, { - "name": "Blake", - "model_dir": "data/tokens+models/Blake-Songs-of-Innocence-and-of-Experience.txt_bs=64_ns=8000_vs=5000", - "tokeniser_file": "data/tokens+models/Blake-Songs-of-Innocence-and-of-Experience.txt_bs=64_ns=8000_vs=5000/Blake-Songs-of-Innocence-and-of-Experience.txt_ns=5000.tokenizer.json", + "name": "BLAKE", + "model_dir": "data/tokens+models/Blake-Songs-of-Innocence-and-of-Experience.txt_bs=64_ns=3000_vs=5000", + "tokeniser_file": "data/tokens+models/Blake-Songs-of-Innocence-and-of-Experience.txt_bs=64_ns=3000_vs=5000/Blake-Songs-of-Innocence-and-of-Experience.txt_ns=5000.tokenizer.json", "temperature": "1.5" }, { - "name": "Friedrich", + "name": "THEO", "model_dir": "data/tokens+models/Schelling-ON-THE-RELATION-OF-THE-PLASTIC-ARTS-TO-NATURE.txt_bs=64_ns=8000_vs=5000", "tokeniser_file": "data/tokens+models/Schelling-ON-THE-RELATION-OF-THE-PLASTIC-ARTS-TO-NATURE.txt_bs=64_ns=8000_vs=5000/Schelling-ON-THE-RELATION-OF-THE-PLASTIC-ARTS-TO-NATURE.txt_ns=5000.tokenizer.json", "temperature": "1.5" + }, + { + "name": "POMPOM", + "model_dir": "data/tokens+models/NEWNATUREPOEMS.txt_bs=64_ns=5000_vs=5000", + "tokeniser_file": "data/tokens+models/NEWNATUREPOEMS.txt_bs=64_ns=5000_vs=5000/NEWNATUREPOEMS.txt_ns=5000.tokenizer.json", + "temperature": "0.5" } - ] } \ No newline at end of file diff --git a/speak_broadcast.py b/speak_broadcast.py new file mode 100644 index 0000000..a9c5ff7 --- /dev/null +++ b/speak_broadcast.py @@ -0,0 +1,113 @@ +import argparse, json, sys, time, random +import utterance.voice +import utterance.utils +import utterance.osc +import examine.metric + +UTTERANCE_LEN = 16 +broadcast = None + +def format_str(text: str) -> str: + t = utterance.utils.clean(text) + return utterance.utils.format(t) + +def broadcast_utterance(v, utterance): + + global broadcast + + text = v.name.upper() + ":\n" + + broadcast.utterance(text, v.channel) + time.sleep(2) + + frags = v.fragments(utterance) + + for f in frags: + text += f + broadcast.utterance(text, v.channel) + time.sleep(2) + + broadcast.command('clear') + time.sleep(2) + + +def main() -> int: + + global broadcast + + 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") + args = p.parse_args() + + with open(args.config) as f: + conf = json.load(f) + + 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.set_channel(v['osc_channel']['root'], v['osc_channel']['utterance']) + voices.append(voice) + + broadcast = utterance.osc.OscBroadcaster(name="osc_broadcast", host=conf['host'], port=conf['port'], command_channel=conf['command_osc_channel']) + broadcast.start_osc() + + nbr_voices = len(voices) + + state = 'c' + + metric = examine.metric.Metric(model_input='data/models/doc2vec.model') + + s = set(range(0, nbr_voices - 1)) + + rindex = random.sample(s, 1)[0] + + v = voices[rindex] + uv = v.utter_one() + uv = format_str(uv) + + v_vec = metric.vector(uv) + + + while state == 'c': + + candidates = random.sample(s, 3) + + results = [] + for c in candidates: + if c == rindex: + continue + vc = voices[c] + vc_texts = vc.utter_n(n=50) + for t in vc_texts: + t = format_str(t) + t_vec = metric.vector(t) + d = examine.metric.cos_dist(v_vec, t_vec) + results.append([d, t, c]) + + + results.sort(key=lambda t: t[0], reverse=True) + + + broadcast_utterance(v, uv) + + + # new round + + top = results[0] + rindex = top[2] + v = voices[rindex] + uv = top[1] + v_vec = metric.vector(top[1]) + + + # state = input("Continue? ") + + + + broadcast.terminate_osc() + + +if __name__ == '__main__': + sys.exit(main()) \ No newline at end of file diff --git a/speak_metric.py b/speak_metric.py index 36a2215..ca18961 100644 --- a/speak_metric.py +++ b/speak_metric.py @@ -1,8 +1,11 @@ import argparse, json, sys, time, random import utterance.voice import utterance.utils +import utterance.osc import examine.metric +UTTERANCE_LEN = 16 + def format_str(text: str) -> str: t = utterance.utils.clean(text) return utterance.utils.format(t) @@ -11,20 +14,23 @@ def format_str(text: str) -> str: 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() - print(args) - with open(args.config) as f: conf = json.load(f) 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=16) + 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.set_channel(v['osc_channel']['root'], v['osc_channel']['utterance']) voices.append(voice) + broadcast = utterance.osc.OscBroadcaster(name="osc_broadcast", host=conf['host'], port=conf['port'], command_channel=conf['command_osc_channel']) + broadcast.start_osc() + nbr_voices = len(voices) state = 'c' @@ -94,23 +100,7 @@ def main() -> int: - - # nbr_voices = len(voices) - # current_voice = "" - # for i in range(args.iterations): - # rindex = random.randint(0, nbr_voices - 1) - # v = voices[rindex] - # if v.name != current_voice: - # 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) + broadcast.terminate_osc() if __name__ == '__main__': diff --git a/speak_metric_broadcast.py b/speak_metric_broadcast.py new file mode 100644 index 0000000..cbcf7d7 --- /dev/null +++ b/speak_metric_broadcast.py @@ -0,0 +1,159 @@ +import argparse, json, sys, time, random +import utterance.voice +import utterance.utils +import examine.metric + +from osc4py3.as_eventloop import * +from osc4py3 import oscbuildparse + +voices_broadcast = {} +conf = {} + +def init_broadcast(): + + global conf, voices_broadcast + + with open('voice.config.json') as f: + conf = json.load(f) + + # configure voices and their channels + for v in conf['voices']: + voices_broadcast[v['name']] = v['osc_channel']['root'] + v['osc_channel']['utterance'] + + print(voices_broadcast) + + # Start the system. + osc_startup() + + # Make client channels to send packets. + osc_udp_client(conf['host'], conf['port'], "aaaa") + +def broadcast_utterance(name, utterance): + + global conf, voices_broadcast + + sentences = utterance.split('\n') + + text = name + ":" + + for t in sentences: + + if ',' in t: + phrases = t.split(',') + for i in range(0, len(phrases)): + p = phrases[i] + if p != "": + text += " " + p + if i != len(phrases) - 1 : + text += ',' + msg = oscbuildparse.OSCMessage(voices_broadcast[name], None, [text]) + osc_send(msg, "aaaa") + osc_process() + time.sleep(2) + else: + if text == "": + text += t + else: + text += "\n" + t + msg = oscbuildparse.OSCMessage(voices_broadcast[name], None, [text]) + osc_send(msg, "aaaa") + osc_process() + time.sleep(2) + + + + msg = oscbuildparse.OSCMessage(conf['command_osc_channel'], None, ["clear"]) + osc_send(msg, "aaaa") + osc_process() + + +def format_str(text: str) -> str: + t = utterance.utils.clean(text) + return utterance.utils.format(t) + + +def main() -> int: + + p = argparse.ArgumentParser() + p.add_argument("-c", "--config", type=str, default="config.json", help="configuratin file") + p.add_argument("-i", "--iterations", type=int, default=10, help="number of iterations") + args = p.parse_args() + + print(args) + + with open(args.config) as f: + conf = json.load(f) + + 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=7) + voices.append(voice) + + init_broadcast() + + nbr_voices = len(voices) + + state = 'c' + + metric = examine.metric.Metric(model_input='data/models/doc2vec.model') + + s = set(range(0, nbr_voices - 1)) + + # rindex = random.sample(s, 1)[0] + rindex = 4 + + v = voices[rindex] + uv = v.utter_one() + uv = format_str(uv) + + v_vec = metric.vector(uv) + + + while 1: + + candidates = random.sample(s, 2) + + results = [] + for c in candidates: + if c == rindex: + continue + vc = voices[c] + vc_texts = vc.utter_n(n=25) + for t in vc_texts: + t = format_str(t) + t_vec = metric.vector(t) + d = examine.metric.cos_dist(v_vec, t_vec) + results.append([d, t, c]) + + + results.sort(key=lambda t: t[0], reverse=True) + + # print('----------------------------') + # print(v.name + ":") + # print(uv) + # print('----------------------------') + + broadcast_utterance(v.name, uv) + + # for r in results[:2]: + # print('-->' + str(r[0])) + # print(r[1]) + # print('+++++++++++++++++++++++++') + + + # new round + + top = results[0] + rindex = top[2] + v = voices[rindex] + uv = top[1] + v_vec = metric.vector(top[1]) + + + # state = input("Continue? ") + + osc_terminate() + + +if __name__ == '__main__': + sys.exit(main()) \ No newline at end of file diff --git a/utterance/osc.py b/utterance/osc.py new file mode 100644 index 0000000..539ff6f --- /dev/null +++ b/utterance/osc.py @@ -0,0 +1,34 @@ +from osc4py3.as_eventloop import * +from osc4py3 import oscbuildparse + +class OscBroadcaster: + + def __init__(self, name: str, host: str, port: str, command_channel: str): + self.name = name + self.host = host + self.port = port + self.cmd = command_channel + osc_udp_client(self.host, self.port, self.name) + + def utterance(self, utterance: str, channel: str): + msg = oscbuildparse.OSCMessage(channel, None, [utterance]) + osc_send(msg, self.name) + osc_process() + + def command(self, command: str): + msg = oscbuildparse.OSCMessage(self.cmd, None, [command]) + osc_send(msg, self.name) + osc_process() + + @staticmethod + def start_osc(): + osc_startup() + + @staticmethod + def terminate_osc(): + osc_terminate() + + + + + diff --git a/utterance/utils.py b/utterance/utils.py index 54f1bcd..76a9483 100644 --- a/utterance/utils.py +++ b/utterance/utils.py @@ -1,4 +1,4 @@ -import string +import string, regex def clean(text: str) -> str: @@ -17,3 +17,32 @@ def clean(text: str) -> str: def format(text: str) -> str: return text.replace('\r\n', '\n').replace('\n\n', '\n') + +def fragments(utterance: str): + frags = [] + sentences = utterance.splitlines() + + PUNCT_RE = regex.compile(r'(\p{Punctuation})') + + for s in sentences: + sf = PUNCT_RE.split(s) + cum = "" + for k in sf: + if len(k) < 1: + continue + elif len(k) > 1: + cum += k + elif k not in string.punctuation: + cum += k + else: + cum += k + frags.append(cum) + cum = "" + cum += '\n' + frags.append(cum) + + return frags + + + + diff --git a/utterance/voice.py b/utterance/voice.py index c734fce..c15e85b 100644 --- a/utterance/voice.py +++ b/utterance/voice.py @@ -1,5 +1,6 @@ from aitextgen import aitextgen import utterance.utils +import regex, string class Voice: @@ -16,3 +17,17 @@ class Voice: 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 + + def channel(self): + return self.channel + + def fragments(self, utt: str): + self.utterance = utt + self.utterance_fragments = utterance.utils.fragments(utt) + return self.utterance_fragments + + + diff --git a/voice.config.json b/voice.config.json new file mode 100644 index 0000000..6627369 --- /dev/null +++ b/voice.config.json @@ -0,0 +1,107 @@ +{ + "command_osc_channel" : "/command", + "host" : "127.0.0.1", + "port" : "1111", + "voices" : [ + { + "background" : "0xff571111", + "color" : "0xffc8d75d", + "font" : "Times New Roman", + "name" : "CHAOS", + "osc_channel" : { + "background" : "/background", + "color" : "/font/color", + "root" : "/chaos", + "size" : "/font/size", + "type" : "/font/type", + "utterance" : "/utterance" + }, + "model" :{ + "model_dir": "data/tokens+models/Lafontaine-Fables[english].txt_bs=64_ns=3000_vs=5000", + "tokeniser_file": "data/tokens+models/Lafontaine-Fables[english].txt_bs=64_ns=3000_vs=5000/Lafontaine-Fables[english].txt_ns=5000.tokenizer.json", + "temperature": "1.2" + }, + "size" : 100 + }, + { + "background" : "0xff020000", + "color" : "0xffebf3f1", + "font" : "Arial-BoldMT", + "name" : "ETHER", + "osc_channel" : { + "background" : "/background", + "color" : "/font/color", + "root" : "/ether", + "size" : "/font/size", + "type" : "/font/type", + "utterance" : "/utterance" + }, + "model" :{ + "model_dir": "data/tokens+models/Emerson-Nature.txt_bs=64_ns=8000_vs=5000", + "tokeniser_file": "data/tokens+models/Emerson-Nature.txt_bs=64_ns=8000_vs=5000/Emerson-Nature.txt_ns=5000.tokenizer.json", + "temperature": "0.9" + }, + "size" : 100 + }, + { + "background" : "0xffdcb213", + "color" : "0xff353a39", + "font" : "NotoSansVai-Regular", + "name" : "BLAKE", + "osc_channel" : { + "background" : "/background", + "color" : "/font/color", + "root" : "/blake", + "size" : "/font/size", + "type" : "/font/type", + "utterance" : "/utterance" + }, + "model" :{ + "model_dir": "data/tokens+models/Blake-Songs-of-Innocence-and-of-Experience.txt_bs=64_ns=3000_vs=5000", + "tokeniser_file": "data/tokens+models/Blake-Songs-of-Innocence-and-of-Experience.txt_bs=64_ns=3000_vs=5000/Blake-Songs-of-Innocence-and-of-Experience.txt_ns=5000.tokenizer.json", + "temperature": "1.5" + }, + "size" : 100 + }, + { + "background" : "0xff020000", + "color" : "0xffebf3f1", + "font" : "Arial-BoldMT", + "name" : "POMPOM", + "osc_channel" : { + "background" : "/background", + "color" : "/font/color", + "root" : "/pompom", + "size" : "/font/size", + "type" : "/font/type", + "utterance" : "/utterance" + }, + "model" :{ + "model_dir": "data/tokens+models/NEWNATUREPOEMS.txt_bs=64_ns=5000_vs=5000", + "tokeniser_file": "data/tokens+models/NEWNATUREPOEMS.txt_bs=64_ns=5000_vs=5000/NEWNATUREPOEMS.txt_ns=5000.tokenizer.json", + "temperature": "0.5" + }, + "size" : 100 + }, + { + "background" : "0xff571111", + "color" : "0xffc8d75d", + "font" : "Times New Roman", + "name" : "THEO", + "osc_channel" : { + "background" : "/background", + "color" : "/font/color", + "root" : "/theo", + "size" : "/font/size", + "type" : "/font/type", + "utterance" : "/utterance" + }, + "model" :{ + "model_dir": "data/tokens+models/Schelling-ON-THE-RELATION-OF-THE-PLASTIC-ARTS-TO-NATURE.txt_bs=64_ns=8000_vs=5000", + "tokeniser_file": "data/tokens+models/Schelling-ON-THE-RELATION-OF-THE-PLASTIC-ARTS-TO-NATURE.txt_bs=64_ns=8000_vs=5000/Schelling-ON-THE-RELATION-OF-THE-PLASTIC-ARTS-TO-NATURE.txt_ns=5000.tokenizer.json", + "temperature": "1.5" + }, + "size" : 100 + } + ] +}