@@ -100,6 +100,7 @@ mod inner {
100
100
use std:: {
101
101
io:: { Cursor , Write as _} ,
102
102
marker:: PhantomData ,
103
+ ops:: Range ,
103
104
sync:: Arc ,
104
105
} ;
105
106
use tracing:: info;
@@ -127,6 +128,30 @@ mod inner {
127
128
use super :: { AccelerationMode , AsyncForOnnxruntime , InitializeOptions , TtsOptions } ;
128
129
129
130
const DEFAULT_SAMPLING_RATE : u32 = 24000 ;
131
+ /// 音が途切れてしまうのを避けるworkaround処理のためのパディング幅(フレーム数)
132
+ const PADDING_FRAME_LENGTH : usize = 38 ; // (0.4秒 * 24000Hz / 256.0).round()
133
+ /// 音声生成の際、音声特徴量の前後に確保すべきマージン幅(フレーム数)
134
+ /// モデルの受容野から計算される
135
+ const MARGIN : usize = 14 ;
136
+ /// 指定した音声区間に対応する特徴量を両端にマージンを追加した上で切り出す
137
+ fn crop_with_margin ( audio : & AudioFeature , range : Range < usize > ) -> ndarray:: ArrayView2 < ' _ , f32 > {
138
+ if range. start > audio. frame_length || range. end > audio. frame_length {
139
+ panic ! (
140
+ "{range:?} is out of range for audio feature of length {frame_length}" ,
141
+ frame_length = audio. frame_length,
142
+ ) ;
143
+ }
144
+ if range. start > range. end {
145
+ panic ! ( "{range:?} is invalid because start > end" , ) ;
146
+ }
147
+ let range = range. start ..range. end + 2 * MARGIN ;
148
+ audio. internal_state . slice ( ndarray:: s![ range, ..] )
149
+ }
150
+ /// 追加した安全マージンを生成音声から取り除く
151
+ fn trim_margin_from_wave ( wave_with_margin : ndarray:: Array1 < f32 > ) -> ndarray:: Array1 < f32 > {
152
+ let len = wave_with_margin. len ( ) ;
153
+ wave_with_margin. slice_move ( ndarray:: s![ MARGIN * 256 ..len - MARGIN * 256 ] )
154
+ }
130
155
131
156
/// 音声の中間表現。
132
157
pub struct AudioFeature {
@@ -138,8 +163,6 @@ mod inner {
138
163
pub frame_length : usize ,
139
164
/// フレームレート。全体の秒数は`frame_length / frame_rate`で表せる。
140
165
pub frame_rate : f64 ,
141
- /// workaroundとして付け足されているパディング長。
142
- padding_frame_length : usize ,
143
166
/// 生成時に利用したクエリ。
144
167
audio_query : AudioQuery ,
145
168
}
@@ -375,28 +398,12 @@ mod inner {
375
398
}
376
399
}
377
400
378
- // 音が途切れてしまうのを避けるworkaround処理が入っている
379
- // NOTE: `render()`内でこのpaddingを取り除くために、padding_frame_lengthにpadding長を保持している。
380
- // TODO: 改善したらここのpadding処理を取り除く
381
- const PADDING_SIZE : f64 = 0.4 ;
382
- let padding_size =
383
- ( ( PADDING_SIZE * DEFAULT_SAMPLING_RATE as f64 ) / 256.0 ) . round ( ) as usize ;
384
- let start_and_end_padding_size = 2 * padding_size;
385
- let length_with_padding = f0. len ( ) + start_and_end_padding_size;
386
- let f0_with_padding = make_f0_with_padding ( & f0, length_with_padding, padding_size) ;
387
- let phoneme_with_padding = make_phoneme_with_padding (
388
- phoneme. as_flattened ( ) ,
389
- OjtPhoneme :: num_phoneme ( ) ,
390
- length_with_padding,
391
- padding_size,
392
- ) ;
393
-
394
401
let spec = self
395
402
. generate_full_intermediate (
396
- f0_with_padding . len ( ) ,
403
+ f0 . len ( ) ,
397
404
OjtPhoneme :: num_phoneme ( ) ,
398
- & f0_with_padding ,
399
- & phoneme_with_padding ,
405
+ & f0 ,
406
+ phoneme . as_flattened ( ) ,
400
407
style_id,
401
408
)
402
409
. await ?;
@@ -405,7 +412,6 @@ mod inner {
405
412
style_id,
406
413
frame_length : f0. len ( ) ,
407
414
frame_rate : ( DEFAULT_SAMPLING_RATE as f64 ) / 256.0 ,
408
- padding_frame_length : padding_size,
409
415
audio_query : audio_query. clone ( ) ,
410
416
} ) ;
411
417
@@ -457,46 +463,6 @@ mod inner {
457
463
pitch,
458
464
}
459
465
}
460
-
461
- fn make_f0_with_padding (
462
- f0_slice : & [ f32 ] ,
463
- length_with_padding : usize ,
464
- padding_size : usize ,
465
- ) -> Vec < f32 > {
466
- // 音が途切れてしまうのを避けるworkaround処理
467
- // 改善したらこの関数を削除する
468
- let mut f0_with_padding = Vec :: with_capacity ( length_with_padding) ;
469
- let padding = vec ! [ 0.0 ; padding_size] ;
470
- f0_with_padding. extend_from_slice ( & padding) ;
471
- f0_with_padding. extend_from_slice ( f0_slice) ;
472
- f0_with_padding. extend_from_slice ( & padding) ;
473
- f0_with_padding
474
- }
475
-
476
- fn make_phoneme_with_padding (
477
- phoneme_slice : & [ f32 ] ,
478
- phoneme_size : usize ,
479
- length_with_padding : usize ,
480
- padding_size : usize ,
481
- ) -> Vec < f32 > {
482
- // 音が途切れてしまうのを避けるworkaround処理
483
- // 改善したらこの関数を削除する
484
- let mut padding_phoneme = vec ! [ 0.0 ; phoneme_size] ;
485
- padding_phoneme[ 0 ] = 1.0 ;
486
- let padding_phoneme_len = padding_phoneme. len ( ) ;
487
- let padding_phonemes: Vec < f32 > = padding_phoneme
488
- . into_iter ( )
489
- . cycle ( )
490
- . take ( padding_phoneme_len * padding_size)
491
- . collect ( ) ;
492
- let mut phoneme_with_padding =
493
- Vec :: with_capacity ( phoneme_size * length_with_padding) ;
494
- phoneme_with_padding. extend_from_slice ( & padding_phonemes) ;
495
- phoneme_with_padding. extend_from_slice ( phoneme_slice) ;
496
- phoneme_with_padding. extend_from_slice ( & padding_phonemes) ;
497
-
498
- phoneme_with_padding
499
- }
500
466
}
501
467
502
468
pub ( super ) async fn render (
@@ -506,41 +472,20 @@ mod inner {
506
472
end : usize ,
507
473
) -> Result < Vec < u8 > > {
508
474
// TODO: 44.1kHzなどの対応
509
- const MARGIN : usize = 14 ; // 使われているHifiGANのreceptive fieldから計算される安全マージン
510
- use std:: cmp:: min;
511
- // 実態(workaround paddingを含まない)上での区間
512
- let clipped_start = min ( start, audio. frame_length ) ;
513
- let clipped_end = min ( end, audio. frame_length ) ;
514
- // 指定領域が空の区間だった場合、ONNXRuntimeに渡す前に早期リターン
515
- if ( clipped_start..clipped_end) . is_empty ( ) {
475
+ if ( start..end) . is_empty ( ) {
476
+ // 指定区間が空のときは早期リターン
516
477
return Ok ( vec ! [ ] ) ;
517
478
}
518
- // マージンがデータからはみ出さないことを保証
519
- // cf. https://github.com/VOICEVOX/voicevox_core/pull/854#discussion_r1803691291
520
- if MARGIN > audio. padding_frame_length + clipped_start
521
- || MARGIN > audio. padding_frame_length + ( audio. frame_length - clipped_end)
522
- {
523
- unreachable ! ( "Validation error: Too short padding for input, please report this issue on GitHub." ) ;
524
- }
525
- let left_margin = MARGIN ;
526
- let right_margin = MARGIN ;
527
- // 安全マージンを追加したデータ上での区間
528
- let slice_start = audio. padding_frame_length + clipped_start - left_margin;
529
- let slice_end = audio. padding_frame_length + clipped_end + right_margin;
530
- let segment = audio
531
- . internal_state
532
- . slice ( ndarray:: s![ slice_start..slice_end, ..] ) ;
479
+ let spec_segment = crop_with_margin ( audio, start..end) ;
533
480
let wave_with_margin = self
534
- . render_audio_segment ( segment . into_owned ( ) , audio. style_id )
481
+ . render_audio_segment ( spec_segment . to_owned ( ) , audio. style_id )
535
482
. await ?;
536
- // 変換前に追加した安全マージンを生成音声から取り除く
537
- let wave = wave_with_margin
538
- . slice ( ndarray:: s![
539
- left_margin * 256 ..wave_with_margin. len( ) - right_margin * 256
540
- ] )
541
- . into_owned ( )
542
- . into_raw_vec ( ) ;
543
- return Ok ( to_s16le_pcm ( & wave, & audio. audio_query ) ) ;
483
+ let wave = trim_margin_from_wave ( wave_with_margin) ;
484
+ return Ok ( to_s16le_pcm (
485
+ wave. as_slice ( )
486
+ . expect ( "`trim_margin_from_wave` should just trim an array" ) ,
487
+ & audio. audio_query ,
488
+ ) ) ;
544
489
545
490
fn to_s16le_pcm (
546
491
wave : & [ f32 ] ,
@@ -999,6 +944,10 @@ mod inner {
999
944
Ok ( output. into_raw_vec ( ) )
1000
945
}
1001
946
947
+ /// モデル`generate_full_intermediate`の実行と、その前後の処理を行う。
948
+ ///
949
+ /// 無音パディングを付加して音声特徴量を計算し、マージン込みの音声特徴量を返す。
950
+ ///
1002
951
/// CPU-boundな操作なので、非同期ランタイム上では直接実行されるべきではない。
1003
952
fn generate_full_intermediate (
1004
953
& self ,
@@ -1010,17 +959,69 @@ mod inner {
1010
959
) -> Result < ndarray:: Array2 < f32 > > {
1011
960
let ( model_id, inner_voice_id) = self . ids_for :: < TalkDomain > ( style_id) ?;
1012
961
1013
- let GenerateFullIntermediateOutput { spec } = self . run_session (
962
+ // 音が途切れてしまうのを避けるworkaround処理が入っている
963
+ // TODO: 改善したらここのpadding処理を取り除く
964
+ let start_and_end_padding_size = 2 * PADDING_FRAME_LENGTH ;
965
+ let length_with_padding = f0. len ( ) + start_and_end_padding_size;
966
+ let f0_with_padding = make_f0_with_padding ( f0, PADDING_FRAME_LENGTH ) ;
967
+ let phoneme_with_padding = make_phoneme_with_padding (
968
+ phoneme_vector. into_shape ( [ length, phoneme_size] ) . unwrap ( ) ,
969
+ PADDING_FRAME_LENGTH ,
970
+ ) ;
971
+
972
+ let GenerateFullIntermediateOutput {
973
+ spec : spec_with_padding,
974
+ } = self . run_session (
1014
975
model_id,
1015
976
GenerateFullIntermediateInput {
1016
- f0 : f0. into_shape ( [ length, 1 ] ) . unwrap ( ) ,
1017
- phoneme : phoneme_vector. into_shape ( [ length, phoneme_size] ) . unwrap ( ) ,
977
+ f0 : f0_with_padding
978
+ . into_shape ( [ length_with_padding, 1 ] )
979
+ . unwrap ( ) ,
980
+ phoneme : phoneme_with_padding,
1018
981
speaker_id : ndarray:: arr1 ( & [ inner_voice_id. raw_id ( ) . into ( ) ] ) ,
1019
982
} ,
1020
983
) ?;
1021
- Ok ( spec)
984
+
985
+ // マージンがデータからはみ出さないことを保証
986
+ // cf. https://github.com/VOICEVOX/voicevox_core/pull/854#discussion_r1803691291
987
+ if MARGIN > PADDING_FRAME_LENGTH {
988
+ unreachable ! ( "Validation error: Too short padding for input, please report this issue on GitHub." ) ;
989
+ }
990
+ // マージン分を両端に残して音声特徴量を返す
991
+ return Ok ( spec_with_padding
992
+ . slice ( ndarray:: s![
993
+ PADDING_FRAME_LENGTH - MARGIN
994
+ ..spec_with_padding. nrows( ) - PADDING_FRAME_LENGTH + MARGIN ,
995
+ ..
996
+ ] )
997
+ . to_owned ( ) ) ;
998
+
999
+ fn make_f0_with_padding (
1000
+ f0_slice : ndarray:: Array1 < f32 > ,
1001
+ padding_size : usize ,
1002
+ ) -> ndarray:: Array1 < f32 > {
1003
+ // 音が途切れてしまうのを避けるworkaround処理
1004
+ // 改善したらこの関数を削除する
1005
+ let padding = ndarray:: Array1 :: < f32 > :: zeros ( padding_size) ;
1006
+ ndarray:: concatenate![ ndarray:: Axis ( 0 ) , padding, f0_slice, padding]
1007
+ }
1008
+
1009
+ fn make_phoneme_with_padding (
1010
+ phoneme_slice : ndarray:: Array2 < f32 > ,
1011
+ padding_size : usize ,
1012
+ ) -> ndarray:: Array2 < f32 > {
1013
+ // 音が途切れてしまうのを避けるworkaround処理
1014
+ // 改善したらこの関数を削除する
1015
+ let mut padding =
1016
+ ndarray:: Array2 :: < f32 > :: zeros ( ( padding_size, phoneme_slice. ncols ( ) ) ) ;
1017
+ padding
1018
+ . slice_mut ( ndarray:: s![ .., 0 ] )
1019
+ . assign ( & ndarray:: arr0 ( 1.0 ) ) ;
1020
+ ndarray:: concatenate![ ndarray:: Axis ( 0 ) , padding, phoneme_slice, padding]
1021
+ }
1022
1022
}
1023
1023
1024
+ /// 与えられた音声特徴量で音声生成。
1024
1025
/// CPU/GPU-boundな操作なので、非同期ランタイム上では直接実行されるべきではない。
1025
1026
fn render_audio_segment (
1026
1027
& self ,
@@ -1049,8 +1050,9 @@ mod inner {
1049
1050
phoneme_vector,
1050
1051
style_id,
1051
1052
) ?;
1052
- let output = self . render_audio_segment ( intermediate, style_id) ?;
1053
- Ok ( output. into_raw_vec ( ) )
1053
+ let output_with_margin = self . render_audio_segment ( intermediate, style_id) ?;
1054
+ let output = trim_margin_from_wave ( output_with_margin) ;
1055
+ Ok ( output. to_vec ( ) )
1054
1056
}
1055
1057
}
1056
1058
0 commit comments