BowlerKernel
CoquiDockerManager.java
Go to the documentation of this file.
1 package com.neuronrobotics.bowlerstudio;
2 
3 import java.util.Arrays;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.io.BufferedInputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.net.URISyntaxException;
10 import java.net.URLEncoder;
11 import java.nio.charset.StandardCharsets;
12 import java.time.Duration;
13 
14 import javax.sound.sampled.AudioInputStream;
15 import javax.sound.sampled.AudioSystem;
16 
17 import org.eclipse.jgit.api.errors.GitAPIException;
18 import org.eclipse.jgit.api.errors.InvalidRemoteException;
19 import org.eclipse.jgit.api.errors.TransportException;
20 import com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;
21 import com.squareup.okhttp.OkHttpClient;
22 import com.squareup.okhttp.Request;
23 import com.squareup.okhttp.Response;
24 import com.github.dockerjava.api.DockerClient;
25 import com.github.dockerjava.api.command.BuildImageResultCallback;
26 import com.github.dockerjava.api.command.CreateContainerResponse;
27 import com.github.dockerjava.api.model.BuildResponseItem;
28 import com.github.dockerjava.api.model.Container;
29 import com.github.dockerjava.api.model.ContainerPort;
30 import com.github.dockerjava.api.model.ExposedPort;
31 import com.github.dockerjava.api.model.Frame;
32 import com.github.dockerjava.api.model.HostConfig;
33 import com.github.dockerjava.api.model.Ports;
34 import com.github.dockerjava.api.model.WaitResponse;
35 import com.github.dockerjava.core.DefaultDockerClientConfig;
36 import com.github.dockerjava.core.DockerClientBuilder;
37 import com.github.dockerjava.core.command.LogContainerResultCallback;
38 import com.github.dockerjava.transport.DockerHttpClient;
39 import com.google.common.collect.Sets;
40 import com.github.dockerjava.api.command.WaitContainerResultCallback;
41 import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
42 
43 import java.io.File;
44 
45 public class CoquiDockerManager implements ITTSEngine {
46 
47  private static HashMap<String, CoquiDockerManager> managers = new HashMap<>();
48 
49  private static List<String> voiceOptions = Arrays.asList( "p225" , "p226" , "p227" , "p228" , "p229" , "p230" , "p231" , "p232" , "p233" , "p234" , "p236" , "p237" ,
50  "p238" , "p239" , "p240" , "p241" , "p243" , "p244" , "p245" , "p246" , "p247" , "p248" , "p249" , "p250" , "p251" ,
51  "p252" , "p253" , "p254" , "p255" , "p256" , "p257" , "p258" , "p259" , "p260" , "p261" , "p262" , "p263" , "p264" , "p265" , "p266" , "p267" , "p268" , "p269" , "p270" ,
52  "p271" , "p272" , "p273" , "p274" , "p275" , "p276" , "p277" , "p278" , "p279" , "p280" , "p281" , "p282" , "p283" , "p284" , "p285" , "p286" , "p287" , "p288" , "p292" ,
53  "p293" , "p294" , "p295" , "p297", "p298" , "p299" , "p300" , "p301" , "p302" , "p303" , "p304" , "p305" , "p306" , "p307" , "p308" , "p310" , "p311" , "p312" , "p313" ,
54  "p314" , "p316" , "p317" , "p318" , "p323" , "p326" , "p329" , "p330" , "p333" , "p334" , "p335" , "p336" , "p339" , "p340" , "p341" , "p343" , "p345" , "p347" , "p351" ,
55  "p360" , "p361" , "p362" , "p363" , "p364" , "p374" , "p376"
56  );
57  private static List<String> options = Arrays.asList("tts_models/en/vctk/vits", "tts_models/en/jenny/jenny");
58  private static List<String> speakerNeeded = Arrays.asList("tts_models/en/vctk/vits");
59  private String voice;
60  private DockerHttpClient client;
61 
62 
63  private String id;
64 
65  private DockerClient dockerClient;
66  private Container forMe = null;
67  private int hostPort = 5002;
68  private int containerPort = 5002;
69  private boolean started=false;
70  private int voiceOption =0;
71 
72  public static int getNummberOfOptions() {
73  return options.size()+voiceOptions.size();
74  }
75 
76  private CoquiDockerManager(String voice) throws InterruptedException, InvalidRemoteException, TransportException,
77  GitAPIException, IOException, URISyntaxException {
78  this.voice = voice;
79  if (managers.get(voice) != null) {
80  throw new RuntimeException("Only one manager allowed");
81  }
82  managers.clear();
83  managers.put(voice, this);
84  System.out.println("Coqui Voice " + voice);
85  String[] parts = voice.split("/");
86  String voiceSlug = "-" + parts[parts.length - 1];
87  ScriptingEngine.cloneRepo("https://github.com/Halloween2020TheChild/CoquiDocker.git", null);
88  ScriptingEngine.pull("https://github.com/Halloween2020TheChild/CoquiDocker.git");
89  String dockerFilePath = ScriptingEngine
90  .fileFromGit("https://github.com/Halloween2020TheChild/CoquiDocker.git", "Dockerfile")
91  .getAbsolutePath(); // Replace with the actual path to your Dockerfile
92  String imageName = "coqui-java"; // Replace with your desired image name
93 
94  // Create Docker client
95  DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build();
96  client = new ApacheDockerHttpClient.Builder().dockerHost(config.getDockerHost())
97  .sslConfig(config.getSSLConfig()).maxConnections(100).connectionTimeout(Duration.ofSeconds(30))
98  .responseTimeout(Duration.ofSeconds(45)).build();
99  dockerClient = DockerClientBuilder.getInstance(config).withDockerHttpClient(client).build();
100 
101  // Build the Docker image
102  String command = "python3 TTS/server/server.py --model_name " + voice;
103  // Create the Docker container
104  List<Container> allCOntainers = dockerClient.listContainersCmd().withShowAll(true).exec();
105 // boolean containerExists=false;
106 
107  for (Container c : allCOntainers) {
108  if (forMe != null)
109  continue;
110  System.out.println("Container: " + c);
111  String[] names = c.getNames();
112  for (String n : names) {
113 
114  String cs = "/" + imageName + voiceSlug;
115 
116  System.out.println("Checking " + n + " to " + cs);
117  boolean PortMatch=false;
118  ContainerPort[] conPorts = c.getPorts();
119  for(ContainerPort cp:conPorts) {
120  if(cp.getPublicPort()==hostPort)
121  PortMatch=true;
122  }
123  if (n.contentEquals(cs) || PortMatch) {
124  if (!c.getStatus().contains("Exited")) {
125  try {
126  dockerClient.killContainerCmd(c.getId()).exec();
127  }catch(com.github.dockerjava.api.exception.ConflictException ex) {
128  ex.printStackTrace();
129  }
130  //return;
131  }
132  dockerClient.removeContainerCmd(c.getId()).exec();
133 
134  }
135  }
136 
137  }
138  if (forMe == null) {
139  File dockerfile = new File(dockerFilePath);
140 
141  dockerClient.buildImageCmd(dockerfile).withTags(Sets.newHashSet(imageName))
142  .exec(new BuildImageResultCallback() {
143  @Override
144  public void onNext(BuildResponseItem item) {
145  // Handle build output (optional)
146  System.out.println(item.getStream());
147  super.onNext(item);
148  }
149  }).awaitImageId();
150  System.out.println("Docker image built successfully.");
151  try {
152  ExposedPort port = ExposedPort.tcp(hostPort);
153  Ports portBindings = new Ports();
154  portBindings.bind(port, Ports.Binding.bindPort(containerPort));
155  CreateContainerResponse containerResponse = dockerClient.createContainerCmd(imageName)
156  .withName(imageName + voiceSlug).withCmd(command.split(" ")).withTty(true)
157  .withExposedPorts(port).withHostConfig(new HostConfig().withPortBindings(portBindings)).exec();
158  id = containerResponse.getId();
159  // Wait for the server to start inside the container
160  dockerClient.waitContainerCmd(id).exec(new WaitContainerResultCallback() {
161  @Override
162  public void onNext(WaitResponse item) {
163  // Handle build output (optional)
164  System.out.println(item.toString());
165  super.onNext(item);
166  }
167  }).awaitCompletion();
168  dockerClient.startContainerCmd(id).exec();
169  LogContainerResultCallback logCallback=new LogContainerResultCallback() {
170  @Override
171  public void onNext(Frame item) {
172  String string = item.toString();
173  System.out.println(string);
174  if(string.contains("Press CTRL+C to quit")) {
175  started=true;
176  }
177  }
178  };
179  dockerClient.logContainerCmd(id).withStdOut(true).withStdErr(true).withFollowStream(true).exec(logCallback);
180 
181 
182  } catch (com.github.dockerjava.api.exception.ConflictException ex) {
183  // container exists
184  ex.printStackTrace();
185 
186  }
187  }
188  while(!started) {
189  Thread.sleep(1000);
190  System.out.println("Waiting for server to start");
191  }
192 
193 
194  }
195 
196  public void disconnect() {
197  // TODO Auto-generated method stub
198  dockerClient.killContainerCmd(id).exec();
199  }
200 
201  public static CoquiDockerManager get(double doubleValue) throws InvalidRemoteException, TransportException,
202  InterruptedException, GitAPIException, IOException, URISyntaxException {
203  int value = (int) Math.round(doubleValue - 800);
204  int vopt=0;
205  if(value>=options.size()) {
206  vopt=value-options.size();
207  value=0;
208  if(vopt>=voiceOptions.size()) {
209  vopt=voiceOptions.size()-1;
210  }
211  }
212  String voiceval = options.get(value);
213  if (managers.get(voiceval) == null)
214  new CoquiDockerManager(voiceval);
215  CoquiDockerManager coquiDockerManager = managers.get(voiceval);
216  coquiDockerManager.voiceOption=vopt;
217  return coquiDockerManager;
218  }
219 
220  @Override
221  public int speak(String text, float gainValue, boolean daemon, boolean join, ISpeakingProgress progress) {
222  try {
223 
224  String content = URLEncoder.encode(text.trim(), StandardCharsets.UTF_8.toString()).replace("+", "%20");
225 
226  // String url = "http://"+ipAddress+":5002/;
227 
228  String string = "";//"&speaker_id=p304";
229  for(String s:speakerNeeded) {
230  if(s.contains(voice))
231  string = "&speaker_id="+voiceOptions.get(voiceOption);
232  }
233  String url = "http://[::1]:5002//api/tts?text=" + content
234  + string + "&style_wav=&language_id=HTTP/1.1\n";
235  Request request = new Request.Builder().url(url).get().build();
236 
237  Response response = new OkHttpClient().newCall(request).execute();
238  // Handle the response as needed
239  InputStream is = response.body().byteStream();
240  AudioPlayer tts = new AudioPlayer(text);
241  AudioInputStream audio = AudioSystem.getAudioInputStream(new BufferedInputStream(is));
242  tts.setAudio(audio);
243  tts.setGain(gainValue);
244  tts.setDaemon(daemon);
245  if (progress != null)
246  tts.setSpeakProgress(progress);
247  tts.start();
248  if (join)
249  tts.join();
250  } catch (Exception e) {
251  e.printStackTrace();
252  return 1;
253  }
254  return 0;
255  }
256 
257 }
void setAudio(AudioInputStream audio)
void setSpeakProgress(ISpeakingProgress speakProgress)
static HashMap< String, CoquiDockerManager > managers
int speak(String text, float gainValue, boolean daemon, boolean join, ISpeakingProgress progress)
static File cloneRepo(String remoteURI, String branch)
static File fileFromGit(String remoteURI, String fileInRepo)
static void pull(String remoteURI, String branch)