<?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: elenaashford</title>
    <description>The latest articles on DEV Community by elenaashford (@elenaashford).</description>
    <link>https://dev.to/elenaashford</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%2F4046017%2F9f50209a-093c-4a7e-bd3b-9a4fec724392.png</url>
      <title>DEV Community: elenaashford</title>
      <link>https://dev.to/elenaashford</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/elenaashford"/>
    <language>en</language>
    <item>
      <title>Multi-Voice Audiobooks: The Character Attribution Problem</title>
      <dc:creator>elenaashford</dc:creator>
      <pubDate>Fri, 24 Jul 2026 19:02:22 +0000</pubDate>
      <link>https://dev.to/elenaashford/multi-voice-audiobooks-the-character-attribution-problem-2dif</link>
      <guid>https://dev.to/elenaashford/multi-voice-audiobooks-the-character-attribution-problem-2dif</guid>
      <description>&lt;p&gt;Turning a novel into a multi-voice audiobook sounds like a TTS problem. It is mostly a parsing problem, and the parsing is harder than the synthesis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attribution is not solved by quotation marks
&lt;/h2&gt;

&lt;p&gt;The naive approach: find text in quotes, that is dialogue, assign a voice. It breaks immediately on real prose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"I told you," she said, "and you did not listen."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One utterance, split across two quoted spans, with the attribution wedged in the middle. Naive splitting produces two separate lines and often assigns them different speakers.&lt;/p&gt;

&lt;p&gt;Then there is the harder case — no attribution at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Are you coming?"
"In a minute."
"You said that an hour ago."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three lines, zero speaker tags. A human tracks the alternation from context. A parser has to model turn-taking, and it has to know when the alternation breaks because a third character walked in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually helps
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Attribution verb lexicons beat regex.&lt;/strong&gt; &lt;code&gt;said / asked / whispered / muttered&lt;/code&gt; plus their inflections cover the majority of tagged dialogue. The pattern is usually &lt;code&gt;"..." &amp;lt;verb&amp;gt; &amp;lt;entity&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;entity&amp;gt; &amp;lt;verb&amp;gt;, "..."&lt;/code&gt; — worth handling both orderings explicitly rather than hoping one regex catches everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coreference is unavoidable.&lt;/strong&gt; "she said" only resolves if you know who "she" is in this scene. Without a coref pass you get correct attribution to the wrong voice, which sounds worse than a monotone read because listeners notice the inconsistency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alternation as a prior, not a rule.&lt;/strong&gt; In untagged runs, alternating between the last two active speakers is right most of the time. Treat it as a default that named attribution overrides — not as ground truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The consistency constraint nobody warns you about
&lt;/h2&gt;

&lt;p&gt;Once a voice is assigned to a character, it must stay assigned. Chapter 14 cannot re-cast the protagonist because your attribution confidence dipped.&lt;/p&gt;

&lt;p&gt;That means voice assignment is a &lt;strong&gt;document-level&lt;/strong&gt; decision made after full-text analysis, not a streaming one made per chapter. Which in turn means you cannot start rendering audio until you have parsed the entire book — an architectural constraint that surprises people building this incrementally.&lt;/p&gt;

&lt;p&gt;Practical shape that works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Full-text parse -&amp;gt; character inventory with mention counts&lt;/li&gt;
&lt;li&gt;Assign voices to the top N by frequency, narrator to everything else&lt;/li&gt;
&lt;li&gt;Persist the mapping&lt;/li&gt;
&lt;li&gt;Render chapters against the frozen mapping&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I built this pipeline into &lt;a href="https://voxforge.app" rel="noopener noreferrer"&gt;VoxForge&lt;/a&gt; — epub or txt in, auto-cast characters, chaptered audiobook out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it still fails
&lt;/h2&gt;

&lt;p&gt;First-person narration where the narrator is also a character. Epistolary novels. Books that switch POV mid-chapter without a section break. All three break the character-inventory assumption, and I have not found a clean general solution — currently they need manual overrides.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>audio</category>
      <category>nlp</category>
    </item>
  </channel>
</rss>
