Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

テキスト書き出しで無視する単語(テキスト)を設定できるようにする #1355

Closed
Hiroshiba opened this issue Jun 29, 2023 · 11 comments
Labels
初心者歓迎タスク 初心者にも優しい簡単めなタスク 機能向上

Comments

@Hiroshiba
Copy link
Member

Hiroshiba commented Jun 29, 2023

内容

[]で囲われたものを読み飛ばす機能が提案されています。

逆に、テキスト書き出し時に無視する気泡もあると色々便利そうです。
例えば()を字幕無視するとすれば、「[DB](ドラゴンボール)」と書けば読みはドラゴンボール、字幕はDBにできます。

Pros 良くなる点

読みと字幕を分けられる

Cons 悪くなる点

記法は何が正解かわからない

実現方法

テキスト書き出しする時にそのテキストを無視するようにすれば実現できそうです。
問題は基本何にするかで、類似ソフトなどで似たような記法がある場合はそれを参考にしたいです。

その他

まず記法を何にするかの議論が必要そう。
調査結果だけでもコメントしていただけると助かります。

@Hiroshiba Hiroshiba added 機能向上 初心者歓迎タスク 初心者にも優しい簡単めなタスク labels Jun 29, 2023
@weweweok
Copy link
Contributor

この問題に取り掛かります。

@Hiroshiba
Copy link
Member Author

Hiroshiba commented Jun 30, 2023

@weweweok おお、ありがとうございます!! 不明な点があれば何でも聞いてください!
(難しそうだった場合も、こちらにコメントを書いていただければ次挑戦される方の助けになると思います!)

@weweweok
Copy link
Contributor

weweweok commented Jun 30, 2023

方針としては、入力された文字列を、読む用と文字として表す用に分けます。
“焼きそば[TDN] (只野)" が与えられた時、

キャラクターが読む場合の文字列は
“焼きそば只野” に変換し、

文字として表す用は
”焼きそばTDN“に変換し、文字列を分けてみたいと思います。

読み飛ばしのルールですが、まずは各audiocellにつき一回までとします。そうすれば、取り敢えず、[]と()の判定は比較的容易に済むと思います。

これまでの場合、入力した文字列は、バッファが許す条件に合っていれば、その入力文字列を、そのままvoicevox enginに渡していたと思われます。しかし、今回のこの方法では、入力テキストとvoicevoxから返された音声データが別になります。
つまり、入力テキスト -> 「voicevox enginによる音声」 が、

    入力テキスト -> 「出力テキスト(仮名)」
           -> 「voicevox enginによる音声」

と分かれると思われます。

現在不明なこと:

・どこで文字列を操作すれば良いか(具体的には、入力される文字列がどのファイルのどのスコープで操作されるべきか)
・Audiocellのテキストがどの箇所に渡されるのか
・最終的に出力する文字テキストは以前はどこから渡されていたのか

※追記
気泡を何にするかについては、とりあえずHiroshibaさんの言うとおりに実装します。

@Hiroshiba
Copy link
Member Author

Hiroshiba commented Jun 30, 2023

方針いい感じに思いました!!

現在不明なこと

読みで無視する方は、テキストを読みに変換する直前で文字列を操作するとかどうでしょう?
たぶんCOMMAND_CHANGE_AUDIO_TEXT actionにあります。

文字として表す方(便宜上「字幕」とします)は、字幕を保存する直前で文字列操作とか・・・?
だとしたら字幕の保存は2箇所、CONNECT_AND_EXPORT_TEXTGENERATE_AND_SAVE_AUDIOのexportTextにあります。


ちなみに僕もどこにあるかはちゃんと把握してなくて、コードを毎回見直して追っかけています。
この機能はコードのどこにあるんだろう?って機能をメニューバーから探して、コード全体をその文字で検索して、関数をまた文字検索したりコードジャンプしたりして追いかけました。
今回の場合だと「テキストを繋げて書き出し」とかで検索しました!
コード追っかけはコードの歩き方.mdが参考になるかもです!

@weweweok
Copy link
Contributor

weweweok commented Jul 1, 2023

以下のプロトタイプにより、特定の条件で機能するようになりました。
条件

  • []()が戦闘の場合正常に機能する
  • []()の前に空白があると正常に機能する

意図しないバグが起きる条件

  • []()の間に文字列がある
  • []()の文字列の前に文字がある
function resolve_brackets(text: string): boolean {
  // []()がこの組で存在するかまったくないか
  const front_square_brackets_cnt: number = (text.match(/[[]/g) || []).length;
  const back_square_brackets_cnt: number = (text.match(/[\]]/g) || []).length;
  const front_parenth: number = (text.match(/[(]/g) || []).length;
  const back_parenth: number = (text.match(/[)]/g) || []).length;

  if (
    front_square_brackets_cnt <= 1 && //[の数
    back_square_brackets_cnt <= 1 && //]の数
    front_square_brackets_cnt === back_square_brackets_cnt && // "["と"]"が一致するとき
    front_parenth <= 1 && //"("の数
    back_parenth <= 1 && //")"の数
    front_parenth === back_parenth //"("と")"が一致するとき
  ) {
    return true;
  } else {
    return false;
  }
}

function div_str(input: string): string | Array<string> | undefined {
  // []に含まれる文字列
  const replacestring: RegExpMatchArray | null = input.match(
    /[[0-9a-zA-Zぁ-ん一-龠]{0,}]/g
  );
  // ()に含まれる文字列
  const parentheses_in: RegExpMatchArray | null = input.match(
    /\([0-9a-zA-Zぁ-ん一-龠]{0,}\)/g
  );
  if (resolve_brackets(input)) {
    if (replacestring === null || parentheses_in === null) return input; // 字幕とエンジンの文字列を書き換える必要がないとき

    let parenthese_str: string =
      replacestring !== null ? replacestring[0].slice(1, -1) : "";
    let square_brackets_str: string =
      parentheses_in !== null ? parentheses_in[0].slice(1, -1) : "";

    //[ が存在するイデックス
    const output_as_text_index_start: number = input.indexOf(parenthese_str);
    const output_as_text_index_end: number = //いらないかもしれない
      output_as_text_index_start + square_brackets_str.length;

    // (が存在するインデックス
    const output_as_voicevox_engine_start_index: number =
      input.indexOf(square_brackets_str);
    const output_as_voicevox_engine_end_index: number =
      output_as_text_index_start + parenthese_str.length;

    //[]()の文字列を抽出("[から")"までの抽出だから間に文字があるとバグが発生する)
    const replaceset = input.substring(
      output_as_text_index_start - 1,
      output_as_voicevox_engine_start_index + parenthese_str.length + 1
    );
    const for_subtitle: string = input.replace(replaceset, parenthese_str);
    const for_read_engin: string = input.replace(
      replaceset,
      square_brackets_str
    );
    return [for_subtitle, for_read_engin];
  } else {
    console.error("次の記号は一回しか使用できません -> [ ] ( )");
  }
}
console.log(div_str("[TDN](ただの)焼きそば ずんだ風味"));
console.log(div_str("[TDN](ただの)焼きそばずんだ風味"));
console.log(div_str("焼きそば ずんだ風味 [TDN](ただの)"));
console.log(div_str("焼きそば ずんだ風味[TDN](ただの)")); //意図しない挙動
console.log(div_str("焼きそば [TDN]ずんだ風味(ただの)"));

//div_str("[ooooohoooooo");動かない
div_str("[ooooohoooooo]");
div_str("ooooohoooooo()");
div_str("ooooohoooooo())"); //

@Hiroshiba
Copy link
Member Author

Hiroshiba commented Jul 1, 2023

プロトタイプありがとうございます!! 参考になります。

JavaScriptはreplaceメソッドを使って正規表現にマッチするものを置き換える操作ができたりします。これを使えばうまくいくかもです。

@weweweok さんのコードをヒントにChatGPT君に聞いてみました。参考になれば!(動くか確かめてませんが・・・!)
https://chat.openai.com/share/9be9135b-bd05-4d52-824f-8bda04a200a9

@weweweok
Copy link
Contributor

weweweok commented Jul 2, 2023

ありがとうございます。
ものすごく参考になりました。
こちらでコードを少し修正したところ角括弧や括弧の数に制限なく分けてくれました。

export const remove_brackets = (text: string): string => {
  let resolvedText = text.replace(/\[.*?\]/g, "");
  resolvedText = resolvedText.replace(/\((.*?)\)/g, "$1");
  return resolvedText;
};

export const remove_parentheses = (text: string): string => {
  let resolvedText = text.replace(/\(.*?\)/g, "");
  resolvedText = resolvedText.replace(/\[(.*?)\]/g, "$1");
  return resolvedText;
};

export default remove_brackets;

test

import remove_brackests from "./reaplacestr";
import { remove_parentheses } from "./reaplacestr";

describe("resolve_brackets", () => {
  it("removes contents inside square brackets and ignores parentheses only", () => {
    expect(remove_brackests("テストA[テストB](テストC)[テストD]")).toBe(
      "テストAテストC"
    );
    expect(remove_brackests("[テスト]")).toBe("");
    expect(remove_brackests("(テスト)")).toBe("テスト");
    expect(remove_brackests("テスト")).toBe("テスト");

    expect(remove_brackests("テストA[testB](テストB)[testC](テストC)")).toBe(
      "テストAテストBテストC"
    );

    expect(remove_brackests("テストA[テストB]テストC[テストD]")).toBe(
      "テストAテストC"
    );

    expect(remove_brackests("テストA(テストB)テストC(テストD)")).toBe(
      "テストAテストBテストCテストD"
    );
  });
});

describe("resolve_parentheses", () => {
  it("remove contents inside parentheses and ignores square brackets only", () => {
    expect(remove_parentheses("テストA[テストB](テストC)[テストD]")).toBe(
      "テストAテストBテストD"
    );
    expect(remove_parentheses("[テスト]")).toBe("テスト");
    expect(remove_parentheses("(テスト)")).toBe("");
    expect(remove_parentheses("テスト")).toBe("テスト");

    expect(remove_parentheses("testA[testB](テストB)[testC](テストC)")).toBe(
      "testAtestBtestC"
    );

    expect(remove_parentheses("テストA[テストB]テストC[テストD]")).toBe(
      "テストAテストBテストCテストD"
    );

    expect(remove_parentheses("テストA(テストB)テストC(テストD)")).toBe(
      "テストAテストC"
    );
  });
});

image

@Hiroshiba
Copy link
Member Author

おー!! 良いですね!!

@weweweok
Copy link
Contributor

weweweok commented Jul 5, 2023

windows環境でテストしたところ
字幕は問題なさそうです。
image
image

しかし、下のリンクのコードにおいて

voicevox/src/store/audio.ts

Lines 1993 to 1997 in 9990a19

async action(
{ state, commit, dispatch },
{ audioKey, text }: { audioKey: AudioKey; text: string }
) {
const engineId = state.audioItems[audioKey].voice.engineId;

(textを別変数で置くとエラーが出るため)まず下記のコードを

text = ((text: string): string => {
              let resolvedText = text.replace(/\[.*?\]/g, "");
              resolvedText = resolvedText.replace(/\((.*?)\)/g, "$1");
              return resolvedText;
            })(text);

下の画像のように加えると
image

下のリンクのようにエディタのテキストも消えてしまいます。
https://github.com/VOICEVOX/voicevox/assets/100256521/299b5dd2-d865-46b8-9f95-ad84d05a002a

下記のtextのみ別変数でテキストを代入することができればよさそうですが、まだよく分かっていません。

const accentPhrases: AccentPhrase[] = await dispatch(
              "FETCH_ACCENT_PHRASES",
              {
                text,
                engineId,
                styleId,
              }
            );

@Hiroshiba
Copy link
Member Author

Hiroshiba commented Jul 5, 2023

なるほどです!!

エンジンに投げるテキストとVOICEVOXのAudioCellを置き換えるテキストを分けると良いと思います!
具体的にはFETCH_ACCENT_PHRASESにはresolvedTextを、COMMAND_CHANGE_AUDIO_TEXTには元のtextを渡す感じでしょうか。

            const resolvedText = resolveText(text); // 変更箇所
            const accentPhrases: AccentPhrase[] = await dispatch(
              "FETCH_ACCENT_PHRASES",
              {
                text: resolvedText, // 変更箇所
                engineId,
                styleId,
              }
            );
            commit("COMMAND_CHANGE_AUDIO_TEXT", {
              audioKey,
              text,
              update: "AccentPhrases",
              accentPhrases,
            });

@Hiroshiba
Copy link
Member Author

にて実装されました!
[]で囲った部分がテキスト書き出しと音声再生のどちらもで省略されます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
初心者歓迎タスク 初心者にも優しい簡単めなタスク 機能向上
Projects
None yet
Development

No branches or pull requests

2 participants