primocode 8.42.1 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -37
- package/bin/primocode.js +599 -327
- package/lib/acervo.js +8 -1
- package/lib/act.js +205 -114
- package/lib/api.js +35 -4
- package/lib/boas-vindas.js +31 -192
- package/lib/catalogo.js +60 -60
- package/lib/config.js +1 -1
- package/lib/conta.js +3 -24
- package/lib/diff.js +161 -0
- package/lib/diretor.js +37 -47
- package/lib/entrada.js +545 -0
- package/lib/especialistas.js +2 -2
- package/lib/estudio/assets.js +343 -0
- package/lib/estudio/identidade.js +560 -0
- package/lib/estudio/index.js +565 -0
- package/lib/estudio/plano.js +327 -0
- package/lib/estudio/projeto.js +246 -0
- package/lib/estudio/remotion.js +606 -0
- package/lib/estudio/slides.js +190 -0
- package/lib/estudio/video.js +296 -0
- package/lib/fala.js +13 -1
- package/lib/marca.js +4 -1
- package/lib/marketplace.js +189 -0
- package/lib/permissao.js +137 -0
- package/lib/registro.js +229 -0
- package/lib/skills.js +329 -0
- package/lib/tools.js +51 -181
- package/lib/ui.js +7 -3
- package/lib/voz.js +239 -0
- package/package.json +12 -3
- package/skills/acessibilidade/SKILL.md +28 -0
- package/skills/api-que-nao-quebra/SKILL.md +27 -0
- package/skills/depurar-de-verdade/SKILL.md +29 -0
- package/skills/editar-meu-video/SKILL.md +59 -0
- package/skills/escrever-em-portugues/SKILL.md +28 -0
- package/skills/git-limpo/SKILL.md +28 -0
- package/skills/motion-de-primeira/SKILL.md +62 -0
- package/skills/pagina-que-converte/SKILL.md +28 -0
- package/skills/performance-web/SKILL.md +28 -0
- package/skills/revisao-antes-de-entregar/SKILL.md +27 -0
- package/skills/slides-que-prendem/SKILL.md +40 -0
- package/studio/edge.py +16 -66
- package/studio/narrar.py +274 -0
- package/studio/pessoa.py +167 -0
- package/voz/primo_voz/__init__.py +0 -0
- package/voz/primo_voz/__main__.py +325 -0
- package/voz/primo_voz/escuta.py +235 -0
- package/voz/primo_voz/orbe.py +216 -0
- package/lib/audio-livre.js +0 -177
- package/lib/chaves.js +0 -149
- package/lib/claude-engine.js +0 -631
- package/lib/codex-engine.js +0 -208
- package/lib/remotion.js +0 -663
- package/lib/studio.js +0 -960
- package/studio/PRIMOCODE.md +0 -58
- package/studio/README.md +0 -154
- package/studio/__pycache__/edge.cpython-311.pyc +0 -0
- package/studio/__pycache__/elevenlabs.cpython-311.pyc +0 -0
- package/studio/__pycache__/voz.cpython-311.pyc +0 -0
- package/studio/arquivos/corte-teste-de-camera-fb823.json +0 -57
- package/studio/ferramentas/00-protocolo.md +0 -125
- package/studio/ferramentas/01-modelos.md +0 -270
- package/studio/ferramentas/README.md +0 -32
- package/studio/ferramentas/corte.md +0 -919
- package/studio/ferramentas/grade.md +0 -92
- package/studio/ferramentas/prisma.md +0 -107
- package/studio/ferramentas/prosa.md +0 -86
- package/studio/ferramentas/tela.md +0 -93
- package/studio/ferramentas/traco.md +0 -79
- package/studio/server.py +0 -2254
- package/studio/studio.log +0 -57
- package/studio/voz.py +0 -388
- package/studio/web/analise.html +0 -52
- package/studio/web/core.css +0 -1167
- package/studio/web/fontes/bebas.woff2 +0 -0
- package/studio/web/fontes/grotesk-700.woff2 +0 -0
- package/studio/web/fontes/inter-400.woff2 +0 -0
- package/studio/web/fontes/inter-700.woff2 +0 -0
- package/studio/web/fontes/jetbrains-400.woff2 +0 -0
- package/studio/web/fontes/playfair-700.woff2 +0 -0
- package/studio/web/fontes.css +0 -23
- package/studio/web/home.html +0 -217
- package/studio/web/js/analise.js +0 -510
- package/studio/web/js/audio.js +0 -607
- package/studio/web/js/editor.js +0 -1802
- package/studio/web/js/exportar.js +0 -222
- package/studio/web/js/ferramentas.js +0 -108
- package/studio/web/js/icones.js +0 -120
- package/studio/web/js/midia.js +0 -157
- package/studio/web/js/narracao.js +0 -325
- package/studio/web/js/player.js +0 -903
- package/studio/web/js/render-grade.js +0 -243
- package/studio/web/js/render-prosa.js +0 -104
- package/studio/web/js/render.js +0 -1852
- package/studio/web/js/sfx.js +0 -233
- package/studio/web/js/video.js +0 -464
- package/studio/web/js/voz-neural.js +0 -152
- package/studio/web/render.css +0 -1352
- package/studio/web/tf.html +0 -15
- package/studio/web/tool.html +0 -78
- package/studio/web/view.html +0 -27
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
/* ==========================================================================
|
|
2
|
-
narracao.js — locução.
|
|
3
|
-
|
|
4
|
-
Três caminhos, do melhor para o pior:
|
|
5
|
-
1. VOZ GRAVADA — o usuário grava com o microfone. Sempre ganha.
|
|
6
|
-
2. VOZ PREMIUM — voz aprimorada do sistema, se estiver instalada.
|
|
7
|
-
3. VOZ PADRÃO — síntese compacta do sistema. Serve para rascunho.
|
|
8
|
-
|
|
9
|
-
As vozes de brincadeira da Apple (Eddy, Grandma, Rocko…) ficam escondidas:
|
|
10
|
-
existem no sistema, mas não servem para narrar nada.
|
|
11
|
-
========================================================================== */
|
|
12
|
-
|
|
13
|
-
var Narracao = (function () {
|
|
14
|
-
"use strict";
|
|
15
|
-
|
|
16
|
-
var sintese = window.speechSynthesis || null;
|
|
17
|
-
var cacheVozes = [];
|
|
18
|
-
var cancelado = false;
|
|
19
|
-
|
|
20
|
-
/* Vozes de novidade do macOS — som proposital de desenho animado. */
|
|
21
|
-
var NOVIDADE = /^(albert|bad news|bahh|bells|boing|bubbles|cellos|deranged|good news|jester|junior|kathy|organ|pipe organ|princess|ralph|superstar|trinoids|whisper|wobble|zarvox|fred|hysterical|eddy|flo|grandma|grandpa|reed|rocko|sandy|shelley|bruce|agnes|vicki|victoria|alice|alva|damayanti|fiona|moira|tessa|veena|rishi|karen|melina|xander)\b/i;
|
|
22
|
-
|
|
23
|
-
var PREMIUM = /premium|enhanced|aprimorad|neural|siri/i;
|
|
24
|
-
|
|
25
|
-
/* "Joana (português (Portugal))" → "Joana" — os parênteses aninham, então
|
|
26
|
-
corta a partir do primeiro em vez de tentar casar os pares. */
|
|
27
|
-
function limpo(nome) {
|
|
28
|
-
return String(nome || "").split(/\s*\(/)[0].trim() || String(nome || "");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function disponivel() { return !!sintese; }
|
|
32
|
-
|
|
33
|
-
/* nota de qualidade: quanto maior, melhor a voz para narrar */
|
|
34
|
-
function nota(v) {
|
|
35
|
-
var n = 0;
|
|
36
|
-
if (PREMIUM.test(v.name)) n += 100;
|
|
37
|
-
if (/pt[-_]?BR/i.test(v.lang)) n += 40;
|
|
38
|
-
else if (/^pt/i.test(v.lang)) n += 25;
|
|
39
|
-
if (v.localService) n += 5;
|
|
40
|
-
if (NOVIDADE.test(limpo(v.name))) n -= 200;
|
|
41
|
-
return n;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function vozes(op) {
|
|
45
|
-
op = op || {};
|
|
46
|
-
if (!sintese) return [];
|
|
47
|
-
var v = sintese.getVoices() || [];
|
|
48
|
-
if (v.length) cacheVozes = v;
|
|
49
|
-
var lista = cacheVozes.slice();
|
|
50
|
-
if (!op.todas) {
|
|
51
|
-
var uteis = lista.filter(function (x) { return !NOVIDADE.test(limpo(x.name)); });
|
|
52
|
-
if (uteis.length) lista = uteis;
|
|
53
|
-
}
|
|
54
|
-
if (op.idioma !== false) {
|
|
55
|
-
var pt = lista.filter(function (x) { return /^pt/i.test(x.lang); });
|
|
56
|
-
if (pt.length && !op.todosIdiomas) lista = pt;
|
|
57
|
-
}
|
|
58
|
-
// o sistema às vezes repete a mesma voz; fica só uma de cada
|
|
59
|
-
var vistas = {};
|
|
60
|
-
lista = lista.filter(function (v) {
|
|
61
|
-
var chave = limpo(v.name) + "|" + v.lang;
|
|
62
|
-
if (vistas[chave]) return false;
|
|
63
|
-
vistas[chave] = true;
|
|
64
|
-
return true;
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
return lista.sort(function (a, b) { return nota(b) - nota(a) || a.name.localeCompare(b.name); });
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function melhorVoz() {
|
|
71
|
-
var v = vozes()[0];
|
|
72
|
-
return v ? v.name : null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* Quantas vozes boas existem: se der zero, vale avisar o usuário. */
|
|
76
|
-
function diagnostico() {
|
|
77
|
-
var todas = (sintese && sintese.getVoices()) || cacheVozes;
|
|
78
|
-
var pt = todas.filter(function (v) { return /^pt/i.test(v.lang); });
|
|
79
|
-
return {
|
|
80
|
-
total: todas.length,
|
|
81
|
-
portugues: pt.length,
|
|
82
|
-
premium: todas.filter(function (v) { return PREMIUM.test(v.name); }).length,
|
|
83
|
-
boasEmPortugues: pt.filter(function (v) { return !NOVIDADE.test(limpo(v.name)); }).length,
|
|
84
|
-
melhor: melhorVoz(),
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function prontas() {
|
|
89
|
-
return new Promise(function (ok) {
|
|
90
|
-
if (!sintese) return ok([]);
|
|
91
|
-
if (vozes().length) return ok(vozes());
|
|
92
|
-
var t = setTimeout(function () { ok(vozes()); }, 1200);
|
|
93
|
-
sintese.addEventListener("voiceschanged", function so() {
|
|
94
|
-
clearTimeout(t);
|
|
95
|
-
sintese.removeEventListener("voiceschanged", so);
|
|
96
|
-
ok(vozes());
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/* ------------------------------------------------------------ prosódia -- */
|
|
102
|
-
|
|
103
|
-
/* Fala corrida soa robótica. Quebrar em frases e respirar entre elas é o
|
|
104
|
-
que mais aproxima de uma locução de verdade. */
|
|
105
|
-
function emFrases(texto) {
|
|
106
|
-
return String(texto || "")
|
|
107
|
-
.split(/(?<=[.!?…:])\s+|\n+/)
|
|
108
|
-
.map(function (f) { return f.trim(); })
|
|
109
|
-
.filter(Boolean);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function pausaDe(frase) {
|
|
113
|
-
if (/[?!]$/.test(frase)) return 420;
|
|
114
|
-
if (/[.…]$/.test(frase)) return 340;
|
|
115
|
-
if (/:$/.test(frase)) return 260;
|
|
116
|
-
return 180;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function montar(texto, cfg) {
|
|
120
|
-
cfg = cfg || {};
|
|
121
|
-
var f = new SpeechSynthesisUtterance(String(texto || ""));
|
|
122
|
-
f.lang = cfg.idioma || "pt-BR";
|
|
123
|
-
// um pouco mais devagar que o padrão soa menos apressado
|
|
124
|
-
f.rate = cfg.velocidade != null ? cfg.velocidade : 0.96;
|
|
125
|
-
f.pitch = cfg.tom != null ? cfg.tom : 1;
|
|
126
|
-
f.volume = cfg.volume != null ? cfg.volume : 1;
|
|
127
|
-
|
|
128
|
-
var alvo = cfg.voz || melhorVoz();
|
|
129
|
-
if (alvo) {
|
|
130
|
-
var achou = (sintese.getVoices() || cacheVozes).filter(function (v) {
|
|
131
|
-
return v.name === alvo;
|
|
132
|
-
})[0];
|
|
133
|
-
if (achou) { f.voice = achou; f.lang = achou.lang; }
|
|
134
|
-
}
|
|
135
|
-
return f;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function falarUma(texto, cfg) {
|
|
139
|
-
return new Promise(function (ok) {
|
|
140
|
-
var f = montar(texto, cfg);
|
|
141
|
-
f.onend = f.onerror = function () { ok(); };
|
|
142
|
-
sintese.speak(f);
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/* Fala o texto inteiro, frase a frase, com respiro entre elas.
|
|
147
|
-
|
|
148
|
-
A VOZ NEURAL VEM PRIMEIRO. O speechSynthesis daqui para baixo é o TTS da
|
|
149
|
-
própria máquina: existe em todo computador, e é exatamente por isso que
|
|
150
|
-
soa a computador. Para vídeo não serve. A voz neural (voz-neural.js) tem
|
|
151
|
-
timbre de locução e dá para escolher — inclusive uma grave, de abertura.
|
|
152
|
-
Se ela não subir, o caminho antigo continua aqui embaixo, intacto. */
|
|
153
|
-
function falar(texto, cfg) {
|
|
154
|
-
cfg = cfg || {};
|
|
155
|
-
var t = String(texto || "").trim();
|
|
156
|
-
if (!t) return Promise.resolve(0);
|
|
157
|
-
|
|
158
|
-
if (cfg.motor !== "sistema" && typeof VozNeural !== "undefined") {
|
|
159
|
-
parar();
|
|
160
|
-
cancelado = false;
|
|
161
|
-
var t0n = performance.now();
|
|
162
|
-
return VozNeural.falar(t, cfg)
|
|
163
|
-
.then(function () { return (performance.now() - t0n) / 1000; })
|
|
164
|
-
.catch(function (e) {
|
|
165
|
-
if (window.console) console.warn("[narração] voz neural falhou, indo de sistema:", e && e.message);
|
|
166
|
-
return pelaSintese(t, cfg);
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
return pelaSintese(t, cfg);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/* O caminho antigo: o TTS do sistema, frase a frase. */
|
|
173
|
-
function pelaSintese(texto, cfg) {
|
|
174
|
-
if (!sintese || !String(texto || "").trim()) return Promise.resolve(0);
|
|
175
|
-
parar();
|
|
176
|
-
cancelado = false;
|
|
177
|
-
|
|
178
|
-
var frases = emFrases(texto);
|
|
179
|
-
var t0 = performance.now();
|
|
180
|
-
|
|
181
|
-
return frases.reduce(function (corrente, frase) {
|
|
182
|
-
return corrente.then(function () {
|
|
183
|
-
if (cancelado) return;
|
|
184
|
-
return falarUma(frase, cfg).then(function () {
|
|
185
|
-
if (cancelado) return;
|
|
186
|
-
return new Promise(function (r) { setTimeout(r, pausaDe(frase)); });
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
}, Promise.resolve()).then(function () {
|
|
190
|
-
return (performance.now() - t0) / 1000;
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/* Mede quanto tempo a fala leva, sem ninguém ouvir.
|
|
195
|
-
|
|
196
|
-
Com a voz neural o áudio vem pronto, em arquivo, e a duração está nos
|
|
197
|
-
metadados dele: perguntar é exato e é silencioso. Só o caminho do
|
|
198
|
-
speechSynthesis ainda precisa falar para cronometrar — e lá o volume
|
|
199
|
-
zero é obedecido de verdade. */
|
|
200
|
-
function medir(texto, cfg) {
|
|
201
|
-
cfg = cfg || {};
|
|
202
|
-
if (cfg.motor !== "sistema" && typeof VozNeural !== "undefined") {
|
|
203
|
-
return VozNeural.duracaoDe(texto, cfg).then(function (s) {
|
|
204
|
-
return s || medirPelaSintese(texto, cfg);
|
|
205
|
-
}).catch(function () { return medirPelaSintese(texto, cfg); });
|
|
206
|
-
}
|
|
207
|
-
return medirPelaSintese(texto, cfg);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function medirPelaSintese(texto, cfg) {
|
|
211
|
-
var c = {};
|
|
212
|
-
for (var k in (cfg || {})) c[k] = cfg[k];
|
|
213
|
-
c.volume = 0;
|
|
214
|
-
c.motor = "sistema";
|
|
215
|
-
return pelaSintese(texto, c);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function parar() {
|
|
219
|
-
cancelado = true;
|
|
220
|
-
if (typeof VozNeural !== "undefined") { try { VozNeural.parar(); } catch (_) {} }
|
|
221
|
-
if (!sintese) return;
|
|
222
|
-
try { sintese.cancel(); } catch (_) {}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/* As vozes que o usuário pode escolher: as neurais primeiro (as boas), as
|
|
226
|
-
do sistema depois, marcadas como o que são. */
|
|
227
|
-
function vozesParaEscolher() {
|
|
228
|
-
var lista = [];
|
|
229
|
-
if (typeof VozNeural !== "undefined") {
|
|
230
|
-
lista = VozNeural.vozes().map(function (v) {
|
|
231
|
-
return { id: v.id, nome: v.nome, tom: v.tom, grave: !!v.grave, motor: "neural" };
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
vozes().forEach(function (v) {
|
|
235
|
-
lista.push({ id: v.name, nome: limpo(v.name), tom: "voz do seu computador", motor: "sistema" });
|
|
236
|
-
});
|
|
237
|
-
return lista;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/* O áudio como arquivo — é o que permite a locução ir junto no vídeo
|
|
241
|
-
exportado. O speechSynthesis nunca deu isso: ele fala e some. */
|
|
242
|
-
function audioDe(texto, cfg) {
|
|
243
|
-
if (typeof VozNeural === "undefined") return Promise.reject(new Error("voz neural indisponível"));
|
|
244
|
-
return VozNeural.audioDe(texto, cfg);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function falando() {
|
|
248
|
-
/* A voz pode estar saindo por três lugares; o player precisa da verdade
|
|
249
|
-
de todos, senão corta a cena no meio da fala neural. */
|
|
250
|
-
if (typeof VozNeural !== "undefined" && VozNeural.falando && VozNeural.falando()) return true;
|
|
251
|
-
return !!sintese && sintese.speaking;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function estimar(texto, velocidade) {
|
|
255
|
-
var palavras = String(texto || "").trim().split(/\s+/).filter(Boolean).length;
|
|
256
|
-
if (!palavras) return 0;
|
|
257
|
-
var frases = emFrases(texto).length;
|
|
258
|
-
return Math.max(1.2, palavras / (2.6 * (velocidade || 0.96)) + frases * 0.32);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/* -------------------------------------------------- gravar a própria voz */
|
|
262
|
-
|
|
263
|
-
var gravador = null, fluxoMic = null, pedacos = [];
|
|
264
|
-
|
|
265
|
-
function podeGravar() {
|
|
266
|
-
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia &&
|
|
267
|
-
window.MediaRecorder);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/* Grava do microfone e devolve {url, nome} já salvo na biblioteca. */
|
|
271
|
-
function gravar() {
|
|
272
|
-
if (!podeGravar()) return Promise.reject(new Error("este navegador não grava áudio"));
|
|
273
|
-
return navigator.mediaDevices.getUserMedia({
|
|
274
|
-
audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true },
|
|
275
|
-
}).then(function (fluxo) {
|
|
276
|
-
fluxoMic = fluxo;
|
|
277
|
-
pedacos = [];
|
|
278
|
-
var tipo = MediaRecorder.isTypeSupported("audio/webm;codecs=opus")
|
|
279
|
-
? "audio/webm;codecs=opus"
|
|
280
|
-
: (MediaRecorder.isTypeSupported("audio/mp4") ? "audio/mp4" : "");
|
|
281
|
-
gravador = new MediaRecorder(fluxo, tipo ? { mimeType: tipo } : undefined);
|
|
282
|
-
gravador.ondataavailable = function (e) { if (e.data.size) pedacos.push(e.data); };
|
|
283
|
-
gravador.start();
|
|
284
|
-
return true;
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function pararGravacao(nomeBase) {
|
|
289
|
-
return new Promise(function (ok, falha) {
|
|
290
|
-
if (!gravador || gravador.state !== "recording") return falha(new Error("nada gravando"));
|
|
291
|
-
gravador.onstop = function () {
|
|
292
|
-
if (fluxoMic) fluxoMic.getTracks().forEach(function (f) { f.stop(); });
|
|
293
|
-
var mime = gravador.mimeType || "audio/webm";
|
|
294
|
-
var blob = new Blob(pedacos, { type: mime });
|
|
295
|
-
var ext = /mp4/.test(mime) ? ".m4a" : ".weba";
|
|
296
|
-
var leitor = new FileReader();
|
|
297
|
-
leitor.onload = function () {
|
|
298
|
-
fetch("/api/midia", {
|
|
299
|
-
method: "POST",
|
|
300
|
-
headers: { "Content-Type": "application/json" },
|
|
301
|
-
body: JSON.stringify({
|
|
302
|
-
nome: (nomeBase || "narracao") + ext,
|
|
303
|
-
dados: leitor.result,
|
|
304
|
-
}),
|
|
305
|
-
}).then(function (r) { return r.json(); })
|
|
306
|
-
.then(function (m) { m.erro ? falha(new Error(m.erro)) : ok(m); })
|
|
307
|
-
.catch(falha);
|
|
308
|
-
};
|
|
309
|
-
leitor.onerror = function () { falha(new Error("não consegui ler a gravação")); };
|
|
310
|
-
leitor.readAsDataURL(blob);
|
|
311
|
-
};
|
|
312
|
-
gravador.stop();
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function gravando() { return !!gravador && gravador.state === "recording"; }
|
|
317
|
-
|
|
318
|
-
return {
|
|
319
|
-
disponivel: disponivel, vozes: vozes, prontas: prontas, melhorVoz: melhorVoz,
|
|
320
|
-
diagnostico: diagnostico, limpo: limpo,
|
|
321
|
-
falar: falar, medir: medir, parar: parar, falando: falando, estimar: estimar,
|
|
322
|
-
podeGravar: podeGravar, gravar: gravar, pararGravacao: pararGravacao, gravando: gravando,
|
|
323
|
-
vozesParaEscolher: vozesParaEscolher, audioDe: audioDe,
|
|
324
|
-
};
|
|
325
|
-
})();
|