audio_util.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. import { setdifference } from '../common/base_util.js';
  2. import * as EngineConst from '../common/engine_const.js';
  3. import { Engine } from '../common/engine.js';
  4. import { Span } from './span.js';
  5. export function mergePause(oldPause, newPause, opt_merge) {
  6. if (!oldPause) {
  7. return newPause;
  8. }
  9. return { pause: mergePause_(oldPause.pause, newPause.pause, opt_merge) };
  10. }
  11. function mergePause_(oldPause, newPause, opt_merge) {
  12. const merge = opt_merge ||
  13. function (x, y) {
  14. if (typeof x === 'number' && typeof y === 'number') {
  15. return x + y;
  16. }
  17. if (typeof x === 'number') {
  18. return y;
  19. }
  20. if (typeof y === 'number') {
  21. return x;
  22. }
  23. return [oldPause, newPause].sort()[0];
  24. };
  25. return merge.call(null, oldPause, newPause);
  26. }
  27. export function mergeMarkup(oldPers, newPers) {
  28. delete oldPers.open;
  29. newPers.close.forEach((x) => delete oldPers[x]);
  30. newPers.open.forEach((x) => (oldPers[x] = newPers[x]));
  31. const keys = Object.keys(oldPers);
  32. oldPers.open = keys;
  33. }
  34. export function sortClose(open, descrs) {
  35. if (open.length <= 1) {
  36. return open;
  37. }
  38. const result = [];
  39. for (let i = 0, descr; (descr = descrs[i]), open.length; i++) {
  40. if (!descr.close || !descr.close.length) {
  41. continue;
  42. }
  43. descr.close.forEach(function (x) {
  44. const index = open.indexOf(x);
  45. if (index !== -1) {
  46. result.unshift(x);
  47. open.splice(index, 1);
  48. }
  49. });
  50. }
  51. return result;
  52. }
  53. let PersonalityRanges_ = {};
  54. let LastOpen_ = [];
  55. export function personalityMarkup(descrs) {
  56. PersonalityRanges_ = {};
  57. LastOpen_ = [];
  58. let result = [];
  59. const currentPers = {};
  60. for (let i = 0, descr; (descr = descrs[i]); i++) {
  61. let pause = null;
  62. const span = descr.descriptionSpan();
  63. const pers = descr.personality;
  64. const join = pers[EngineConst.personalityProps.JOIN];
  65. delete pers[EngineConst.personalityProps.JOIN];
  66. if (typeof pers[EngineConst.personalityProps.PAUSE] !== 'undefined') {
  67. pause = {
  68. [EngineConst.personalityProps.PAUSE]: pers[EngineConst.personalityProps.PAUSE]
  69. };
  70. delete pers[EngineConst.personalityProps.PAUSE];
  71. }
  72. const diff = personalityDiff_(pers, currentPers);
  73. appendMarkup_(result, span, diff, join, pause, true);
  74. }
  75. result = result.concat(finaliseMarkup_());
  76. result = simplifyMarkup_(result);
  77. result = Engine.getInstance().cleanpause ? cleanPause(result) : result;
  78. return result;
  79. }
  80. function cleanPause(markup) {
  81. while (isPauseElement(markup[0])) {
  82. markup.shift();
  83. }
  84. while (isPauseElement(markup[markup.length - 1])) {
  85. markup.pop();
  86. }
  87. return markup;
  88. }
  89. function appendElement_(markup, element) {
  90. const last = markup[markup.length - 1];
  91. if (!last) {
  92. markup.push(element);
  93. return;
  94. }
  95. if (isSpanElement(element) && isSpanElement(last)) {
  96. if (typeof last.join === 'undefined') {
  97. last.span = last.span.concat(element.span);
  98. return;
  99. }
  100. const lstr = last['span'].pop();
  101. const fstr = element['span'].shift();
  102. last['span'].push(lstr + last.join + fstr);
  103. last['span'] = last['span'].concat(element.span);
  104. last.join = element.join;
  105. return;
  106. }
  107. if (isPauseElement(element) && isPauseElement(last)) {
  108. last.pause = mergePause_(last.pause, element.pause);
  109. return;
  110. }
  111. markup.push(element);
  112. }
  113. function simplifyMarkup_(markup) {
  114. const lastPers = {};
  115. const result = [];
  116. for (let i = 0, element; (element = markup[i]); i++) {
  117. if (!isMarkupElement(element)) {
  118. appendElement_(result, element);
  119. continue;
  120. }
  121. if (!element.close || element.close.length !== 1 || element.open.length) {
  122. copyValues_(element, lastPers);
  123. result.push(element);
  124. continue;
  125. }
  126. let nextElement = markup[i + 1];
  127. if (!nextElement || isSpanElement(nextElement)) {
  128. copyValues_(element, lastPers);
  129. result.push(element);
  130. continue;
  131. }
  132. const pauseElement = isPauseElement(nextElement) ? nextElement : null;
  133. if (pauseElement) {
  134. nextElement = markup[i + 2];
  135. }
  136. if (nextElement &&
  137. isMarkupElement(nextElement) &&
  138. nextElement.open[0] === element.close[0] &&
  139. !nextElement.close.length &&
  140. nextElement[nextElement.open[0]] === lastPers[nextElement.open[0]]) {
  141. if (pauseElement) {
  142. appendElement_(result, pauseElement);
  143. i = i + 2;
  144. }
  145. else {
  146. i = i + 1;
  147. }
  148. }
  149. else {
  150. copyValues_(element, lastPers);
  151. result.push(element);
  152. }
  153. }
  154. return result;
  155. }
  156. function copyValues_(from, to) {
  157. if (from['rate']) {
  158. to['rate'] = from['rate'];
  159. }
  160. if (from['pitch']) {
  161. to['pitch'] = from['pitch'];
  162. }
  163. if (from['volume']) {
  164. to['volume'] = from['volume'];
  165. }
  166. }
  167. function finaliseMarkup_() {
  168. const final = [];
  169. for (let i = LastOpen_.length - 1; i >= 0; i--) {
  170. const pers = LastOpen_[i];
  171. if (pers.length) {
  172. const markup = { open: [], close: [] };
  173. for (let j = 0; j < pers.length; j++) {
  174. const per = pers[j];
  175. markup.close.push(per);
  176. markup[per] = 0;
  177. }
  178. final.push(markup);
  179. }
  180. }
  181. return final;
  182. }
  183. export function isMarkupElement(element) {
  184. return typeof element === 'object' && element.open;
  185. }
  186. export function isPauseElement(element) {
  187. return (typeof element === 'object' &&
  188. Object.keys(element).length === 1 &&
  189. Object.keys(element)[0] === EngineConst.personalityProps.PAUSE);
  190. }
  191. function isSpanElement(element) {
  192. const keys = Object.keys(element);
  193. return (typeof element === 'object' &&
  194. ((keys.length === 1 && keys[0] === 'span') ||
  195. (keys.length === 2 &&
  196. ((keys[0] === 'span' && keys[1] === 'join') ||
  197. (keys[1] === 'span' && keys[0] === 'join')))));
  198. }
  199. function appendMarkup_(markup, span, pers, join, pause, merge = false) {
  200. if (merge) {
  201. const last = markup[markup.length - 1];
  202. let oldJoin;
  203. if (last) {
  204. oldJoin = last[EngineConst.personalityProps.JOIN];
  205. }
  206. if (last && !span.speech && pause && isPauseElement(last)) {
  207. const pauseProp = EngineConst.personalityProps.PAUSE;
  208. last[pauseProp] = mergePause_(last[pauseProp], pause[pauseProp]);
  209. pause = null;
  210. }
  211. if (last &&
  212. span.speech &&
  213. Object.keys(pers).length === 0 &&
  214. isSpanElement(last)) {
  215. if (typeof oldJoin !== 'undefined') {
  216. const lastSpan = last['span'].pop();
  217. span = Span.stringAttr(lastSpan.speech + oldJoin + span.speech, lastSpan.attributes);
  218. }
  219. last['span'].push(span);
  220. span = Span.empty();
  221. last[EngineConst.personalityProps.JOIN] = join;
  222. }
  223. }
  224. if (Object.keys(pers).length !== 0) {
  225. markup.push(pers);
  226. }
  227. if (span.speech) {
  228. markup.push({ span: [span], join: join });
  229. }
  230. if (pause) {
  231. markup.push(pause);
  232. }
  233. }
  234. function personalityDiff_(current, old) {
  235. if (!old) {
  236. return current;
  237. }
  238. const result = {};
  239. for (const prop of EngineConst.personalityPropList) {
  240. const currentValue = current[prop];
  241. const oldValue = old[prop];
  242. if ((!currentValue && !oldValue) ||
  243. (currentValue && oldValue && currentValue === oldValue)) {
  244. continue;
  245. }
  246. const value = currentValue || 0;
  247. if (!isMarkupElement(result)) {
  248. result.open = [];
  249. result.close = [];
  250. }
  251. if (!currentValue) {
  252. result.close.push(prop);
  253. }
  254. if (!oldValue) {
  255. result.open.push(prop);
  256. }
  257. if (oldValue && currentValue) {
  258. result.close.push(prop);
  259. result.open.push(prop);
  260. }
  261. old[prop] = value;
  262. result[prop] = value;
  263. PersonalityRanges_[prop]
  264. ? PersonalityRanges_[prop].push(value)
  265. : (PersonalityRanges_[prop] = [value]);
  266. }
  267. if (isMarkupElement(result)) {
  268. let c = result.close.slice();
  269. while (c.length > 0) {
  270. let lo = LastOpen_.pop();
  271. const loNew = setdifference(lo, c);
  272. c = setdifference(c, lo);
  273. lo = loNew;
  274. if (c.length === 0) {
  275. if (lo.length !== 0) {
  276. LastOpen_.push(lo);
  277. }
  278. continue;
  279. }
  280. if (lo.length === 0) {
  281. continue;
  282. }
  283. result.close = result.close.concat(lo);
  284. result.open = result.open.concat(lo);
  285. for (let i = 0, open; (open = lo[i]); i++) {
  286. result[open] = old[open];
  287. }
  288. }
  289. LastOpen_.push(result.open);
  290. }
  291. return result;
  292. }