<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sp Gamer</title>
    <description>The latest articles on DEV Community by Sp Gamer (@sp_gamer_e8a7ce484708618f).</description>
    <link>https://dev.to/sp_gamer_e8a7ce484708618f</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4079365%2Fdfbfe66b-c89a-43a2-9941-449d73fad4a9.png</url>
      <title>DEV Community: Sp Gamer</title>
      <link>https://dev.to/sp_gamer_e8a7ce484708618f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sp_gamer_e8a7ce484708618f"/>
    <language>en</language>
    <item>
      <title>The Case of the Disappearing Timestamps: Untangling a C++ Database Bug in Mixxx</title>
      <dc:creator>Sp Gamer</dc:creator>
      <pubDate>Thu, 20 Aug 2026 19:03:52 +0000</pubDate>
      <link>https://dev.to/sp_gamer_e8a7ce484708618f/the-case-of-the-disappearing-timestamps-untangling-a-c-database-bug-in-mixxx-3kkc</link>
      <guid>https://dev.to/sp_gamer_e8a7ce484708618f/the-case-of-the-disappearing-timestamps-untangling-a-c-database-bug-in-mixxx-3kkc</guid>
      <description>&lt;p&gt;As a 3rd-year computer science student, jumping into a massive, production-grade open-source project is incredibly intimidating. When I first set up my build environment for Mixxx (the open-source DJ software) in VS Code, just getting it to compile felt like a victory. But I wanted to make a real contribution, which meant picking up a real issue.&lt;/p&gt;

&lt;p&gt;I ended up tackling a bug that looked like spooky action at a distance: deleting a history playlist was somehow erasing metadata from tracks in the main library.&lt;/p&gt;

&lt;p&gt;Here is the story of how I tracked down the issue, learned how Mixxx handles its SQLite database under the hood, and pushed a fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Weird Behavior&lt;/strong&gt;&lt;br&gt;
Imagine you are a DJ. You play a track on Friday night. Mixxx records the play count and logs a last_played_at timestamp for that track in your main library. Everything is working perfectly.&lt;/p&gt;

&lt;p&gt;A few weeks later, you decide to clean up your workspace and delete the history playlist from that Friday night gig. You’d expect the history log to disappear, but the track in your main library should still remember that you’ve played it before, right?&lt;/p&gt;

&lt;p&gt;Wrong. For some users, deleting that old history playlist caused the last_played_at timestamp on those tracks to vanish completely. It was as if the tracks had never been played.&lt;/p&gt;

&lt;p&gt;Why would deleting a temporary history list corrupt the persistent metadata of the main library?&lt;/p&gt;

&lt;p&gt;Following the Trail&lt;br&gt;
To figure this out, I had to understand how Mixxx actually stores this data. I started tracing the code from the UI down into the data access layer.&lt;/p&gt;

&lt;p&gt;Mixxx uses a Data Access Object (DAO) pattern to interface between the C++ application and the underlying SQLite database. The main library data lives in a library table, but the history is managed through Playlists and PlaylistTracks.&lt;/p&gt;

&lt;p&gt;When a user deletes a history playlist, Mixxx doesn't just drop the rows. It tries to be smart and synchronize the play statistics for the tracks that were in that deleted history. The logic for this lived in src/library/dao/trackdao.cpp, specifically inside a function called updatePlayCounterFromPlayedHistory().&lt;/p&gt;

&lt;p&gt;Staring at trackdao.cpp for the first time was overwhelming, but eventually, the flow started to make sense. When a history list was deleted, the application asked TrackDAO to recalculate the track's history.&lt;/p&gt;

&lt;p&gt;Finding the Culprit&lt;br&gt;
The root of the bug came down to how SQL and C++ handle "nothing."&lt;/p&gt;

&lt;p&gt;To figure out the last time a track was played, TrackDAO was executing an SQL query across the history tables, looking for MAX(pl_datetime_added) where the playlist type was a history log (PlaylistDAO::PLHT_SET_LOG).&lt;/p&gt;

&lt;p&gt;Here was the fatal flaw: if a user deleted the only history playlists that contained a specific track, that track no longer had any history records left in the database.&lt;/p&gt;

&lt;p&gt;When the SQL query ran MAX(pl_datetime_added) for that TrackId, it found nothing. In C++ (using Qt), this resulted in a QSqlQuery returning an invalid or empty QDateTime object.&lt;/p&gt;

&lt;p&gt;Instead of recognizing that the history was just missing and leaving the main library alone, the old code was taking that invalid QDateTime and blindly updating the last_played_at column in the main library table. It was overwriting perfectly good timestamps with "null."&lt;/p&gt;

&lt;p&gt;The Fix&lt;br&gt;
The solution was to isolate the history lookup and add some safety checks so we never overwrite good data with bad data.&lt;/p&gt;

&lt;p&gt;I modified the logic in TrackDAO and created a dedicated helper function: findLastTimeAddedToHistory(TrackId trackId).&lt;/p&gt;

&lt;p&gt;This function encapsulates the QSqlQuery (using Mixxx's ScopedQuery for safety) to cleanly fetch the latest timestamp from the PlaylistTracks and Playlists tables.&lt;/p&gt;

&lt;p&gt;Instead of mixing the database query directly into the update logic, updatePlayCounterFromPlayedHistory now simply calls my helper. More importantly, it checks the result. If findLastTimeAddedToHistory returns an invalid QDateTime (meaning there is no history left for that track), we gracefully handle it instead of wiping out the existing last_played_at data in the main library.&lt;/p&gt;

&lt;p&gt;What I Learned&lt;br&gt;
When I first started looking at Issue #14427, I thought I was going to have to rewrite massive chunks of the database architecture.&lt;/p&gt;

&lt;p&gt;What I actually learned is that in a mature codebase, bugs rarely require you to tear down the walls. It’s usually about finding the exact point where data flows from one system to another—in this case, from an SQLite query into a Qt QDateTime object—and realizing an edge case wasn't accounted for.&lt;/p&gt;

&lt;p&gt;By extracting the database query into its own cleanly scoped helper function, not only did the bug get fixed, but TrackDAO became just a little bit easier for the next student or contributor to read.&lt;/p&gt;

&lt;p&gt;I pushed my fix in &lt;a href="https://github.com/mixxxdj/mixxx/pull/16178" rel="noopener noreferrer"&gt;PR #16178&lt;/a&gt; to isolate the database query.&lt;/p&gt;

&lt;p&gt;This strange behavior was originally reported in &lt;a href="https://github.com/mixxxdj/mixxx/issues/14427" rel="noopener noreferrer"&gt;Issue #14427&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>opensource</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Building Shiksha: What I Learned Creating a Real-Time AI English Coach in 10 Days</title>
      <dc:creator>Sp Gamer</dc:creator>
      <pubDate>Sat, 15 Aug 2026 18:51:07 +0000</pubDate>
      <link>https://dev.to/sp_gamer_e8a7ce484708618f/building-shiksha-what-i-learned-creating-a-real-time-ai-english-coach-in-10-days-jn6</link>
      <guid>https://dev.to/sp_gamer_e8a7ce484708618f/building-shiksha-what-i-learned-creating-a-real-time-ai-english-coach-in-10-days-jn6</guid>
      <description>&lt;h1&gt;
  
  
  Building Shiksha: An AI English Coach for Indian Learners
&lt;/h1&gt;

&lt;p&gt;For many Indian learners, the biggest barrier to speaking English fluently isn't a lack of vocabulary or grammar rules learned in school—it's &lt;strong&gt;speaking anxiety&lt;/strong&gt; and the fear of making mistakes in front of peers or teachers.&lt;/p&gt;

&lt;p&gt;Over the past 10 days, as part of the &lt;strong&gt;10 Days of Voice Agents — Voice for Bharat Edition&lt;/strong&gt; under the &lt;strong&gt;Learning &amp;amp; Literacy&lt;/strong&gt; track, I built &lt;strong&gt;Shiksha&lt;/strong&gt;: an interactive, real-time AI English Communication Coach designed to provide friendly, judgment-free spoken practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌟 Why Voice?
&lt;/h2&gt;

&lt;p&gt;Text chatbots don't build spoken confidence. Reading and typing are passive activities, whereas real-world conversations require instant auditory processing, cognitive framing, and spoken articulation. &lt;/p&gt;

&lt;p&gt;Shiksha gives learners a low-latency, empathetic voice partner that understands &lt;strong&gt;Hinglish&lt;/strong&gt; (code-mixed Hindi and English), allowing them to practice daily presentations, grammar rules, and workplace conversations without embarrassment.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ High-Level Architecture
&lt;/h2&gt;

&lt;p&gt;User Speech (WebRTC / SIP) ──► LiveKit Audio Ingest&lt;br&gt;
│&lt;br&gt;
▼&lt;br&gt;
Speech-to-Text (STT)&lt;br&gt;
│&lt;br&gt;
▼&lt;br&gt;
LLM + Tools (agent.py + db.py)&lt;br&gt;
│&lt;br&gt;
▼&lt;br&gt;
Murf Falcon (Ultra-Low Latency TTS)&lt;br&gt;
│&lt;br&gt;
▼&lt;br&gt;
Audio Output ◄────────────── WebRTC Audio Sink&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Key Features Built Over the 10 Days
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ultra-Low Latency Indian Voice:&lt;/strong&gt; Powered by &lt;strong&gt;Murf Falcon TTS&lt;/strong&gt;, Shiksha delivers natural, culturally resonant Indian English voice output with near-instant response times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent Conversational Memory (SQLite):&lt;/strong&gt; Retains learner names, historical presentation goals, and specific practice needs across calls (&lt;code&gt;agent_memory.db&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Curriculum-Driven Vocabulary Tools:&lt;/strong&gt; Dynamically fetches context-specific vocabulary drills from &lt;code&gt;exercises.json&lt;/code&gt; and evaluates sentences live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outbound Daily Practice Telephony (LiveKit SIP):&lt;/strong&gt; Initiates automated daily check-in calls straight to a learner's phone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-in-the-Loop Escalation &amp;amp; Privacy Guardrails:&lt;/strong&gt; Detects severe learner frustration or explicit requests for human mentors, requests explicit permission, and logs sanitized support tickets with clear reference IDs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Call Analytics Dashboard:&lt;/strong&gt; A real-time Next.js dashboard displaying aggregated metrics (Total Calls, Successful Drills, Incomplete Calls) with zero personal transcripts exposed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Agent Specialist Handoff:&lt;/strong&gt; Dynamically transitions the call from Shiksha (general coach) to &lt;strong&gt;Arjun&lt;/strong&gt; (Grammar Specialist with a distinct male voice persona) for complex syntactic queries without dropping the WebRTC session.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🛠️ Hardest Technical Challenges &amp;amp; Fixes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Hindi/Devanagari Pronunciation Glitches in TTS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issue:&lt;/strong&gt; Romanized Hindi text caused phonetic glitches in English voice models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; Structured the system prompt to output pure Hindi terms in native &lt;strong&gt;Devanagari script&lt;/strong&gt; (&lt;code&gt;नमस्ते!&lt;/code&gt;), allowing Murf Falcon to pronounce localized nuances cleanly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Next.js Dashboard Real-Time Cache vs. SQLite
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issue:&lt;/strong&gt; Call logs updated in SQLite, but the Next.js &lt;code&gt;/dashboard&lt;/code&gt; served cached numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; Enforced dynamic rendering with &lt;code&gt;export const dynamic = "force-dynamic"&lt;/code&gt; and &lt;code&gt;export const revalidate = 0&lt;/code&gt; at the top of the dashboard page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Context Preservation During Specialist Handoff
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issue:&lt;/strong&gt; Switching agents risked losing conversational context, requiring the user to repeat themselves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; Implemented dynamic prompt-state switching in the same LiveKit session loop, passing the &lt;code&gt;handoff_reason&lt;/code&gt; and recent turns directly into Arjun's context.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💻 How to Run the Project Locally
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Clone Repository &amp;amp; Setup Backend
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
git clone [https://github.com/](https://github.com/)[YOUR_USERNAME]/[YOUR_REPO].git
cd shiksha-voice-agent/backend
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
pip install -r requirements.txt


Configure Environment Variables
Create a .env.local file in both backend/ and frontend/:
LIVEKIT_URL=wss://your-livekit-project.livekit.cloud
LIVEKIT_API_KEY=your_api_key
LIVEKIT_API_SECRET=your_api_secret
MURF_API_KEY=your_murf_falcon_api_key
OPENAI_API_KEY=your_llm_api_key

Start Backend Worker &amp;amp; Frontend UI

# Terminal 1 (Backend)
python agent.py dev

# Terminal 2 (Frontend)
cd ../frontend
npm install
npm run dev
Open http://localhost:3000, click Start Conversation, and begin speaking!

🔗 Links &amp;amp; Resources
📂 GitHub Repository: https://github.com/Spgamer0407/murf-livekit-starter_voice_agent/tree/day-10

💼 LinkedIn Profile: https://www.linkedin.com/in/srinivasa-puranik-911609369/

Built as part of the #10DaysOfVoiceAgents — Voice for Bharat Edition powered by @Murf.ai.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>ai</category>
      <category>webrtc</category>
      <category>voiceagents</category>
      <category>buildinpublic</category>
    </item>
  </channel>
</rss>
