Skip to content

Instantly share code, notes, and snippets.

@trueroad
Last active January 12, 2024 12: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/2b4214a3c033e90ee64f097a7dcccbc1 to your computer and use it in GitHub Desktop.
Save trueroad/2b4214a3c033e90ee64f097a7dcccbc1 to your computer and use it in GitHub Desktop.
Find groups of equivalent SMFs.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Find groups of equivalent SMFs.
https://gist.github.com/trueroad/2b4214a3c033e90ee64f097a7dcccbc1
Copyright (C) 2023 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.
複数の SMF を入力として、等価 SMF のグループを見つける。
等価というのは、
ノートON/OFFのタイミング、チャンネル、ノート番号、ベロシティ
がまったく同じとし、それ以外のメッセージやメタイベント等は考慮しない。
入力はコマンドライン引数に比較したい SMF のファイル名をすべて記述する。
出力は標準出力に、
グループ間に空行を入れつつ、
グループ内は所属する SMF のノート数とファイル名をタブ区切りで 1 行ずつ、
という形式となる。
グループに所属しない SMF については出力しない。
例:
group1-1.mid, group1-2.mid, group1-3.mid がそれぞれ等価でノート数 10、
group2-1.mid, group2-2.mid それぞれ等価でノート数 5、
different1.mid, different2.mid が等価でない場合は以下のようになる。
```
$ ./find_smf_groups.py group1-1.mid group1-2.mid group1-3.mid \
group2-1.mid group2-2.mid \
different1.mid different2.mid
10 group1-1.mid
10 group1-2.mid
10 group1-3.mid
5 group2-1.mid
5 group2-2.mid
$
```
"""
import sys
# https://gist.github.com/trueroad/52b7c4c98eec5fdf0ff3f62d64ec17bd
import smf_parse
def main() -> None:
"""Do main."""
if len(sys.argv) < 3:
# 比較対象の SMF が 2 つ以上ないので終了
return
# 入力ファイル名の一覧
input_filenames: list[str] = sys.argv[1:]
# ファイル名とノートリストのタプルのリスト
sn_list: list[tuple[str, smf_parse.smf_notes]] = []
# 入力ファイルを読み込んでリストに格納
filename: str
sn: smf_parse.smf_notes
for filename in input_filenames:
sn = smf_parse.smf_notes()
sn.load(filename)
sn_list.append((filename, sn))
# 既に等価 SMF グループに入っている SMF のインデックス格納用
already_grouped: set[int] = set()
# 外側の判定ループ
i: int
for i in range(len(sn_list)):
if i in already_grouped:
# 既に等価 SMF グループに入っているインデックスはスキップ
continue
# 等価 SMF グループ開始フラグを未開始に初期化
b_group_started: bool = False
# 内側の判定ループ
j: int
for j in range(len(sn_list)):
if i >= j:
# 内側外側で同じインデックス同士は比較せずにスキップ、
# 内側外側が逆のインデックス組み合わせで比較するものもスキップ
continue
if j in already_grouped:
# 既に等価 SMF グループに入っているインデックスはスキップ
continue
if sn_list[i][1].get_notes() == sn_list[j][1].get_notes():
# SMF 全ノートが一致!
if not b_group_started:
# まだ等価 SMF グループを開始していなかったので開始処理
# 最初の SMF の情報を出力
print(f'\n{len(sn_list[i][1].get_notes())}'
f'\t{sn_list[i][0]}')
# 等価 SMF グループ開始済フラグを立てる
b_group_started = True
# 2 番目以降の SMF の情報を出力
print(f'{len(sn_list[j][1].get_notes())}\t{sn_list[j][0]}')
# このインデックスはグループ化されたので次以降はスキップ
already_grouped.add(i)
already_grouped.add(j)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment