1919import torch
2020import torch .nn .functional as F
2121
22- from ..signals import mseq
22+ from ..signals import mseq_like
2323from ..typing import Precomputed
2424from ..utils .private import TAU , UNVOICED_SYMBOL , filter_values
2525from .base import BaseFunctionalModule
2626from .linear_intpl import LinearInterpolation
2727
2828
29+ def generate_pulse (
30+ pitch : torch .Tensor ,
31+ phase : torch .Tensor ,
32+ bipolar : bool ,
33+ ) -> torch .Tensor :
34+ def get_pulse_pos (p ):
35+ return torch .ge (torch .diff (torch .ceil (p )), 1 )
36+
37+ e = torch .zeros_like (pitch )
38+ pulse_pos = get_pulse_pos (phase )
39+ e [pulse_pos ] = torch .sqrt (pitch [pulse_pos ])
40+
41+ if bipolar :
42+ pulse_pos_double = get_pulse_pos (0.5 * phase )
43+ e [pulse_pos & ~ pulse_pos_double ] *= - 1
44+
45+ return e
46+
47+
48+ def generate_harmonic_pulse (
49+ pitch : torch .Tensor ,
50+ phase : torch .Tensor ,
51+ bipolar : bool ,
52+ ) -> torch .Tensor :
53+ # The number of harmonics is floor(0.5 * fs / f0) = floor(0.5 * 1 / T)
54+ n_harmonics = torch .floor (0.5 * pitch )
55+
56+ # The summation of sinusoids can be computed efficiently using the closed-form
57+ # expression of the Dirichlet kernel.
58+ theta = TAU * phase [..., :- 1 ]
59+ half_theta = 0.5 * theta
60+ if bipolar :
61+ numer = torch .cos (half_theta ) - torch .cos ((n_harmonics + 0.5 ) * theta )
62+ else :
63+ numer = - torch .sin (half_theta ) + torch .sin ((n_harmonics + 0.5 ) * theta )
64+ denom = 2 * torch .sin (half_theta )
65+
66+ # Handle singularities at phase is zero (i.e., harmonics).
67+ eps = 1e-6
68+ is_on_peak = denom .abs () < eps
69+ safe_denom = torch .where (is_on_peak , torch .ones_like (denom ), denom )
70+ e = numer / safe_denom
71+ if bipolar :
72+ e [is_on_peak ] = 0
73+ else :
74+ e [is_on_peak ] = n_harmonics [is_on_peak ]
75+
76+ # Normalize as the same energy as the pulse excitation.
77+ norm_factor = torch .sqrt (2 / n_harmonics .clip (min = 1 ))
78+ return norm_factor * e
79+
80+
81+ def generate_sinusoidal (phase : torch .Tensor , bipolar : bool ) -> torch .Tensor :
82+ if bipolar :
83+ e = torch .sin (TAU * phase )
84+ else :
85+ e = 0.5 * (1 - torch .cos (TAU * phase ))
86+ return e
87+
88+
89+ def generate_sawtooth (phase : torch .Tensor , bipolar : bool ) -> torch .Tensor :
90+ e = torch .fmod (phase , 1 )
91+ if bipolar :
92+ e = 2 * e - 1
93+ return e
94+
95+
96+ def generate_inverted_sawtooth (phase : torch .Tensor , bipolar : bool ) -> torch .Tensor :
97+ e = 1 - torch .fmod (phase , 1 )
98+ if bipolar :
99+ e = 2 * e - 1
100+ return e
101+
102+
103+ def generate_triangle (phase : torch .Tensor , bipolar : bool ) -> torch .Tensor :
104+ if bipolar :
105+ e = 2 * torch .abs (2 * torch .fmod (phase + 0.75 , 1 ) - 1 ) - 1
106+ else :
107+ e = torch .abs (2 * torch .fmod (phase + 0.5 , 1 ) - 1 )
108+ return e
109+
110+
111+ def generate_square (phase : torch .Tensor , bipolar : bool ) -> torch .Tensor :
112+ e = torch .le (torch .fmod (phase , 1 ), 0.5 ).to (phase .dtype )
113+ if bipolar :
114+ e = 2 * e - 1
115+ return e
116+
117+
118+ def generate_gauss (source : torch .Tensor ) -> torch .Tensor :
119+ return torch .randn_like (source )
120+
121+
122+ def generate_mseq (source : torch .Tensor ) -> torch .Tensor :
123+ return mseq_like (source )
124+
125+
126+ def generate_uniform (source : torch .Tensor ) -> torch .Tensor :
127+ return math .sqrt (12 ) * torch .rand_like (source )
128+
129+
29130class ExcitationGeneration (BaseFunctionalModule ):
30131 """See `this page <https://sp-nitech.github.io/sptk/latest/main/excite.html>`_
31132 for details.
@@ -36,7 +137,7 @@ class ExcitationGeneration(BaseFunctionalModule):
36137 The frame period in samples, :math:`P`.
37138
38139 voiced_region : ['pulse', 'sinusoidal', 'sawtooth', 'inverted-sawtooth', \
39- 'triangle', 'square']
140+ 'triangle', 'square', 'harmonic-pulse' ]
40141 The type of voiced region.
41142
42143 unvoiced_region : ['zeros', 'gauss', 'm-sequence', 'uniform']
@@ -141,13 +242,7 @@ def _forward(
141242 p = p .transpose (- 2 , - 1 )
142243 p *= mask
143244
144- # Compute phase.
145- voiced_pos = torch .gt (p , 0 )
146- q = torch .zeros_like (p )
147- q [voiced_pos ] = torch .reciprocal (p [voiced_pos ])
148- s = torch .cumsum (q .double (), dim = - 1 )
149- bias , _ = torch .cummax (s * ~ mask , dim = - 1 )
150- phase = (s - bias ).to (p .dtype )
245+ # Compute initial phase.
151246 if not isinstance (init_phase , str ):
152247 shift = init_phase / TAU
153248 elif init_phase == "zeros" :
@@ -156,74 +251,56 @@ def _forward(
156251 shift = torch .rand_like (p [..., :1 ])
157252 else :
158253 raise ValueError (f"init_phase { init_phase } is not supported." )
159- if isinstance (shift , torch .Tensor ) or shift != 0.0 :
160- phase += shift
161254
162- # Generate excitation signal using phase.
255+ # Compute phase.
256+ voiced_pos = torch .gt (p , 0 )
257+ q = torch .zeros_like (p )
258+ q [voiced_pos ] = torch .reciprocal (p [voiced_pos ])
259+ s = torch .cumsum (q .double (), dim = - 1 )
260+ bias , _ = torch .cummax (s * ~ mask , dim = - 1 )
261+ phase = (s - bias ).to (p .dtype )
262+
163263 if polarity == "auto" :
164- unipolar = voiced_region = = "pulse"
264+ bipolar = voiced_region ! = "pulse"
165265 elif polarity in ("unipolar" , "bipolar" ):
166- unipolar = polarity == "unipolar "
266+ bipolar = polarity == "bipolar "
167267 else :
168268 raise ValueError (f"polarity { polarity } is not supported." )
169- e = torch .zeros_like (p )
170- if voiced_region == "pulse" :
171-
172- def get_pulse_pos (p ):
173- r = torch .ceil (p )
174- return torch .ge (torch .diff (r ), 1 )
175-
176- if isinstance (shift , float ):
177- padded_phase = F .pad (phase , (1 , 0 ), value = shift )
178- else :
179- padded_phase = torch .cat ([shift , phase ], dim = - 1 )
180-
181- if unipolar :
182- pulse_pos = get_pulse_pos (padded_phase )
183- e [pulse_pos ] = torch .sqrt (p [pulse_pos ])
184- else :
185- pulse_pos1 = get_pulse_pos (padded_phase )
186- pulse_pos2 = get_pulse_pos (0.5 * padded_phase )
187- e [pulse_pos1 ] = torch .sqrt (p [pulse_pos1 ])
188- e [pulse_pos1 & ~ pulse_pos2 ] *= - 1
189- elif voiced_region == "sinusoidal" :
190- if unipolar :
191- e [mask ] = 0.5 * (1 - torch .cos (TAU * phase [mask ]))
192- else :
193- e [mask ] = torch .sin (TAU * phase [mask ])
194- elif voiced_region == "sawtooth" :
195- if unipolar :
196- e [mask ] = torch .fmod (phase [mask ], 1 )
197- else :
198- e [mask ] = 2 * torch .fmod (phase [mask ], 1 ) - 1
199- elif voiced_region == "inverted-sawtooth" :
200- if unipolar :
201- e [mask ] = 1 - torch .fmod (phase [mask ], 1 )
202- else :
203- e [mask ] = 1 - 2 * torch .fmod (phase [mask ], 1 )
204- elif voiced_region == "triangle" :
205- if unipolar :
206- e [mask ] = torch .abs (2 * torch .fmod (phase [mask ] + 0.5 , 1 ) - 1 )
207- else :
208- e [mask ] = 2 * torch .abs (2 * torch .fmod (phase [mask ] + 0.75 , 1 ) - 1 ) - 1
209- elif voiced_region == "square" :
210- if unipolar :
211- e [mask ] = torch .le (torch .fmod (phase [mask ], 1 ), 0.5 ).to (e .dtype )
212- else :
213- e [mask ] = 2 * torch .le (torch .fmod (phase [mask ], 1 ), 0.5 ).to (e .dtype ) - 1
269+
270+ if "pulse" in voiced_region :
271+ generators = {
272+ "pulse" : generate_pulse ,
273+ "harmonic-pulse" : generate_harmonic_pulse ,
274+ }
275+ if voiced_region not in generators :
276+ raise ValueError (f"voiced_region { voiced_region } is not supported." )
277+ phase = F .pad (phase , (1 , 0 ))
278+ phase += shift
279+ e = generators [voiced_region ](p , phase , bipolar )
214280 else :
215- raise ValueError (f"voiced_region { voiced_region } is not supported." )
281+ generators = {
282+ "sinusoidal" : generate_sinusoidal ,
283+ "sawtooth" : generate_sawtooth ,
284+ "inverted-sawtooth" : generate_inverted_sawtooth ,
285+ "triangle" : generate_triangle ,
286+ "square" : generate_square ,
287+ }
288+ if voiced_region not in generators :
289+ raise ValueError (f"voiced_region { voiced_region } is not supported." )
290+ phase += shift
291+ e = torch .zeros_like (p )
292+ e [mask ] = generators [voiced_region ](phase [mask ], bipolar )
216293
217294 if unvoiced_region == "zeros" :
218295 pass
219- elif unvoiced_region == "gauss" :
220- e [~ mask ] = torch .randn (torch .sum (~ mask ), device = e .device , dtype = e .dtype )
221- elif unvoiced_region == "m-sequence" :
222- e [~ mask ] = mseq (torch .sum (~ mask ) - 1 , device = e .device , dtype = e .dtype )
223- elif unvoiced_region == "uniform" :
224- e [~ mask ] = math .sqrt (12 ) * (
225- torch .rand (torch .sum (~ mask ), device = e .device , dtype = e .dtype ) - 0.5
226- )
227296 else :
228- raise ValueError (f"unvoiced_region { unvoiced_region } is not supported." )
297+ generators = {
298+ "gauss" : generate_gauss ,
299+ "m-sequence" : generate_mseq ,
300+ "uniform" : generate_uniform ,
301+ }
302+ if unvoiced_region not in generators :
303+ raise ValueError (f"unvoiced_region { unvoiced_region } is not supported." )
304+ e [~ mask ] = generators [unvoiced_region ](e [~ mask ])
305+
229306 return e
0 commit comments