mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2025-06-03 02:45:24 +00:00
MSQ 流式适配与校验增强,新增 NBS 音色表
This commit is contained in:
parent
889f8f9641
commit
c14489f3a7
@ -22,8 +22,8 @@ The Licensor of Musicreater("this project") is Eilles Wan, bgArray.
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
__version__ = "2.2.3"
|
||||
__vername__ = "Java版欲适配、初步MSQ流式适配"
|
||||
__version__ = "2.2.4"
|
||||
__vername__ = "MSQ 流式适配与校验增强,新增 NBS 音色表"
|
||||
__author__ = (
|
||||
("金羿", "Eilles"),
|
||||
("诸葛亮与八卦阵", "bgArray"),
|
||||
@ -48,6 +48,14 @@ __all__ = [
|
||||
"MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE",
|
||||
"MM_TOUCH_PITCHED_INSTRUMENT_TABLE",
|
||||
"MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE",
|
||||
"MM_NBS_PITCHED_INSTRUMENT_TABLE",
|
||||
"MM_NBS_PERCUSSION_INSTRUMENT_TABLE",
|
||||
# 操作性函数
|
||||
"natural_curve",
|
||||
"straight_line",
|
||||
"load_decode_msq_metainfo",
|
||||
"load_decode_msq_flush_release",
|
||||
"guess_deviation",
|
||||
]
|
||||
|
||||
from .main import *
|
||||
|
@ -16,7 +16,8 @@ Terms & Conditions: License.md in the root directory
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
from .types import Dict, List, Tuple, MidiInstrumentTableType, MidiNoteNameTableType
|
||||
# from .types import Dict, List, Tuple, MidiInstrumentTableType, MidiNoteNameTableType
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
x = "x"
|
||||
"""
|
||||
@ -478,7 +479,12 @@ MM_INSTRUMENT_DEVIATION_TABLE: Dict[str, int] = {
|
||||
"fire.ignite": 0,
|
||||
"note.cow_bell": 6,
|
||||
}
|
||||
"""不同乐器的音调偏离对照表"""
|
||||
"""
|
||||
不同乐器的音调偏离对照表
|
||||
*注意* 该表中的单位是对于 Midi Pitch 音调(整数)的低音偏移。
|
||||
也就是说,该数值越高,则在 Midi Pitch 中的值域越低
|
||||
默认的偏移量为 6 ,因为在计算音高时候少减去了 6 个 Pitch 单位
|
||||
"""
|
||||
|
||||
# Midi乐器对MC乐器对照表
|
||||
|
||||
@ -853,6 +859,8 @@ MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = {
|
||||
"""“偷吃”打击乐器对照表"""
|
||||
|
||||
# Dislink “断联” 音色对照表
|
||||
# https://github.com/Dislink/midi2bdx/blob/main/index.html
|
||||
|
||||
|
||||
MM_DISLINK_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = {
|
||||
0: "note.harp",
|
||||
@ -1037,6 +1045,210 @@ MM_DISLINK_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = {
|
||||
}
|
||||
"""“断联”打击乐器对照表"""
|
||||
|
||||
# NoteBlockStudio “NBS”音色对照表
|
||||
# https://github.com/OpenNBS/NoteBlockStudio/blob/main/scripts/midi_instruments/midi_instruments.gml
|
||||
|
||||
MM_NBS_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = {
|
||||
0: "note.harp",
|
||||
1: "note.pling",
|
||||
2: "note.harp",
|
||||
3: "note.pling",
|
||||
4: "note.harp",
|
||||
5: "note.harp",
|
||||
6: "note.guitar",
|
||||
7: "note.banjo",
|
||||
8: "note.bell",
|
||||
9: "note.bell",
|
||||
10: "note.bell",
|
||||
11: "note.iron_xylophone",
|
||||
12: "note.iron_xylophone",
|
||||
13: "note.xylophone",
|
||||
14: "note.bell",
|
||||
15: "note.iron_xylophone",
|
||||
16: "note.flute",
|
||||
17: "note.flute",
|
||||
18: "note.flute",
|
||||
19: "note.flute",
|
||||
20: "note.flute",
|
||||
21: "note.flute",
|
||||
22: "note.flute",
|
||||
23: "note.flute",
|
||||
24: "note.guitar",
|
||||
25: "note.guitar",
|
||||
26: "note.guitar",
|
||||
27: "note.bass",
|
||||
28: "note.guitar",
|
||||
29: "note.guitar",
|
||||
30: "note.bass",
|
||||
31: "note.bass",
|
||||
32: "note.bass",
|
||||
33: "note.guitar",
|
||||
34: "note.guitar",
|
||||
35: "note.bass",
|
||||
36: "note.pling",
|
||||
37: "note.flute",
|
||||
38: "note.flute",
|
||||
39: "note.flute",
|
||||
40: "note.flute",
|
||||
41: "note.flute",
|
||||
42: "note.didgeridoo",
|
||||
43: "note.flute",
|
||||
44: "note.didgeridoo",
|
||||
45: "note.flute",
|
||||
46: "note.flute",
|
||||
47: "note.flute",
|
||||
48: "note.flute",
|
||||
49: "note.flute",
|
||||
50: "note.flute",
|
||||
51: "note.flute",
|
||||
52: "note.flute",
|
||||
53: "note.flute",
|
||||
54: "note.flute",
|
||||
55: "note.flute",
|
||||
56: "note.flute",
|
||||
57: "note.flute",
|
||||
58: "note.flute",
|
||||
59: "note.flute",
|
||||
60: "note.bit",
|
||||
61: "note.flute",
|
||||
62: "note.flute",
|
||||
63: "note.flute",
|
||||
64: "note.flute",
|
||||
65: "note.guitar",
|
||||
66: "note.flute",
|
||||
67: "note.flute",
|
||||
68: "note.flute",
|
||||
69: "note.bell",
|
||||
70: "note.flute",
|
||||
71: "note.flute",
|
||||
72: "note.flute",
|
||||
73: "note.flute",
|
||||
74: "note.chime",
|
||||
75: "note.flute",
|
||||
76: "note.flute",
|
||||
77: "note.guitar",
|
||||
78: "note.pling",
|
||||
79: "note.flute",
|
||||
80: "note.guitar",
|
||||
81: "note.banjo",
|
||||
82: "note.banjo",
|
||||
83: "note.banjo",
|
||||
84: "note.guitar",
|
||||
85: "note.iron_xylophone",
|
||||
86: "note.flute",
|
||||
87: "note.flute",
|
||||
88: "note.chime",
|
||||
89: "note.cow_bell",
|
||||
90: "note.iron_xylophone",
|
||||
91: "note.xylophone",
|
||||
92: "note.basedrum",
|
||||
93: "note.snare",
|
||||
94: "note.snare",
|
||||
95: "note.basedrum",
|
||||
96: "note.snare",
|
||||
97: "note.hat",
|
||||
98: "note.snare",
|
||||
99: "note.hat",
|
||||
100: "note.basedrum",
|
||||
101: "note.hat",
|
||||
102: "note.basedrum",
|
||||
103: "note.hat",
|
||||
104: "note.basedrum",
|
||||
105: "note.snare",
|
||||
106: "note.snare",
|
||||
107: "note.snare",
|
||||
108: "note.cow_bell",
|
||||
109: "note.snare",
|
||||
110: "note.hat",
|
||||
111: "note.snare",
|
||||
112: "note.hat",
|
||||
113: "note.hat",
|
||||
114: "note.hat",
|
||||
115: "note.hat",
|
||||
116: "note.hat",
|
||||
117: "note.chime",
|
||||
118: "note.hat",
|
||||
119: "note.snare",
|
||||
120: "note.hat",
|
||||
121: "note.hat",
|
||||
122: "note.hat",
|
||||
123: "note.hat",
|
||||
124: "note.hat",
|
||||
125: "note.snare",
|
||||
126: "note.basedrum",
|
||||
127: "note.basedrum",
|
||||
}
|
||||
"""“NBS”乐音乐器对照表"""
|
||||
|
||||
|
||||
MM_NBS_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = {
|
||||
24: "note.bit",
|
||||
25: "note.snare",
|
||||
26: "note.hat",
|
||||
27: "note.snare",
|
||||
28: "note.snare",
|
||||
29: "note.hat",
|
||||
30: "note.hat",
|
||||
31: "note.hat",
|
||||
32: "note.hat",
|
||||
33: "note.hat",
|
||||
34: "note.chime",
|
||||
35: "note.basedrum",
|
||||
36: "note.basedrum",
|
||||
37: "note.hat",
|
||||
38: "note.snare",
|
||||
39: "note.hat",
|
||||
40: "note.snare",
|
||||
41: "note.basedrum",
|
||||
42: "note.snare",
|
||||
43: "note.basedrum",
|
||||
44: "note.snare",
|
||||
45: "note.basedrum",
|
||||
46: "note.basedrum",
|
||||
47: "note.snare",
|
||||
48: "note.snare",
|
||||
49: "note.snare",
|
||||
50: "note.snare",
|
||||
51: "note.snare",
|
||||
52: "note.snare",
|
||||
53: "note.hat",
|
||||
54: "note.snare",
|
||||
55: "note.snare",
|
||||
56: "note.cow_bell",
|
||||
57: "note.snare",
|
||||
58: "note.hat",
|
||||
59: "note.snare",
|
||||
60: "note.hat",
|
||||
61: "note.hat",
|
||||
62: "note.hat",
|
||||
63: "note.basedrum",
|
||||
64: "note.basedrum",
|
||||
65: "note.snare",
|
||||
66: "note.snare",
|
||||
67: "note.xylophone",
|
||||
68: "note.xylophone",
|
||||
69: "note.hat",
|
||||
70: "note.hat",
|
||||
71: "note.flute",
|
||||
72: "note.flute",
|
||||
73: "note.hat",
|
||||
74: "note.hat",
|
||||
75: "note.hat",
|
||||
76: "note.hat",
|
||||
77: "note.hat",
|
||||
78: "note.didgeridoo",
|
||||
79: "note.didgeridoo",
|
||||
80: "note.hat",
|
||||
81: "note.chime",
|
||||
82: "note.hat",
|
||||
83: "note.chime",
|
||||
84: "note.chime",
|
||||
85: "note.hat",
|
||||
86: "note.basedrum",
|
||||
87: "note.basedrum",
|
||||
}
|
||||
"""“NBS”打击乐器对照表"""
|
||||
|
||||
# Midi音高对MC方块对照表
|
||||
|
||||
# 金羿ELS 音符方块对照表
|
||||
|
@ -34,7 +34,6 @@ import os
|
||||
import math
|
||||
|
||||
import mido
|
||||
from xxhash import xxh3_64, xxh3_128
|
||||
|
||||
from .constants import *
|
||||
from .exceptions import *
|
||||
@ -169,6 +168,7 @@ class MusicSequence:
|
||||
minimum_vol: float = 0.1,
|
||||
volume_processing_function: FittingFunctionType = natural_curve,
|
||||
deviation: float = 0,
|
||||
note_referance_table_replacement: Dict[str, str] = {},
|
||||
):
|
||||
"""
|
||||
自mido对象导入一个音符序列类
|
||||
@ -210,6 +210,7 @@ class MusicSequence:
|
||||
default_tempo_value=default_tempo,
|
||||
vol_processing_function=volume_processing_function,
|
||||
ignore_mismatch_error=mismatch_error_ignorance,
|
||||
note_rtable_replacement=note_referance_table_replacement,
|
||||
)
|
||||
else:
|
||||
note_channels = {}
|
||||
@ -241,6 +242,7 @@ class MusicSequence:
|
||||
music_name_ = bytes_buffer_in[8 : (stt_index := 8 + (group_1 >> 10))].decode(
|
||||
"GB18030"
|
||||
)
|
||||
|
||||
channels_: MineNoteChannelType = empty_midi_channels(default_staff=[])
|
||||
total_note_count = 0
|
||||
if verify:
|
||||
@ -477,8 +479,11 @@ class MusicSequence:
|
||||
"""重命名此音乐"""
|
||||
self.music_name = new_name
|
||||
|
||||
def add_note(self, channel_no: int, note: MineNote, is_sort: bool = False):
|
||||
"""在指定通道添加一个音符"""
|
||||
def add_note(self, channel_no: int, note: MineNote, is_sort: bool = True):
|
||||
"""
|
||||
在指定通道添加一个音符
|
||||
值得注意:在版本 2.2.3 及之前 is_sort 参数默认为 False ;在此之后为 True
|
||||
"""
|
||||
self.channels[channel_no].append(note)
|
||||
self.total_note_count += 1
|
||||
if note.sound_name in self.note_count_per_instrument.keys():
|
||||
@ -498,6 +503,7 @@ class MusicSequence:
|
||||
pitched_note_rtable: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
||||
percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
||||
vol_processing_function: FittingFunctionType = natural_curve,
|
||||
note_rtable_replacement: Dict[str, str] = {},
|
||||
) -> Tuple[MineNoteChannelType, int, Dict[str, int]]:
|
||||
"""
|
||||
将midi解析并转换为频道音符字典
|
||||
@ -616,6 +622,7 @@ class MusicSequence:
|
||||
else pitched_note_rtable
|
||||
),
|
||||
volume_processing_method_=vol_processing_function,
|
||||
note_table_replacement=note_rtable_replacement,
|
||||
)
|
||||
)
|
||||
note_count += 1
|
||||
@ -693,6 +700,7 @@ class MidiConvert(MusicSequence):
|
||||
enable_old_exe_format: bool = False,
|
||||
minimum_volume: float = 0.1,
|
||||
vol_processing_function: FittingFunctionType = natural_curve,
|
||||
note_rtable_replacement: Dict[str, str] = {},
|
||||
):
|
||||
"""
|
||||
简单的midi转换类,将midi对象转换为我的世界结构或者包
|
||||
@ -742,6 +750,7 @@ class MidiConvert(MusicSequence):
|
||||
volume_processing_function=vol_processing_function,
|
||||
default_tempo=default_tempo_value,
|
||||
mismatch_error_ignorance=ignore_mismatch_error,
|
||||
note_referance_table_replacement=note_rtable_replacement,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@ -756,6 +765,7 @@ class MidiConvert(MusicSequence):
|
||||
old_exe_format: bool = False,
|
||||
min_volume: float = 0.1,
|
||||
vol_processing_func: FittingFunctionType = natural_curve,
|
||||
note_table_replacement: Dict[str, str] = {},
|
||||
):
|
||||
"""
|
||||
直接输入文件地址,将midi文件读入
|
||||
@ -802,6 +812,7 @@ class MidiConvert(MusicSequence):
|
||||
enable_old_exe_format=old_exe_format,
|
||||
minimum_volume=min_volume,
|
||||
vol_processing_function=vol_processing_func,
|
||||
note_rtable_replacement=note_table_replacement,
|
||||
)
|
||||
except (ValueError, TypeError) as E:
|
||||
raise MidiDestroyedError(f"文件{midi_file_path}可能损坏:{E}")
|
||||
|
@ -18,9 +18,9 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
|
||||
from dataclasses import dataclass
|
||||
from .types import Optional, Any, List, Mapping, Tuple, Union
|
||||
from typing import Optional, Any, List, Tuple, Union
|
||||
|
||||
from .constants import MC_PERCUSSION_INSTRUMENT_LIST
|
||||
from .constants import MC_PITCHED_INSTRUMENT_LIST
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
@ -91,7 +91,7 @@ class MineNote:
|
||||
"""高精度开始时间偏量 0.4 毫秒"""
|
||||
|
||||
self.percussive = (
|
||||
(mc_sound_name in MC_PERCUSSION_INSTRUMENT_LIST)
|
||||
(mc_sound_name not in MC_PITCHED_INSTRUMENT_LIST)
|
||||
if (is_percussion is None)
|
||||
else is_percussion
|
||||
)
|
||||
@ -463,7 +463,7 @@ class SingleNoteBox:
|
||||
self.annotation_text = annotation
|
||||
"""音符注释"""
|
||||
if percussion is None:
|
||||
self.is_percussion = percussion in MC_PERCUSSION_INSTRUMENT_LIST
|
||||
self.is_percussion = percussion not in MC_PITCHED_INSTRUMENT_LIST
|
||||
else:
|
||||
self.is_percussion = percussion
|
||||
|
||||
@ -653,14 +653,3 @@ DEFAULT_PROGRESSBAR_STYLE = ProgressBarStyle(
|
||||
"""
|
||||
默认的进度条样式
|
||||
"""
|
||||
|
||||
|
||||
MineNoteChannelType = Mapping[
|
||||
int,
|
||||
List[MineNote,],
|
||||
]
|
||||
"""
|
||||
我的世界频道信息类型
|
||||
|
||||
Dict[int,Dict[int,List[MineNote,],],]
|
||||
"""
|
||||
|
@ -17,19 +17,17 @@ Terms & Conditions: License.md in the root directory
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
List,
|
||||
Literal,
|
||||
Optional,
|
||||
Tuple,
|
||||
Union,
|
||||
Iterable,
|
||||
Sequence,
|
||||
Mapping,
|
||||
Callable,
|
||||
)
|
||||
|
||||
from .subclass import MineNote
|
||||
|
||||
|
||||
MidiNoteNameTableType = Mapping[int, Tuple[str, ...]]
|
||||
"""
|
||||
@ -64,3 +62,14 @@ ChannelType = Dict[
|
||||
|
||||
Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],]
|
||||
"""
|
||||
|
||||
|
||||
MineNoteChannelType = Mapping[
|
||||
int,
|
||||
List[MineNote,],
|
||||
]
|
||||
"""
|
||||
我的世界频道信息类型
|
||||
|
||||
Dict[int,Dict[int,List[MineNote,],],]
|
||||
"""
|
||||
|
@ -18,6 +18,22 @@ Terms & Conditions: License.md in the root directory
|
||||
import math
|
||||
import random
|
||||
|
||||
# from io import BytesIO
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
Tuple,
|
||||
Optional,
|
||||
Callable,
|
||||
Literal,
|
||||
Union,
|
||||
List,
|
||||
Generator,
|
||||
BinaryIO,
|
||||
)
|
||||
|
||||
from xxhash import xxh3_64, xxh3_128
|
||||
|
||||
from .constants import (
|
||||
MC_INSTRUMENT_BLOCKS_TABLE,
|
||||
MM_INSTRUMENT_DEVIATION_TABLE,
|
||||
@ -26,16 +42,9 @@ from .constants import (
|
||||
)
|
||||
from .subclass import MineNote, mctick2timestr
|
||||
|
||||
from .types import (
|
||||
Any,
|
||||
Dict,
|
||||
Tuple,
|
||||
Optional,
|
||||
Callable,
|
||||
Literal,
|
||||
Union,
|
||||
MidiInstrumentTableType,
|
||||
)
|
||||
from .types import MidiInstrumentTableType, MineNoteChannelType
|
||||
|
||||
from .exceptions import MusicSequenceDecodeError
|
||||
|
||||
|
||||
def empty_midi_channels(
|
||||
@ -251,6 +260,7 @@ def midi_msgs_to_minenote(
|
||||
play_speed: float,
|
||||
midi_reference_table: MidiInstrumentTableType,
|
||||
volume_processing_method_: Callable[[float], float],
|
||||
note_table_replacement: Dict[str, str] = {},
|
||||
) -> MineNote:
|
||||
"""
|
||||
将Midi信息转为我的世界音符对象
|
||||
@ -275,7 +285,7 @@ def midi_msgs_to_minenote(
|
||||
mc_distance_volume = volume_processing_method_(velocity_)
|
||||
|
||||
return MineNote(
|
||||
mc_sound_name=mc_sound_ID,
|
||||
mc_sound_name=note_table_replacement.get(mc_sound_ID, mc_sound_ID),
|
||||
midi_pitch=note_,
|
||||
midi_velocity=velocity_,
|
||||
start_time=(tk := int(start_time_ / float(play_speed) / 50000)),
|
||||
@ -378,3 +388,291 @@ def soundID_to_blockID(
|
||||
return random.choice(MC_INSTRUMENT_BLOCKS_TABLE.get(sound_id, (default_block,)))
|
||||
else:
|
||||
return MC_INSTRUMENT_BLOCKS_TABLE.get(sound_id, (default_block,))[0]
|
||||
|
||||
|
||||
def load_decode_msq_metainfo(
|
||||
buffer_in: BinaryIO,
|
||||
) -> Tuple[str, float, float, bool, int]:
|
||||
"""
|
||||
以流的方式解码MSQ音乐序列元信息
|
||||
|
||||
Parameters
|
||||
----------
|
||||
buffer_in: BytesIO
|
||||
MSQ格式的字节流
|
||||
|
||||
Returns
|
||||
-------
|
||||
Tuple[str, float, float, bool, int]
|
||||
音乐名称,最小音量,音调偏移,是否启用高精度,最后的流指针位置
|
||||
|
||||
"""
|
||||
buffer_in.seek(4, 0)
|
||||
group_1 = int.from_bytes(buffer_in.read(2), "big")
|
||||
group_2 = int.from_bytes(buffer_in.read(2), "big", signed=False)
|
||||
|
||||
# high_quantity = bool(group_2 & 0b1000000000000000)
|
||||
# print(group_2, high_quantity)
|
||||
|
||||
music_name_ = buffer_in.read(stt_index := (group_1 >> 10)).decode("GB18030")
|
||||
|
||||
return (
|
||||
music_name_,
|
||||
(group_1 & 0b1111111111) / 1000,
|
||||
(
|
||||
(-1 if group_2 & 0b100000000000000 else 1)
|
||||
* (group_2 & 0b11111111111111)
|
||||
/ 1000
|
||||
),
|
||||
bool(group_2 & 0b1000000000000000),
|
||||
stt_index + 8,
|
||||
)
|
||||
|
||||
|
||||
def load_decode_msq_flush_release(
|
||||
buffer_in: BinaryIO,
|
||||
starter_index: int,
|
||||
high_quantity_note: bool,
|
||||
) -> Generator[Tuple[int, MineNote], Any, None]:
|
||||
"""以流的方式解码MSQ音乐序列的音符序列并流式返回
|
||||
|
||||
Parameters
|
||||
----------
|
||||
buffer_in : BytesIO
|
||||
输入的MSQ格式二进制字节流
|
||||
starter_index : int
|
||||
字节流中,音符序列的起始索引
|
||||
high_quantity_note : bool
|
||||
是否启用高精度音符解析
|
||||
|
||||
Returns
|
||||
-------
|
||||
Generator[Tuple[int, MineNote], Any, None]
|
||||
以流的方式返回解码后的音符序列
|
||||
|
||||
Raises
|
||||
------
|
||||
MusicSequenceDecodeError
|
||||
当解码过程中出现错误,抛出异常
|
||||
|
||||
"""
|
||||
|
||||
# _total_verify = xxh3_64(buffer_in.read(starter_index), seed=total_note_count)
|
||||
|
||||
# buffer_in.seek(starter_index, 0)
|
||||
buffer_in.seek(starter_index)
|
||||
_bytes_buffer_in = buffer_in.read()
|
||||
# int.from_bytes(_bytes_buffer_in[0 : 4], "big")
|
||||
|
||||
_now_channel_starter_index = 0
|
||||
|
||||
_total_note_count = 1
|
||||
|
||||
_channel_infos = empty_midi_channels(
|
||||
default_staff={"NOW_INDEX": 0, "NOTE_COUNT": 0, "HAVE_READ": 0, "END_INDEX": -1}
|
||||
)
|
||||
|
||||
for __channel_index in _channel_infos.keys():
|
||||
# _channel_note_count = 0
|
||||
|
||||
_now_channel_ender_sign = xxh3_64(
|
||||
_bytes_buffer_in[
|
||||
_now_channel_starter_index : _now_channel_starter_index + 4
|
||||
],
|
||||
seed=3,
|
||||
).digest()
|
||||
|
||||
# print(
|
||||
# "[DEBUG] 索引取得:",
|
||||
# _bytes_buffer_in[
|
||||
# _now_channel_starter_index : _now_channel_starter_index + 4
|
||||
# ],
|
||||
# "校验索引",
|
||||
# _now_channel_ender_sign,
|
||||
# )
|
||||
|
||||
_now_channel_ender_index = _bytes_buffer_in.find(_now_channel_ender_sign)
|
||||
|
||||
# print("[DEBUG] 索引取得:", _now_channel_ender_index,)
|
||||
|
||||
_channel_note_count = int.from_bytes(
|
||||
_bytes_buffer_in[
|
||||
_now_channel_starter_index : _now_channel_starter_index + 4
|
||||
],
|
||||
"big",
|
||||
)
|
||||
|
||||
if _channel_note_count == 0:
|
||||
continue
|
||||
|
||||
while (
|
||||
xxh3_64(
|
||||
_bytes_buffer_in[_now_channel_starter_index:_now_channel_ender_index],
|
||||
seed=_channel_note_count,
|
||||
).digest()
|
||||
!= _bytes_buffer_in[
|
||||
_now_channel_ender_index + 8 : _now_channel_ender_index + 16
|
||||
]
|
||||
):
|
||||
_now_channel_ender_index += 8 + _bytes_buffer_in[
|
||||
_now_channel_ender_index + 8 :
|
||||
].find(_now_channel_ender_sign)
|
||||
|
||||
# print(
|
||||
# "[WARNING] XXHASH 无法匹配,当前序列",
|
||||
# __channel_index,
|
||||
# "当前全部序列字节串",
|
||||
# _bytes_buffer_in[
|
||||
# _now_channel_starter_index:_now_channel_ender_index
|
||||
# ],
|
||||
# "校验值",
|
||||
# xxh3_64(
|
||||
# _bytes_buffer_in[
|
||||
# _now_channel_starter_index:_now_channel_ender_index
|
||||
# ],
|
||||
# seed=_channel_note_count,
|
||||
# ).digest(),
|
||||
# _bytes_buffer_in[
|
||||
# _now_channel_ender_index + 8 : _now_channel_ender_index + 16
|
||||
# ],
|
||||
# "改变结尾索引",
|
||||
# _now_channel_ender_index,
|
||||
# )
|
||||
|
||||
_channel_infos[__channel_index]["NOW_INDEX"] = _now_channel_starter_index + 4
|
||||
_channel_infos[__channel_index]["END_INDEX"] = _now_channel_ender_index
|
||||
_channel_infos[__channel_index]["NOTE_COUNT"] = _channel_note_count
|
||||
|
||||
# print(
|
||||
# "[DEBUG] 当前序列", __channel_index, "值", _channel_infos[__channel_index]
|
||||
# )
|
||||
|
||||
_total_note_count += _channel_note_count
|
||||
|
||||
_now_channel_starter_index = _now_channel_ender_index + 16
|
||||
# for i in range(
|
||||
# int.from_bytes(
|
||||
# bytes_buffer_in[stt_index : (stt_index := stt_index + 4)], "big"
|
||||
# )
|
||||
# ):
|
||||
_to_yield_note_list: List[Tuple[MineNote, int]] = []
|
||||
|
||||
# {"NOW_INDEX": 0, "NOTE_COUNT": 0, "HAVE_READ": 0, "END_INDEX": -1}
|
||||
|
||||
while _total_note_count:
|
||||
_read_in_note_list: List[Tuple[MineNote, int]] = []
|
||||
for __channel_index in _channel_infos.keys():
|
||||
if (
|
||||
_channel_infos[__channel_index]["HAVE_READ"]
|
||||
< _channel_infos[__channel_index]["NOTE_COUNT"]
|
||||
):
|
||||
# print("当前已读", _channel_infos[__channel_index]["HAVE_READ"])
|
||||
try:
|
||||
_end_index = (
|
||||
(_stt_index := _channel_infos[__channel_index]["NOW_INDEX"])
|
||||
+ 13
|
||||
+ high_quantity_note
|
||||
+ (_bytes_buffer_in[_stt_index] >> 2)
|
||||
)
|
||||
# print("读取音符字节串", _bytes_buffer_in[_stt_index:_end_index])
|
||||
_read_in_note_list.append(
|
||||
(
|
||||
MineNote.decode(
|
||||
code_buffer=_bytes_buffer_in[_stt_index:_end_index],
|
||||
is_high_time_precision=high_quantity_note,
|
||||
),
|
||||
__channel_index,
|
||||
)
|
||||
)
|
||||
_channel_infos[__channel_index]["HAVE_READ"] += 1
|
||||
_channel_infos[__channel_index]["NOW_INDEX"] = _end_index
|
||||
_total_note_count -= 1
|
||||
except Exception as _err:
|
||||
# print(channels_)
|
||||
raise MusicSequenceDecodeError("难以定位的解码错误", _err)
|
||||
if not _read_in_note_list:
|
||||
break
|
||||
# _note_list.append
|
||||
min_stt_note: MineNote = min(_read_in_note_list, key=lambda x: x[0].start_tick)[
|
||||
0
|
||||
]
|
||||
for i in range(len(_to_yield_note_list)):
|
||||
__note, __channel_index = _to_yield_note_list[i]
|
||||
if __note.start_tick >= min_stt_note.start_tick:
|
||||
break
|
||||
else:
|
||||
yield __channel_index, __note
|
||||
_to_yield_note_list.pop(i)
|
||||
|
||||
_to_yield_note_list.extend(_read_in_note_list)
|
||||
_to_yield_note_list.sort(key=lambda x: x[0].start_tick)
|
||||
|
||||
for __note, __channel_index in sorted(
|
||||
_to_yield_note_list, key=lambda x: x[0].start_tick
|
||||
):
|
||||
yield __channel_index, __note
|
||||
# 俺寻思能用
|
||||
|
||||
|
||||
def guess_deviation(
|
||||
total_note_count: int,
|
||||
total_instrument_count: int,
|
||||
note_count_per_instrument: Optional[Dict[str, int]] = None,
|
||||
qualified_note_count_per_instrument: Optional[Dict[str, int]] = None,
|
||||
music_channels: Optional[MineNoteChannelType] = None,
|
||||
) -> float:
|
||||
"""
|
||||
通过乐器权重来计算一首歌的音调偏移
|
||||
这个方法未经验证,但理论有效,金羿首创
|
||||
|
||||
Parameters
|
||||
----------
|
||||
total_note_count: int
|
||||
歌曲总音符数
|
||||
total_instrument_count: int
|
||||
歌曲乐器总数
|
||||
note_count_per_instrument: Dict[str, int]
|
||||
乐器名称与乐器音符数对照表
|
||||
qualified_note_count_per_instrument: Dict[str, int]
|
||||
每个乐器中,符合该乐器的音调范围的音符数
|
||||
music_channels: MineNoteChannelType
|
||||
MusicSequence类的音乐通道字典
|
||||
|
||||
Returns
|
||||
-------
|
||||
float估测的音调偏移值
|
||||
"""
|
||||
if note_count_per_instrument is None or qualified_note_count_per_instrument is None:
|
||||
if music_channels is None:
|
||||
raise ValueError("参数不足,算逑!")
|
||||
note_count_per_instrument = {}
|
||||
qualified_note_count_per_instrument = {}
|
||||
for this_note in [k for j in music_channels.values() for k in j]:
|
||||
if this_note.sound_name in note_count_per_instrument.keys():
|
||||
note_count_per_instrument[this_note.sound_name] += 1
|
||||
qualified_note_count_per_instrument[
|
||||
this_note.sound_name
|
||||
] += is_note_in_diapason(this_note)
|
||||
else:
|
||||
note_count_per_instrument[this_note.sound_name] = 1
|
||||
qualified_note_count_per_instrument[this_note.sound_name] = int(
|
||||
is_note_in_diapason(this_note)
|
||||
)
|
||||
return (
|
||||
sum(
|
||||
[
|
||||
(
|
||||
(
|
||||
MM_INSTRUMENT_RANGE_TABLE[inst][-1]
|
||||
* note_count
|
||||
/ total_note_count
|
||||
- MM_INSTRUMENT_RANGE_TABLE[inst][-1]
|
||||
)
|
||||
* (note_count - qualified_note_count_per_instrument[inst])
|
||||
)
|
||||
for inst, note_count in note_count_per_instrument.items()
|
||||
]
|
||||
)
|
||||
/ total_instrument_count
|
||||
/ total_note_count
|
||||
)
|
||||
|
25
README.md
25
README.md
@ -1,6 +1,12 @@
|
||||
<h1 align="center">
|
||||
音·创 Musicreater
|
||||
</h1>
|
||||
[Bilibili: 金羿ELS]: https://img.shields.io/badge/Bilibili-%E9%87%91%E7%BE%BFELS-00A1E7?style=for-the-badge
|
||||
[Bilibili: 诸葛亮与八卦阵]: https://img.shields.io/badge/Bilibili-%E8%AF%B8%E8%91%9B%E4%BA%AE%E4%B8%8E%E5%85%AB%E5%8D%A6%E9%98%B5-00A1E7?style=for-the-badge
|
||||
[CodeStyle: black]: https://img.shields.io/badge/code%20style-black-121110.svg?style=for-the-badge
|
||||
[python]: https://img.shields.io/badge/python-3.8-AB70FF?style=for-the-badge
|
||||
[release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge
|
||||
[license]: https://img.shields.io/badge/Licence-Apache-228B22?style=for-the-badge
|
||||
|
||||
|
||||
<h1 align="center">音·创 Musicreater </h1>
|
||||
|
||||
<p align="center">
|
||||
<img width="128" height="128" src="https://gitee.com/TriM-Organization/Musicreater/raw/master/resources/msctIcon.png">
|
||||
@ -92,10 +98,10 @@
|
||||
- <table><tr><td>感谢 **油炸**<QQ2836146704> 激励我们不断开发新的内容。</td><td><img height="50" src="https://foruda.gitee.com/images/1695478907647543027/08ea9909_9911226.jpeg"></td></tr></table>
|
||||
- 感谢 **雨**\<QQ237667809\> 反馈在新版本的指令格式下,计分板播放器的附加包无法播放的问题。
|
||||
- 感谢 **梦幻duang**\<QQ13753593\> 为我们提供 Java 1.12.2 版本命令格式参考。
|
||||
- 感谢 [_Open Note Block Studio_](https://github.com/OpenNBS/NoteBlockStudio) 项目的开发为我们提供持续的追赶动力。
|
||||
|
||||
> 感谢广大群友为此库提供的测试和建议等
|
||||
>
|
||||
> 若您对我们有所贡献但您的名字没有出现在此列表中,请联系我们!
|
||||
> 感谢广大群友为此库提供的测试和建议等
|
||||
> 若您对我们有所贡献但您的名字没有出现在此列表中,请联系我们!
|
||||
|
||||
## 联系 📞
|
||||
|
||||
@ -122,10 +128,3 @@ NOT AN OFFICIAL MINECRAFT PRODUCT.
|
||||
NOT APPROVED BY OR ASSOCIATED WITH MOJANG OR MICROSOFT.
|
||||
|
||||
NOT APPROVED BY OR ASSOCIATED WITH NETEASE.
|
||||
|
||||
[Bilibili: 金羿ELS]: https://img.shields.io/badge/Bilibili-%E9%87%91%E7%BE%BFELS-00A1E7?style=for-the-badge
|
||||
[Bilibili: 诸葛亮与八卦阵]: https://img.shields.io/badge/Bilibili-%E8%AF%B8%E8%91%9B%E4%BA%AE%E4%B8%8E%E5%85%AB%E5%8D%A6%E9%98%B5-00A1E7?style=for-the-badge
|
||||
[CodeStyle: black]: https://img.shields.io/badge/code%20style-black-121110.svg?style=for-the-badge
|
||||
[python]: https://img.shields.io/badge/python-3.8-AB70FF?style=for-the-badge
|
||||
[release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge
|
||||
[license]: https://img.shields.io/badge/Licence-Apache-228B22?style=for-the-badge
|
||||
|
20
README_EN.md
20
README_EN.md
@ -1,3 +1,10 @@
|
||||
[Bilibili: Eilles]: https://img.shields.io/badge/Bilibili-%E9%87%91%E7%BE%BFELS-00A1E7?style=for-the-badge
|
||||
[Bilibili: bgArray]: https://img.shields.io/badge/Bilibili-%E8%AF%B8%E8%91%9B%E4%BA%AE%E4%B8%8E%E5%85%AB%E5%8D%A6%E9%98%B5-00A1E7?style=for-the-badge
|
||||
[CodeStyle: black]: https://img.shields.io/badge/code%20style-black-121110.svg?style=for-the-badge
|
||||
[python]: https://img.shields.io/badge/python-3.8-AB70FF?style=for-the-badge
|
||||
[release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge
|
||||
[license]: https://img.shields.io/badge/Licence-Apache-228B22?style=for-the-badge
|
||||
|
||||
<h1 align="center">
|
||||
音·创 Musicreater
|
||||
</h1>
|
||||
@ -91,10 +98,11 @@ This list is not in any order.
|
||||
- Thank _小埋_\<QQ2039310975\> for reporting the empty add-on packs title and description problem.
|
||||
- <table><tr><td>Thank <i>油炸</i> <QQ2836146704> for inspiring us to constantly develop something new.</td><td><img width="260" src="https://foruda.gitee.com/images/1695478907647543027/08ea9909_9911226.jpeg" alt="The groupmate on the picture was saying that our convert-QQ-bot had once brought him great convinience but now it closed down by some reason so he was feeling regretful." title=""It was once, a convert-QQ-bot is just in front my eyes" "Until lose, I finally know cannot chase back what I needs""></td><td><small>"It was once, a convert-QQ-bot is just in front my eyes"<br>"Until lose, I finally know cannot chase back what I needs"</small></td></tr></table>
|
||||
- Thank _雨_\<QQ237667809\> for give us report that under the new `execute` command format that the scoreboard player's add-on packs cannot play correctly.
|
||||
- Thank _梦幻duang_\<QQ13753593\> for providing us with his knowlodeg of the command format in Minecraft: Java Edition Version 1.12.2.
|
||||
- Thank [_Open Note Block Studio_](https://github.com/OpenNBS/NoteBlockStudio)'s Project for giving us the power and energy of continual developing.
|
||||
|
||||
> Thanks for the support and help of a lot of groupmates
|
||||
>
|
||||
> If you have given contributions but have not been in the list, please contact us!
|
||||
> Thanks for the support and help of a lot of groupmates
|
||||
> If you have given contributions but have not been in the list, please contact us!
|
||||
|
||||
## Contact Us 📞
|
||||
|
||||
@ -122,9 +130,3 @@ NOT APPROVED BY OR ASSOCIATED WITH NETEASE.
|
||||
|
||||
- 上文提及的 网易 公司,指代的是在中国大陆运营《我的世界:中国版》的上海网之易璀璨网络科技有限公司
|
||||
|
||||
[Bilibili: Eilles]: https://img.shields.io/badge/Bilibili-%E9%87%91%E7%BE%BFELS-00A1E7?style=for-the-badge
|
||||
[Bilibili: bgArray]: https://img.shields.io/badge/Bilibili-%E8%AF%B8%E8%91%9B%E4%BA%AE%E4%B8%8E%E5%85%AB%E5%8D%A6%E9%98%B5-00A1E7?style=for-the-badge
|
||||
[CodeStyle: black]: https://img.shields.io/badge/code%20style-black-121110.svg?style=for-the-badge
|
||||
[python]: https://img.shields.io/badge/python-3.8-AB70FF?style=for-the-badge
|
||||
[release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge
|
||||
[license]: https://img.shields.io/badge/Licence-Apache-228B22?style=for-the-badge
|
||||
|
@ -13,9 +13,7 @@ def main():
|
||||
if file.endswith(".egg-info"):
|
||||
egg_info.append(file)
|
||||
console.print(file)
|
||||
for file in track(
|
||||
["build", "dist", "logs", *egg_info], description="正删档"
|
||||
):
|
||||
for file in track(["build", "dist", "logs", *egg_info], description="正删档"):
|
||||
if os.path.isdir(file) and os.access(file, os.W_OK):
|
||||
shutil.rmtree(file)
|
||||
|
||||
|
@ -92,11 +92,17 @@ ADD XXH64(seq_2_note_count, 3)
|
||||
ADD XXH64(note_seq_2, seq_2_note_count)
|
||||
ADD XXH128(
|
||||
XOR(
|
||||
XXH64(meta_info, note_count),
|
||||
XOR(
|
||||
XXH64(seq_1_note_count, 3),
|
||||
XXH64(note_seq_1, seq_1_note_count)
|
||||
XXH64(meta_info, note_count),
|
||||
XOR(
|
||||
XXH64(seq_1_note_count, 3),
|
||||
XXH64(note_seq_1, seq_1_note_count)
|
||||
),
|
||||
),
|
||||
XOR(
|
||||
XXH64(seq_2_note_count, 3),
|
||||
XXH64(note_seq_2, seq_2_note_count),
|
||||
)
|
||||
),
|
||||
note_count
|
||||
)
|
||||
|
@ -187,7 +187,7 @@ print(
|
||||
cvt_method(
|
||||
cvt_mid,
|
||||
out_path,
|
||||
Musicreater.DEFAULT_PROGRESSBAR_STYLE if prompts[2] else None, # type: ignore
|
||||
Musicreater.DEFAULT_PROGRESSBAR_STYLE if prompts[2] else None, # type: ignore
|
||||
*prompts[3:],
|
||||
)
|
||||
)
|
||||
|
@ -1,14 +1,31 @@
|
||||
import Musicreater
|
||||
from Musicreater.utils import load_decode_msq_flush_release, load_decode_msq_metainfo
|
||||
|
||||
from rich.pretty import pprint
|
||||
|
||||
msc_seq = Musicreater.MusicSequence.from_mido(
|
||||
Musicreater.mido.MidiFile("./resources/测试片段.mid",),
|
||||
Musicreater.mido.MidiFile(
|
||||
"./resources/测试片段.mid",
|
||||
),
|
||||
"TEST-测试片段",
|
||||
)
|
||||
|
||||
with open("test.msq","wb") as f:
|
||||
pprint("音乐源取入成功:")
|
||||
pprint(msc_seq)
|
||||
|
||||
with open("test.msq", "wb") as f:
|
||||
f.write(msq_bytes := msc_seq.encode_dump())
|
||||
|
||||
with open("test.msq","rb") as f:
|
||||
with open("test.msq", "rb") as f:
|
||||
msc_seq_r = Musicreater.MusicSequence.load_decode(f.read())
|
||||
|
||||
print(msc_seq_r)
|
||||
|
||||
pprint("常规 MSQ 读取成功:")
|
||||
pprint(msc_seq_r)
|
||||
|
||||
|
||||
with open("test.msq", "rb") as f:
|
||||
pprint("流式 MSQ 元数据:")
|
||||
pprint(metas := load_decode_msq_metainfo(f))
|
||||
pprint("流式 MSQ 音符序列:")
|
||||
for i in load_decode_msq_flush_release(f, metas[-1], metas[-2]):
|
||||
pprint(i)
|
||||
|
@ -6,10 +6,6 @@ Copyright © 2025 Eilles & bgArray
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
@ -130,6 +126,15 @@ print(
|
||||
input("midi路径:"),
|
||||
play_speed=float(input("播放速度:")),
|
||||
old_exe_format=True,
|
||||
note_table_replacement={
|
||||
"note.iron_xylophone": "note.xylophone",
|
||||
"note.cow_bell": "note.xylophone",
|
||||
"note.didgeridoo": "note.guitar",
|
||||
"note.bit": "note.harp",
|
||||
"note.banjo": "note.flute",
|
||||
"note.pling": "note.harp",
|
||||
},
|
||||
# pitched_note_table=Musicreater.MM_NBS_PITCHED_INSTRUMENT_TABLE,
|
||||
),
|
||||
input("输出路径:"),
|
||||
Musicreater.experiment.ProgressBarStyle(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user