Last active
January 12, 2024 13:51
-
-
Save trueroad/216cb39902bccd776d8e9da670460fa3 to your computer and use it in GitHub Desktop.
Frequency component vectors and SMF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
""" | |
Frequency component vectors and SMF. | |
https://gist.github.com/trueroad/216cb39902bccd776d8e9da670460fa3 | |
Copyright (C) 2022-2024 Masamichi Hosoda. | |
All rights reserved. | |
Redistribution and use in source and binary forms, with or without | |
modification, are permitted provided that the following conditions | |
are met: | |
* Redistributions of source code must retain the above copyright notice, | |
this list of conditions and the following disclaimer. | |
* Redistributions in binary form must reproduce the above copyright notice, | |
this list of conditions and the following disclaimer in the documentation | |
and/or other materials provided with the distribution. | |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
ARE DISCLAIMED. | |
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
SUCH DAMAGE. | |
""" | |
import math | |
import os | |
import sys | |
from typing import Any, Callable, Dict, Final, Optional, Tuple, Union | |
import mido # type: ignore[import-untyped] | |
import numpy as np | |
import numpy.typing as npt | |
# https://gist.github.com/trueroad/a5fee9ee0e447b78761dfdb336bbed7e | |
import fcvs_dtw | |
# https://gist.github.com/trueroad/52b7c4c98eec5fdf0ff3f62d64ec17bd | |
import smf_parse | |
VERSION: Final[str] = "20240112.01" | |
class fcvs_smf(fcvs_dtw.fcvs_dtw): | |
"""周波数成分ベクトル (NPZ) と SMF クラス.""" | |
def __init__(self, | |
weight_timbre: Union[float, np.float64] = 1.0, | |
exponent_timbre: Union[float, np.float64] = 1.0, | |
weight_power_diff: Union[float, np.float64] = 0.25, | |
exponent_power_diff: Union[float, np.float64] = 1.0, | |
exponent_excl_weak_power: Union[float, np.float64] = 0.2, | |
dist: Optional[Callable[[npt.NDArray[np.float64], | |
npt.NDArray[np.float64]], | |
np.float64]] = None, | |
b_strict_dtw: bool = False, | |
b_partial_dtw: bool = False, | |
radius: int = 1, | |
radius_x: int = 4, | |
verbose: int = 0, | |
sampling_rate: float = 44100.0, | |
sliding_size: int = 2205, | |
offset_index: int = 10, | |
b_auto_radius: bool = False, | |
auto_radius_ratio: float = 4.0) -> None: | |
""" | |
__init__. | |
Args: | |
weight_timbre (float): Weight for timbre cost | |
exponent_timbre (float): Exponent for timbre cost | |
weight_power_diff (float): Weight for power difference cost | |
exponent_power_diff (float): Exponent for power difference cost | |
exponent_excl_weak_power (float): Exponent for excluding weak power | |
dist (Optional[Callable[[npt.NDArray[np.float], | |
npt.NDArray[np.float]], | |
np.float64]]): Cost function | |
b_strict_dtw (bool): Whether or not to calculate strict DTW | |
b_partial_dtw (bool): Partial matching DTW | |
This requires extended fastdtw. | |
radius (int): fastdtw radius | |
radius_x (int): fastdtw radius_x | |
This requires extended fastdtw. | |
verbose (int): Verbose level | |
sampling_rate (float): サンプリング周波数 (Hz) | |
sliding_size (int): STFT スライディングサイズ・周期(サンプル数) | |
offset_index (int): モデル SMF とモデル NPZ のオフセット | |
SMF の MBT 1:1:0 に相当する NPZ のインデックス | |
b_auto_radius (bool): radius, radius_x を自動設定する | |
auto_radius_ratio (float): radius 自動設定時の倍率(正の数) | |
1.0 は最小ノート ON 間隔と同程度の目の細かさにする | |
小さいと目が細かくて正確になるが遅くなる | |
大きいと目が粗くて速くなるが誤差が大きくなる | |
""" | |
super().__init__( | |
weight_timbre=weight_timbre, | |
exponent_timbre=exponent_timbre, | |
weight_power_diff=weight_power_diff, | |
exponent_power_diff=exponent_power_diff, | |
exponent_excl_weak_power=exponent_excl_weak_power, | |
dist=dist, | |
b_strict_dtw=b_strict_dtw, | |
b_partial_dtw=b_partial_dtw, | |
radius=radius, | |
radius_x=radius_x, | |
verbose=verbose) | |
# サンプリング周波数 (Hz) | |
self.sampling_rate: float = sampling_rate | |
# STFT スライディングサイズ・周期(サンプル数) | |
self.sliding_size: int = sliding_size | |
# モデル SMF とモデル NPZ のオフセット | |
# SMF の MBT 1:1:0 に相当する NPZ のインデックス | |
self.offset_index: int = offset_index | |
# radius, radius_x 自動設定するか否か | |
self.b_auto_radius: bool = b_auto_radius | |
# radius 自動設定時の倍率 | |
self.auto_radius_ratio: float = auto_radius_ratio | |
# 評価対象・モデルの全体での DTW 距離 | |
self.distance: float = -1.0 | |
# 評価対象・モデルのモデル側インデックス毎の DTW 距離 | |
self.cost_in_model: npt.NDArray[np.float64] | |
# モデル SMF 格納用 | |
self.smf: smf_parse.smf_notes = smf_parse.smf_notes(verbose=verbose) | |
# 評価対象・モデルの全体時間割合 | |
self.time_ratio: float = -1.0 | |
def load_smf(self, filename: Union[str, bytes, os.PathLike[Any]]) -> bool: | |
""" | |
モデル SMF をファイルからロードする. | |
Args: | |
filename (PathLike): ロードする SMF のパス名 | |
Returns: | |
bool: 成功なら True そうでなければ False | |
""" | |
return self.smf.load(filename) | |
def read_data_smf(self, mid: mido.MidiFile) -> bool: | |
""" | |
モデル SMF をデータで読み込む. | |
Args: | |
mid (mido.MidiFile): SMF データの mido.MidiFile オブジェクト | |
Returns: | |
bool: 成功なら True そうでなければ False | |
""" | |
return self.smf.read_data(mid) | |
def calc_auto_radius(self) -> None: | |
"""自動的に radius, radius_x を設定する.""" | |
if not self.b_auto_radius: | |
return | |
# ノート ON 間隔の要素数(モデル側) | |
elements: int | |
# 最小要素数 | |
elements_min: Optional[int] = None | |
# 最初の区間開始位置(最初のノート ON) | |
before_mbt: smf_parse.mbt_container = self.smf.notes[0].note_on.mbt | |
# 音符 | |
n: smf_parse.note_container | |
for n in self.smf.notes: | |
if n.note_on.mbt == before_mbt: | |
# 同時発音はスキップ | |
continue | |
# ノート ON 間隔の要素数を計算 | |
begin_index: Optional[int] = self.calc_begin_index(before_mbt) | |
end_index: Optional[int] = self.calc_end_index(n.note_on.mbt) | |
if begin_index is None or end_index is None: | |
continue | |
elements = end_index - begin_index | |
# 最小要素数を更新 | |
if elements_min is None or elements_min > elements: | |
elements_min = elements | |
if elements_min is None: | |
return | |
self.radius = round(len(self.freq_comp_vectors_model) / | |
(elements_min * self.auto_radius_ratio)) | |
if self.b_partial_dtw: | |
self.radius_x = round(len(self.freq_comp_vectors_foreval) / | |
(elements_min * self.auto_radius_ratio)) | |
else: | |
self.radius_x = self.radius | |
def calc_base(self) -> bool: | |
""" | |
基本的な計算を実施. | |
Returns: | |
bool: 成功なら True そうでなければ False | |
""" | |
self.calc_auto_radius() | |
# DTW 計算 | |
self.distance, _ = self.calc_dtw() | |
# パス毎の DTW 距離を計算 | |
self.calc_cost_in_path() | |
# モデル側インデックス毎の DTW 距離を計算 | |
self.cost_in_model = self.calc_cost_in_model() | |
# 評価対象・モデルの全体時間割合を計算 | |
self.time_ratio = self.calc_time_ratio_range_mbt( | |
self.smf.notes[0].note_on.mbt, | |
self.smf.notes[-1].note_on.mbt) | |
return True | |
def mbt_to_time(self, mbt: smf_parse.mbt_container) -> Optional[float]: | |
""" | |
モデル SMF の MBT からモデル NPZ の絶対時刻を求める. | |
MBT 1:1:0 およびノート ON/OFF イベントが存在する MBT でのみ有効 | |
オフセットは加算しない | |
(MBT 1:1:0 が NPZ の インデックス 0 でない場合は別途要加算) | |
Args: | |
mbt (smf_parse.mbt_container): MBT | |
Returns: | |
Optional[float]: 絶対時刻 (s) | |
None はエラーを意味する | |
""" | |
if mbt.measure == 0 and mbt.beat == 0 and mbt.tick == 0: | |
return 0.0 | |
n: smf_parse.note_event_time_container | |
for n in self.smf.note_on_off: | |
if n.mbt == mbt: | |
return n.abs_time | |
return None | |
def time_to_index(self, time: float) -> int: | |
""" | |
時刻から周波数成分ベクトル NPZ のインデックスを求める. | |
オフセットは考慮しない(加算しない) | |
Args: | |
time (float): 時刻 (s) | |
Returns: | |
int: 周波数成分ベクトル NPZ のインデックス | |
""" | |
return int(time * (self.sampling_rate / self.sliding_size)) | |
def index_to_time(self, index: int) -> float: | |
""" | |
周波数成分ベクトル NPZ のインデックスから時刻を求める. | |
オフセットは考慮しない(減算しない) | |
Args: | |
index (int): 周波数成分ベクトル NPZ のインデックス | |
Returns: | |
float: 時刻 (s) | |
""" | |
return index * (self.sliding_size / self.sampling_rate) | |
def calc_begin_index(self, | |
begin: Optional[smf_parse.mbt_container] | |
) -> Optional[int]: | |
""" | |
モデル側区間開始 MBT からインデックスを計算する. | |
Args: | |
begin (smf_parse.mbt_container): モデル SMF の MBT | |
基本的にはオフセットを考慮して加算するが | |
None の場合は加算せず周波数成分ベクトル NPZ の冒頭を意味する | |
Returns: | |
int: 周波数成分ベクトル NPZ のインデックス | |
""" | |
if begin is None: | |
return 0 | |
else: | |
begin_time: Optional[float] = self.mbt_to_time(begin) | |
if begin_time is None: | |
return None | |
return self.time_to_index(begin_time) + self.offset_index | |
def calc_end_index(self, | |
end: Optional[smf_parse.mbt_container] | |
) -> Optional[int]: | |
""" | |
モデル側区間終了 MBT からインデックスを計算する. | |
Args: | |
end (smf_parse.mbt_container): モデル SMF の MBT | |
オフセットを考慮して加算するが | |
None の場合は周波数成分ベクトル NPZ の最後までを意味する | |
Returns: | |
int: 周波数成分ベクトル NPZ のインデックス | |
""" | |
if end is None: | |
return len(self.cost_in_model) | |
else: | |
end_time: Optional[float] = self.mbt_to_time(end) | |
if end_time is None: | |
return None | |
return self.time_to_index(end_time) + self.offset_index | |
def calc_distance_range_index(self, | |
begin: int, end: int) -> float: | |
""" | |
区間 DTW 距離を計算する. | |
モデル側の区間 [begin, end) の DTW 距離を計算する | |
Args: | |
begin (int): モデル側の周波数成分ベクトル NPZ のインデックス | |
end (int): モデル側の周波数成分ベクトル NPZ のインデックス | |
Returns: | |
float: 区間 DTW 距離 | |
""" | |
return float(self.cost_in_model[begin:end].sum()) | |
def calc_distance_range_mbt(self, | |
begin: Optional[smf_parse.mbt_container], | |
end: Optional[smf_parse.mbt_container] | |
) -> Tuple[float, int]: | |
""" | |
区間 DTW 距離を計算する. | |
モデル側の区間 [begin, end) の DTW 距離を計算する | |
Args: | |
begin (smf_parse.mbt_container): モデル SMF の MBT | |
基本的にはオフセットを考慮して加算するが | |
None の場合は加算せず周波数成分ベクトル NPZ の冒頭を意味する | |
end (smf_parse.mbt_container): モデル SMF の MBT | |
オフセットを考慮して加算するが | |
None の場合は周波数成分ベクトル NPZ の最後までを意味する | |
Returns: | |
Tuple: | |
float: 区間 DTW 距離 | |
int: begin から end までの要素数 | |
""" | |
begin_index: Optional[int] = self.calc_begin_index(begin) | |
if begin_index is None: | |
return (math.nan, 0) | |
end_index: Optional[int] = self.calc_end_index(end) | |
if end_index is None: | |
return (math.nan, 0) | |
return (self.calc_distance_range_index(begin_index, | |
end_index), | |
end_index - begin_index) | |
def model_to_foreval(self, model_index: int) -> Optional[int]: | |
""" | |
モデル側インデックスから評価対象側インデックスを求める. | |
Args: | |
model_index (int): モデル側の周波数成分ベクトル NPZ のインデックス | |
Returns: | |
Optional[int]: 評価対象側の周波数成分ベクトル NPZ のインデックス | |
None はエラーを意味する | |
""" | |
i_foreval: int | |
i_model: int | |
for i_foreval, i_model in self.path: | |
if i_model == model_index: | |
return i_foreval | |
if i_model + 1 == model_index: | |
return i_foreval + 1 | |
return None | |
def calc_elements_foreval_index(self, | |
begin: int, end: int) -> Optional[int]: | |
""" | |
指定したモデル側区間に対応する評価対象側の要素数を計算する. | |
Args: | |
begin (int): モデル側の周波数成分ベクトル NPZ のインデックス | |
end (int): モデル側の周波数成分ベクトル NPZ のインデックス | |
Returns: | |
Optional[int]: 評価対象側の要素数 | |
""" | |
foreval_begin: Optional[int] = self.model_to_foreval(begin) | |
if foreval_begin is None: | |
return None | |
foreval_end: Optional[int] = self.model_to_foreval(end) | |
if foreval_end is None: | |
return None | |
return foreval_end - foreval_begin | |
def calc_elements_foreval_mbt(self, | |
begin: Optional[smf_parse.mbt_container], | |
end: Optional[smf_parse.mbt_container] | |
) -> Optional[int]: | |
""" | |
指定したモデル側区間に対応する評価対象側の要素数を計算する. | |
Args: | |
begin (smf_parse.mbt_container): モデル SMF の MBT | |
基本的にはオフセットを考慮して加算するが | |
None の場合は加算せず周波数成分ベクトル NPZ の冒頭を意味する | |
end (smf_parse.mbt_container): モデル SMF の MBT | |
オフセットを考慮して加算するが | |
None の場合は周波数成分ベクトル NPZ の最後までを意味する | |
Returns: | |
Optional[int]: 評価対象側の要素数 | |
""" | |
begin_index: Optional[int] = self.calc_begin_index(begin) | |
if begin_index is None: | |
return None | |
end_index: Optional[int] = self.calc_end_index(end) | |
if end_index is None: | |
return None | |
return self.calc_elements_foreval_index(begin_index, end_index) | |
def calc_time_ratio_range_index(self, | |
begin: int, end: int) -> float: | |
""" | |
評価対象・モデルの時間割合を計算する(テンポ補正なし). | |
Args: | |
begin (int): モデル側の周波数成分ベクトル NPZ のインデックス | |
end (int): モデル側の周波数成分ベクトル NPZ のインデックス | |
Returns: | |
float: 時間比率 | |
begin から end までの時間差の比率 | |
1.0 なら評価対象とモデルのテンポは同じ、 | |
2.0 なら評価対象はモデルの半分の速度、 | |
0.5 なら評価対象はモデルの倍速 | |
""" | |
elements_foreval: Optional[int] = self.calc_elements_foreval_index( | |
begin, end) | |
if elements_foreval is None: | |
return math.nan | |
return elements_foreval / float(end - begin) | |
def calc_time_ratio_range_mbt(self, | |
begin: Optional[smf_parse.mbt_container], | |
end: Optional[smf_parse.mbt_container] | |
) -> float: | |
""" | |
評価対象・モデルの時間割合を計算する(テンポ補正なし). | |
Args: | |
begin (smf_parse.mbt_container): モデル SMF の MBT | |
基本的にはオフセットを考慮して加算するが | |
None の場合は加算せず周波数成分ベクトル NPZ の冒頭を意味する | |
end (smf_parse.mbt_container): モデル SMF の MBT | |
オフセットを考慮して加算するが | |
None の場合は周波数成分ベクトル NPZ の最後までを意味する | |
Returns: | |
float: 時間比率 | |
begin から end までの時間差の比率 | |
1.0 なら評価対象とモデルのテンポは同じ、 | |
2.0 なら評価対象はモデルの半分の速度、 | |
0.5 なら評価対象はモデルの倍速 | |
""" | |
begin_index: Optional[int] = self.calc_begin_index(begin) | |
if begin_index is None: | |
return math.nan | |
end_index: Optional[int] = self.calc_end_index(end) | |
if end_index is None: | |
return math.nan | |
return self.calc_time_ratio_range_index(begin_index, end_index) | |
def calc_time_ratio_range_conv( | |
self, | |
begin: Optional[smf_parse.mbt_container], | |
end: Optional[smf_parse.mbt_container]) -> float: | |
""" | |
評価対象・モデルの時間割合を計算する(テンポ補正あり). | |
Args: | |
begin (smf_parse.mbt_container): モデル SMF の MBT | |
基本的にはオフセットを考慮して加算するが | |
None の場合は加算せず周波数成分ベクトル NPZ の冒頭を意味する | |
end (smf_parse.mbt_container): モデル SMF の MBT | |
オフセットを考慮して加算するが | |
None の場合は周波数成分ベクトル NPZ の最後までを意味する | |
Returns: | |
float: テンポ補正後の時間比率 | |
begin から end までの時間差の比率 | |
1.0 なら評価対象とモデルの補正後テンポは同じ、 | |
2.0 なら評価対象はモデルの補正後半分の速度、 | |
0.5 なら評価対象はモデルの補正後倍速 | |
""" | |
ratio: float | |
try: | |
r = self.calc_time_ratio_range_mbt(begin, end) / self.time_ratio | |
except ZeroDivisionError: | |
r = math.nan | |
return r | |
def main() -> None: | |
"""Test main.""" | |
print(f'Frequency component vectors and SMF {VERSION}\n\n' | |
'https://gist.github.com/trueroad/' | |
'216cb39902bccd776d8e9da670460fa3\n\n' | |
'Copyright (C) 2022-2024 Masamichi Hosoda.\n' | |
'All rights reserved.\n') | |
import argparse | |
parser: argparse.ArgumentParser = argparse.ArgumentParser() | |
parser.add_argument('MODEL.mid', help='Model SMF.') | |
parser.add_argument('MODEL.npz', help='Model NPZ.') | |
parser.add_argument('FOR_EVAL.npz', help='For-eval NPZ.') | |
parser.add_argument('--weight-timbre', | |
help='Weight for timbre cost', | |
type=float, default=1.0, required=False) | |
parser.add_argument('--exponent-timbre', | |
help='Exponent for timbre cost', | |
type=float, default=1.0, required=False) | |
parser.add_argument('--weight-power-diff', | |
help='Weight for power difference cost', | |
type=float, default=0.25, required=False) | |
parser.add_argument('--exponent-power-diff', | |
help='Exponent for power difference cost', | |
type=float, default=1.0, required=False) | |
parser.add_argument('--exponent-excl-weak-power', | |
help='Exponent for excluding weak power', | |
type=float, default=0.2, required=False) | |
parser.add_argument('--norm', | |
help='Use norm for cost function.' | |
'0 means the default cost function, not the norm.', | |
type=int, default=0, required=False) | |
parser.add_argument('--strict-dtw', | |
help='Enable strict DTW.', | |
action='store_true') | |
parser.add_argument('--partial-dtw', | |
help='Enable partial DTW.', | |
action='store_true') | |
parser.add_argument('--radius', | |
help='fastdtw radius', | |
type=int, default=1, required=False) | |
parser.add_argument('--radius-x', | |
help='fastdtw radius_x for partial DTW', | |
type=int, default=4, required=False) | |
parser.add_argument('--sampling-rate', | |
help='Sampling rate (Hz)', | |
type=float, default=44100.0, required=False) | |
parser.add_argument('--sliding-size', | |
help='STFT sliding size (Number of samples)', | |
type=int, default=2205, required=False) | |
parser.add_argument('--offset-index', | |
help='Offset index ' | |
"(model NPZ index at SMF's MBT 1:1:0)", | |
type=int, default=10, required=False) | |
parser.add_argument('--auto-radius', | |
help='Auto radius and radius_x setting', | |
action='store_true') | |
parser.add_argument('--auto-radius-ratio', | |
help='Auto radius ratio', | |
type=float, default=4.0, required=False) | |
args: argparse.Namespace = parser.parse_args() | |
vargs: Dict[str, Any] = vars(args) | |
model_smf_filename: str = vargs['MODEL.mid'] | |
model_npz_filename: str = vargs['MODEL.npz'] | |
foreval_filename: str = vargs['FOR_EVAL.npz'] | |
weight_timbre: float = vargs['weight_timbre'] | |
exponent_timbre: float = vargs['exponent_timbre'] | |
weight_power_diff: float = vargs['weight_power_diff'] | |
exponent_power_diff: float = vargs['exponent_power_diff'] | |
exponent_excl_weak_power: float = vargs['exponent_excl_weak_power'] | |
norm: int = vargs['norm'] | |
b_strict_dtw: bool = vargs['strict_dtw'] | |
b_partial_dtw: bool = vargs['partial_dtw'] | |
radius: int = vargs['radius'] | |
radius_x: int = vargs['radius_x'] if b_partial_dtw else radius | |
sampling_rate: float = vargs['sampling_rate'] | |
sliding_size: int = vargs['sliding_size'] | |
offset_index: int = vargs['offset_index'] | |
b_auto_radius: bool = vargs['auto_radius'] | |
auto_radius_ratio: float = vargs['auto_radius_ratio'] | |
dist: Optional[Callable[[npt.NDArray[np.float64], | |
npt.NDArray[np.float64]], | |
np.float64]] = None | |
if norm != 0: | |
dist = fcvs_smf.func_norm(norm) | |
print(f'Model SMF : {model_smf_filename}\n' | |
f'Model NPZ : {model_npz_filename}\n' | |
f'For-eval SMF : {foreval_filename}\n' | |
f'weight-timbre : {weight_timbre}\n' | |
f'exponent-timbre : {exponent_timbre}\n' | |
f'weight-power-diff : {weight_power_diff}\n' | |
f'exponent-power-diff : {exponent_power_diff}\n' | |
f'exponent-excl-weak-power: {exponent_excl_weak_power}\n' | |
f'Norm : {norm}\n' | |
f'Strict DTW : {b_strict_dtw}\n' | |
f'Partial DTW : {b_partial_dtw}\n' | |
f'radius : {radius}\n' | |
f'radius_x : {radius_x}\n' | |
f'sampling-rate : {sampling_rate}\n' | |
f'sliding-size : {sliding_size}\n' | |
f'offset-index : {offset_index}\n' | |
f'Auto radius : {b_auto_radius}\n' | |
f'Auto radius ratio : {auto_radius_ratio}\n') | |
# 比較クラス | |
fs: fcvs_smf = fcvs_smf(weight_timbre=weight_timbre, | |
exponent_timbre=exponent_timbre, | |
weight_power_diff=weight_power_diff, | |
exponent_power_diff=exponent_power_diff, | |
exponent_excl_weak_power=exponent_excl_weak_power, | |
dist=dist, | |
b_strict_dtw=b_strict_dtw, | |
b_partial_dtw=b_partial_dtw, | |
radius=radius, | |
radius_x=radius_x, | |
sampling_rate=sampling_rate, | |
sliding_size=sliding_size, | |
offset_index=offset_index, | |
b_auto_radius=b_auto_radius, | |
auto_radius_ratio=auto_radius_ratio, | |
verbose=1) | |
# モデル SMF をロード | |
fs.load_smf(model_smf_filename) | |
# モデル NPZ をロード | |
fs.load_model_vectors(model_npz_filename) | |
# 評価対象をロード | |
fs.load_foreval_vectors(foreval_filename) | |
# NPZ 長 | |
print('\nModel NPZ length: ' | |
f'{len(fs.freq_comp_vectors_model)} elements, ' | |
f'{len(fs.freq_comp_vectors_model) * sliding_size / sampling_rate}' | |
' s\n' | |
'Foreval NPZ length: ' | |
f'{len(fs.freq_comp_vectors_foreval)} elements, ' | |
f'{len(fs.freq_comp_vectors_foreval) * sliding_size / sampling_rate}' | |
' s') | |
# 基本的計算 | |
if not fs.calc_base(): | |
print('Error: calc_base()') | |
return | |
# 自動 radius 結果 | |
if b_auto_radius: | |
print(f'Auto radius results: radius = {fs.radius}, ' | |
f'radius_x = {fs.radius_x}') | |
# トータル DTW 距離とテンポ比を表示 | |
print(f'\nTotal DTW distance : {fs.distance}\n' | |
f'Tempo ratio (conv. ratio): {fs.time_ratio * 100} %\n') | |
# ノート ON 間の区間における情報を表示 | |
print('Range between note on:') | |
# 区間 DTW 距離の合計計算用 | |
sum_distance: float = 0.0 | |
# 区間 DTW 距離 | |
range_distance: float | |
# 区間における要素数(モデル側) | |
elements: int | |
# 区間における要素数(評価対象側) | |
elements_foreval: Optional[int] | |
# 区間のテンポ比率(補正後) | |
time_ratio: float | |
# 最初の区間開始位置(NPZ 冒頭) | |
before_mbt: Optional[smf_parse.mbt_container] = None | |
# 音符 | |
n: smf_parse.note_container | |
for n in fs.smf.notes: | |
if n.note_on.mbt == before_mbt: | |
# 同時発音はスキップ | |
continue | |
range_distance, elements = fs.calc_distance_range_mbt(before_mbt, | |
n.note_on.mbt) | |
elements_foreval = fs.calc_elements_foreval_mbt(before_mbt, | |
n.note_on.mbt) | |
time_ratio = fs.calc_time_ratio_range_conv(before_mbt, | |
n.note_on.mbt) | |
print(f'{before_mbt} ~ {n.note_on.mbt}: elements {elements}\n' | |
f' Distance: abs {range_distance},' | |
f' rel {range_distance / elements}\n' | |
f' Elements foreval: {elements_foreval}\n' | |
f' Time ratio {(time_ratio * 100):.0f} %') | |
sum_distance += range_distance | |
before_mbt = n.note_on.mbt | |
# 最後のノート ON から最後のノート OFF まで | |
range_distance, elements \ | |
= fs.calc_distance_range_mbt(before_mbt, | |
fs.smf.note_on_off[-1].mbt) | |
elements_foreval = fs.calc_elements_foreval_mbt(before_mbt, | |
n.note_on.mbt) | |
time_ratio = fs.calc_time_ratio_range_conv(before_mbt, | |
fs.smf.note_on_off[-1].mbt) | |
print(f'{before_mbt} ~ ({fs.smf.note_on_off[-1].mbt}):' | |
f' elements {elements}\n' | |
f' Distance abs {range_distance},' | |
f' rel {range_distance / elements}\n' | |
f' Elements foreval: {elements_foreval}\n' | |
f' Time ratio {(time_ratio * 100):.0f} %') | |
sum_distance += range_distance | |
# 最後のノート OFF から NPZ 末尾まで | |
range_distance, elements \ | |
= fs.calc_distance_range_mbt(fs.smf.note_on_off[-1].mbt, | |
None) | |
elements_foreval = fs.calc_elements_foreval_mbt(before_mbt, | |
n.note_on.mbt) | |
time_ratio = fs.calc_time_ratio_range_conv(fs.smf.note_on_off[-1].mbt, | |
None) | |
print(f'({fs.smf.note_on_off[-1].mbt}) ~ {None}: elements {elements}\n' | |
f' Distance abs {range_distance},' | |
f' rel {range_distance / elements}\n' | |
f' Elements foreval: {elements_foreval}\n' | |
f' Time ratio {(time_ratio * 100):.0f} %') | |
sum_distance += range_distance | |
# 区間 DTW 距離の合計(トータルとほぼ一致するハズ) | |
print(f'\nSum of range distances: {sum_distance}') | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment