La semaine du 19 janvier au 25 javier 2026

| french

Lundi, le dix-neuf janvier

Ma fille s'est levée juste à temps pour l'école. Elle a dit qu'elle était fatiguée et n'a pas voulu y aller. Je l'ai câlinée et j'ai fait la lessive. Cette fois, j'ai mis un minuteur pour penser à suivre notre routine pendant la pause déjeuner. J'ai revu mon journal avec l'aide de quelques nouvelles fonctions qui utilisent Spookfox pour communiquer avec Mozilla Firefox, envoyer mes brouillons à Google Gemini et afficher les résultats au format Org Mode dans Emacs. J'ai aussi étudié les points de prononciation du rendez-vous précédent avec l'aide de la synthèse vocale et de la reconnaissance vocale en continu. Petit à petit, je m'améliore.

Ma fille a manqué les cours de l'après-midi. D'une part, je me suis inquiétée parce qu'elle pourrait développer de mauvaises habitudes. D'autre part, au moins, elle est dans un endroit sûr au lieu de faire l'école buissonnière, de réprimer ses pensées pendant qu'elle est assise en classe, ou de faire des bêtises. Certaines familles préfèrent l'école à temps partiel pour adapter le système à leurs besoins plutôt que l'inverse, ou même ne rien faire de formel du tout. Je comprends qu'elle ait voulu quelque chose de différent. C'est difficile de rester assise et d'écouter un cours qui se déroule si lentement. Je ne sais pas si j'en serais capable, alors je ne peux pas la contraindre. Je lui ai déjà demandé des choses difficiles, par exemple de porter un masque dans les espaces publics fermés.

Je pense que mon premier objectif est de l'élever pour qu'elle puisse explorer le monde par elle-même, y compris choisir ses propres objectifs, planifier et concrétiser ses idées, et évaluer les résultats. En revanche, je veux la soutenir jusqu'à ce qu'elle y parvienne. Si ce n'est pas une question de santé ou de sécurité, je veux la laisser décider autant que possible, particulièrement quand elle le veut vraiment. C'est normal qu'elle ait envie de plus d'indépendance à l'adolescence. La quête d'indépendance pourrait impliquer de rejeter les décisions ou les conseils des parents. Nous n'en sommes pas encore là, donc je dois m'y préparer en resserrant les liens entre nous. Quand elle se retrouve dans une situation qui serait peut-être trop difficile à gérer toute seule, elle saura au plus profond d'elle-même que nous sommes toujours là pour elle. Elle est généralement raisonnable. Mon but n'est pas de lui faire la leçon. Ce n'est même pas de lui poser des questions pour qu'elle réfléchisse, ce qui pourrait être perçu comme un interrogatoire. Mes sous-objectifs consistent peut-être à lui assurer une base solide (la nourriture, les câlins et ainsi de suite). Je peux aussi partager ce que j'apprends dans ma propre vie en discutant avec elle si elle est curieuse. Si elle n'est ni curieuse ni prête, je peux lui laisser des notes.

Bon, je l'ai emmenée à son cours de gymnastique, et nous avons joué à Pokémon Go dans un nouveau quartier. Il faisait très froid. Mon vélo cargo n'a pas bien fonctionné sur le chemin du retour. Au début, mon écran refusait de se connecter. Puis il s'est connecté, mais je n'ai pas pu changer de vitesse. Heureusement, j'étais bloquée sur une vitesse moyenne et les pistes étaient gérables. Débrancher et rebrancher l'écran a finalement résolu le problème.

Mon mari a trouvé une boîte de rouleaux de printemps faits maison au congélateur. Il les a préparés avec notre fille.

Je commence un brouillon en français :

Mardi, le vingt janvier

J'ai évalué quelques modèles et serveurs pour la reconnaissance vocale. J'ai trouvé que le serveur Speaches avec le modèle faster-whisper-base est plus rapide que ma configuration précédente. J'ai aussi essayé le mode continu de Speaches, mais les résultats sont mauvais. Peut-être que mon ordinateur n'a pas assez de puissance pour ça, ou peut-être que j'ai juste besoin de le configurer correctement.

Pendant la pause déjeuner, ma fille a joué à Minecraft Education. Elle a appris un peu de Python. Pendant qu'elle jouait, j'ai continué mon jeu de Pokémon. J'ai terminé la Forêt de Jade et j'ai vaincu le champion d'arène d'Argenta, qui était facile à battre avec mon Carapuce.

J'ai discuté des distorsions cognitives et des perspectives différentes avec ma thérapeute. J'ai aussi discuté des limites, que je trouve un peu difficiles à gérer avec ma fille. Mes limites avec d'autres personnes sont peut-être trop rigides, et il faut que j'apprenne à demander de l'aide. Mes limites avec ma fille sont peut-être trop perméables, et il faut que j'apprenne à négocier ou parfois faire de moi une priorité.

Pour le souper, ma fille a préparé du Kraft Dinner aux saucisses. Elle a trouvé qu'elle préférait la marque Annie plutôt que la marque KD.

La météo a annoncé de la neige demain. Nous avons décidé qu'il valait mieux faire les courses aujourd'hui avant les intempéries de demain parce que la neige rendra la marche difficile.

J'ai réessayé les exercices d'écoute et de parole sur Google Translate. Étonnamment, j'ai réussi. Peut-être que je peux m'entraîner avec ça chaque jour. C'est gratuit.

À l'heure du coucher, ma fille et moi avons lu à tour de rôle. J'ai été impressionnée par sa lecture. Elle peut lire de manière expressive et sans problème.

Mercredi, le vingt-et-un janvier

L'école virtuelle avait une remplaçante aujourd'hui, donc ma fille n'a pas voulu participer à cause des problèmes techniques et à cause des bêtises de ses camarades. Cette fois, elle nous a vraiment demandé si elle pouvait sécher, ce qui est… un progrès, je suppose? Je me souviens quand il y a un remplaçant, le rythme est souvent trop lent et ma fille pense que c'est une perte de temps. Bon, j'ai essayé de considérer l'école comme un des outils pour l'apprentissage et en ce moment comme une expérience de l'autodétermination. Elle s'est assise sur mes genoux pendant qu'elle travaillait sur ses devoirs, ce qui est probablement plus qu'elle aurait accompli si elle était assise en classe de mauvaise humeur.

J'ai mis à jour mon logiciel pour renouveler les livres que nous avons empruntés à la bibliothèque. Je n'ai pas pu réécrire certaines fonctions parce que je n'ai pas de livres qui attendaient à la bibliothèque. Cette fonction est en attente. Pour le moment, mon logiciel peut encore afficher un rapport des livres qui doivent être rendus.

Malgré la neige, j'ai emmené ma fille à la patinoire pour patiner avec ses amies. Sa nouvelle nourrice est venue au Canada depuis peu et n'a pas ses propres patins, donc j'aidais les enfants avec leurs patins et leurs casques, même si personne ne savait comment attacher la grille du casque. Cette amie a 5 ans et c'était sa première fois à la patinoire, donc je voulais être très très prudente. J'ai même demandé à l'homme qui jouait au hockey de l'autre côté, mais il ne savait pas non plus. Après une brève consultation avec la mère de son amie, j'ai attaché son casque mais j'ai laissé la grille pendre. Je l'ai aidée à tenir un support et je la guidais pendant que ma fille et son autre amie jouaient ensemble. Heureusement, j'ai apporté du chocolat chaud et beaucoup de chauffe-mains, parce que ses amies et leur nourrice avaient froid. Nous les avons accompagnées près de chez elles parce que ma poussette était très utile pour apporter leurs patins, casques, et sacs. Ma fille et son amie discutaient en route, mais après s'être quittées, elle a dit qu'elle était surstimulée. C'est possible que les intérêts de ma fille et de cette amie aient divergé. Ma fille espère trouver une amie qui aime parler de Pokemon, Star Wars, Minecraft, ou KPop Demon Hunters au lieu de se vanter de ses affaires ou de leurs autres amies, et qui préfère aussi une conversation calme au lieu de bavarder sans cesse. C'est la vie, on passe à autre chose.

Après avoir poussé la poussette dans tant de neige, j'étais très fatiguée. Ma fille a dit qu'elle voulait une journée tranquille demain, et j'ai totalement approuvé.

Avant de faire son pain, mon mari a préchauffé le four en faisant des biscuits au chocolat. C'était une très bonne idée.

Jeudi, le vingt-deux janvier

J'étais encore fatiguée quand je me suis levée à cause de l'effort hier. À cause de mon manque d'énergie, il faut choisir soigneusement mes tâches.

  • Je dois rendre les livres cet après-midi parce que la météo annonce qu'il fera très froid demain.
  • Ma fille est allée à l'école en ligne sans petit-déjeuner, donc je dois préparer sa collation pour qu'elle puisse manger sur le pouce pendant la récré.
  • J'entraîne une bénévole pour l'infolettre de Bike Brigade, donc je dois organiser ses éléments dans le brouillon pour qu'elle puisse voir le processus.

J'ai rendu les livres en retard et j'ai emprunté beaucoup de livres pour ma fille pour les prochains jours à la maison. J'ai aussi joué un peu à Pokémon Go en chemin. Mon mari a joué à Pokémon Émeraude sur sa Game Boy Advance à l'extérieur grâce à ses boutons physiques, qui pouvaient être utilisés malgré les gants. Il était emmitouflé dans des couvertures et profitait du soleil.

J'ai mis à jour des données pour mon client. Je ne veux pas travailler aujourd'hui, donc j'ai laissé de côté les autres tâches.

Au lieu de travailler, j'ai revu mon journal en français et créé une fonction pour sauter à la prochaine correction de l'IA. Pour le moment, j'affiche le commentaire en bas de la fenêtre. Je voudrais l'afficher près du curseur (peut-être via un popup) et souligner toutes les corrections pour revoir plus facilement.

Nous avons joué à Pokémon Go en faisant les courses. Nous avons suivi une route qui commence près de la maison, et nous avons attrapé beaucoup de nouveaux Pokémon. Nous sommes allés dans un parc plus loin. Nous avons vaincu trois arènes ! Ça signifie que nous avons pu laisser nos Pokémon là-bas jusqu'à ce que nos Pokémon soient vaincus ou qu'ils ne manquent de motivation. Ça nous fera gagner des PokéCoins, ce qui nous permettra d'augmenter nos inventaires plus tard. Ma fille avait suffisamment d'énergie pour marcher plus, donc après le retour de mon mari, ma fille et moi sommes allées au parc à proximité pour activer davantage de PokéStops et collecter plus d'objets. Comme il va faire très froid ces prochains jours, si elle veut jouer à l'extérieur aujourd'hui, je suis contente de l'accompagner.

Pour le souper, j'ai préparé une tourte au poulet avec de la pâte brisée que j'avais préparée hier soir. Ma fille m'a aidée à couper les ingrédients. Elle a aussi préparé sa collation pour l'école demain. Mon mari a préparé une salade romaine. On s'est régalés. Malheureusement, ma fille a laissé tomber son petit bol fait à la main et il s'est cassé. C'est la vie. Heureusement, elle avait fait beaucoup de petits bols grâce à son intérêt pour la poterie.

Vendredi, le vingt-trois janvier

J'ai réussi à donner envie à ma fille de se lever avec un pain au chocolat pour le petit-déjeuner, donc elle a eu du temps avant l'école.

J'ai copié la mauvaise clé de la diffusion en direct, donc j'ai été un peu en retard pour mon événement. Ce n'était pas grave. En tout cas, c'était gratuit. J'ai modifié ma fonction pour la reconnaissance vocale pour qu'elle insère le résultat au point que j'avais sélectionné. C'est même possible d'insérer à plusieurs points si je veux. J'ai aussi créé une fonction pour mettre les enregistrements en file d'attente pour la reconnaissance vocale et insérer au fur et à mesure les résultats.

Le coordinateur du cours de patinage m'a appelé pour dire que le cours de dimanche est annulé à cause du temps très froid.

Ma fille était enrhumée et malade. J'ai une petite inquiétude parce que son amie qui était venue chez nous commençait à éternuer à l'intérieur, mais c'était trop tard pour changer le plan. J'avais pensé que nos masques avaient peut-être suffi. J'étais un peu enrhumée, donc je vais faire un test rapide de COVID demain parce qu'elle déteste ça. C'est le bon moment pour hiberner. On doit persévérer.

Samedi, le vingt-quatre janvier

Ma fille a continué à se sentir malade, avec un mal de gorge et des éternuements. J'ai préparé de la bouillie de riz et elle l'a mangée. Elle a regardé Le Retour de Mary Poppins. J'ai effectué un test COVID, qui s'est avéré négatif. Il faut beaucoup de repos et de câlins. J'ai fait chauffer de l'eau au citron et au miel. Il faisait moins quinze degrés, très froid.

J'ai modifié mon logiciel pour transformer l'infolettre de Bike Brigade pour qu'il puisse traiter les émojis dans les textes. J'ai aussi mis à jour mon logiciel de recherche de livres sur le site de la bibliothèque.

Pour le souper, j'ai encore préparé de la bouillie de riz parce que j'aime ça quand je suis malade.

Une réflexion sur les limites :

Mes limites avec ma fille sont peut-être trop perméables. Voici quelques points de friction occasionnels.

  • Surstimulation à cause du chant ou du bavardage
  • Temps ou énergie, par exemple si elle veut passer du temps avec moi et que je suis préoccupée ou fatiguée
  • Mentalement, à cause des interruptions ou des demandes, ou aussi si je fais de ses problèmes les miens
  • Émotionnellement, par exemple si elle est en colère et veut me faire mal
  • Physiquement, par exemple si elle se blottit contre moi, ce qui m'empêche de taper sur l'ordinateur ou de bien dormir; ou si elle est en colère et me pousse

Quelles seraient les meilleures limites ?

  • Avant de me sentir surstimulée, je peux lui demander d'être un peu plus tranquille.
  • Pour le temps, je peux mettre un minuteur.
  • Mentalement, la plupart de mes tâches peuvent être mises de côté. Elle veut passer du temps avec moi, c'est génial et je sais que ce temps passe rapidement. Quant à ses problèmes… Il est difficile pour la plupart des parents de voir que leurs enfants sont en difficulté. Mais si j'essaie de résoudre ses problèmes à sa place (par exemple, son ennui à l'école), en fait ça revient à dire qu'elle ne peut pas les résoudre toute seule. Le moment idéal pour apprendre à résoudre des problèmes est celui où les problèmes sont petits.
  • Émotionnellement ou physiquement : si elle est en colère, il n'y a pas grand-chose que je puisse faire. Elle peut se calmer dans sa chambre ou bien je peux me calmer dans ma chambre. Il est important que ma fille apprenne qu'elle doit gérer ses propres émotions.

Le fait de fixer de meilleures limites nous aidera parce que j'aurai plus d'énergie et je serai plus heureuse, et elle sera plus compétente dans la vie et dans les interactions.

Mais c'est un peu difficile pour moi parce que je pense souvent pouvoir m'adapter, et le temps file. Elle grandira bientôt. Je doute aussi parfois de connaître la bonne voie.

J'ai mis en pratique le maintien de mes limites. Elle a fini par se fatiguer en regardant la télévision, donc elle a voulu jouer à un jeu de société avec moi. Mais je n'ai pas voulu jouer aux jeux qu'elle proposait parce qu'ils demandaient trop de concentration. De son côté, elle n'a pas voulu jouer aux jeux que je proposais parce qu'ils étaient trop simples. J'ai aussi voulu finir de modifier mon logiciel pour l'infolettre. J'ai mis un minuteur pour une demi-heure et je suis allée au sous-sol pour y travailler. Elle m'a demandé un câlin. J'ai dit que je n'étais pas libre mais que je pourrais lui faire un câlin après avoir fini mon travail. Elle est remontée à l'étage et s'est amusée. Comme promis, après avoir fini, je lui ai fait un câlin pendant qu'elle regardait la télévision. J'ai proposé de jouer avec elle, mais à ce moment-là, elle n'a pas voulu jouer. C'est la vie. Nous avons regardé la télévision ensemble.

Concernant mes limites avec les autres, je pense qu'en ce moment, je voudrais devenir plus prudente au lieu de faire confiance aux autres. C'est vrai que c'est facile de ressentir ça quand on est malade. Si j'avais eu une machine à remonter le temps, je n'aurais pas invité son amie. Les moments plus optimistes me manquent, mais on en est là. Peut-être qu'un jour, se voir en vrai sera plus facile, mais pour l'instant, il faudra se contenter d'être en ligne ou à l'extérieur.

Dimanche, le vingt-cinq janvier

Nous continuons d'être malades, mais je pense que ma fille va mieux. Mon mari et moi portons des masques dans les pièces communes chez nous pour réduire les risques. Nous y sommes habitués. La fois où mon mari a eu le COVID, ma fille et moi avons réussi à l'éviter.

J'ai créé des fonctions pour corriger la grammaire en français sous Emacs avec flyspell combiné à gptel (pour le modèle d'IA openai/gpt-oss-120b sur groq.com) ou à Spookfox (pour l'interface web gratuite de Google Gemini). Je pense que les résultats de Gemini sont meilleurs que ceux d'OpenAI/gpt-oss-120b, mais l'utilisation d'OpenAI/gpt-oss-120b présente aussi un avantage, car je n'ai pas besoin d'appuyer sur une touche une fois le résultat prêt. Comme j'apprends le français, je ne veux pas qu'Emacs réécrive mon texte automatiquement. Je veux souligner les erreurs, afficher les explications et les corriger moi-même.

À cause du froid extrême et de l'énorme quantité de neige, le cours de patinage de ma fille a été annulé, donc elle est restée une vraie pantouflarde toute la journée. Malgré la neige, je suis sortie pour déneiger. Il valait mieux le faire quand la neige était légère. J'ai déneigé nos allées, la terrasse, le porche, les marches et les trottoirs même jusqu'à la rue d'à côté parce que des voisins ne déneigent pas souvent bien les leurs. Chaque fois que je finissais le travail, il tombait davantage de neige, donc je recommençais. Deux heures ! La neige continuait à tomber. Je tournais toujours en rond. On dirait que rien n'a été déneigé du tout. Nous manquons d'espace pour entasser la neige dans la cour avant. Heureusement, la cour arrière a plus d'espace, donc je peux transporter la neige avec une luge jusqu'au jardin arrière si c'est nécessaire. En tout cas, c'est un bon exercice. Une fois que je suis rentrée, mon mari est sorti et a aussi déneigé.

Ma sœur qui habite aux Pays-Bas m'a appelée parce que Donjons et Dragons intéressait l'une de ses filles et elle se demande si ma fille veut y jouer avec elles et avec notre autre sœur qui était souvent la meneuse de jeu quand nous étions plus jeunes. Je pense que c'est une bonne occasion de créer des liens entre les cousines. Nous allons organiser une partie. Ma sœur a aussi apprécié le discours du Premier ministre du Canada, Mark Carney, à Davos. Elle pense que c'est un bon modèle pour sa fille qui subit du harcèlement à l'école.

Prononciation

Extrait de La semaine du 12 janvier au 18 janvier:

  • … et elle a du mal à se débrouiller. (day broo yay)
  • Peut-être que je ne pose pas la bonne question (kes tion) quand je me demande ce qu'un parent responsable fait.
  • … ce qui est étonnamment utile pour me souvenir de ce que je viens de penser. (pehn say)
  • J'ai aussi travaillé sur l'outil (loo teel) de visualisation et la gestion du routage audio.
  • J'ai publié un article (ar tee kleuh) avec quelques captures d'écran sur mon blog.
  • Elle m'a distribué de l'eau chaude, des chaussettes pelucheuses pour moi et des gouttes pour ses yeux. (says yeuh)
  • … si l'énoncé était très court, l'horodatage initial est peut-être manquant (man kahn) ou inexact (ein ex act)
  • Il faudra peut-être que j'analyse les silences (see lehns) dans le fichier audio pour déterminer un horodatage initial raisonnable.
  • Je ne consulte pas souvent mes courriels professionnels, donc j'ai failli (faie yee) la rater.
  • Ils ont besoin du plan de salle (sal) pour des événements.
  • … nous avons déneigé des trottoirs (trawh twoirs) et des coins des rues dans notre voisinage
View org source for this post

Using Silero voice activity detection to automatically queue multiple transcriptions with natrys/whisper.el

| audio, speech-recognition, emacs

I can queue multiple transcriptions with whisper.el so that they get processed sequentially with backup audio. It catches up when I pause to think. Now I want to use Silero voice activity detection to do that kind of segmentation for me automatically.

First, I need a Python server that can print out events when it notices the start or stop of a speech segment. If I print out the timestamps, I might be able to cross-reference it someday with interestingthings. For now, even just paying attention to the end of a segment is enough for what I want to do.

Python script for printing out events
import sounddevice as sd
import numpy as np
import torch
import sys
from datetime import datetime, timedelta

SAMPLING_RATE = 16000
CHUNK_SIZE = 512
model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad',
                              model='silero_vad',
                              force_reload=False)

(get_speech_timestamps, save_audio, read_audio, VADIterator, collect_chunks) = utils
vad_iterator = VADIterator(model, threshold=0.5, min_silence_duration_ms=1000)

stream_start_time = None

def format_iso_with_offset(offset_seconds):
    if stream_start_time is None:
        return "PENDING"
    event_time = stream_start_time + timedelta(seconds=offset_seconds)
    return event_time.astimezone().isoformat(timespec='milliseconds')

def audio_callback(indata, frames, time, status):
    global stream_start_time
    if status:
        print(status, file=sys.stderr)
    if stream_start_time is None:
        stream_start_time = datetime.now()
    tensor_input = torch.from_numpy(indata.copy()).flatten()
    speech_dict = vad_iterator(tensor_input, return_seconds=True)
    if speech_dict:
        if "start" in speech_dict:
            print(f"START {format_iso_with_offset(speech_dict['start'])}", flush=True)
        if "end" in speech_dict:
            print(f"END {format_iso_with_offset(speech_dict['end'])}", flush=True)
try:
    with sd.InputStream(samplerate=SAMPLING_RATE,
                        channels=1,
                        callback=audio_callback,
                        blocksize=CHUNK_SIZE):
        while True:
            pass
except KeyboardInterrupt:
    print("\nStopping...")

Then I can start this process from Emacs:

(defvar my-vad-events-process nil)
(defvar my-vad-events-dir "~/proj/speech/")
(defvar my-vad-events-command `(,(expand-file-name ".venv/bin/python" my-vad-events-dir)
                                "vad-events.py"))

(defun my-vad-events-filter (proc string)
  (when (and (string-match "^END" string)
             (process-live-p whisper--recording-process))
    (message "Noticed speech turn...")
    (my-whisper-continue)))

(defun my-vad-events-ensure ()
  "Start the process if it's not already running."
  (interactive)
  (unless (process-live-p my-vad-events-process)
    (let ((default-directory my-vad-events-dir)
          (process-environment
           (cons
            (format
             "PULSE_PROP=node.description='%s' media.name='%s' node.name='%s'"
             "vad" "vad" "vad")
            process-environment)))
      (setq my-vad-events-process
            (make-process
             :name "vad-events"
             :command my-vad-events-command
             :buffer (get-buffer-create "*vad-events*")
             :stderr (get-buffer-create "*vad-events-err*")
             :filter #'my-vad-events-filter)))))

Because I added Pulse properties to the process environment, I can easily use epwgraph to rewire the input so that it gets the input from my VirtualMicSink instead of the default system audio device. (Someday I'll figure out how to specify that as the input automatically.)

Now I can press my shortcut for my-whisper-continue to start the process. As I keep talking, it will continue to record. When I pause for more than a second between sentences, then it will send that chunk to the server for transcription without me having to press another button, while still listening for more speech.

How is this different from the streaming approach that many real-time speech recognition services offer? I think this gives me a bit more visibility into and control of the process. For my personal use, I don't need to have everything processed as quickly as possible, and I'm not trying to replicate live captions. I just want to be able to look back over the last five minutes to try to remember what I was talking about. I usually have a lot of quiet time as I think through my next steps, and it's fine to have it catch up then. I also like that I can save time-stamped audio files for later processing, divided according to the speech segments. Those might be a little bit easier to work with when I get around to compositing them into a video.

This is part of my Emacs configuration.
View org source for this post

Emacs: This year, I will... / Cette année, je vais...

| emacs
In English

Inspired by the Emacs Carnival theme for January, this year, I will:

  • take more notes, perhaps with the help of speech recognition
    • Now I can manage my time better since the kiddo can study more independently, but I still have to manage interruptions from my life and from my own brain. If I write or dictate notes while I think or work, I can get back into things more easily. Speech recognition allows me to capture more thoughts, even if the results need a fair bit of reviewing and revising. I'm looking forward to checking out the ideas others have shared, such as configuring more transient.el interfaces and adding more tweaks to my Org Mode publishing.
  • stream to think out loud
    • My daily routines are gradually becoming more predictable, so I might be able to schedule streams or jump onto one to share ideas. If I do a live broadcast, I can learn from the questions and comments I get. Fridays at 10:30 AM seems like a good time for that, and when possible, I can also do it spontaneously.
  • record videos to share what I learn, and package my functions to make them reusable when possible
    • Like the previous points, sharing my ideas and my work can help others and start conversations. If I improve subed-record.el to combine video and audio recordings with screenshots and subtitles, I might be able to create videos more easily.
  • write more in French and improve my environment for this purpose
    • I like the mental stimulation of writing in another language. Lots of other people are learning languages too, so we might be able to help each other. For example, my functions for highlighting new words and grammatical errors could be useful.
  • practice speaking by making audio recordings with the help of subed-record and speech synthesis to improve my pronunciation
    • The advantage of implementing this in Emacs is that I can customize my workflow. For example, I want to write my draft and then record the audio sentence by sentence after listening to an example. I also want to see if I can more easily edit a recording of my session with my tutor so that I can keep only my last pronunciation attempts.
  • improve processes for EmacsConf and Emacs News
    • I might be able to organize much of it by myself, but other volunteers might also be able to help, which would be even better. I want to create the infrastructure to manage several virtual meetings simultaneously, probably with speech recognition, audio spatialization, and lots of keyboard shortcuts. I also want to improve my subtitling process.

The more I do in Emacs, the more I think of ideas for improvement…

En français

Sur le thème du Carnaval d'Emacs pour janvier - Cette année, je vais :

  • prendre plus de notes, peut-être avec l'aide de la reconnaissance vocale
    • Même si je peux mieux gérer mon temps maintenant que ma fille peut étudier de manière plus indépendante, je dois toujours gérer les interruptions de ma vie et de mon propre cerveau. Si j'écris ou dicte des notes pendant que je pense ou que je travaille, je reprendrai le fil de mes pensées plus facilement. La reconnaissance vocale me permet de capter plus de pensées, même si les résultats nécessitent de revoir et de réviser. Je suis enthousiasmée par les idées que d'autres partagent, comme la configuration de l'interface transient.el et l'amélioration de la publication d'Org Mode.
  • streamer pour réfléchir à voix haute
    • Mes routines quotidiennes deviennent plus prévisibles petit à petit, je peux donc planifier des événements ou me lancer dans le streaming pour partager des idées. Si je fais une diffusion en direct, je peux profiter des questions et des commentaires que je reçois. Le vendredi à 10h30 semble être un bon moment pour ça, et quand c'est possible, je peux aussi le faire spontanément.
  • enregistrer des vidéos pour partager ce que j'apprends, et packager mes fonctions pour les rendre réutilisables quand c'est possible
    • Comme les éléments précédents, partager mes idées et mon travail peut aider d'autres personnes et lancer des conversations. Si j'améliore subed-record.el pour combiner des enregistrements vidéo et audio avec des captures d'écran et des sous-titres, je peux créer des vidéos plus facilement.
  • écrire davantage en français et améliorer mon environnement à cet effet
    • J'apprécie la stimulation mentale d'écrire dans une autre langue. Beaucoup de gens apprennent d'autres langues, donc nous pouvons nous aider. Par exemple, mes fonctions pour surligner les nouveaux mots et les erreurs grammaticales pourraient être utiles.
  • pratiquer l'expression orale en faisant des enregistrements audio avec l'aide de subed-record et la synthèse vocale pour améliorer ma prononciation
    • L'avantage de l'implémentation dans Emacs est que je peux personnaliser mon flux de travail. Par exemple, je veux écrire mon brouillon, puis enregistrer l'audio phrase par phrase après l'avoir écouté. Je veux aussi modifier un enregistrement de mon rendez-vous avec ma tutrice pour que je garde seulement mes dernières essaies de prononciation.
  • améliorer les processus pour EmacsConf et Emacs News
    • Il se peut que je puisse l'organiser en grande partie par moi-même, mais il se peut aussi que d'autres bénévoles m'aident, ce qui serait encore mieux. Je veux créer l'infrastructure pour gérer plusieurs réunions virtuelles simultanément, probablement grâce à la reconnaissance vocale, la spatialisation audio, et de nombreux raccourcis clavier. Je veux aussi améliorer mon processus de sous-titrage.

Plus j'en fais dans Emacs, plus je pense à des idées d'amélioration…

Thanks to Christian Tietze for hosting!

View org source for this post

2026-01-26 Emacs news

| emacs, emacs-news
View org source for this post

Queuing multiple transcriptions with whisper.el speech recognition

| audio, speech-recognition, emacs

I want to be able to talk out loud and have the ideas go into Emacs. I can do this in a number of different ways:

  1. I briefly demonstrated a step-by-step approach with natrys/whisper.el with a single file. I press a keyboard shortcut to start the recording, another shortcut to stop the recording, and it transcribes it in the background. But the way whisper.el is set up is that if I press the keyboard shortcut to start recording again it will offer to interrupt the transcription process, which is not what I want. I want to just keep talking and have it process results as things come in.
  2. I'm also experimenting with Google Chrome's web speech API to do continuous speech recognition, which I can get into Emacs using a web socket.
  3. What I've just figured out is how to layer a semi-continuous interface for speech recognition on top of whisper.el so that while it's processing in the background, I can just press a keyboard shortcut (I'm using numpad 9 to call my-whisper-continue) to stop the previous recording, queue it for processing, and start the next recording. If I use this keyboard shortcut to separate my thoughts, then Whisper has a much easier time making sense of the whole sentence or paragraph or whatever, instead of trying to use the sliding 30 second context window that many streaming approaches to speech recognition try to use.

Question: Did you fix the keyboard delay you've got while speech catches what you're saying?

Sometimes, when the speed recognition kicks in, my computer gets busy. When my computer gets really busy, it doesn't process my keystrokes in the right order, which is very annoying because then I have to delete the previous word and retype it. I haven't sorted that out yet, but it seems like I probably have to lower the priority on different processes. On the plus side, as I mentioned, if I dictate things instead of typing them, then I don't run into that problem at all.

Also, other notes on delays: The continuous speech recognition via Google Chrome shows up fairly quickly, but it's not very precise, and it doesn't have punctuation. Even if there's a little bit of a delay, as long as I press the my-whisper-continue shortcut after each thought, then I can get that text into my Emacs buffer using the nicer transcription from my selected model. There is going to be a bit of a delay for that one because it gets processed at the end of the thought. Also, I need to start thinking in complete sentences instead of just adding one cause after the other as my brain goes on all of these tangents. I think it's pretty promising. There's the continuous speech recognition via Google Chrome if I don't mind the lower accuracy and lack of punctuation, and I can still get the pretty version on the other side.

Why talk out loud? I liked the Bookclub Tapas presentation that Maddie Sullivan did at EmacsConf 2025. Talking out loud helps me be a lot more verbose about what I'm saying, compared to typing things out or even like having to switch to my notes or interrupting my screen with an Org capture buffer. Of course I want to clean that up for putting into a blog post, but given that my life still sometimes has random interruptions from a kiddo who must have my attention at that very minute, having that kind of record that I can at least try to reread afterwards to reconstruct what I was thinking about sounds like it might be helpful.

Still, making sense out loud is hard. I'm not actually used to talking to people that much now. This is probably a good reason for me to experiment with streaming more. Then I get the practice in talking out loud, there are backup recordings, and people can ask questions when things are unclear.

Of course, sometimes the text doesn't quite make sense because of the speech recognition errors. I can usually figure it out from the context. I save the audio as well so that I can go back and listen to it again if I really need to.

Anyway, here's the code for sending the current recording to whisper in the background and starting another recording. It assumes a lot about how things are set up. For example, I'm only testing this with a local speaches server instead of whisper.cpp. You might need to look at my other speech related configuration blog posts and sections in order to make sense of it.

Code for queuing whisper.el requests to a local server
(defvar my-whisper--queue nil)
(defun my-whisper-continue (&optional arg)
  "Send what we've got so far for transcription and then continue recording.
Call with \\[universal-argument] to signal that we can stop."
  (interactive "P")
  (require 'whisper)
  (if arg
      (my-whisper-done)
    (setq whisper--marker (point-marker) whisper--point-buffer (current-buffer))
    (when (process-live-p whisper--recording-process)
      (add-to-list
       'my-whisper--queue
       (list :file whisper--temp-file
             :buffer
             (format "*result: %s*" (file-name-base whisper--temp-file)))
       t)
      ;; Remove the sentinel; handle results ourselves
      (set-process-sentinel whisper--recording-process
                            (lambda (process event)
                              (my-whisper-process-queue)))
      (interrupt-process whisper--recording-process))
    (run-hooks 'whisper-before-transcription-hook)
    (whisper--setup-mode-line :show 'recording)
    (whisper--record-audio)))

(defun my-whisper-discard ()
 "Ignore the previous recording."
  (interactive)
  (when (process-live-p whisper--recording-process)
    ;; Remove the sentinel; handle results ourselves
    (set-process-sentinel whisper--recording-process
                          (lambda (process event)
                            (when (file-exists-p whisper--temp-file)
                              (delete-file whisper--temp-file))
                            (my-whisper-process-queue)))
    (interrupt-process whisper--recording-process)))

(defun my-whisper-discard-and-continue ()
 "Ignore the previous recording and continue."
  (interactive)
  (if (process-live-p whisper--recording-process)
      (progn
        ;; Remove the sentinel; handle results ourselves
        (set-process-sentinel whisper--recording-process
                              (lambda (process event)
                                (my-whisper-process-queue)
                                (my-whisper-continue)))
        (interrupt-process whisper--recording-process))
    (my-whisper-continue)))

(defun my-whisper-done ()
  (interactive)
  (when (process-live-p whisper--recording-process)
    (add-to-list
     'my-whisper--queue
     (list :file whisper--temp-file
           :buffer
           (format "*result: %s*" (file-name-base whisper--temp-file)))
     t)
    ;; Remove the sentinel; handle results ourselves
    (set-process-sentinel whisper--recording-process
                          (lambda (process event)
                            (my-whisper-process-queue)))
    (whisper--setup-mode-line :hide 'recording)
    (interrupt-process whisper--recording-process)))

(defun my-whisper-process-queue-result ()
  "Process the first part of the queue that already has results."
  (while (plist-get (car my-whisper--queue) :results)
    (let ((o (pop my-whisper--queue)))
      (unless my-whisper-target-markers
        (setq whisper--marker (point-marker)
              whisper--point-buffer (current-buffer)))
      (with-current-buffer (plist-get o :buffer)
        (erase-buffer)
        (insert (plist-get o :results))
        (goto-char (point-min))
        (run-hook-wrapped
         'whisper-after-transcription-hook
         (lambda (f)
           (with-current-buffer (get-buffer (plist-get o :buffer))
             (save-excursion
               (funcall f)))
           nil))))))

(defun my-whisper-process-queue ()
  (let (o)
    (while (setq o (seq-find (lambda (o) (and (plist-get o :file)
                                              (not (plist-get o :process))
                                              (not (plist-get o :results))))
                             my-whisper--queue))
      (let* ((headers (list "Content-Type: multipart/form-data"))
             (params (list (concat "file=@"
                                   (plist-get o :file))
                           "temperature=0.0"
                           "temperature_inc=0.2"
                           "response_format=json"
                           (concat "model=" whisper-model)
                           (concat "language=" whisper-language)))
             (url (format my-whisper-url-format whisper-server-host whisper-server-port))
             (command `("curl" "-s"
                        ,url
                        ,@(mapcan (lambda (h) (list "-H" h)) headers)
                        ,@(mapcan (lambda (p) (list "-F" p)) params))))
        (with-current-buffer (get-buffer-create (plist-get o :buffer))
          (erase-buffer))
        (plist-put
         o :process
         (make-process
          :name "whisper-curl"
          :command command
          :buffer (plist-get o :buffer)
          :coding 'utf-8
          :sentinel
          (lambda (process event)
            (with-current-buffer (process-buffer process)
              (let ((current my-whisper--queue-item))
                (when (and (get-buffer (plist-get current :buffer))
                           (string-equal "finished\n" event))
                  (with-current-buffer (plist-get current :buffer)
                    (goto-char (point-min))
                    (plist-put current :results
                               (or
                                (condition-case nil
                                    (gethash "text" (json-parse-buffer))
                                  (error ""))
                                "(error)"))))))
            (my-whisper-process-queue-result))))
        (plist-put o :command (string-join command " "))
        (with-current-buffer (process-buffer (plist-get o :process))
          (setq-local my-whisper--queue-item o))))))
(defvar-local my-whisper--queue-item nil)

(defun my-whisper-reprocess-queue ()
  (interactive)
  (setq whisper--marker (point-marker) whisper--point-buffer (current-buffer))
  (mapc (lambda (o)
          (when (process-live-p (plist-get o :process))
            (kill-process (plist-get o :process)))
          (when (get-buffer (plist-get o :buffer))
            (kill-buffer (plist-get o :buffer)))
          (plist-put o :process nil)
          (plist-put o :results nil))
        my-whisper--queue)
  (my-whisper-process-queue))

(defun my-whisper-clear-queue ()
  (interactive)
  (mapc (lambda (o)
          (when (process-live-p (plist-get o :process))
            (kill-process (plist-get o :process)))
          (when (get-buffer (plist-get o :buffer))
            (kill-buffer (plist-get o :buffer)))
          (plist-put o :process nil)
          (plist-put o :results nil))
        my-whisper--queue)
  (setq my-whisper--queue nil))

(keymap-global-set "<kp-9>" #'my-whisper-continue)
(keymap-global-set "<kp-8>" #'my-whisper-discard-and-continue)
(keymap-global-set "C-<kp-9>" #'my-whisper-done)
This is part of my Emacs configuration.
View org source for this post

Emacs and whisper.el: Trying out different speech-to-text backends and models

| audio, emacs, speech-recognition

I was curious about parakeet because I heard that it was faster than Whisper on the HuggingFace leaderboard. When I installed it and got it running on my laptop (CPU only, no GPU), it seemed like my results were a little faster than whisper.cpp with the large model, but much slower than whisper.cpp with the base model. The base model is decent for quick dictation, so I got curious about other backends and other models.

In order to try natrys/whisper.el with other backends, I needed to work around how whisper.el validates the model names and sends requests to the servers. Here's the quick and dirty code for doing so, in case you want to try it out for yourself.

(defvar my-whisper-url-format "http://%s:%d/transcribe")
(defun my-whisper--transcribe-via-local-server ()
  "Transcribe audio using the local whisper server."
  (message "[-] Transcribing via local server")
  (whisper--setup-mode-line :show 'transcribing)
  (whisper--ensure-server)
  (setq whisper--transcribing-process
        (whisper--process-curl-request
         (format my-whisper-url-format whisper-server-host whisper-server-port)
         (list "Content-Type: multipart/form-data")
         (list (concat "file=@" whisper--temp-file)
               "temperature=0.0"
               "temperature_inc=0.2"
               "response_format=json"
               (concat "model=" whisper-model)
               (concat "language=" whisper-language)))))
(defun my-whisper--check-model-consistency () t)
(defun my-whisper--ensure-server () t)

(with-eval-after-load 'whisper
  (advice-add 'whisper--transcribe-via-local-server :override #'my-whisper--transcribe-via-local-server)
  (advice-add 'whisper--check-model-consistency :override #'my-whisper--check-model-consistency)
  (advice-add 'whisper--ensure-server :override #'my-whisper--ensure-server)
  )

Then I have this function for trying things out.

(defun my-test-whisper-api (url &optional args)
  (with-temp-buffer
    (apply #'call-process "curl" nil t nil "-s"
           url
         (append (mapcan
                  (lambda (h) (list "-H" h))
                  (list "Content-Type: multipart/form-data"))
                 (mapcan
                  (lambda (h) (list "-F" h))
                  (list (concat "file=@" whisper--temp-file)
                        "temperature=0.0"
                        "temperature_inc=0.2"
                        "response_format=verbose_json"
                        (concat "language=" whisper-language)))
                 args))
    (message "%s %s" (buffer-string) url)))

Here's the audio file. It is around 10 seconds long. I run the benchmark 3 times and report the average time.

Download

Code for running the benchmarks
(let ((times '3))
(mapcar
 (lambda (group)
   (let ((whisper--temp-file "/home/sacha/recordings/whisper/2026-01-19-14-17-53.wav"))
     ;; warm up the model
     (eval (cadr group))
     (list
      (format "%.3f"
              (/ (car
                  (benchmark-call (lambda () (eval (cadr group))) times))
                 times))
      (car group))))
 '(
   ("parakeet"
    (my-test-whisper-api
     (format "http://%s:%d/v1/audio/transcriptions" whisper-server-host 5092)))
   ("whisper.cpp base-q4_0"
    (my-test-whisper-api
     (format "http://%s:%d/inference" whisper-server-host 8642)))
   ("speaches whisper-base"
    (my-test-whisper-api
     (format "http://%s:%d/v1/audio/transcriptions" whisper-server-host 8001)
     (list "-F" "model=Systran/faster-whisper-base")))
   ("speaches whisper-base.en"
    (my-test-whisper-api
     (format "http://%s:%d/v1/audio/transcriptions" whisper-server-host 8001)
     (list "-F" "model=Systran/faster-whisper-base.en")))
   ("speaches whisper-small"
    (my-test-whisper-api
     (format "http://%s:%d/v1/audio/transcriptions" whisper-server-host 8001)
     (list "-F" "model=Systran/faster-whisper-small")))
   ("speaches whisper-small.en"
    (my-test-whisper-api
     (format "http://%s:%d/v1/audio/transcriptions" whisper-server-host 8001)
     (list "-F" "model=Systran/faster-whisper-small.en")))
   ("speaches lorneluo/whisper-small-ct2-int8"
    (my-test-whisper-api
     (format "http://%s:%d/v1/audio/transcriptions" whisper-server-host 8001)
     (list "-F" "model=lorneluo/whisper-small-ct2-int8")))
   ;; needed export TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD=1
   ("whisperx-server Systran/faster-whisper-small"
    (my-test-whisper-api
     (format "http://%s:%d/transcribe" whisper-server-host 8002)))))
)

I tried it with:

Looks like speaches + faster-whisper-base is the winner for now. I like how speaches lets me switch models on the fly, so maybe I can use base.en generally and switch to base when I want to try dictating in French. Here's how I've set it up to use the server I just set up.

(setq whisper-server-port 8001 whisper-model "Systran/faster-whisper-base.en"
      my-whisper-url-format "http://%s:%d/v1/audio/transcriptions")

At some point, I'll override whisper--ensure-server so that starting it up is smoother.

Benchmark notes: I have a Lenovo P52 laptop (released 2018) with an Intel Core i7-8850H (6 cores, 12 threads; 2.6 GHz base / 4.3 GHz turbo) with 64GB RAM and an SSD. I haven't figured out how to get the GPU working under Ubuntu yet.

This is part of my Emacs configuration.
View org source for this post

2026-01-19 Emacs news

| emacs, emacs-news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, Mastodon #emacs, Bluesky #emacs, Hacker News, lobste.rs, programming.dev, lemmy.world, lemmy.ml, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!

View org source for this post