<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Projects on Jasper</title><link>https://jasper-aa64.github.io/projects/</link><description>Recent content in Projects on Jasper</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 30 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jasper-aa64.github.io/projects/index.xml" rel="self" type="application/rss+xml"/><item><title>IELTS: Code Structure</title><link>https://jasper-aa64.github.io/projects/ielts-studio-structure/</link><pubDate>Fri, 30 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/projects/ielts-studio-structure/</guid><description>&lt;p&gt;IELTS is a tool I built for practising IELTS speaking and writing, and it recently gained a mock-interview mode. The code is on &lt;a href="https://github.com/Jasper-aa64/AI-realtime_speech-for-IELTS-Job_Interview"&gt;GitHub&lt;/a&gt;; the newest branch is &lt;code&gt;desktop-realtime-voice&lt;/code&gt;. This post takes the current code as the baseline and walks through three questions: who does the work and when, what the data looks like and where it lives, and why it is split this way. Section 5 lists the current problems; how they came about is in the &lt;a href="../ielts-studio-retrospective/"&gt;retrospective&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>IELTS: Retrospective</title><link>https://jasper-aa64.github.io/projects/ielts-studio-retrospective/</link><pubDate>Fri, 30 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/projects/ielts-studio-retrospective/</guid><description>&lt;p&gt;This is the retrospective for &lt;a href="../ielts-studio-structure/"&gt;IELTS&lt;/a&gt;. What the code looks like now and what is wrong with it is listed in section 5 of the structure post; this post is about how those problems came about and what I would do next time. The conclusions first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reuse the threading model, not just the classes.&lt;/strong&gt; The IELTS command-line version reused &lt;code&gt;RealtimeClient&lt;/code&gt; and the audio classes but not the microphone and playback threads or the state machine, so the examiner&amp;rsquo;s audio is sent and never played.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Get one simple end-to-end path working before polishing a layer.&lt;/strong&gt; &lt;code&gt;native_voice&lt;/code&gt;, written in early October, has a clean device layer and no network, recognition or synthesis, so it could not go into the product. At the end of October the approach changed: fix the old prototype&amp;rsquo;s connection layer first, then write a layer on top that only &amp;ldquo;reads one thing and listens to one thing&amp;rdquo;. It was in the desktop shell the next day.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Every shortcut has a cost; write it down.&lt;/strong&gt; Half duplex keeps echo out at the cost of interruptions; using the dialogue model as a reader saved a speech-synthesis integration at the cost of having no way to check it reads verbatim. The new engine uses Volcengine&amp;rsquo;s given-text interfaces, which settles the second one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A parameter in an API document counts only once it has been measured.&lt;/strong&gt; The end-of-speech window needs a separate switch to take effect, and a required field was never sent by the old code; both turned up only by checking the document field by field and then measuring.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Three from the website&lt;/strong&gt;: the slow part was the call path, not the logic; get evidence before fixing a bug; only one place may decide that something has failed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="1-seven-months-four-shapes"&gt;&lt;span class="h-num"&gt;1.&lt;/span&gt; Seven months, four shapes&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;When&lt;/th&gt;
 &lt;th&gt;Shape&lt;/th&gt;
 &lt;th&gt;What happened&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;March&lt;/td&gt;
 &lt;td&gt;C++/Qt desktop prototype&lt;/td&gt;
 &lt;td&gt;Job-interview simulator: read a CV PDF, LLM-generated questions, Volcengine&amp;rsquo;s real-time dialogue service for the conversation; finished with a pass of cppcheck and ASan&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Early May&lt;/td&gt;
 &lt;td&gt;C++ command line&lt;/td&gt;
 &lt;td&gt;Became the three parts of IELTS speaking, with scoring through Codex, the OpenAI API or Claude&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Mid May&lt;/td&gt;
 &lt;td&gt;Single-file Python site&lt;/td&gt;
 &lt;td&gt;Recording in the browser, reports, a wallet; scoring calibration and an unattended long-run audit&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Mid to late May&lt;/td&gt;
 &lt;td&gt;Django&lt;/td&gt;
 &lt;td&gt;Moved to Django and retired the old server; streamed HTTP follow-ups, WASM audio preprocessing, live recognition&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;June to September&lt;/td&gt;
 &lt;td&gt;Features and wrap-up&lt;/td&gt;
 &lt;td&gt;Corpus, Takeaway, spaced spelling review, public-network speed-ups, onboarding, P3 bank progress, deployment docs&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;October&lt;/td&gt;
 &lt;td&gt;Qt desktop shell&lt;/td&gt;
 &lt;td&gt;QtWebEngine around the web app; interview mode and an AI assistant; the new &lt;code&gt;native_voice&lt;/code&gt; device layer; an audit of the old C++ voice code&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Late October to early November&lt;/td&gt;
 &lt;td&gt;Real-time voice in the shell&lt;/td&gt;
 &lt;td&gt;One I/O thread owns the connection; a new content-free &lt;code&gt;VoiceSession&lt;/code&gt;; the interview&amp;rsquo;s pressure mode becomes real-time voice, and the other practice pages use it for captions&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;By month: 13 commits in March, 406 in May, 143 in June, 25 across July to September, 156 in October and 1 in November up to the 1st. From May on, most code came from the same loop: I wrote a spec, Codex or Claude Code implemented it, and I accepted it or not.&lt;/p&gt;</description></item></channel></rss>