Domain: actvoice.xyz
Build an open-source, accessible-first audio drama production service with:
- REST API for humans/web UI;
- MCP-compatible tool surface for AI agents;
- server-side rendering of audio artifacts;
- Edge/Microsoft neural voices as the default free TTS mode;
- RHVoice local/offline fallback and explicit open-source mode;
- Openverse CC0 SFX provider for real ambience/foley;
- synthetic ambience/SFX fallback when no real asset is available;
- future provider slot for OpenAI BYO key.
The service should not depend on a built-in LLM. External agents use MCP/API to create projects and render final MP3 files.
- No built-in AI Director yet.
- No username/password user accounts; MVP uses agent registration + bearer keys.
- No payment system yet.
- No large third-party SFX library yet.
- No browser DAW/timeline editor yet.
- No full deployment automation yet; render queue and artifact serving are implemented in-process for hosted MVP.
Initial stack:
- Python 3
- FastAPI
- Pydantic
- Edge TTS via
edge-ttsfor default free neural voices - RHVoice via
RHVoice-testas fallback/local mode - ffmpeg/ffprobe
- SQLite-backed project/agent/render-job storage plus filesystem artifact storage for MVP
- pytest for tests
Rationale: Python is best for server-side audio processing, RHVoice subprocess calls, synthetic audio generation, and quick FastAPI/MCP implementation.
Website / REST API / MCP tools
-> Project service
-> SQLite storage (projects + agents + render jobs)
-> Render queue/service
-> TTS provider abstraction
-> FallbackTTSProvider (default free chain: Edge -> RHVoice)
-> EdgeTTSProvider
-> RHVoiceProvider
-> future OpenAITTSProvider
-> SFX provider abstraction
-> OpenverseSFXProvider (CC0 real sounds)
-> Synthetic SFX/Ambience fallback
-> ffmpeg mixer
-> artifact storage
Project:
- id
- title
- language
- characters
- scenes
- render settings
- artifact metadata
Character:
- id
- display name
- role/gender hint
- voice provider
- voice id
Scene:
- id
- title
- ambience
- lines
- sound cues
Line:
- speaker id
- text
- pause_after_ms
- optional rate/pitch/volume
SoundCue:
- semantic type:
footsteps,water_drip,notification,heartbeat, etc. - absolute timing:
start_ms,duration_ms,level - production relative timing: optional
anchorwithtype, optionalline_id, andoffset_ms - supported anchor types:
scene_start,scene_end,before_line,after_line - optional attributes: mood, surface, intensity, Openverse query override
Auth:
POST /api/agents/register— issue bearer key for an agent; optionalACTVOICE_REGISTRATION_CODEinvite gate.
Public endpoints:
GET /healthGET /api/voicesGET /api/projects/{project_id}GET /api/jobs/{job_id}GET /api/projects/{project_id}/artifactGET /api/projects/{project_id}/artifact.mp3GET /api/projects/{project_id}/artifact.wavGET /api/projects/{project_id}/render-manifest.json
Write/render endpoints require Authorization: Bearer [REDACTED].
POST /api/projectsPOST /api/projects/{project_id}/charactersPOST /api/projects/{project_id}/scenesPOST /api/projects/{project_id}/scenes/{scene_id}/linesPOST /api/projects/{project_id}/scenes/{scene_id}/sound-cuesPOST /api/projects/{project_id}/render— returns202 Acceptedqueued job metadata.
Render queue:
- implemented as
app/render_queue.pywith configurableACTVOICE_RENDER_WORKERS; - returns queued jobs immediately instead of blocking HTTP requests;
- persists job status/artifact/error in SQLite, so done/failed jobs survive aaPanel/process restarts;
- marks interrupted
renderingjobs back toqueuedon startup and can auto-resume pending work; - stores final artifact metadata on the project;
- can later be swapped for Redis/RQ/Celery without changing the API surface.
MCP transport:
- implemented as
app/mcp_server.pyusing the official Python MCP SDK (FastMCP); - default transport is stdio for local clients;
ACTVOICE_MCP_TRANSPORT=streamable-httpcan expose HTTP transport later;- write/render MCP tools require ActVoice API key via
ACTVOICE_API_KEYenv orapi_keyargument.
MCP tool functions call the same service layer as REST:
create_audio_drama_projectadd_characterlist_voicesassign_voiceadd_sceneadd_dialogue_lineadd_sound_cuerender_final_mixget_render_statusget_final_artifact
MVP:
EdgeTTSProviderfor the default free neural voice path.RHVoiceProviderfor local/offline rendering and fallback.FallbackTTSProviderroutes the free mode asedge -> rhvoice.
Future:
OpenAITTSProviderwith user-provided key.
Provider abstraction:
synthesize(text, voice, output_path, *, rate=100, pitch=100, volume=100, provider=None, fallback_voice=None) -> SynthesisResultThe render manifest records tts_provider, tts_voice, requested provider/voice, and fallback reason per dialogue line.
MVP prefers real CC0 sounds through Openverse:
- semantic cue types map to Openverse queries, e.g.
brook -> brook stream water,birds -> forest birds,footsteps -> footsteps gravel walking,laptop_close -> laptop close; - Openverse results are filtered to
license=cc0; - downloaded audio is cached under the project build directory;
- long ambience is trimmed to the requested
duration_ms; - short one-shots can be looped/truncated by ffmpeg to fit the requested duration;
- render manifests preserve provider/title/creator/license/source/landing URL metadata.
Synthetic generation remains a fallback for unsupported cue types or Openverse/network failures:
- room tone
- wind/noise bed
- tension pad/drone
- heartbeat pulse
- notification/beep
- water drip approximation
- footsteps approximation
Later add a curated local CC0 SFX pack with provenance manifest so common cues do not require network search at render time.
Agents should add semantic cues, not raw filenames. Timing can be controlled either by absolute start_ms or by relative anchors such as after_line plus line_id and offset_ms. During render, ActVoice measures generated line durations, writes a timing_map into render_manifest.json, resolves anchors into final start_ms, and mixes cues deterministically without any built-in AI.
For each line:
- synthesize voice to WAV;
- normalize/convert sample rate;
- concatenate with pauses into scene dialogue;
- generate ambience/cues for scene duration;
- mix scene dialogue + ambience + cues;
- concatenate scenes;
- encode final MP3;
- write render manifest;
- store artifact URLs for MP3/WAV/manifest download endpoints.
actvoice/
app/
tests/
data/
projects/{project_id}/project.json
projects/{project_id}/build/
lines/
scenes/
final_mix.wav
final_mix.mp3
render_manifest.json
data/ is gitignored.
Unit tests:
- manifest models validate correctly;
- project service creates/mutates projects;
- sound cue generator creates WAV files;
- render queue state transitions;
- TTS fallback chain uses Edge first, falls back to RHVoice, and records manifest metadata.
- RHVoice provider detects availability and can be skipped if missing.
Integration smoke:
- create tiny project;
- render with Edge when available or RHVoice fallback if installed;
- final MP3 exists and ffprobe duration is positive.
plan.mdexists.- Python package scaffold exists.
- REST app starts.
- Project CRUD service works.
- Edge provider and Edge→RHVoice fallback chain implemented.
- RHVoice provider implemented.
- Synthetic SFX generator implemented for basic cues.
- Render service can create a short MP3 from a manifest.
- REST render endpoint queues jobs and exposes job status.
- Artifact endpoints serve MP3/WAV/manifest files by URL.
- Tests pass.
- README explains domain, concept, setup, and API flow.