1+ import React , { useEffect , useRef , useState } from 'react' ;
2+ import * as d3 from 'd3' ;
3+
4+ const TTSFlowChart = ( ) => {
5+ const svgRef = useRef ( ) ;
6+ const [ selectedPipeline , setSelectedPipeline ] = useState ( 0 ) ;
7+
8+ const pipelines = [
9+ {
10+ id : 0 ,
11+ title : "Traditional TTS Pipeline" ,
12+ description : "Multi-stage pipeline with separate text analysis, acoustic model, and vocoder" ,
13+ stages : [
14+ { id : "text" , label : "Text" , type : "input" } ,
15+ { id : "analysis" , label : "Text Analysis" , type : "process" } ,
16+ { id : "linguistic" , label : "Linguistic Features" , type : "intermediate" } ,
17+ { id : "acoustic" , label : "Acoustic Model" , type : "process" } ,
18+ { id : "features" , label : "Acoustic Features" , type : "intermediate" } ,
19+ { id : "vocoder1" , label : "Vocoder" , type : "process" } ,
20+ { id : "waveform1" , label : "Waveform" , type : "output" }
21+ ]
22+ } ,
23+ {
24+ id : 1 ,
25+ title : "Mel-Spectrogram Based TTS" ,
26+ description : "Uses mel-spectrogram as intermediate representation" ,
27+ stages : [
28+ { id : "char1" , label : "Character" , type : "input" } ,
29+ { id : "acoustic1" , label : "Acoustic Model" , type : "process" } ,
30+ { id : "melspec" , label : "Mel-Spectrogram" , type : "intermediate" } ,
31+ { id : "vocoder2" , label : "Vocoder" , type : "process" } ,
32+ { id : "waveform2" , label : "Waveform" , type : "output" }
33+ ]
34+ } ,
35+ {
36+ id : 2 ,
37+ title : "Direct Character-to-Waveform TTS" ,
38+ description : "Simplified pipeline from characters directly to waveform" ,
39+ stages : [
40+ { id : "char2" , label : "Character" , type : "input" } ,
41+ { id : "vocoder3" , label : "Vocoder" , type : "process" } ,
42+ { id : "waveform3" , label : "Waveform" , type : "output" }
43+ ]
44+ } ,
45+ {
46+ id : 3 ,
47+ title : "Advanced Multi-Stage TTS" ,
48+ description : "Enhanced pipeline with detailed acoustic processing" ,
49+ stages : [
50+ { id : "char3" , label : "Character/Phoneme" , type : "input" } ,
51+ { id : "acoustic2" , label : "Acoustic Model" , type : "process" } ,
52+ { id : "features2" , label : "Acoustic Features" , type : "intermediate" } ,
53+ { id : "vocoder4" , label : "Vocoder" , type : "process" } ,
54+ { id : "waveform4" , label : "Waveform" , type : "output" }
55+ ]
56+ } ,
57+ {
58+ id : 4 ,
59+ title : "Fully End-to-End TTS Model" ,
60+ description : "Single model that learns the entire text-to-speech mapping" ,
61+ stages : [
62+ { id : "char4" , label : "Character/Phoneme" , type : "input" } ,
63+ { id : "e2e" , label : "Fully End-to-End TTS Model" , type : "process" , isMain : true } ,
64+ { id : "waveform5" , label : "Waveform" , type : "output" }
65+ ]
66+ }
67+ ] ;
68+
69+ const colors = {
70+ input : "#4ade80" ,
71+ process : "#3b82f6" ,
72+ intermediate : "#f59e0b" ,
73+ output : "#ef4444" ,
74+ connection : "#64748b"
75+ } ;
76+
77+ useEffect ( ( ) => {
78+ const svg = d3 . select ( svgRef . current ) ;
79+ svg . selectAll ( "*" ) . remove ( ) ;
80+
81+ const width = 900 ;
82+ const height = 500 ;
83+ const margin = { top : 20 , right : 20 , bottom : 20 , left : 20 } ;
84+
85+ svg . attr ( "width" , width ) . attr ( "height" , height ) ;
86+
87+ const g = svg . append ( "g" )
88+ . attr ( "transform" , `translate(${ margin . left } ,${ margin . top } )` ) ;
89+
90+ // Add gradient definitions
91+ const defs = svg . append ( "defs" ) ;
92+
93+ // Gradient for connections
94+ const gradient = defs . append ( "linearGradient" )
95+ . attr ( "id" , "connectionGradient" )
96+ . attr ( "gradientUnits" , "userSpaceOnUse" ) ;
97+
98+ gradient . append ( "stop" )
99+ . attr ( "offset" , "0%" )
100+ . attr ( "stop-color" , colors . connection )
101+ . attr ( "stop-opacity" , 0.8 ) ;
102+
103+ gradient . append ( "stop" )
104+ . attr ( "offset" , "100%" )
105+ . attr ( "stop-color" , colors . connection )
106+ . attr ( "stop-opacity" , 0.3 ) ;
107+
108+ // Glow effect for main process
109+ const filter = defs . append ( "filter" )
110+ . attr ( "id" , "glow" ) ;
111+
112+ filter . append ( "feGaussianBlur" )
113+ . attr ( "stdDeviation" , "3" )
114+ . attr ( "result" , "coloredBlur" ) ;
115+
116+ const feMerge = filter . append ( "feMerge" ) ;
117+ feMerge . append ( "feMergeNode" ) . attr ( "in" , "coloredBlur" ) ;
118+ feMerge . append ( "feMergeNode" ) . attr ( "in" , "SourceGraphic" ) ;
119+
120+ const currentPipeline = pipelines [ selectedPipeline ] ;
121+ const stages = currentPipeline . stages ;
122+
123+ const stageWidth = ( width - margin . left - margin . right ) / stages . length ;
124+ const centerY = ( height - margin . top - margin . bottom ) / 2 ;
125+
126+ // Create flowing connections
127+ const connections = [ ] ;
128+ for ( let i = 0 ; i < stages . length - 1 ; i ++ ) {
129+ connections . push ( {
130+ source : i ,
131+ target : i + 1
132+ } ) ;
133+ }
134+
135+ // Draw flowing connections with animation
136+ const connectionGroup = g . append ( "g" ) . attr ( "class" , "connections" ) ;
137+
138+ connections . forEach ( ( conn , index ) => {
139+ const sourceX = conn . source * stageWidth + stageWidth / 2 ;
140+ const targetX = conn . target * stageWidth + stageWidth / 2 ;
141+ const controlX1 = sourceX + ( targetX - sourceX ) / 3 ;
142+ const controlX2 = sourceX + 2 * ( targetX - sourceX ) / 3 ;
143+
144+ const path = connectionGroup . append ( "path" )
145+ . attr ( "d" , `M ${ sourceX } ${ centerY } C ${ controlX1 } ${ centerY - 30 } ${ controlX2 } ${ centerY - 30 } ${ targetX } ${ centerY } ` )
146+ . attr ( "stroke" , "url(#connectionGradient)" )
147+ . attr ( "stroke-width" , 3 )
148+ . attr ( "fill" , "none" )
149+ . attr ( "opacity" , 0 ) ;
150+
151+ // Animate connection appearance
152+ path . transition ( )
153+ . delay ( index * 200 )
154+ . duration ( 800 )
155+ . attr ( "opacity" , 1 ) ;
156+
157+ // Add flowing particles
158+ const particle = connectionGroup . append ( "circle" )
159+ . attr ( "r" , 4 )
160+ . attr ( "fill" , colors . connection )
161+ . attr ( "opacity" , 0 ) ;
162+
163+ const pathLength = path . node ( ) . getTotalLength ( ) ;
164+
165+ const animateParticle = ( ) => {
166+ particle
167+ . attr ( "opacity" , 0.8 )
168+ . transition ( )
169+ . duration ( 2000 )
170+ . ease ( d3 . easeLinear )
171+ . attrTween ( "transform" , ( ) => {
172+ return ( t ) => {
173+ const point = path . node ( ) . getPointAtLength ( t * pathLength ) ;
174+ return `translate(${ point . x } , ${ point . y } )` ;
175+ } ;
176+ } )
177+ . transition ( )
178+ . duration ( 100 )
179+ . attr ( "opacity" , 0 )
180+ . on ( "end" , ( ) => {
181+ setTimeout ( animateParticle , Math . random ( ) * 2000 + 1000 ) ;
182+ } ) ;
183+ } ;
184+
185+ setTimeout ( ( ) => animateParticle ( ) , index * 300 + 1000 ) ;
186+ } ) ;
187+
188+ // Draw stages
189+ const stageGroup = g . append ( "g" ) . attr ( "class" , "stages" ) ;
190+
191+ stages . forEach ( ( stage , index ) => {
192+ const x = index * stageWidth + stageWidth / 2 ;
193+ const isMainProcess = stage . isMain ;
194+
195+ const stageContainer = stageGroup . append ( "g" )
196+ . attr ( "class" , "stage" )
197+ . attr ( "transform" , `translate(${ x } , ${ centerY } )` ) ;
198+
199+ // Add stage box
200+ const rect = stageContainer . append ( "rect" )
201+ . attr ( "x" , - 60 )
202+ . attr ( "y" , - 25 )
203+ . attr ( "width" , 120 )
204+ . attr ( "height" , 50 )
205+ . attr ( "rx" , 8 )
206+ . attr ( "fill" , colors [ stage . type ] )
207+ . attr ( "stroke" , "#ffffff" )
208+ . attr ( "stroke-width" , 2 )
209+ . attr ( "opacity" , 0 )
210+ . style ( "cursor" , "pointer" ) ;
211+
212+ if ( isMainProcess ) {
213+ rect . attr ( "filter" , "url(#glow)" )
214+ . attr ( "width" , 160 )
215+ . attr ( "x" , - 80 )
216+ . attr ( "height" , 60 )
217+ . attr ( "y" , - 30 ) ;
218+ }
219+
220+ // Animate stage appearance
221+ rect . transition ( )
222+ . delay ( index * 150 )
223+ . duration ( 600 )
224+ . attr ( "opacity" , 1 ) ;
225+
226+ // Add stage label
227+ const text = stageContainer . append ( "text" )
228+ . attr ( "text-anchor" , "middle" )
229+ . attr ( "dy" , "0.35em" )
230+ . attr ( "fill" , "white" )
231+ . attr ( "font-size" , isMainProcess ? "11px" : "12px" )
232+ . attr ( "font-weight" , "600" )
233+ . attr ( "opacity" , 0 )
234+ . style ( "pointer-events" , "none" ) ;
235+
236+ // Handle text wrapping for longer labels
237+ const words = stage . label . split ( / \s + / ) ;
238+ if ( words . length > 1 && ! isMainProcess ) {
239+ words . forEach ( ( word , i ) => {
240+ text . append ( "tspan" )
241+ . attr ( "x" , 0 )
242+ . attr ( "dy" , i === 0 ? "-0.2em" : "1.2em" )
243+ . text ( word ) ;
244+ } ) ;
245+ } else if ( isMainProcess ) {
246+ const lines = stage . label . split ( / (?< = T T S ) \s / ) ;
247+ lines . forEach ( ( line , i ) => {
248+ text . append ( "tspan" )
249+ . attr ( "x" , 0 )
250+ . attr ( "dy" , i === 0 ? "-0.6em" : "1.2em" )
251+ . text ( line ) ;
252+ } ) ;
253+ } else {
254+ text . text ( stage . label ) ;
255+ }
256+
257+ text . transition ( )
258+ . delay ( index * 150 + 300 )
259+ . duration ( 400 )
260+ . attr ( "opacity" , 1 ) ;
261+
262+ // Add hover effects
263+ stageContainer
264+ . on ( "mouseover" , function ( ) {
265+ d3 . select ( this ) . select ( "rect" )
266+ . transition ( )
267+ . duration ( 200 )
268+ . attr ( "transform" , "scale(1.05)" ) ;
269+ } )
270+ . on ( "mouseout" , function ( ) {
271+ d3 . select ( this ) . select ( "rect" )
272+ . transition ( )
273+ . duration ( 200 )
274+ . attr ( "transform" , "scale(1)" ) ;
275+ } ) ;
276+ } ) ;
277+
278+ } , [ selectedPipeline ] ) ;
279+
280+ return (
281+ < div className = "w-full max-w-6xl mx-auto p-6 bg-gradient-to-br from-slate-50 to-slate-100 rounded-xl shadow-lg" >
282+ < div className = "mb-8" >
283+ < h1 className = "text-3xl font-bold text-gray-800 mb-2" >
284+ Text-to-Speech Pipeline Architectures
285+ </ h1 >
286+ < p className = "text-gray-600" >
287+ Interactive visualization of different TTS model approaches
288+ </ p >
289+ </ div >
290+
291+ { /* Pipeline Selection */ }
292+ < div className = "mb-6" >
293+ < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3" >
294+ { pipelines . map ( ( pipeline ) => (
295+ < button
296+ key = { pipeline . id }
297+ onClick = { ( ) => setSelectedPipeline ( pipeline . id ) }
298+ className = { `p-3 rounded-lg text-left transition-all duration-200 ${
299+ selectedPipeline === pipeline . id
300+ ? 'bg-blue-500 text-white shadow-lg transform scale-105'
301+ : 'bg-white text-gray-700 hover:bg-blue-50 hover:shadow-md'
302+ } `}
303+ >
304+ < div className = "font-semibold text-sm mb-1" > { pipeline . title } </ div >
305+ < div className = { `text-xs ${
306+ selectedPipeline === pipeline . id ? 'text-blue-100' : 'text-gray-500'
307+ } `} >
308+ { pipeline . stages . length } stages
309+ </ div >
310+ </ button >
311+ ) ) }
312+ </ div >
313+ </ div >
314+
315+ { /* Current Pipeline Info */ }
316+ < div className = "mb-6 p-4 bg-white rounded-lg shadow-sm border-l-4 border-blue-500" >
317+ < h3 className = "font-bold text-gray-800 mb-2" >
318+ { pipelines [ selectedPipeline ] . title }
319+ </ h3 >
320+ < p className = "text-gray-600 text-sm" >
321+ { pipelines [ selectedPipeline ] . description }
322+ </ p >
323+ </ div >
324+
325+ { /* SVG Chart */ }
326+ < div className = "bg-white rounded-lg shadow-sm p-4 overflow-x-auto" >
327+ < svg ref = { svgRef } className = "w-full" > </ svg >
328+ </ div >
329+
330+ { /* Legend */ }
331+ < div className = "mt-6 flex flex-wrap justify-center gap-6 text-sm" >
332+ < div className = "flex items-center gap-2" >
333+ < div className = "w-4 h-4 rounded bg-green-400" > </ div >
334+ < span className = "text-gray-700" > Input</ span >
335+ </ div >
336+ < div className = "flex items-center gap-2" >
337+ < div className = "w-4 h-4 rounded bg-blue-500" > </ div >
338+ < span className = "text-gray-700" > Processing</ span >
339+ </ div >
340+ < div className = "flex items-center gap-2" >
341+ < div className = "w-4 h-4 rounded bg-amber-500" > </ div >
342+ < span className = "text-gray-700" > Intermediate</ span >
343+ </ div >
344+ < div className = "flex items-center gap-2" >
345+ < div className = "w-4 h-4 rounded bg-red-500" > </ div >
346+ < span className = "text-gray-700" > Output</ span >
347+ </ div >
348+ </ div >
349+ </ div >
350+ ) ;
351+ } ;
352+
353+ export default TTSFlowChart ;
0 commit comments