Stemdrop
Colle une URL, Stemdrop sépare la voix de l'instru, retrouve les paroles synchronisées mot à mot et lance un karaoké multi-appareils.
L’idée
Un karaoké sans catalogue figé : on colle l’URL d’un morceau et, quelques minutes plus tard, on a un lecteur avec les paroles qui s’allument mot à mot et deux curseurs, voix et instru, pour baisser le chanteur d’origine et prendre sa place.
Stemdrop est le projet sur lequel je passe le plus de temps aujourd’hui. Il est en ligne sur stemdrop.noufele.fr.
Comment ça marche
Le projet tient en deux morceaux qui ne se ressemblent pas du tout.
Un Worker Cloudflare sert l’API REST, les WebSockets des salons et la SPA React. Le catalogue vit dans un bucket R2 privé : l’audio est toujours servi par le Worker, jamais par une URL publique du bucket.
Un nœud Python fait le travail lourd, sur un Mac ou un VPS :
yt-dlprécupère l’audio et les métadonnées.- BS-RoFormer sépare la voix de l’instrumental (demucs en repli).
- Les paroles : de vraies paroles d’abord (LRCLIB synchronisé, sinon d’autres sources réalignées par Whisper), et la transcription Whisper seulement en dernier recours.
ffmpegencode les stems en Opus, puis le bundle part dans R2.
Le nœud n’a besoin ni d’IP publique ni de tunnel : c’est lui qui ouvre une WebSocket sortante vers le Worker, et les jobs redescendent par ce canal. Les étapes GPU peuvent aussi être déléguées à une fonction Modal, le nœud ne gardant alors que le téléchargement et l’envoi.
Le karaoké à plusieurs
- Salons partagés : l’hôte ouvre un salon et partage un lien signé (ou son QR code), valable 12 h. Les invités rejoignent la file d’attente et le karaoké sans abonnement ni compte, et l’hôte peut révoquer le lien pour tout le monde en un clic.
- Le téléphone comme micro : la voix du chanteur part du téléphone vers l’écran du salon en WebRTC pair-à-pair. Le Durable Object ne fait que la signalisation, avec un relais de secours pour les réseaux où le P2P échoue.
- Score à la justesse : une mélodie de référence est extraite de la piste voix (pYIN) et comparée à ce que chante chaque participant.
- Paroles : romanisation pour les langues non latines, traduction à la demande, et correction des paroles depuis le lecteur.
Sous le capot
- Worker : Hono, Durable Objects (un par salon, plus un répartiteur unique qui tient le pool de nœuds et la file de jobs), D1 avec Drizzle, Better Auth pour les sessions, R2 pour le catalogue.
- Front : React 19, Vite, Tailwind CSS, TanStack Query, interface en français et en anglais, des AudioWorklets pour le traitement de la voix.
- Nœud : Python, BS-RoFormer, Whisper, ffmpeg.
- Protocole : un contrat partagé entre le Worker et le nœud, qui sert de source de vérité aux deux côtés.
Qualité et exploitation
Le déploiement passe uniquement par la CI : lint, typage et tests, migrations D1, déploiement du Worker, puis une vérification de santé. La suite compte environ 730 tests (399 côté Python, 273 côté Worker, 61 côté front).
Une tâche planifiée surveille le service toutes les dix minutes et m’envoie un e-mail quand quelque chose change : nœud hors ligne, jobs bloqués, job en échec. Le catalogue R2 et la base D1 ont chacun leur procédure de sauvegarde.