BowlerKernel
RhubarbManager.java
Go to the documentation of this file.
1 package com.neuronrobotics.bowlerstudio.lipsync;
2 
3 import java.io.BufferedInputStream;
4 import java.io.File;
5 import java.io.FileOutputStream;
6 import java.io.FileWriter;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.StringWriter;
10 import java.lang.reflect.Type;
11 import java.net.URL;
12 import java.nio.charset.StandardCharsets;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import javax.sound.sampled.AudioFileFormat;
18 import javax.sound.sampled.AudioInputStream;
19 import javax.sound.sampled.AudioSystem;
20 import org.apache.commons.io.IOUtils;
21 
22 import com.google.gson.Gson;
23 import com.google.gson.GsonBuilder;
24 import com.google.gson.reflect.TypeToken;
25 import com.neuronrobotics.bowlerstudio.AudioStatus;
26 import com.neuronrobotics.bowlerstudio.IAudioProcessingLambda;
27 import com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;
28 import com.neuronrobotics.video.OSUtil;
29 
30 import net.lingala.zip4j.ZipFile;
31 import net.lingala.zip4j.exception.ZipException;
32 
33 public class RhubarbManager implements IAudioProcessingLambda {
34  ArrayList<TimeCodedViseme> timeCodedVisemes = null;
35  private static String RhubarbVersion = "1.13.0";
36 
37  public void processRaw(File f, String ttsLocation) throws Exception {
38  String os = OSUtil.isLinux() ? "Linux" : OSUtil.isOSX() ? "macOS" : "Windows";
39  String exeExtention = OSUtil.isWindows() ? ".exe" : "";
40  File exe = new File(ScriptingEngine.getWorkspace().getAbsolutePath() + "/Rhubarb-Lip-Sync/Rhubarb-Lip-Sync-"
41  + RhubarbVersion + "-" + os + "/rhubarb" + exeExtention);
42  timeCodedVisemes = new ArrayList<>();
43  if (!exe.exists()) {
44  System.out.println("Downloading " + exe.getAbsolutePath());
45  String zipfileName = "Rhubarb-Lip-Sync-" + RhubarbVersion + "-" + os + ".zip";
46  String urlStr = "https://github.com/DanielSWolf/rhubarb-lip-sync/releases/download/v" + RhubarbVersion + "/"
47  + zipfileName;
48  File zipfile = new File(ScriptingEngine.getWorkspace().getAbsolutePath() + "/" + zipfileName);
49  if (!zipfile.exists()) {
50  URL url = new URL(urlStr);
51  BufferedInputStream bis = new BufferedInputStream(url.openStream());
52  FileOutputStream fis = new FileOutputStream(zipfile);
53  byte[] buffer = new byte[1024];
54  int count = 0;
55  while ((count = bis.read(buffer, 0, 1024)) != -1) {
56  fis.write(buffer, 0, count);
57  }
58  fis.close();
59  bis.close();
60 
61  String source = zipfile.getAbsolutePath();
62  String destination = ScriptingEngine.getWorkspace().getAbsolutePath() + "/Rhubarb-Lip-Sync/";
63  try {
64  ZipFile zipFile = new ZipFile(source);
65  zipFile.extractAll(destination);
66  } catch (ZipException e) {
67  e.printStackTrace();
68  }
69  }
70  }
71  File homeDirectory = exe.getParentFile();
72  boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
73 
74  Process process;
75  String command = exe +" --dialogFile "+ttsLocation+" --machineReadable -f json " + f.getAbsolutePath();
76  System.out.println(command);
77  process = Runtime.getRuntime().exec(command);
78 
79  int exitCode = process.waitFor();
80 
81  InputStream is = process.getInputStream();
82  StringWriter writer = new StringWriter();
83  String utf8 = StandardCharsets.UTF_8.toString();
84  IOUtils.copy(is, writer, utf8);
85  String result = writer.toString();
86  // System.out.println(status);
87  // System.out.println(result);
88  Type TT_mapStringString = new TypeToken<HashMap<String, Object>>() {
89  }.getType();
90  Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();
91 
92  HashMap<String, Object> resultParsed = gson.fromJson(result, TT_mapStringString);
93  double duration = Double
94  .parseDouble(((Map<String, Object>) resultParsed.get("metadata")).get("duration").toString());
95  List<Map<String, Object>> cues = (List<Map<String, Object>>) resultParsed.get("mouthCues");
96  for (Map<String, Object> cue : cues) {
97  double end = Double.parseDouble(cue.get("end").toString());
98  double start = Double.parseDouble(cue.get("start").toString());
99 // double percent = end / duration * 100.0;
100 //
101  AudioStatus val = AudioStatus.get(cue.get("value").toString().charAt(0));
102 // System.out.println("End at " + percent + " " + val);
103 // HashMap<AudioStatus, Double> map = new HashMap<>();
104 // map.put(val, percent);
105  TimeCodedViseme map = new TimeCodedViseme(val, start, end, duration);
106  timeCodedVisemes.add(map);
107  }
108 
109  }
110 
111  @Override
112  public AudioInputStream startProcessing(AudioInputStream ais, String TTSString) {
113  File audio = new File(ScriptingEngine.getWorkspace().getAbsolutePath() + "/tmp-tts.wav");
114  try {
115  System.out.println("Begin writing..");
116  AudioSystem.write(ais, AudioFileFormat.Type.WAVE, audio);
117  ais = AudioSystem.getAudioInputStream(audio);
118  File text = new File(ScriptingEngine.getWorkspace().getAbsolutePath() + "/tmp-tts.txt");
119  if (!text.exists())
120  text.createNewFile();
121  try {
122  FileWriter myWriter = new FileWriter(text);
123  myWriter.write(TTSString);
124  myWriter.close();
125  } catch (IOException e) {
126  e.printStackTrace();
127  }
128  // rhubarb!
129  processRaw(audio, text.getAbsolutePath());
130  System.out.println("Done writing!");
131  } catch (Exception e) {
132  // TODO Auto-generated catch block
133  e.printStackTrace();
134  }
135 
136  return ais;
137  }
138 
139  public AudioStatus update(AudioStatus current, double amplitudeUnitVector, double currentRollingAverage,
140  double currentDerivitiveTerm, double percent) {
141  // println timeCodedVisemes
142  AudioStatus ret = null;
143  if (timeCodedVisemes.size() > 0) {
144  TimeCodedViseme map = timeCodedVisemes.get(0);
145  AudioStatus key = map.status;
146  double value = map.getEndPercentage();
147  if (percent > value) {
148  timeCodedVisemes.remove(0);
149  if (timeCodedVisemes.size() > 0)
150  ret = timeCodedVisemes.get(0).status;
151  else {
152  // println "\n\nERROR Audio got ahead of lip sync "+percent+"\n\n"
153  ret = AudioStatus.X_NO_SOUND;
154  }
155  } else if (percent > map.getStartPercentage())
156  ret = key;
157  } else {
158  // println "\n\nERROR Audio got ahead of lip sync "+percent+"\n\n"
159  }
160  if (ret == null)
161  ret = current;
162  if (current != ret) {
163  // println ret.toString()+" staarting at "+percent
164  }
165  return ret;
166 
167  }
168 
169 }
AudioInputStream startProcessing(AudioInputStream ais, String TTSString)
AudioStatus update(AudioStatus current, double amplitudeUnitVector, double currentRollingAverage, double currentDerivitiveTerm, double percent)
static AudioStatus get(char code)