Skip to content

Releases: santiquiroz/upflow

v0.72.0 — Cambiar de voz ahora corre OpenVoice

Choose a tag to compare

@santiquiroz santiquiroz released this 19 Aug 22:06

Cambiar una voz por otra ahora corre OpenVoice.

Le das una grabación y una muestra de la voz que querés, y devuelve lo mismo dicho con
esa voz. Antes lo hacía SpeechT5, que es de 2022.

Por qué OpenVoice y no los que salen primero

F5-TTS, E2-TTS y XTTS aparecen antes en cualquier búsqueda y clonan muy bien. Pero
publican el código con licencia permisiva y los pesos con CC-BY-NC: no comercial. Eso
alcanza para un experimento y no para meterlo en una aplicación que otra gente descarga.

OpenVoice V2 es MIT, con "free for commercial use" escrito en su propio README.

Le faltaba el camino ONNX —los exports que circulan son repos sin mantenimiento— así que
se hizo uno, documentado y con la paridad medida:
port-openvoice-onnx.

Qué tan bien clona

Convirtiendo una voz femenina a una masculina, similitud coseno entre embeddings:

convertido vs voz destino 0,913
convertido vs voz original 0,535
línea base: destino vs original 0,511

Se parece mucho al destino, y ya no se parece al original más de lo que el destino se le
parecía.

Salvedad: está medido con el propio encoder de hablante de OpenVoice, que lo favorece.
Sirve para comparar configuraciones de este modelo entre sí; no es una comparación
limpia contra el modelo anterior, que se midió con otro encoder. Por eso no ponemos los
dos números en la misma tabla.

Paridad del port contra el modelo original: 4,54e-09 en el conversor y 9,54e-07 en
el extractor de timbre.

El modelo viejo no se borra

Si ya tenías bajado el pack de SpeechT5, la función te sigue funcionando igual. Sería una
molestia gratuita hacerte bajar 128 MB para recuperar algo que ya andaba.

En una instalación nueva se ofrece OpenVoice, que pesa menos y clona mejor.

Correcciones

  • La pantalla de Tareas decía que "cambiar de voz" no estaba lista aunque el modelo
    nuevo estuviera instalado y funcionando, y encima mandaba a bajar 400+ MB del viejo. Era
    una regresión introducida al agregar OpenVoice, detectada y corregida antes de publicar.
  • "Video a video" le echaba la culpa a ONNX. La generación de video de esta app corre
    por Vulkan, sin ONNX en ningún lado, así que ese motivo hacía pensar que faltaba algo que
    no falta. Ahora dice lo que pasa: la función no está construida.
  • El empaquetado ya no se rompe cuando un editor abierto sobre el repo tiene tomado el
    Python de build.

3905 pruebas de backend y 1226 de frontend, todas en verde.

v0.71.0 — El karaoke enciende palabra por palabra

Choose a tag to compare

@santiquiroz santiquiroz released this 19 Aug 04:26

El karaoke ahora enciende la letra palabra por palabra.

Antes la línea aparecía entera en blanco, que es un video con subtítulos. Lo que lo
vuelve karaoke es ver dónde va la voz ahora.

De dónde salen los tiempos

No de un reparto: son los del modelo. Whisper sabe en qué segundo cae cada palabra,
pero esa información no está en el texto — vive en los pesos de cross-attention del
decoder, y hay que alinearlos con DTW.

El problema: el grafo ONNX oficial de Whisper no publica esos pesos. Con eso,
pedirle tiempos por palabra devuelve cero.

Resultó que sí los calcula, solo que no los expone. Los nodos están ahí, con nombre.
Promoverlos a salidas del grafo alcanza — sin PyTorch, sin volver a descargar nada, y
sobre el .onnx que ya tenés instalado.

Cuánto se equivoca

Medido contra una verdad conocida, no estimada: se sintetiza cada palabra por
separado —así se sabe su duración exacta— y se pegan con silencios fijos, de modo que
los límites son un cálculo.

tiempos del modelo repartir la línea por letras
error medio 147 ms 320 ms
p90 259 ms 583 ms
máximo 260 ms 665 ms

La segunda columna importa tanto como la primera: 147 ms no significa nada hasta saber
contra qué. Le gana 2,2x en promedio y 2,6x en la cola, y eso es lo único que justifica
pagar una pasada más del decoder.

Podés rehacer la medición vos: scripts/make-word-alignment-bench.ps1 arma el banco con
la voz que ya trae Windows, y scripts/bench-word-alignment.py mide.

Si algo falla, no perdés la transcripción

Alinear es una mejora del karaoke. Si el modelo instalado no declara sus alignment
heads
, o el grafo no expone las atenciones, o cualquier otra cosa se rompe, el texto
sale igual y el karaoke vuelve al reparto proporcional. Nunca se cambia algo que
funciona por algo que decora.

Para quien quiera hacer esto en su propio proyecto

El algoritmo, la validación contra transformers (1.06e-06), la accuracy medida y las
trampas que costaron tiempo están en un repo aparte, MIT y documentado:
port-whisper-words-onnx.

Dos que valen solas: transformers usa sdpa por defecto y ese kernel nunca materializa
la matriz de pesos (las atenciones vuelven None), y alignment_heads aparece en
la config del generador, lo que hace parecer que debería funcionar cuando no.


3889 pruebas de backend en verde.

v0.70.0 — Las descargas de YouTube funcionan de nuevo

Choose a tag to compare

@santiquiroz santiquiroz released this 19 Aug 03:26

Las descargas de YouTube funcionan de nuevo.

Qué había pasado

YouTube empezó a exigir dos cosas que ningún descargador traía: resolver un
desafío en JavaScript, y un PO Token (proof-of-origin). Sin el token la
extracción anda —el título, la duración y las calidades salían bien— pero bajar
el archivo devolvía 403 Forbidden.

Antes de tocar nada se midió, contra la última versión publicada de yt-dlp:
fallaba con todos los clientes de YouTube (android_vr, web, web_safari,
ios, mweb, tv_simply), con y sin motor JavaScript, y con dos videos
distintos. O sea no era la versión de yt-dlp, ni el enlace, ni el formato que
hubieras elegido.

Cómo se resolvió

La salida habitual es pasarle a yt-dlp las cookies de tu navegador. Se
descartó: si la app la usa un equipo de gente, leerle el navegador a cada uno no
es una opción.

En cambio se escribió el acuñador del token: ceca,
repo propio y MIT. Genera el token sin cuentas, sin cookies y sin sesión de
nadie. Existe porque el proveedor de referencia del ecosistema es GPL-3.0 y no se
puede distribuir dentro de una aplicación propia.

Qué tenés que hacer

En el instalador hay un componente nuevo, tildado por defecto en la
instalación Completa:

Descargar de YouTube: motor JS y el token que YouTube empezó a exigir (~93 MB)

Si ya tenés Upflow, el botón está en la pantalla de Tareas, junto al resto de los
paquetes.

Si no lo instalás no se rompe nada: los demás sitios siguen funcionando, y
YouTube te explica qué falta en vez de mostrarte un 403 pelado.

Dos detalles, por si te sirven

Costaron encontrar y ninguno de los dos dice su causa en el error:

  • El token se ata al visitor_data de la sesión, no al id del video. Atarlo
    al id —que es lo intuitivo— produce un token válido que igual no descarga.
  • Con el mismo token, el cliente web devuelve solo miniaturas. tv_simply es
    el único que se verificó bajando de punta a punta.

3866 pruebas de backend en verde. Verificado bajando el video que falló en
producción: WAV de 59 MB y 308 segundos.

v0.69.1 — corrección de fugas de disco en rutas de fallo

Choose a tag to compare

@santiquiroz santiquiroz released this 18 Aug 23:14

Corrección sobre v0.69.0,
que es donde están las seis funciones nuevas. Si vas a instalar, usá esta.

Qué se arregló

Tres rutas nuevas escriben archivos temporales grandes y los borraban solo si
todo salía bien. Cuando algo fallaba, el archivo más pesado del trabajo se
quedaba ocupando disco para siempre, porque nadie más conoce ese nombre.

  • Karaoke: si ffmpeg fallaba al armar el video, el wav sin comprimir del
    instrumental y su carpeta quedaban en disco. Lo mismo si la separación
    reventaba antes, con lo que hubiera alcanzado a escribir el decodificado.
  • Comparación de separadores: si ffmpeg fallaba después de escribir el
    fragmento a medias, ese archivo también quedaba.

Y una suposición que iba a envejecer mal: el instrumental del karaoke se elegía
por posición en la lista de pistas. Hoy la posición es la correcta, pero cuál es
la pista principal lo decide el catálogo de modelos, así que ahora se pregunta.

Ninguno de los tres afecta un trabajo que termina bien: si usaste v0.69.0 y no
te falló nada, no perdiste nada más que el tiempo de leer esto.


3862 pruebas de backend en verde. Las tres pruebas nuevas se verificaron quitando
el arreglo: sin él, fallan.

v0.69.0 — Karaoke completo, encadenado de descargas y comparación de separadores

Choose a tag to compare

@santiquiroz santiquiroz released this 18 Aug 23:03

Seis funciones nuevas, casi todas alrededor del audio, más una que cambia cómo se
usa toda la app.

🎤 Karaoke completo, en un solo trabajo

Le das una canción y te devuelve el video: sin voz, con la letra sincronizada en
pantalla
. Si el archivo no tiene imagen, el fondo se genera solo.

Las tres piezas ya estaban sueltas —separar la voz, transcribir con tiempos,
quemar subtítulos—. Lo que faltaba era el empalme: pegarle a la imagen la pista
instrumental en vez de la original, que es justo lo que ninguno de los tres
pasos hace por su cuenta.

Está en Transcripción, como un modo de salida más. Ahora ese selector también
aparece para archivos de audio, porque el karaoke es el único modo con video de
salida que no necesita video de entrada.

🔗 Pegás un link y te llegan las pistas

La descarga puede encadenarse con la separación, sin pasar por el módulo de audio
a mano. Una playlist dispara un trabajo por canción.

El archivo que bajaste no se pierde: cada trabajo tiene su propio nombre para
el mismo contenido. Y si la separación falla —falta el modelo, se acabó la
cuota— la descarga sigue estando completa, con su archivo en disco, en vez de
volverse roja por algo que sí salió bien.

⚖️ Probar 2-3 separadores antes de comprometer el tema

Cuál separa mejor depende de tu material, y los rankings publicados promedian
sobre un dataset que no es el tuyo.

Elegís dos o tres modelos y se corren sobre 30 segundos de tu archivo, que cuesta
unas 8 veces menos que la canción entera. Después elegís con el oído.

El fragmento sale del medio y no del arranque: las intros suelen ser
instrumentales, y juzgar un separador de voces en una parte sin voces no dice
nada.

🎯 Máxima calidad: combinar separadores

Corre dos o tres modelos y promedia pista por pista. Sirve porque cada
arquitectura falla distinto: lo que comparten es la señal —todos estiman la misma
voz— y eso se suma; sus artefactos, que no están correlacionados, se cancelan en
parte.

Lo que no hace, y lo dice también la pantalla: inventar separación que ninguno
logró. Si los tres se dejan la voz adentro del instrumental, el promedio también
la tiene.

El costo se dice antes de esperarlo: son N pasadas completas sobre el tema.

🪄 Detalle generativo por tiles

Recorre una foto a su tamaño real, de a pedazos, pidiéndole a un modelo de
difusión que dibuje textura. Está en Generar, junto a imagen a imagen — que hasta
ahora reducía la foto al tamaño del modelo, o sea servía para reinterpretarla, no
para agregarle detalle.

Una aclaración que la app hace en pantalla y que vale repetir acá: inventa lo
que agrega
. Los poros, los pelos y las letras chicas que aparecen no estaban en
el original. Si querés una copia fiel y más grande, eso es el reescalado del
módulo de imagen, que reconstruye en vez de dibujar.

📥 Varios archivos de una, en todos los módulos

Imagen y audio ya aceptaban lotes. Ahora también video, transcripción y
descargas
: elegís un montón de archivos (o pegás varias líneas de URLs) y
quedan encolados con los mismos ajustes, sin repetir el proceso uno por uno.

El primero se sigue en vivo y el resto entra a la cola lateral. De a uno y no
todos juntos, porque en paralelo se pelean el ancho de banda.


Instalación: bajá el .exe de acá abajo y ejecutalo. Si ya tenés Upflow, el
instalador actualiza sobre lo que hay — los modelos que ya bajaste se conservan.

3859 pruebas de backend y 1226 de frontend, todas en verde.

v0.68.1

Choose a tag to compare

@santiquiroz santiquiroz released this 18 Aug 18:16

La app dejó de mentir sobre lo que puede hacer 🩹

Cinco tarjetas de la pantalla de capacidades decían "todavía no se puede" sobre funciones que ya estaban hechas:

  • Separar stems decía que un intento "devolvió ruido" — la app separa en cuatro pistas desde la versión anterior.
  • Texto a 3D e Imagen a 3D decían que no había forma de correrlo, mientras el módulo de impresión genera mallas desde texto y desde foto.
  • Subtítulos decía que faltaba el paso de sincronizado, con .srt, .vtt, el muxeo, el quemado en la imagen y la traducción ya funcionando.
  • Y un texto suelto que anunciaba que reparar mallas "todavía no está hecho".

Cada una fue cierta el día que se escribió y envejeció sin que nadie la releyera. Ahora las tarjetas que muestran la misma función desde dos lugares distintos no pueden contradecirse: hay una prueba que compara una contra otra, y otra que falla si sobra un texto de "todavía no" sin nadie que lo use.

Cada modelo dice su propia contra

El selector de karaoke marcaba "Lento" a cualquier modelo con advertencia. Funcionó mientras hubo uno solo —el lento— y se rompió al llegar el segundo: el de cuatro pistas empezó a aparecer como lento siendo el más rápido de los dos (unos 13 segundos por canción, contra varios minutos del otro).

Ahora cada modelo declara qué mostrar: el de máxima calidad dice "Lento", el de cuatro pistas dice "Menos preciso", que es su verdadera contra.

Bajo el capó

La separación en cuatro pistas se verificó de punta a punta con los modelos reales, no simulada: el instalador baja los cuatro archivos con sus firmas, la GPU separa a 19x tiempo real —la primera pasada de cada sesión paga unos 3 segundos de preparación— y las cuatro pistas se descargan por separado.

3792 tests backend + 1185 frontend.

v0.68.0

Choose a tag to compare

@santiquiroz santiquiroz released this 18 Aug 03:58

Cuatro pistas 🥁

Hasta ahora Upflow separaba una canción en dos: voz e instrumental. Ahora puede darte cuatro archivos: voz, batería, bajo y todo lo demás. Está en el mismo selector de siempre, en Audio.

Tarda unos 13 segundos por canción de 4 minutos, con las cuatro pistas.

Y separa peor que los modelos de voz. Eso está dicho en la app, antes de que lo elijas, no después de escuchar el resultado: deja más cruce entre pistas que Inst HQ 3 o el de máxima calidad. Se ofrece por lo que hace —cuatro pistas—, no porque suene mejor.

Si te preguntás por qué no usamos uno mejor: los hay, y bastante mejores. Ninguno permite redistribuir sus pesos. Los de más calidad directamente no declaran licencia; el más conocido dice que sus pesos "se proveen solo con fines científicos". Este modelo (Open-Unmix umxhq) es MIT de verdad, declarado sobre los archivos mismos, y por eso es el que se puede incluir. Está pedido el permiso para el bueno; si llega, entra.

Los grafos salen de un port propio, publicado: port-openunmix-onnx. Descarga 136 MB en cuatro archivos, la primera vez que lo usás.

Bajo el capó

La parte interesante es lo que no hubo que tocar. El soporte de N pistas entró en la v0.66.0 preparado para un modelo que todavía no existía, y aguantó al primero real sin un solo cambio.

Lo que sí se corrigió: con tres de los cuatro archivos bajados, la app ofrecía el modelo y el trabajo moría al cargar el segundo. Ahora un modelo cuenta como instalado solo si están todos sus archivos.

3780 tests backend + 1184 frontend.

v0.67.0

Choose a tag to compare

@santiquiroz santiquiroz released this 18 Aug 02:43

AudioSR pesa la mitad 🪶

Si tenés GPU, el botón de descarga de AudioSR ahora baja 1.26 GB en vez de 2.51 GB, y corre un 9% más rápido. La calidad es la misma: la diferencia con la versión anterior está 59.4 dB por debajo de la señal — inaudible.

No hay nada que configurar. El botón elige solo según tu equipo: fp16 si el dispositivo por defecto es la placa de video, fp32 si es el procesador. La versión liviana no sirve para CPU (el proveedor de CPU tiene muchos menos operadores en esa precisión, y los que tiene suelen ser más lentos), así que si intentás correrla ahí te lo dice antes de empezar y te explica cómo reinstalar la otra, en vez de reventar a mitad del proceso.

Los modelos livianos se publicaron aparte, con el hash de cada archivo y el del original del que salieron: port-audiosr-onnx / models-fp16-v1.0.

Un control de acceso que ahora está probado

Los STL reparados y las piezas generadas se descargan por un token de 32 caracteres. Ese token ya estaba atado a su dueño desde hace varias versiones — pero no había ni una prueba que lo verificara, y un control de acceso sin prueba es una afirmación, no un hecho. Ahora hay 11, incluida la comprobación de que si se quita el control, las pruebas efectivamente fallan.

Bajo el capó

La medición de fp16 sobre AudioSR quedó documentada entera, incluidas las dos veces que casi sale mal: la primera lectura decía "1.66x más rápido" y era falsa —esa corrida pagaba el calentamiento del sistema—, y la varianza entre corridas resultó ser más grande que el efecto que se buscaba medir, así que el número final salió de un A/B pareado de 6 pares.

También se reportó río arriba un bug del proveedor DirectML encontrado durante el port de RoFormer: onnxruntime#32146. Afecta en silencio a cualquier modelo que exporte nn.GLU, que son bastantes en audio y en lenguaje.

3771 tests backend + 1184 frontend.

v0.66.0

Choose a tag to compare

@santiquiroz santiquiroz released this 17 Aug 18:31

La cola sobrevive a recargar el navegador 🔄

Transcripción, descargas y 3D no volvían si recargabas la página a mitad de un trabajo: seguía corriendo en el servidor, pero la interfaz lo perdía y no había forma de volver a él ni de bajar el resultado. Ahora las siete familias se recuperan igual.

De paso, la vista de administrador ("ver todos") pasó de cubrir cuatro familias a las siete: antes un admin veía media aplicación.

Y un hueco de privacidad corregido en el camino: las respuestas de trabajos 3D no incluían el dueño, así que un admin veía trabajos ajenos sin saber de quién eran.

Scripts de descarga que no pueden mentir

Continuación del arreglo de la versión anterior, ahora sobre los que quedaban:

  • Magpie: si no se puede descargar la licencia GPL-3.0 del componente que se redistribuye, ya no pasa en silencio.
  • Traducción: verifica los tres modelos y su contenido, no solo que exista la carpeta.
  • Shap-E (los dos): fallan si el paso de Python falla, en vez de continuar.
  • FFmpeg: exige una única raíz al descomprimir y verifica los binarios y la licencia al terminar; antes tomaba el primer directorio que encontrara.

Bajo el capó

La separación de audio pasó a soportar N stems internamente: cada modelo declara si su pista sale directo del modelo o es el complemento del resto. Los tres motores (MDX, VR y RoFormer) quedaron unificados detrás de una sola función, y el comportamiento de los modelos de dos pistas es idéntico al anterior.

Separación en 4 pistas (batería/bajo/voz/resto): no se publica, por licencia. El modelo que la haría posible se anunciaba como MIT, pero esa licencia cubre solo el código y nadie dijo nada sobre los pesos. La alternativa obvia (Demucs) sí es un no explícito: su autor dice que los pesos "se proveen solo con fines científicos". El trabajo técnico quedó hecho y verificado (4 de 4 pistas separadas correctamente), esperando permiso — ya pedida el 2026-08-17 en ZFTurbo/Music-Source-Separation-Training#249.

Corrección sobre la nota original: decía que el autor "declinó explícitamente licenciar los pesos". No fue así — declinó decidir por modelos que entrenaron otros, que es distinto, y la pregunta por este checkpoint en concreto nunca se había hecho. Ya está hecha.

3742 tests backend + 1184 frontend.

v0.65.0

Choose a tag to compare

@santiquiroz santiquiroz released this 10 Aug 20:39

Convertir formato sin tocar nada 🔁

Nuevo camino en Audio: elegís el formato de salida y listo — sin pedir ningún procesamiento. Un FLAC a MP3 para que entre en cualquier lado.

Y de verdad no toca nada más. La conversión va por su propio camino, en una sola pasada, conservando el sample rate y la profundidad de bits: tu FLAC de 44.1 kHz / 24 bits sale como MP3 a 44.1, no remuestreado a 48. (El camino de procesamiento sigue decodificando a 48 kHz porque los motores lo necesitan; eso no cambió.)

Cuando el formato destino no puede conservar algo, queda registrado y visible en el detalle del trabajo: si hubo que remuestrear, si se bajó de 5.1 a estéreo, si se redujo la profundidad. Nunca en silencio.

  • Formato nuevo: M4A/AAC — el más compatible con teléfonos y Apple. También disponible como salida del procesamiento normal.
  • Calidad elegible para formatos con pérdida: máxima (MP3 320k / AAC 256k), equilibrada (192k) o compacta (128k). El valor por defecto pasó de 192k a máxima.
  • Aviso no bloqueante cuando convertís de lossless a lossy, que es irreversible.
  • Si el archivo ya está en ese formato, te lo dice en vez de encolar un trabajo que no hace nada.
  • Un M4A que ya era AAC se copia sin re-comprimir, en lugar de degradarlo otra vez.

Voz: el pack decía estar listo sin estarlo 🎙️

La conversión de voz necesita tres piezas. El script bajaba dos, para la tercera imprimía instrucciones para generarla a mano… y terminaba con éxito. La tarjeta, que revisaba una sola de las tres, se ponía verde. El panel, que le pregunta al motor real, decía "falta el modelo". Tres capas contradiciéndose.

Ahora la pieza que faltaba se descarga con un click, verificada por hash, desde un repositorio propio publicado para eso: port-xvector-onnx (Apache-2.0, con atribución al modelo original).

El mismo defecto estaba en cinco packs más y se arregló en todos:

  • Texto a voz (Kokoro): el tokenizer solo se bajaba por una rama del script, y la tarjeta miraba una carpeta que el script creaba antes de descargar nada — una corrida fallida igual quedaba en verde.
  • Interpolación (RIFE): la tarjeta pedía solo el binario; también hace falta la carpeta del modelo.
  • Generar video: pedía el binario de sd.cpp contra un paquete que baja 16 GB de pesos y nunca puede producirlo. Descargarlo jamás habría alcanzado.
  • Generar 3D: apuntaba a la carpeta en vez del índice; una descarga a medias contaba como completa.
  • Dos scripts más ganaron verificación final.

Y quedó una defensa permanente: un test compara los requisitos que declara cada tarjeta contra los que su motor exige de verdad. Si alguna promete menos de lo que necesita, la suite falla.

3710 tests backend + 1177 frontend.