Voice conversion on the emotion trajectories — what was done, and what it measures

The pages in this build are a corrected copy of the emotion tier pages. This page says exactly what the correction is, and reports whether it worked — including where it did not.

1. What voice conversion is, in plain language

Every chain on this site is several separate recordings of what the mining believed was one speaker, played one after another. They were only ever filtered for speaker similarity, never made into one voice, so as a chain plays you can hear the voice itself shift between segments.

Voice conversion takes a clip and re-speaks it in someone else's voice. The words, the timing, the pauses and most of the delivery come from the original clip; the vocal identity — the thing that makes a voice recognisably one person — comes from a short reference recording of the target. Here the target is always segment 1 of that same chain. So the chain ends up sounding like segment 1's speaker throughout, saying what each of the later clips actually said.

What it fixes. Only one thing: who the voice sounds like. If two clips of a chain sounded like two different people, they should now sound like one.

What it does not fix. It does not make the clips one continuous performance — they are still separate takes, recorded at different moments, with different room and microphone, and the joins are still joins. It does not correct the mining: if the wrong clips were chained together, they are still the wrong clips.

What it can break. Delivery and identity are not cleanly separable. A converted clip is re-synthesised, so its pacing, emphasis and emotional colour can move — which matters enormously here, because the only thing these chains exist to demonstrate is an emotional change. If conversion flattened the emotion, these pages would be worse than the ones they copy and there would be no reason to ship them. That is the first thing section 5 measures, and it is the reason the emotion re-scoring runs on every single segment rather than on a sample.

2. Exactly what was run

For each chain, segment 1 was loudness-prepared (EBU R128 integrated −23 LUFS, peak ceiling 0.95) and used as the target voice. Segments 2…k were tokenised and re-synthesised onto it with ChatterboxVC (the s3gen voice-conversion path, one candidate, seed derived from the chain id so the run is reproducible), then restored with SIDON, which also takes the output to 48 kHz. TF32 is enabled for s3gen (measured free) and explicitly disabled around SIDON (measured ~15 dB cost).

Then, exactly as the uncorrected pages do, every segment is scaled to −20 dBFS RMS before the chain is concatenated, a 0.15 s silence marks each boundary, and the peak guard is applied once to the finished chain so the level relationship between segments survives.

1,304 chains and 3,438 segments were converted onto a new voice (4,742 segments passed through the pipeline in total, counting the anchors used for the third render below), in 1.5 GPU-hours. 0 chains failed.
Two things are deliberately NOT in this build.
• The VoiceNet (VN1) tiers are untouched — the request was for the emotion pages only.
• The voice-profile (vprof_vc) tiers are untouched, because they have no problem to fix: each of those chains is already a single cloned voice by construction. No tier mixes profile chains with mined ones, so this is a clean cut and no page is half-converted.
Both remain available in full on the original site.

3. What changed in this build, and why

The first version of these pages converted only segments 2…k and left segment 1 exactly as recorded. Listening found the problem the measurement had also found: the chain then glues one raw 24 kHz recording to k−1 neural 48 kHz re-syntheses, and the acoustic — most audibly the reverb — changes at the first join. The cosine across that first join measured 0.836 before and 0.728 after, the single worst thing conversion did anywhere in the build.

So segment 1 is now converted too, with itself as the target, and that render is the default on every card. The earlier one is kept behind an expander on each card, because it retains more of the emotional move (96 % against 90 %) — a real trade-off rather than a strict improvement, and the choice stays visible.
Crossfades replace the silence. Segments are now joined with an equal-power crossfade — cos/sin, so that the two sides sum to constant energy. A linear crossfade sums to less than unit energy at its midpoint and dips audibly on uncorrelated material, which two different recordings always are.

The nominal length is 150 ms, shortened where it would do damage: never more than a quarter of either neighbouring segment, and cut to 100 ms wherever the incoming segment starts loud or the outgoing one ends loud, since a crossfade attenuates the incoming signal exactly where a word-initial consonant would be.

Across 6,995 joins: 5,088 took the full 150 ms, 1,382 were shortened for a hot onset and 525 for a hot tail. The shortest fade anywhere in the build was 100 ms — the 25 % duration cap never bound, because the shortest segment here is 3 s. Each card prints its own fade lengths.
The chain is normalised as one signal. Previously every segment was scaled to −20 dBFS RMS independently, which forces them all to the same loudness and erases the dynamics between them. Now a single gain is applied to the finished chain and the peak guard is applied once after it.

That would have been unsafe on the original recordings — those carry up to 8 dB of level steps from the MOSS encoder's own per-clip normalisation, an artifact of the pipeline rather than the performance. It is safe here because SIDON returns every converted segment at a consistent level, and the result is measured rather than assumed: the largest step between adjacent segments is a median 1.17 dB, 3.39 dB at the 95th percentile. For comparison the raw, un-normalised chains reach 17.96 dB. What is left is the conversion's own dynamics, not a per-clip reset.

4. Did it unify the voice? — speaker identity

Measured with Orange/Speaker-wavLM-id, the 250-dimensional speaker verification embedding — the same space the min_cos_* numbers on the original pages live in, and the space in which the mining's 0.80 threshold is meaningful. (Not -tbr, which is a 128-d timbre embedding on an unrelated scale.) For every chain: the worst cosine between any segment and segment 1, and the worst between any two neighbours. Higher is more like one person. All figures are over the same 1,304 chains.
worst cosine to segment 1p05p25medianp75p95
original0.0690.5020.7560.8610.935
corrected (anchor kept raw)0.3760.5550.6740.7550.840
uniform (anchor converted too)0.4550.6790.7810.8570.923
worst cosine between neighboursp05p25medianp75p95
original0.0690.5220.7650.8660.937
corrected0.4150.6040.7060.7850.859
uniform0.4590.6890.7850.8580.925
40 %of chains at or above the 0.80 identity threshold, original
13 %… corrected
44 %… uniform

4a. The result depends entirely on how loose the chain already was

The overall before/after average is close to useless here, because it averages two opposite effects. The conversion pulls every chain towards a common level of identity agreement: it rescues chains whose segments were plainly different people, and it damages chains that were already one voice. Splitting the chains by their original worst-to-anchor cosine shows it directly. Median values; “improved” is the share of chains in that band whose number went up at all.
original worst-to-anchor cosinechainssharebeforecorrectedchangeimproveduniformchangeimprovedemotion kept
below 0.50 - clearly different voices32625 %0.2210.519+0.29696 %0.653+0.41397 %96 %
0.50-0.70 - audibly different19015 %0.6430.578-0.05529 %0.682+0.04568 %96 %
0.70-0.80 - close but under threshold27221 %0.7560.671-0.08314 %0.762+0.00352 %96 %
0.80 and above - already one voice51640 %0.8790.761-0.1172 %0.865-0.01534 %95 %
Read as a recommendation. For the 25 % of chains that started below 0.50 — segments that really are different people — the conversion is a large, near-universal improvement: 0.221 → 0.519, and 96 % of them go up. For the 40 % that were already at or above 0.80, it is a loss (-0.117, only 2 % improved) and those chains are better listened to on the original site. The uniform render is the better general-purpose correction on this measure: it is a large gain where there was a problem (+0.413) and close to neutral where there was not (-0.015).
The corrected render does not improve the identity number, and on the median it makes it worse (-0.069). That is a real result and it has a mechanical explanation: the converted segments are compared against a raw recording of segment 1. Neural re-synthesis moves a clip in this embedding space even when the voice it imitates is right, so a converted segment scores further from a raw anchor than another raw recording of the same speaker would. The number is punishing the resynthesis, not only the voice.

The uniform render is the processing-matched comparison — converted audio against a converted anchor — and it is the one that answers “did the conversion make these sound like one person?”. There the median change is +0.015, and the share of chains reaching the 0.80 threshold goes 40 % → 44 %.

4b. Separating “different person” from “re-synthesised”

The number above mixes two things a listener does not hear the same way: how well the conversion unified the voice, and how far neural re-synthesis moves a clip in this embedding space at all — which it does even when the imitated voice is right. In the shipped render the second effect is concentrated in one place: the first join, where a raw recording meets a re-synthesis.

So the full segment×segment cosine matrix was recomputed for every one of the 1,304 chains and split up. seam 1 is that first join. after the first join is the worst agreement among segments 2…k measured against each other — note that this is identical for the corrected and uniform renders by construction, since both contain the same converted segments 2 onward. spread is how far the identity wanders across the whole chain, max minus min over every pair.
first join only (seam 1)p05p25medianp75p95
original0.1660.7110.8360.9100.959
corrected0.4490.6230.7280.7970.867
uniform0.5260.7540.8400.9000.944
after the first join — segments 2…k against each otherp05p25medianp75p95
original0.1070.6270.8050.8890.953
converted0.5070.7110.8040.8700.935
identity wander across the chain (lower is steadier)p05p25medianp75p95
original0.0000.0430.1310.2770.765
corrected0.0000.0950.1790.2800.433
uniform0.0000.0450.1060.2010.379
The awkward result, stated plainly. Among the converted segments themselves, the conversion changed essentially nothing: worst agreement of segments 2…k against each other was a median 0.805 before and 0.804 after, and the share reaching the 0.80 threshold went 51.5 % → 51.4 %.

So the conversion is not making the later segments sound more like one another. What it does is pull each of them towards segment 1, which is why the chains that started far from segment 1 improve so much and the chains that started close to it do not. The residual differences between the converted segments survive the conversion — the speech tokens the vocoder is driven from evidently still carry some of each source speaker.

The one identity measure that improves across the board is spread, and only for the uniform render: 0.131 → 0.106. The corrected render makes spread worse (0.179), because it adds the raw-to-synthetic step at seam 1 on top of whatever variation was already there. On identity, the uniform render is the better artefact on every measure taken here.

5. Did it destroy the emotion? — the point of the exercise

Every segment, original and converted, was re-scored through the same emotion stack the corpus was annotated with (laion/BUD-E-Whisper + Empathic-Insight-Voice-Plus) and put on the same tie-aware corpus-percentile scale the mining used (ecdfm over the exact 127 M-utterance global histograms). Then d_b — the end-to-end move on the axis the chain was mined for — was recomputed exactly as mine5.py defines it.

Both sides are scored with the same scorer, so any offset between this scorer and the stored corpus annotation cancels. As a check that it does: re-scoring the originals reproduces the mined d_a/d_b to three decimals.

Retention is d_b(after) / d_b(before), kept signed: a conversion that reverses a trajectory has destroyed it, and must not be able to score 1.0 by flipping. Chains whose original delta is under 0.02 are excluded, because a ratio against zero is noise.
The check, run first. Re-scoring the original audio and comparing the recomputed d_b against the value stored by the miner: median absolute error 0.0003, 95th percentile 0.0215, over 1,304 chains — on a scale where a tier threshold is 0.20. The re-scoring reproduces the mining, so the before/after numbers below are a measurement of the conversion and not of a scorer mismatch.
end-to-end move on the mined axis (d_b)p05p25medianp75p95
as mined (from the parquet)+0.202+0.268+0.337+0.515+0.686
re-scored, original audio+0.201+0.267+0.341+0.518+0.706
re-scored, corrected-0.035+0.218+0.304+0.457+0.660
re-scored, uniform-0.098+0.167+0.307+0.506+0.717
fraction of the delta retainedp05p25medianp75p95
corrected+0.000+0.805+0.956+1.000+1.111
uniform-0.077+0.565+0.903+1.086+1.927
93 %corrected chains that keep the DIRECTION of the move
87 %… that keep at least half of it
91 %uniform: direction kept
78 %uniform: at least half kept

6. What it cost in quality

The Empathic-Insight overall quality head, per segment, on the same audio the players serve. A cheap proxy, not a listening test, and it is scored on 48 kHz SIDON output against a 24 kHz original — so part of any gain here is bandwidth, not conversion. Read it as “the conversion did not wreck the audio”, not as “the conversion improved it”.
predicted overall quality, per segmentp05p25medianp75p95
original2.4282.7262.9223.0513.221
corrected2.5782.8943.0903.2373.413
uniform2.6872.9793.1483.2783.431
change, corrected − original+0.000+0.000+0.152+0.268+0.478

7. By corpus — where the correction had something to fix

The corpora differ enormously in how well “speaker” was ever defined. mls ships real speaker identities; everywhere else it meant “same source recording”. podcast is the loose one — only about 54.6 % of consecutive clips inside one nominal podcast speaker track are actually the same person — so it is where a conversion pass has the most to prove.
corpuschainscos→seg1 origcorrecteduniform≥0.80 orig≥0.80 corrd_b origd_b corrretentionquality
emolia7460.7750.6750.77140 %12 %+0.401+0.35395 %2.85 → 3.01
eurospeech1090.8680.7310.87656 %26 %+0.300+0.27089 %3.05 → 3.25
evasnippets630.7570.6960.84343 %13 %+0.272+0.26698 %3.10 → 3.25
mls790.9260.7860.90996 %38 %+0.281+0.27398 %3.11 → 3.27
podcast2320.5730.6180.74821 %5 %+0.303+0.27096 %2.79 → 3.02
snippets750.1490.4560.5707 %0 %+0.344+0.31895 %2.72 → 2.85

8. By rule

rulechainscos→seg1 origcorrecteduniformd_b origd_b corrretention
AB23000.7840.6730.799+0.304+0.27794 %
B13800.7450.6600.769+0.331+0.28395 %
B1 UNION VN1200.7320.6730.768+0.238+0.22889 %
BASE200.7530.7220.797+0.347+0.353100 %
PXR4040.7520.6610.774+0.364+0.33796 %
S1200.6880.6760.776+0.042+0.02248 %
S2200.7330.6390.748+0.552+0.51695 %
S31000.7490.7040.787+0.551+0.52496 %
S4200.7780.7570.824+0.475+0.46295 %
mixed200.9100.7450.860+0.006-0.024108 %

9. Intelligibility — did the words survive?

1,140 segments from 300 chains, stratified over the six corpora, transcribed with openai/whisper-large-v3-turbo. Character error rate, not word error rate: these chains include Korean, Japanese and Chinese, where word error rate is not defined in any useful way.

The headline number uses the ASR of the original clip as the reference, not the corpus transcript. Whisper's own error on this material is large and multilingual and would swamp the effect being measured; comparing converted-ASR against original-ASR cancels it and answers the actual question — did the conversion change what is said? The language detected on the original is forced on the converted side, so a language flip cannot masquerade as a transcription difference.
comparisonCER
converted vs. ASR of the original — how much the conversion changed the words6.7 %
original vs. the corpus transcript (context: Whisper's own error here)10.0 %
converted vs. the corpus transcript12.3 %
The conversion costs about 2.3 character-error points. Against the corpus transcript the error rate goes 10.0 % → 12.3 %. That is a real degradation and it is the clearest price in this whole build: the speech is still the same speech, but it is measurably less cleanly articulated. It is small enough that the chains remain listenable and large enough that this audio should not be treated as a transcription-grade rendering of the originals.
corpussegmentsconverted vs original-ASRvs transcript, originalvs transcript, converted
emolia1775.0 %6.4 %8.1 %
eurospeech1979.8 %13.6 %19.9 %
evasnippets2004.9 %7.1 %6.7 %
mls1824.6 %2.7 %6.3 %
podcast1929.4 %20.9 %22.7 %
snippets1927.0 %11.2 %13.5 %

10. Do we need voice conversion at all?

The alternative to converting a mismatched chain is to not ship it: apply a hard identity cut and keep only chains that already sound like one person. Whether that is better is answerable from what is already measured here, because the conversion's effect is known separately for the chains each cut would keep and the chains it would drop.
hard cutchains keptof the gridmedian cosine keptVC on those KEPTimprovedVC on those DROPPEDimproved
≥ 0.7078860.4 %0.841-0.1086 %+0.17371 %
≥ 0.7567551.8 %0.858-0.1103 %+0.07662 %
≥ 0.8051639.6 %0.879-0.1172 %+0.00651 %
≥ 0.8536528.0 %0.905-0.1251 %-0.02644 %
The table answers the question, and the answer is mostly “no”. Wherever a cut would keep a chain, conversion makes that chain worse on identity — at ≥ 0.80 it costs 0.117 and helps only 2 % of them. And at ≥ 0.80 the chains that would be dropped gain almost nothing from conversion either (+0.006, 51 % improved — a coin flip).

Conversion only clearly earns its place further down: below 0.70 it is worth +0.173 on the 516 chains a 0.70 cut would discard, improving 71 % of them. Those are exactly the chains that are beyond rescue by filtering — and they are also the ones where the listener hears the conversion happen as a stylistic break, because the voices being reconciled were audibly different to begin with.
The two embedding spaces, on the same chains. The corpus-wide counts below come from two different populations, so their percentages cannot be compared with each other. The grid is the one place both numbers exist for the same chains: on 298 of them (all emolia, the only corpus here carrying a stored timbre anchor), -id ≥ 0.80 is best matched by -tbr ≥ 0.788 (85.9 % agreement), and the standing calibration of 0.795 agrees on 84.9 %. That confirms the calibration rather than revising it, and it means a timbre cut in the 0.75–0.80 range is slightly more permissive than the identity test at 0.80, not less. Correlation between the two spaces is r = 0.923.

This has to be done store-against-store: the identity cosine recomputed for this build is measured on the rendered concatenation and sits a median -0.0184 from the corpus store (r = 0.967). Pairing across the two sources instead moves the apparent equivalent threshold by nearly 0.1 — a measurement offset masquerading as a disagreement between the models.
corpus-wide hard cut-id chains keptof its own population-tbr chains keptof its own population
≥ 0.701,099,14940.1 %4,312,66186.0 %
≥ 0.75980,78135.8 %4,037,04580.5 %
≥ 0.80811,69029.6 %3,520,07070.2 %
≥ 0.85571,97520.9 %2,630,52852.5 %
Read the two columns separately. They are measured over different sets of chains — -id on 2,737,699 and -tbr on 5,012,091 of the 9,744,940 mined chains, with different corpus mixes. The gap between 40 % and 86 % at the same nominal threshold is not evidence that the timbre space is more lenient; it is mostly a different denominator. The paired test above is the comparison that controls for that.

The number that matters for planning is the absolute one: at -tbr ≥ 0.80 there are 3,520,070 chains that need no conversion at all.

Where the build lives

Corrected audio is under vcaudio/ (the corrected render) and vcaudio_u/ (the uniform render); the untouched originals stay under audio/. Every number on these pages is in vc_grid.json, per chain, machine-readable. Bulk artefacts — the converted segments as float16, the per-shard measurement files — are on the cluster under traj2/vcgrid/out.