Commit Graph

59 Commits

Author SHA1 Message Date
luke 390f138601 Devon improvements: independent audio, realism overhaul
- Devon audio independent of caller hangup (separate stop events)
- Personal anecdotes capped at ~30% of responses (was every time)
- Interjection criteria tightened ("default is silence")
- Devon sees his own recent history to avoid repeating info
- Response variety: permits minimal reactions, confusion, silence
- Monitor prompt rewritten to be gatekeeping, not encouraging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:08:22 -06:00
luke 9eaf2fe5e3 Fix avatar misgendering, returning caller overflow, false callbacks
- Avatar prefetch checks gender marker, re-fetches on mismatch
- Returning callers need 2+ actual calls before re-eligible (was 1)
- Promotion rate lowered 10% → 5% to prevent pool flooding
- Callback injection skipped for returning callers (already have context)
- Show history clarifies "you are NOT that caller" to prevent identity confusion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:03:07 -06:00
luke 314d5f9452 Caller model routing — cycle, style-matched, mid-show override
- Three strategies: single model, cycle through pool, style-matched
- 18 communication styles mapped to 7 models (Grok, Sonnet, Mistral, Qwen, DeepSeek, Gemini, Llama)
- Per-caller model locked for entire call, overridable mid-show
- Model badges on caller buttons and info panel
- Settings UI for strategy, pool, style mapping, fallback
- Fallback to Sonnet on model failure
- 6 new models added to pricing and dropdown
- Checkpoint persistence for all model state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 01:58:03 -06:00
luke eb1e18a997 Strip stage directions before TTS, strengthen prompt bans
- Regex strips all parentheticals and asterisk actions before TTS
- Catches (laughs nervously), *sighs*, etc. that Grok generates
- Strengthened SPEECH ONLY instructions in caller and Devon prompts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 23:40:45 -06:00
luke 6dcdf20289 Grok 4 routing, guardrails, pricing fix, strip silence improvements
- Route caller_dialog, devon_ask, background_gen to x-ai/grok-4
- Add Grok-4 to OPENROUTER_MODELS and OPENROUTER_PRICING
- Add Grok-specific banned phrases (I hear you, fair enough, that's wild, etc.)
- Add background gen guardrails for Grok (no active violence, no real public figures)
- Soften theme prompt hot-take language for organic connections
- Tighten Devon flirting guardrail (awkward not crude)
- Fix Devon "first day" contradiction on line 36
- Strip silence: preserve music intro, fix ad normalization (direct WAV reading)
- Strip silence: loop range starts 0.5s before audible music

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:46:29 -06:00
luke 3dd6a83c68 Full app audit: 24 fixes across backend, frontend, infra, content, social
Critical fixes:
- Fix hangup-during-respond crash (null caller guard)
- Fix double-click caller race condition
- Stem recorder: non-daemon thread, disk error handling, 30s flush timeout
- Frontend startCall() error handling

High priority:
- Devon: filter tool errors from speech, shorter monitor prompt, 30s interval
- TTS ghost message fix (add to history after TTS, not before)
- Expand banned phrase list (12 new phrases)
- Increase returning callers from 1 to 2 per session
- Platform-tailored social posts with staggered scheduling
- YouTube dynamic tags from episode content
- Social post retry logic (2 attempts, 5s delay)
- Frontend: error handling on all raw fetch calls

Medium:
- stem_recorder null check race (local var capture in audio.py)
- Reactive shape directive expanded
- REACT TO LUKE moved higher in caller prompt
- Devon tenure updated ("few weeks" not "first day")
- D shortcut Escape to unfocus
- Volume slider debounced (150ms)
- Settings modal widened to 550px
- Backup script (daily MariaDB dump + data/ rsync to NAS)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 14:57:50 -06:00
luke 90e51698b8 Devon fixes, theme prompt rewrite, sentence trimmer, cost tracker, normalization
- Fix Devon "if that makes sense" overuse (limit to once per show)
- Suppress Devon failed lookup notifications for self-initiated searches
- Strengthen show theme prompts (2/3 callers call because of theme)
- Fix sentence trimmer splitting on abbreviations (Mr. Mrs. Dr. etc.)
- Fix cost tracker data lost on server restart (persist in checkpoint)
- Ad/ident normalization targets -4dB below dialog for perceived loudness match
- Lower cross-speaker transition threshold to 5s

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 03:55:55 -06:00
luke 164cad456c Devon own stem/track/channel, per-category LLM routing, settings UI cleanup
Audio:
- Devon gets own stem, Reaper track (Input 17), and configurable channel
- play_caller_audio accepts stem_name + channel_override params
- Reaper script checks 4 voice tracks (Host, Devon, Live Caller, AI Caller)
- postprod.py includes devon stem in gap detection

Cost optimization:
- Per-category model routing: Sonnet for caller dialog, Gemini Flash for everything else
- Estimated 65% cost reduction ($4.32 → ~$1.50/show)
- Category models configurable from settings UI

Frontend:
- Settings panel: clean routing grid for output channels, model routing grid for LLM categories
- Devon channel added to audio routing
- Share icon SVG fill fix (currentColor)
- Website homepage iterations

Publishing:
- Revert Castopod API workaround (API re-enabled)
- Fix container media path

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:05:19 -06:00
luke 8a64a269f3 Remove duplicate h1, fix avatar gender caching, blacklist Celeste voice
- Hide h1 (sr-only) on homepage — banner already shows show name
- Promote tagline as visual lead after banner
- Fix avatar gender: add .gender marker files, re-fetch on mismatch
- Clear stale avatar cache so all re-fetch with correct gender
- Blacklist Celeste voice from caller pool

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 03:28:18 -06:00
luke c70f83d04a Cost monitoring, PTT fix, Devon tuning, WEIRD pool expansion, YT thumbnails, LLM SEO, publish ep37
- Add real-time LLM/TTS cost tracking with live status bar display and post-show reports
- Fix PTT bug where Devon suggestion layout shift stopped recording via mouseleave
- Devon: facts-only during calls, full personality between calls
- Double WEIRD topic pool (109→203), bump weight to 14-25%
- Auto-generate YouTube thumbnails with bold hook text in publish pipeline
- LLM SEO: llms.txt, robots.txt for LLM crawlers, structured data, BreadcrumbList schemas
- Publish episode 37

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 05:33:27 -06:00
luke 3329cf9ac2 UI cleanup, Devon overhaul, bug fixes, publish ep36
- Fix Devon double messages, add conversation persistence, voice-to-Devon when no caller
- Devon personality: weird/lovable intern on first day, handles name misspellings
- Fix caller gender/avatar mismatch (avatar seed includes gender)
- Reserve Sebastian voice for Silas, ban "eating at me" phrase harder
- Callers now hear Devon's commentary in conversation context
- CSS cleanup: expand compressed blocks, remove inline styles, fix Devon color to warm tawny
- Reaper silence threshold 7s → 6s
- Publish episode 36

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:42:21 -06:00
luke 6d4e490283 Caller generation overhaul, Devon intern, frontend redesign
Caller system: structured JSON backgrounds, voice-personality matching (68 profiles),
thematic inter-caller awareness, adaptive call shapes, show pacing, returning caller
memory with relationships/arcs, post-call quality signals, 95 comedy writer entries.

Devon the Intern: persistent show character with tool-calling LLM (web search, Wikipedia,
headlines, webpage fetch), auto-monitoring, 6 API endpoints, full frontend UI.

Frontend: wrap-up nudge button, caller info panel with shape/energy/emotion badges,
keyboard shortcuts (1-0/H/W/M/D), pinned SFX, visual polish, Devon panel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 01:54:08 -06:00
luke d3490e1521 Expand all caller topic pools, add cross-episode topic dedup, publish ep35
Massively expanded all 8 caller topic pools from ~1200 to ~2500 entries to
reduce repeat calls. Added persistent topic history (data/used_topics_history.json)
with 30-day aging to prevent cross-episode duplicates. Published episode 35.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 05:45:22 -06:00
luke f7b75fa72f Clips page, new episodes, TTS/audio improvements, publish pipeline updates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 05:38:58 -06:00
luke 0bdac16250 Upgrade Whisper to distil-large-v3, fix caller identity confusion, sort clips list
- Whisper base → distil-large-v3 for much better live transcription accuracy
- Add context hints to transcription (caller name, screening status)
- Increase beam_size 3→5 for better decoding
- Add explicit role clarification in caller system prompt so LLM knows Luke is the host
- Prefix host messages with [Host Luke] in LLM conversation
- Fix upload_clips episode list sorting (natural numeric order)
- Episodes 26-28 transcripts, data updates, misc fixes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 12:46:51 -07:00
luke 6eeab58464 TTS fixes, Inworld improvements, footer redesign, episodes 15-25, invoice script fix
- Fix TTS text pipeline: new caps handling (spell out unknown acronyms, lowercase
  emphasis words), action-word lookahead for parenthetical stripping, abbreviation
  expansions (US→United States, NM→New Mexico), pronunciation fixes
- Inworld TTS: camelCase API fields, speakingRate per-voice overrides, retry logic
  with exponential backoff (3 attempts)
- Footer redesign: SVG icons for social/podcast links across all pages
- Stats page: show "Rate us on Spotify" instead of "not public" placeholder
- New voices, expanded caller prompts and problem scenarios
- Social posting via Postiz, YouTube upload in publish pipeline
- Episode transcripts 15-25, terms page, sitemap updates
- Fix invoice script: match Timing totals using merged Task+App intervals

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 12:38:58 -07:00
luke 08a35bddeb Play idents in stereo on channels 15/16 with configurable ident_channel setting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 22:28:26 -07:00
luke bbcf767a8f Add idents playback section — loads from idents/ folder, plays on ads channel
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 22:24:40 -07:00
luke 3164a70e48 Ep13 publish, MLX whisper, voicemail system, hero redesign, massive topic expansion
- Switch whisper transcription from faster-whisper (CPU) to lightning-whisper-mlx (GPU)
- Fix word_timestamps hanging, use ffprobe for accurate duration
- Add Cloudflare Pages Worker for SignalWire voicemail fallback when server offline
- Add voicemail sync on startup, delete tracking, save feature
- Add /feed RSS proxy to _worker.js (was broken by worker taking over routing)
- Redesign website hero section: ghost buttons, compact phone, plain text links
- Rewrite caller prompts for faster point-getting and host-following
- Expand TOPIC_CALLIN from ~250 to 547 entries across 34 categories
- Add new categories: biology, psychology, engineering, math, geology, animals,
  work, money, books, movies, relationships, health, language, true crime,
  drunk/high/unhinged callers
- Remove bad Inworld voices (Pixie, Dominus), reduce repeat caller frequency
- Add audio monitor device routing, uvicorn --reload-dir fix
- Publish episode 13

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:56:47 -07:00
luke 28af0723c7 Ep12 publish, caller prompt overhaul, favicon, publish fixes, website updates
- Reworked caller prompt: edgy/flirty personality, play along with host bits
- Bumped caller token budget (200-550 range, was 150-450)
- Added 20 layered/morally ambiguous caller stories
- Valentine's Day awareness in seasonal context
- Default LLM model: claude-sonnet-4-5 (was claude-3-haiku)
- Publish: SCP-based SQL transfer (fixes base64 encoding on NAS)
- Favicons: added .ico, 48px, 192px PNGs for Google search results
- Website: button layout cleanup, privacy page, ep12 transcript
- Control panel: channel defaults match audio_settings.json
- Disabled OP3 permanently (YouTube ingest issues on large files)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 22:53:34 -07:00
luke 95c2d06435 Postprod improvements: denoise, phone EQ, ad muting, ducking, voice mappings
- Add host mic noise reduction (afftdn + anlmdn)
- Add phone EQ bandpass on caller stem
- Mute music during ads with 2s lookahead/tail
- Increase ducking release to 3s to reduce pumping
- Add Inworld voice mappings for all regular callers
- Recording toggle endpoint, stem sync fixes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 03:59:08 -07:00
luke 75f15ba2d2 Add persistent caller voices, Discord, REC/on-air linking, SEO fixes, ep9
- Returning callers now keep their voice across sessions (stored in regulars.json)
- Backfilled voice assignments for all 11 existing regulars
- Discord button on homepage + link in all page footers
- REC and On-Air buttons now toggle together (both directions)
- Fixed host mic double-stream bug (stem_mic vs host_stream conflict)
- SEO: JSON-LD structured data on episode + how-it-works pages
- SEO: noscript fallbacks, RSS links, twitter meta tags
- Episode 9 transcript and sitemap update

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 00:24:37 -07:00
luke cee78b5d88 Add speaker-labeled transcripts, favicon, host stream fix, episode page
- Re-label all 8 episode transcripts with LUKE:/CALLER: speaker labels
  using LLM-based diarization (relabel_transcripts.py)
- Add episode.html transcript page with styled speaker labels
- Update publish_episode.py to generate speaker-labeled transcripts
  and copy to website/transcripts/ for Cloudflare Pages
- Add SVG favicon with PNG fallbacks
- Fix CPU issue: tie host audio stream to on-air toggle, not per-caller
- Update how-it-works page with post-production pipeline info
- Add transcript links to episode cards in app.js

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 15:19:45 -07:00
luke 7b7f9b8208 Add BunnyCDN integration, on-air website badge, publish script fixes
- On-air toggle uploads status.json to BunnyCDN + purges cache, website
  polls it every 15s to show live ON AIR / OFF AIR badge
- Publish script downloads Castopod's copy of audio for CDN upload
  (byte-exact match), removes broken slug fallback, syncs all episode
  media to CDN after publishing
- Fix f-string syntax error in publish_episode.py (Python <3.12)
- Enable CORS on BunnyCDN pull zone for json files
- CDN URLs for website OG images, stem recorder bug fixes, LLM token
  budget tweaks, session context in CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 17:34:18 -07:00
luke 7d88c76f90 Add post-production pipeline: stem recorder, postprod script, recording UI
New stem recording system captures 5 time-aligned WAV files (host, caller,
music, sfx, ads) during live shows. Standalone postprod.py processes stems
into broadcast-ready MP3 with gap removal, voice compression, music ducking,
and EBU R128 loudness normalization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 17:53:32 -07:00
luke 356bf145b8 Add show improvement features: crossfade, emotions, returning callers, transcripts, screening
- Music crossfade: smooth 3-second blend between tracks instead of hard stop/start
- Emotional detection: analyze host mood from recent messages so callers adapt tone
- AI caller summaries: generate call summaries with timestamps for show history
- Returning callers: persist regular callers across sessions with call history
- Session export: generate transcripts with speaker labels and chapter markers
- Caller screening: AI pre-screens phone callers to get name and topic while queued

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 02:43:01 -07:00
luke bd6c8ccbab Landing page: testimonials slider, how-it-works page, 25 TTS voices
- Add testimonial slider with 8 fake caller reviews
- Add how-it-works page with visual architecture diagram
- Expand voice pools: Inworld 25 voices (14M/11F), ElevenLabs 22 (14M/8F)
- Voice pools auto-switch when TTS provider changes
- Add cover art locally, update cache-busted image refs
- Add "More from Luke" footer links (MMG, prints, YouTube)
- Ad channel configurable in settings UI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 01:34:30 -07:00
luke 79e6bc79be Add topic callers, town knowledge, dynamic response lengths
- 30% of callers now call about topics (prestige TV, science, poker,
  astrophotography, physics, tech, US news) instead of personal problems
- 86 curated interests weighted toward shows like Severance, Breaking Bad,
  The Wire, LOST, Westworld, etc. Removed reality TV/celebrity gossip
- 32-town knowledge base with real facts so callers don't invent landmarks
- Smart topic detection for news enrichment (keyword->search query mapping)
- Enrichment now summarizes articles naturally via LLM instead of quoting headlines
- Prompt rewrite for varied response lengths and no rehashing
- Extra weight for Animas and Lordsburg callers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 00:14:38 -07:00
luke 9452b07c5c Ads play once on channel 11, separate from music
- Add dedicated ad playback system (no loop, own channel)
- Ad channel defaults to 11, saved/loaded with audio settings
- Separate play_ad/stop_ad methods and API endpoints
- Frontend stop button now calls /api/ads/stop instead of stopMusic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 22:35:07 -07:00
luke aa3899b1fc Harden LLM: model fallback chain, reuse client, remove fighting timeouts
- Primary model gets 15s, then auto-falls back through gemini-flash,
  gpt-4o-mini, llama-3.1-8b (10s each)
- Always returns a response — canned in-character line as last resort
- Reuse httpx client instead of creating new one per request
- Remove asyncio.timeout wrappers that were killing requests before
  the LLM service could try fallbacks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 22:07:39 -07:00
luke e45ba2617a Switch news service from Google News to local SearXNG
- Use local SearXNG at localhost:8888 instead of Google News RSS
- No more 302 redirects or blocked requests — local is fast and reliable
- 5s timeout on all SearXNG requests
- Removed async locks (no contention needed for local service)
- Re-enabled research and headlines

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 21:34:45 -07:00
luke 69b7078142 Fix research hanging: add timeouts, fix keyword extraction, cache failures
- Google News RSS returns 302: add follow_redirects and User-Agent header
- Cache failed headline fetches for 5min so they don't retry every call
- Add 8s timeout on background research tasks
- Fix keyword extraction: skip short texts, require 2+ proper nouns (not names),
  increase min word length to 6, add radio show filler to stop words
- Stops garbage searches like "Megan welcome" and "sounds thats youre"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 21:25:31 -07:00
luke b3fb3b1127 Fix AI caller hanging on 'thinking...' indefinitely
- Add 30s timeout to all frontend fetch calls (safeFetch)
- Add 20s asyncio.timeout around lock+LLM in chat, ai-respond, auto-respond
- Reduce OpenRouter timeout from 60s to 25s
- Reduce Inworld TTS timeout from 60s to 25s
- Return graceful fallback responses on timeout instead of hanging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 21:16:15 -07:00
luke 7adf1bbcad Fix LLM model list, Castopod API, and server runner
- Remove gpt-4o-realtime (WebSocket-only) from OpenRouter models
- Increase OpenRouter timeout to 60s and max_tokens to 150
- Handle empty LLM responses
- Fix publish_episode.py for current Castopod API fields
- Add port conflict check and graceful shutdown to run.sh

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 01:56:09 -07:00
luke a94fc92647 Improve SignalWire streaming, randomize caller names, update frontend
- Add streamSid tracking and per-caller send locks for SignalWire
- Improve TTS streaming with real-time pacing and detailed logging
- Block host audio to caller during TTS playback
- Randomize caller names between sessions from name pools
- Update page title and show phone number in UI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 01:56:05 -07:00
luke b0643d6082 Add recording diagnostics and refresh music list on play
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 01:00:41 -07:00
luke e28579f909 Add NewsService for current events awareness
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 00:18:40 -07:00
luke 051790136e Update CallerService for SignalWire protocol
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 17:41:27 -07:00
luke a1c94a3682 Fix unnatural response cutoffs
- Replace aggressive sentence-count limiting with ensure_complete_thought()
  which only trims if the LLM was actually cut off mid-sentence
- Softer prompt guidance for natural brevity instead of rigid sentence count
- max_tokens at 100 as natural length cap

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 17:18:22 -07:00
luke 9d4b8a0d22 Replace token-based truncation with sentence-count limiting
- max_tokens back to 150 so LLM can finish thoughts
- New limit_sentences() keeps only first 2 complete sentences
- Never cuts mid-sentence — always ends at punctuation
- Applied to both chat and auto-respond paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 17:15:04 -07:00
luke 9c5f7c5cfe Add debug logging and safety for piggybacked recording
- Log chunk count and peak audio level on recording stop
- Add null check on _recorded_audio in callback
- Small delay after stopping piggybacked recording for callback to finish

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 17:11:51 -07:00
luke 6a56967540 Enforce shorter AI responses and prevent cut-off sentences
- Reduce max_tokens from 100 to 75 for shorter output
- Add truncate_to_complete_sentence() to trim at last punctuation
- Applied to both chat and auto-respond paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 17:07:41 -07:00
luke 0e65fa5084 Force shorter AI responses — max 1-2 sentences
- Much stronger prompt language: "no more than 2 sentences EVER"
- Added "DO NOT ramble" instruction
- Reduced max_tokens back to 100 as hard limit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 17:05:51 -07:00
luke 3192735615 Fix AI responses being cut off
- Increase max_tokens from 100 to 150 to avoid mid-sentence truncation
- Tighten prompt to 1-2 short sentences with emphasis on completing them

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 17:04:12 -07:00
luke d583b48af0 Fix choppy/distorted audio to live caller
- Mute host mic forwarding while TTS is streaming to prevent interleaving
  both audio sources into the same playback buffer
- Replace nearest-neighbor downsampling with box-filter averaging on both
  server (host mic) and browser (caller mic) for anti-aliased resampling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 17:01:33 -07:00
luke d4e25ceb88 Stream TTS audio to caller in real-time chunks
TTS audio was sent as a single huge WebSocket frame that overflowed the
browser's 3s ring buffer. Now streams in 60ms chunks at real-time rate.
Also increased browser ring buffer from 3s to 10s as safety net.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 16:56:22 -07:00
luke eaedc4214b Reduce live caller latency and improve reliability
- Replace per-callback async task spawning with persistent queue-based sender
- Buffer host mic to 60ms chunks (was 21ms) to reduce WebSocket frame rate
- Reduce server ring buffer prebuffer from 150ms to 80ms
- Reduce browser playback jitter buffer from 150ms to 100ms

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 16:47:17 -07:00
luke af8606b5b7 Fix recording conflict when host stream is active
When a live caller is on air, the host stream already has an InputStream
open. Opening a second one for push-to-talk recording causes a conflict.
Now recording piggybacks on the host stream callback instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 16:42:07 -07:00
luke 4d97ea9099 Replace queue with ring buffer jitter absorption for live caller audio
- Server: 150ms pre-buffer ring buffer eliminates gaps from timing mismatches
- Browser playback: 150ms jitter buffer (up from 80ms) for network jitter
- Capture chunks: 960 samples/60ms (better network efficiency)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 16:37:50 -07:00
luke 7aed4d9c34 Fix live caller audio latency and choppiness
- Reduce capture chunk from 4096 to 640 samples (256ms → 40ms)
- Replace BufferSource scheduling with AudioWorklet playback ring buffer
- Add 80ms jitter buffer with linear interpolation upsampling
- Reduce host mic and live caller stream blocksizes from 4096/2048 to 1024
- Replace librosa.resample with numpy interpolation in send_audio_to_caller

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 16:32:27 -07:00