Skip to content

Instantly share code, notes, and snippets.

@trueroad
Last active January 12, 2024 13:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trueroad/216cb39902bccd776d8e9da670460fa3 to your computer and use it in GitHub Desktop.
Save trueroad/216cb39902bccd776d8e9da670460fa3 to your computer and use it in GitHub Desktop.
Frequency component vectors and SMF
#!/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