Feat: pitch intonation changer

This commit is contained in:
litagin02
2024-02-15 22:55:08 +09:00
parent b693d4aa28
commit c474bbe5af
3 changed files with 66 additions and 6 deletions

20
app.py
View File

@@ -57,6 +57,8 @@ def tts_fn(
kata_tone_json_str,
use_tone,
speaker,
pitch_scale,
intonation_scale,
):
model_holder.load_model_gr(model_name, model_path)
@@ -111,6 +113,8 @@ def tts_fn(
style_weight=style_weight,
given_tone=tone,
sid=speaker_id,
pitch_scale=pitch_scale,
intonation_scale=intonation_scale,
)
except InvalidToneError as e:
logger.error(f"Tone error: {e}")
@@ -306,6 +310,20 @@ if __name__ == "__main__":
refresh_button = gr.Button("更新", scale=1, visible=True)
load_button = gr.Button("ロード", scale=1, variant="primary")
text_input = gr.TextArea(label="テキスト", value=initial_text)
pitch_scale = gr.Slider(
minimum=0,
maximum=2,
value=1,
step=0.1,
label="音程",
)
intonation_scale = gr.Slider(
minimum=0,
maximum=2,
value=1,
step=0.1,
label="抑揚",
)
line_split = gr.Checkbox(
label="改行で分けて生成(分けたほうが感情が乗ります)",
@@ -441,6 +459,8 @@ if __name__ == "__main__":
tone,
use_tone,
speaker,
pitch_scale,
intonation_scale,
],
outputs=[text_output, audio_output, tone],
)

View File

@@ -1,17 +1,18 @@
import numpy as np
import gradio as gr
import torch
import os
import warnings
from gradio.processing_utils import convert_to_16_bit_wav
from typing import Dict, List, Optional, Union
import gradio as gr
import numpy as np
import pyworld
import torch
from gradio.processing_utils import convert_to_16_bit_wav
import utils
from infer import get_net_g, infer
from models import SynthesizerTrn
from models_jp_extra import SynthesizerTrn as SynthesizerTrnJPExtra
from .log import logger
from .constants import (
DEFAULT_ASSIST_TEXT_WEIGHT,
DEFAULT_LENGTH,
@@ -23,6 +24,36 @@ from .constants import (
DEFAULT_STYLE,
DEFAULT_STYLE_WEIGHT,
)
from .log import logger
from torchfcpe import spawn_bundled_infer_model
import librosa
def adjust_voice(fs, wave, pitch_scale, intonation_scale):
if pitch_scale == 1.0 and intonation_scale == 1.0:
return fs, wave
# pyworldでf0を加工して合成
# pyworldよりもよいのがあるかもしれないが……
wave = wave.astype(np.double)
logger.debug(f"wave: shape={wave.shape}, max={max(wave)}, min={min(wave)}")
f0, t = pyworld.harvest(wave, fs)
# 質が高そうだしとりあえずharvestにしておく
sp = pyworld.cheaptrick(wave, f0, t, fs)
ap = pyworld.d4c(wave, f0, t, fs)
non_zero_f0 = [f for f in f0 if f != 0]
f0_mean = sum(non_zero_f0) / len(non_zero_f0)
logger.debug(f"f0: shape={f0.shape}, mean={f0_mean}, max={max(f0)}")
for i, f in enumerate(f0):
if f == 0:
continue
f0[i] = pitch_scale * f0_mean + intonation_scale * (f - f0_mean)
wave = pyworld.synthesize(f0, sp, ap, fs)
return fs, wave
class Model:
@@ -97,6 +128,8 @@ class Model:
style: str = DEFAULT_STYLE,
style_weight: float = DEFAULT_STYLE_WEIGHT,
given_tone: Optional[list[int]] = None,
pitch_scale: float = 1.0,
intonation_scale: float = 1.0,
) -> tuple[int, np.ndarray]:
logger.info(f"Start generating audio data from text:\n{text}")
if language != "JP" and self.hps.version.endswith("JP-Extra"):
@@ -165,7 +198,13 @@ class Model:
warnings.simplefilter("ignore")
audio = convert_to_16_bit_wav(audio)
logger.info("Audio data generated successfully")
return (self.hps.data.sampling_rate, audio)
fs, audio = adjust_voice(
fs=self.hps.data.sampling_rate,
wave=audio,
pitch_scale=pitch_scale,
intonation_scale=intonation_scale,
)
return (fs, audio)
class ModelHolder:

View File

@@ -19,6 +19,7 @@ pyannote.audio>=3.1.0
pyloudnorm
pyopenjtalk-prebuilt
pypinyin
pyworld
PyYAML
requests
safetensors