Skip to content

Commit

Permalink
ピッチ形状を記憶して呼び出すプラグインを追加
Browse files Browse the repository at this point in the history
  • Loading branch information
oatsu-gh committed Feb 12, 2023
1 parent c742ed0 commit 36a95d0
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 6 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- join_cl(促音を前のノートに結合する)
- lyric_bracketed(歌詞を括弧でくくってファイル出力する)
- makijita_maker(巻き舌っぽくする)
- memorize_and_recall_pitch_pattern(ピッチパターンを登録する/呼び出す)
- open_enuconfig(enuconfig を開く)
- open_with_notepad(メモ帳で開く)
- preprocess_for_enunu(ENUNU 用に前処理する)
Expand Down Expand Up @@ -98,3 +99,8 @@

- ピッチ形状を一括変更するプラグイン を追加
- 同梱の Python を 3.9.5 → 3.9.12 に更新

### v0.10.0 (2023-02-12)

- ピッチ形状を記憶したり呼び出したりするプラグインを追加
- 同梱の Python を 3.9.12 → 3.11.2 に更新
4 changes: 2 additions & 2 deletions install.bat
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@REM Copyright (c) 2021 oatsu
@REM Copyright (c) 2021-2023 oatsu
@REM utau_plugins installer launcher

@python-3.9.12-embed-amd64\python.exe .\install.py
@python-3.11.2-embed-amd64\python.exe .\install.py

@PAUSE
12 changes: 8 additions & 4 deletions install.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ def select_plugin(start='./'):
available_plugins_names = \
[read_plugin_txt_as_dict(path)['name'] for path in available_plugins]
# ユーザーに指定させるために表示する文字列を作成する。
message = '\n'.join(f' {i}: {name}' for i, name in enumerate(available_plugins_names))
message = '\n'.join(f' {i}: {name}' for i,
name in enumerate(available_plugins_names))
# ユーザーにプラグインを指定させる。
print('インストールしたいプラグインを番号で指定してください。')
print(message)
Expand Down Expand Up @@ -165,7 +166,8 @@ def pip_install_requirements(plugin_installed_dir):
)
else:
subprocess.run(
[python_exe, '-m', 'pip', 'install', '--no-warn-script-location', 'utaupy'],
[python_exe, '-m', 'pip', 'install',
'--no-warn-script-location', 'utaupy'],
check=True
)
# 作業フォルダをもとに戻す
Expand Down Expand Up @@ -204,12 +206,14 @@ def main():
# オフライン版とオンライン版のどちらとしてダウンロードされたか調べる。
release_name = basename(dirname(__file__))
offline_mode = \
any(keyword in release_name for keyword in ('offline', 'off-line', 'オフライン'))
any(keyword in release_name for keyword in (
'offline', 'off-line', 'オフライン'))
# インストールしたいプラグインを指定する。
input_dir, plugin_name = select_plugin()
# プラグインをインストールする。
utau_appdata_roaming_plugins_dir = join(utau_appdata_root(), 'plugins')
plugin_installed_dir = install_plugin(input_dir, utau_appdata_roaming_plugins_dir)
plugin_installed_dir = install_plugin(
input_dir, utau_appdata_roaming_plugins_dir)

# オンライン実行の場合は、Pythonやパッケージをオンデマンドインストールする。
if not offline_mode:
Expand Down
2 changes: 2 additions & 0 deletions memorize_and_recall_pitch_pattern/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.json
*.ust
41 changes: 41 additions & 0 deletions memorize_and_recall_pitch_pattern/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# memorize_and_recall_pitch

ピッチパターンを登録したり呼び出したりするプラグイン

## 目標

よく使うピッチパターンを登録して、AutoPitchWriter のように簡単に呼び出せるようにする。

## 要件

### 登録モード

- 選択範囲のピッチパターンを記録する。その際に記録するのは次の情報。
- 直前のノートとの音高差
- 現在の歌詞
- 直前のノート長
- 現在のノート長
- ピッチ情報


### 呼び出しモード

- 選択範囲に対してピッチパターンを適用する。登録時に使用した情報とすべて一致するものを適用する。

### バックアップ機能

- 誤って上書きしたときのために、実行直前の状態のピッチ学習データをバックアップする。

## 仕様

### データ登録方法

- CSVファイル → 不採用
- メリット:Excelでデータ確認や編集ができる。
- デメリット:具体的なピッチ形状をイメージしづらい。重複に弱い。→データ確認専用のUSTファイルを生成する。
- JSONファイル → 採用
- メリット:呼び出しモードの優先順位が固定であれば探索が非常に容易
- デメリット:プラグインの仕様変更に対して脆弱
- USTファイル → 不採用
- メリット:UTAUで編集できれば視覚的にデータを確認できる。
- デメリット:直前のノートと併せて保存しなければならない。UTAUで開いて上書きしてしまうとバグる可能性がある(データ重複など)。分類が難しそう。
169 changes: 169 additions & 0 deletions memorize_and_recall_pitch_pattern/memorize_and_recall_pitch_pattern.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#!/usr/bin/env python3
# Copyright (c) 2023 oatsu
"""
ピッチパターンを記憶したり呼び出したりする
"""

import json
from os import remove
from os.path import dirname, join
from shutil import copy2

import utaupy

JSON_FILE = join(dirname(__file__), 'memory.json')
UST_FILE = join(dirname(__file__), 'memory_view.ust')
MAX_NOTE_LENGTH = 3840
MIN_NOTE_LENGTH = 10


def json2ust(path_json, path_ust):
"""ピッチを記録しているjsonファイルの内容をUTAUで確認できるようにUSTファイルに変換する
"""
with open(path_json, 'r', encoding='utf-8') as f:
d = json.load(f)
ust = utaupy.ust.Ust()
ust.setting['Mode2'] = True
ust.setting['Charset'] = 'UTF-8'
for key, v in d.items():
previous_length, length, delta_notenum, lyric = \
key.split('_', maxsplit=3)
# 音程変化を確認するための音符を追加
note = utaupy.ust.Note()
note.lyric = 'dummy'
note.length = previous_length
note.label = previous_length
note.notenum = 60
ust.notes.append(note)
# 本体
note = utaupy.ust.Note()
note.length = length
note.label = length
note.lyric = lyric
note.pbw = v['PBW']
note.pby = v['PBY']
note.pbm = v['PBM']
note.pbs = v['PBS']
note['Modulation'] = 0
note.notenum = 60 + int(delta_notenum)
ust.notes.append(note)
# 見た目を区切るための休符を追加
note = utaupy.ust.Note()
note.lyric = 'R'
note.length = 1920 - (int(previous_length) + int(length)) % 1920
note.notenum = 60
ust.notes.append(note)
ust.write(path_ust, encoding='utf-8')


def generate_key(note, previous_note, max_length, min_length):
"""ピッチ登録・呼び出し用のキーを生成する
"""
# 直前の音高と現在の音高の差
delta_notenum = note.notenum - previous_note.notenum
# 直前のノート長を丸める
previous_note_length = min(
max_length,
max(round(previous_note.length / min_length) * min_length, min_length))
# 現在のノート長を丸める
note_length = min(
max_length,
max(round(previous_note.length / min_length) * min_length, min_length))
return f'{previous_note_length}_{note_length}_{delta_notenum}_{note.lyric}'


def memorize(plugin: utaupy.utauplugin.UtauPlugin):
"""指定されたノートのピッチパターンを辞書として返す
辞書のキーは
直前のノート長_現在のノート長_音程変化_直前の歌詞_現在の歌詞
のようにする(暫定)
"""
# 既存のファイルを読み取る
try:
with open(JSON_FILE, 'r', encoding='utf-8') as f:
d = json.load(f)
copy2(JSON_FILE, JSON_FILE.replace('.json', '_backup.json'))
except FileNotFoundError:
d = {}
# 各ノートのピッチ情報を読み取って記録していく
if plugin.previous_note is not None:
notes = [plugin.previous_note] + plugin.notes
else:
notes = plugin.notes
# UST内からピッチパターンを読み取る
d_temp = {}
for note, previous_note in zip(notes[1:], notes[:-1]):
# 登録に必要な情報が一つでもなければスキップ
if any(k not in note for k in ['PBS', 'PBW', 'PBY', 'PBM', 'Lyric', 'Length']):
continue
# ピッチパターンを分類するときのキー
key = generate_key(
note, previous_note, max_length=MAX_NOTE_LENGTH, min_length=MIN_NOTE_LENGTH)
# ピッチパターンを記録させる内容
d_temp[key] = {
'PBS': note.pbs,
'PBW': note.pbw,
'PBY': note.pby,
'PBM': note.pbm
}
d.update(d_temp)
with open(JSON_FILE, 'w', encoding='utf-8') as f:
json.dump(d, f, sort_keys=True, indent=4, ensure_ascii=False)
json2ust(JSON_FILE, UST_FILE)


def recall(plugin: utaupy.utauplugin.UtauPlugin):
"""指定されたノートのピッチパターンを辞書として返す
"""
# 既存のファイルを読み取る
try:
with open(JSON_FILE, 'r', encoding='utf-8') as f:
d = json.load(f)
except FileNotFoundError:
d = {}
# 各ノートのピッチ情報を読み取って記録していく
if plugin.previous_note is not None:
notes = [plugin.previous_note] + plugin.notes
else:
notes = plugin.notes
# UST内からピッチパターンを読み取る
for note, previous_note in zip(notes[1:], notes[:-1]):
# ピッチ情報を検索する文字列を生成
key = generate_key(
note, previous_note, max_length=MAX_NOTE_LENGTH, min_length=MIN_NOTE_LENGTH)
# 一致するピッチ情報を探して登録
if key in d:
note.pbs = d[key]['PBS']
note.pbw = d[key]['PBW']
note.pby = d[key]['PBY']
note.pbm = d[key]['PBM']


def clean_data():
"""ピッチデータを記録しているファイルを削除する
"""
copy2(JSON_FILE, JSON_FILE.replace('.json', '_backup.json'))
remove(JSON_FILE)
remove(UST_FILE)


def main():
"""機能の分岐
"""
s = '動作モードを選択して下さい\n'\
'1: Memorize mode / ピッチ登録モード\n'\
'2: Recall mode / ピッチ呼び出しモード\n'\
'99: Clean mode / プラグインデータ初期化\n'\
'>>> '
mode = input(s).strip()
if mode in ['1', '1']:
utaupy.utauplugin.run(memorize)
elif mode in ['2', '2']:
utaupy.utauplugin.run(recall)
elif mode in ['99', '99']:
if input('Really? / 本当に削除していいですか?(yes/no)\n>>> ') == 'yes':
clean_data()


if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions memorize_and_recall_pitch_pattern/plugin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name=�s�b�`�`����L������^�Ăяo��
execute=.\memorize_and_recall_pitch_pattern.bat
ustversion=1.20

0 comments on commit 36a95d0

Please sign in to comment.