diff --git a/Musicreater/__init__.py b/Musicreater/__init__.py index 192c038..c0f6df2 100644 --- a/Musicreater/__init__.py +++ b/Musicreater/__init__.py @@ -17,8 +17,8 @@ Terms & Conditions: License.md in the root directory # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md -__version__ = "1.7.4" -__vername__ = "部分音色更新" +__version__ = "1.8" +__vername__ = "音调总体偏移的处理支持" __author__ = ( ("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"), @@ -27,6 +27,7 @@ __author__ = ( ) __all__ = [ # 主要类 + "MusicSequence", "MidiConvert", # 附加类 "SingleNote", @@ -35,7 +36,7 @@ __all__ = [ # "TimeStamp", 未来功能 # 默认值 "DEFAULT_PROGRESSBAR_STYLE", - "MM_INSTRUMENT_DEVIATION_TABLE", + "MM_INSTRUMENT_RANGE_TABLE", "MM_CLASSIC_PITCHED_INSTRUMENT_TABLE", "MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE", "MM_TOUCH_PITCHED_INSTRUMENT_TABLE", diff --git a/Musicreater/constants.py b/Musicreater/constants.py index e66d000..31f3074 100644 --- a/Musicreater/constants.py +++ b/Musicreater/constants.py @@ -302,7 +302,7 @@ MIDI_PITCHED_NOTE_NAME_TABLE: Dict[int, Tuple[str, str]] = { 111: ("古提琴", "Fiddle"), 112: ("唢呐", "Shanai"), 113: ("铃铛", "Tinkle Bell"), - 114: ("拉丁打铃", "Agogo"), + 114: ("阿哥铃", "Agogo"), 115: ("钢鼓", "Steel Drums"), 116: ("木块", "Woodblock"), 117: ("太鼓", "Taiko Drum"), @@ -353,8 +353,8 @@ MIDI_PERCUSSION_NOTE_NAME_TABLE: Dict[int, Tuple[str, str]] = { 64: ("低音康加鼓", "Low Conga"), 65: ("高音天巴鼓", "High Timbale"), 66: ("低音天巴鼓", "Low Timbale"), - 67: ("高音阿哥哥", "High Agogo"), - 68: ("低音阿哥哥", "Low Agogo"), + 67: ("高音阿哥铃", "High Agogo"), + 68: ("低音阿哥铃", "Low Agogo"), 69: ("串珠", "Cabasa"), 70: ("沙槌", "Maracas"), 71: ("短口哨", "Short Whistle"), @@ -414,402 +414,589 @@ MC_INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str, ...]] = { # Midi对MC通用对照表 -MM_INSTRUMENT_DEVIATION_TABLE: Dict[str, int] = { - "note.harp": 6, - "note.pling": 6, - "note.guitar": 7, - "note.iron_xylophone": 6, - "note.bell": 4, - "note.xylophone": 4, - "note.chime": 4, - "note.banjo": 6, - "note.flute": 5, - "note.bass": 8, - "note.snare": -1, - "note.didgeridoo": 8, - "mob.zombie.wood": -1, - "note.bit": 6, - "note.hat": -1, - "note.bd": -1, - "firework.blast": -1, - "firework.twinkle": -1, - "fire.ignite": -1, - "note.share": -1, - "note.cow_bell": 5, +MM_INSTRUMENT_RANGE_TABLE: Dict[str, Tuple[int, int]] = { + "note.harp": (42, 66), + "note.pling": (42, 66), + "note.guitar": (30, 54), + "note.iron_xylophone": (42, 66), + "note.bell": (66, 90), + "note.xylophone": (66, 90), + "note.chime": (66, 90), + "note.banjo": (42, 66), + "note.flute": (54, 78), + "note.bass": (18, 42), + "note.snare": (-1, 128), # 实际上是 0~127 + "note.didgeridoo": (18, 42), + "mob.zombie.wood": (-1, 128), + "note.bit": (42, 66), + "note.hat": (-1, 128), + "note.bd": (-1, 128), + "note.basedrum": (-1, 128), + "firework.blast": (-1, 128), + "firework.twinkle": (-1, 128), + "fire.ignite": (-1, 128), + "note.cow_bell": (54, 78), } -"""不同乐器的音调偏离对照表""" +"""不同乐器的音域偏离对照表""" # Midi乐器对MC乐器对照表 -MM_CLASSIC_PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = { - 0: ("note.harp", 6), - 1: ("note.harp", 6), - 2: ("note.pling", 6), - 3: ("note.harp", 6), - 4: ("note.pling", 6), - 5: ("note.pling", 6), - 6: ("note.harp", 6), - 7: ("note.harp", 6), - 8: ("note.share", -1), # 打击乐器无音域 - 9: ("note.harp", 6), - 10: ("note.didgeridoo", 8), - 11: ("note.harp", 6), - 12: ("note.xylophone", 4), - 13: ("note.chime", 4), - 14: ("note.harp", 6), - 15: ("note.harp", 6), - 16: ("note.bass", 8), - 17: ("note.harp", 6), - 18: ("note.harp", 6), - 19: ("note.harp", 6), - 20: ("note.harp", 6), - 21: ("note.harp", 6), - 22: ("note.harp", 6), - 23: ("note.guitar", 7), - 24: ("note.guitar", 7), - 25: ("note.guitar", 7), - 26: ("note.guitar", 7), - 27: ("note.guitar", 7), - 28: ("note.guitar", 7), - 29: ("note.guitar", 7), - 30: ("note.guitar", 7), - 31: ("note.bass", 8), - 32: ("note.bass", 8), - 33: ("note.bass", 8), - 34: ("note.bass", 8), - 35: ("note.bass", 8), - 36: ("note.bass", 8), - 37: ("note.bass", 8), - 38: ("note.bass", 8), - 39: ("note.bass", 8), - 40: ("note.harp", 6), - 41: ("note.harp", 6), - 42: ("note.harp", 6), - 43: ("note.harp", 6), - 44: ("note.iron_xylophone", 6), - 45: ("note.guitar", 7), - 46: ("note.harp", 6), - 47: ("note.harp", 6), - 48: ("note.guitar", 7), - 49: ("note.guitar", 7), - 50: ("note.bit", 6), - 51: ("note.bit", 6), - 52: ("note.harp", 6), - 53: ("note.harp", 6), - 54: ("note.bit", 6), - 55: ("note.flute", 5), - 56: ("note.flute", 5), - 57: ("note.flute", 5), - 58: ("note.flute", 5), - 59: ("note.flute", 5), - 60: ("note.flute", 5), - 61: ("note.flute", 5), - 62: ("note.flute", 5), - 63: ("note.flute", 5), - 64: ("note.bit", 6), - 65: ("note.bit", 6), - 66: ("note.bit", 6), - 67: ("note.bit", 6), - 68: ("note.flute", 5), - 69: ("note.harp", 6), - 70: ("note.harp", 6), - 71: ("note.flute", 5), - 72: ("note.flute", 5), - 73: ("note.flute", 5), - 74: ("note.harp", 6), - 75: ("note.flute", 5), - 76: ("note.harp", 6), - 77: ("note.harp", 6), - 78: ("note.harp", 6), - 79: ("note.harp", 6), - 80: ("note.bit", 6), - 81: ("note.bit", 6), - 82: ("note.bit", 6), - 83: ("note.bit", 6), - 84: ("note.bit", 6), - 85: ("note.bit", 6), - 86: ("note.bit", 6), - 87: ("note.bit", 6), - 88: ("note.bit", 6), - 89: ("note.bit", 6), - 90: ("note.bit", 6), - 91: ("note.bit", 6), - 92: ("note.bit", 6), - 93: ("note.bit", 6), - 94: ("note.bit", 6), - 95: ("note.bit", 6), - 96: ("note.bit", 6), - 97: ("note.bit", 6), - 98: ("note.bit", 6), - 99: ("note.bit", 6), - 100: ("note.bit", 6), - 101: ("note.bit", 6), - 102: ("note.bit", 6), - 103: ("note.bit", 6), - 104: ("note.harp", 6), - 105: ("note.banjo", 6), - 106: ("note.harp", 6), - 107: ("note.harp", 6), - 108: ("note.harp", 6), - 109: ("note.harp", 6), - 110: ("note.harp", 6), - 111: ("note.guitar", 7), - 112: ("note.harp", 6), - 113: ("note.bell", 4), - 114: ("note.harp", 6), - 115: ("note.cow_bell", 5), - 116: ("note.bd", -1), # 打击乐器无音域 - 117: ("note.bass", 8), - 118: ("note.bit", 6), - 119: ("note.bd", -1), # 打击乐器无音域 - 120: ("note.guitar", 7), - 121: ("note.harp", 6), - 122: ("note.harp", 6), - 123: ("note.harp", 6), - 124: ("note.harp", 6), - 125: ("note.hat", -1), # 打击乐器无音域 - 126: ("note.bd", -1), # 打击乐器无音域 - 127: ("note.snare", -1), # 打击乐器无音域 +MM_CLASSIC_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { + 0: "note.harp", + 1: "note.harp", + 2: "note.pling", + 3: "note.harp", + 4: "note.pling", + 5: "note.pling", + 6: "note.harp", + 7: "note.harp", + 8: "note.snare", + 9: "note.harp", + 10: "note.didgeridoo", + 11: "note.harp", + 12: "note.xylophone", + 13: "note.chime", + 14: "note.harp", + 15: "note.harp", + 16: "note.bass", + 17: "note.harp", + 18: "note.harp", + 19: "note.harp", + 20: "note.harp", + 21: "note.harp", + 22: "note.harp", + 23: "note.guitar", + 24: "note.guitar", + 25: "note.guitar", + 26: "note.guitar", + 27: "note.guitar", + 28: "note.guitar", + 29: "note.guitar", + 30: "note.guitar", + 31: "note.bass", + 32: "note.bass", + 33: "note.bass", + 34: "note.bass", + 35: "note.bass", + 36: "note.bass", + 37: "note.bass", + 38: "note.bass", + 39: "note.bass", + 40: "note.harp", + 41: "note.harp", + 42: "note.harp", + 43: "note.harp", + 44: "note.iron_xylophone", + 45: "note.guitar", + 46: "note.harp", + 47: "note.harp", + 48: "note.guitar", + 49: "note.guitar", + 50: "note.bit", + 51: "note.bit", + 52: "note.harp", + 53: "note.harp", + 54: "note.bit", + 55: "note.flute", + 56: "note.flute", + 57: "note.flute", + 58: "note.flute", + 59: "note.flute", + 60: "note.flute", + 61: "note.flute", + 62: "note.flute", + 63: "note.flute", + 64: "note.bit", + 65: "note.bit", + 66: "note.bit", + 67: "note.bit", + 68: "note.flute", + 69: "note.harp", + 70: "note.harp", + 71: "note.flute", + 72: "note.flute", + 73: "note.flute", + 74: "note.harp", + 75: "note.flute", + 76: "note.harp", + 77: "note.harp", + 78: "note.harp", + 79: "note.harp", + 80: "note.bit", + 81: "note.bit", + 82: "note.bit", + 83: "note.bit", + 84: "note.bit", + 85: "note.bit", + 86: "note.bit", + 87: "note.bit", + 88: "note.bit", + 89: "note.bit", + 90: "note.bit", + 91: "note.bit", + 92: "note.bit", + 93: "note.bit", + 94: "note.bit", + 95: "note.bit", + 96: "note.bit", + 97: "note.bit", + 98: "note.bit", + 99: "note.bit", + 100: "note.bit", + 101: "note.bit", + 102: "note.bit", + 103: "note.bit", + 104: "note.harp", + 105: "note.banjo", + 106: "note.harp", + 107: "note.harp", + 108: "note.harp", + 109: "note.harp", + 110: "note.harp", + 111: "note.guitar", + 112: "note.harp", + 113: "note.bell", + 114: "note.harp", + 115: "note.cow_bell", + 116: "note.bd", + 117: "note.bass", + 118: "note.bit", + 119: "note.bd", + 120: "note.guitar", + 121: "note.harp", + 122: "note.harp", + 123: "note.harp", + 124: "note.harp", + 125: "note.hat", + 126: "note.bd", + 127: "note.snare", } """“经典”乐音乐器对照表""" -MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = { - 34: ("note.bd", -1), - 35: ("note.bd", -1), - 36: ("note.hat", -1), - 37: ("note.snare", -1), - 38: ("note.snare", -1), - 39: ("note.snare", -1), - 40: ("note.hat", -1), - 41: ("note.snare", -1), - 42: ("note.hat", -1), - 43: ("note.snare", -1), - 44: ("note.snare", -1), - 45: ("note.bell", 4), - 46: ("note.snare", -1), - 47: ("note.snare", -1), - 48: ("note.bell", 4), - 49: ("note.hat", -1), - 50: ("note.bell", 4), - 51: ("note.bell", 4), - 52: ("note.bell", 4), - 53: ("note.bell", 4), - 54: ("note.bell", 4), - 55: ("note.bell", 4), - 56: ("note.snare", -1), - 57: ("note.hat", -1), - 58: ("note.chime", 4), - 59: ("note.iron_xylophone", 6), - 60: ("note.bd", -1), - 61: ("note.bd", -1), - 62: ("note.xylophone", 4), - 63: ("note.xylophone", 4), - 64: ("note.xylophone", 4), - 65: ("note.hat", -1), - 66: ("note.bell", 4), - 67: ("note.bell", 4), - 68: ("note.hat", -1), - 69: ("note.hat", -1), - 70: ("note.snare", -1), - 71: ("note.flute", 5), - 72: ("note.hat", -1), - 73: ("note.hat", -1), - 74: ("note.xylophone", 4), - 75: ("note.hat", -1), - 76: ("note.hat", -1), - 77: ("note.xylophone", 4), - 78: ("note.xylophone", 4), - 79: ("note.bell", 4), - 80: ("note.bell", 4), +MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = { + 34: "note.bd", + 35: "note.bd", + 36: "note.hat", + 37: "note.snare", + 38: "note.snare", + 39: "note.snare", + 40: "note.hat", + 41: "note.snare", + 42: "note.hat", + 43: "note.snare", + 44: "note.snare", + 45: "note.bell", + 46: "note.snare", + 47: "note.snare", + 48: "note.bell", + 49: "note.hat", + 50: "note.bell", + 51: "note.bell", + 52: "note.bell", + 53: "note.bell", + 54: "note.bell", + 55: "note.bell", + 56: "note.snare", + 57: "note.hat", + 58: "note.chime", + 59: "note.iron_xylophone", + 60: "note.bd", + 61: "note.bd", + 62: "note.xylophone", + 63: "note.xylophone", + 64: "note.xylophone", + 65: "note.hat", + 66: "note.bell", + 67: "note.bell", + 68: "note.hat", + 69: "note.hat", + 70: "note.snare", + 71: "note.flute", + 72: "note.hat", + 73: "note.hat", + 74: "note.xylophone", + 75: "note.hat", + 76: "note.hat", + 77: "note.xylophone", + 78: "note.xylophone", + 79: "note.bell", + 80: "note.bell", } """“经典”打击乐器对照表""" -# 以下是由 Touch “偷吃” 带来的高准确率音效对照表 +# 以下是由 Touch “偷吃” 带来的高准确率音色对照表 # 包括乐音乐器对照和打击乐器对照 -MM_TOUCH_PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = { - 0: ("note.harp", 6), - 1: ("note.harp", 6), - 2: ("note.pling", 6), - 3: ("note.harp", 6), - 4: ("note.pling", 6), - 5: ("note.pling", 6), - 6: ("note.guitar", 7), - 7: ("note.guitar", 7), - 8: ("note.iron_xylophone", 8), - 9: ("note.bell", 4), # 打击乐器无音域 - 10: ("note.iron_xylophone", 6), - 11: ("note.iron_xylophone", 6), - 12: ("note.iron_xylophone", 6), - 13: ("note.xylophone", 4), - 14: ("note.chime", 4), - 15: ("note.banjo", 6), - 16: ("note.xylophone", 6), - 17: ("note.iron_xylophone", 6), - 18: ("note.flute", 5), - 19: ("note.flute", 5), - 20: ("note.flute", 5), - 21: ("note.flute", 5), - 22: ("note.flute", 5), - 23: ("note.flute", 5), - 24: ("note.guitar", 7), - 25: ("note.guitar", 7), - 26: ("note.guitar", 7), - 27: ("note.guitar", 7), - 28: ("note.guitar", 7), - 29: ("note.guitar", 7), - 30: ("note.guitar", 7), - 31: ("note.bass", 8), - 32: ("note.bass", 8), - 33: ("note.bass", 8), - 34: ("note.bass", 8), - 35: ("note.bass", 8), - 36: ("note.bass", 8), - 37: ("note.bass", 8), - 38: ("note.bass", 8), - 39: ("note.bass", 8), - 40: ("note.flute", 5), - 41: ("note.flute", 5), - 42: ("note.flute", 5), - 43: ("note.bass", 8), - 44: ("note.flute", 5), - 45: ("note.iron_xylophone", 6), - 46: ("note.harp", 6), - 47: ("note.snare", -1), - 48: ("note.flute", 5), - 49: ("note.flute", 5), - 50: ("note.flute", 5), - 51: ("note.flute", 5), - 52: ("note.didgeridoo", 5), - 53: ("note.flute", 5), # 合唱“啊”音 - 54: ("note.flute", 5), # 人声“嘟”音 - 55: ("mob.zombie.wood", -1), # 合成人声 - 56: ("note.flute", 5), - 57: ("note.flute", 5), - 58: ("note.flute", 5), - 59: ("note.flute", 5), - 60: ("note.flute", 5), - 61: ("note.flute", 5), - 62: ("note.flute", 5), - 63: ("note.flute", 5), - 64: ("note.bit", 6), - 65: ("note.bit", 6), - 66: ("note.bit", 6), - 67: ("note.bit", 6), - 68: ("note.flute", 5), - 69: ("note.bit", 6), - 70: ("note.banjo", 6), - 71: ("note.flute", 5), - 72: ("note.flute", 5), - 73: ("note.flute", 5), - 74: ("note.flute", 5), - 75: ("note.flute", 5), - 76: ("note.iron_xylophone", 6), - 77: ("note.iron_xylophone", 6), - 78: ("note.flute", 5), - 79: ("note.flute", 5), - 80: ("note.bit", 6), - 81: ("note.bit", 6), - 82: ("note.flute", 5), - 83: ("note.flute", 5), - 84: ("note.guitar", 7), - 85: ("note.flute", 5), - 86: ("note.bass", 8), - 87: ("note.bass", 8), - 88: ("note.bit", 6), - 89: ("note.flute", 5), - 90: ("note.bit", 6), - 91: ("note.flute", 5), - 92: ("note.bell", 4), - 93: ("note.guitar", 7), - 94: ("note.flute", 5), - 95: ("note.bit", 6), - 96: ("note.bit", 6), # 雨声 - 97: ("note.flute", 5), - 98: ("note.bell", 4), - 99: ("note.bit", 6), # 大气 - 100: ("note.bit", 6), # 明亮 - 101: ("note.bit", 6), # 鬼怪 - 102: ("note.bit", 6), # 回声 - 103: ("note.bit", 6), # 科幻 - 104: ("note.iron_xylophone", 6), - 105: ("note.banjo", 6), - 106: ("note.harp", 6), - 107: ("note.harp", 6), - 108: ("note.bell", 4), - 109: ("note.flute", 5), - 110: ("note.flute", 5), - 111: ("note.flute", 5), - 112: ("note.bell", 4), - 113: ("note.xylophone", 4), - 114: ("note.flute", 5), - 115: ("note.hat", -1), # 打击乐器无音域 - 116: ("note.snare", -1), # 打击乐器无音域 - 117: ("note.snare", -1), # 打击乐器无音域 - 118: ("note.bd", -1), # 打击乐器无音域 - 119: ("firework.blast", -1), # 打击乐器无音域 - 120: ("note.guitar", 7), # 吉他还把杂音 - 121: ("note.harp", 6), # 呼吸声 - 122: ("note.harp", 6), # 海浪声 - 123: ("note.harp", 6), # 鸟鸣 - 124: ("note.bit", 6), - 125: ("note.hat", -1), # 直升机 - 126: ("firework.twinkle", -1), # 打击乐器无音域 - 127: ("mob.zombie.wood", -1), # 打击乐器无音域 +MM_TOUCH_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { + 0: "note.harp", + 1: "note.harp", + 2: "note.pling", + 3: "note.harp", + 4: "note.pling", + 5: "note.pling", + 6: "note.guitar", + 7: "note.guitar", + 8: "note.iron_xylophone", + 9: "note.bell", + 10: "note.iron_xylophone", + 11: "note.iron_xylophone", + 12: "note.iron_xylophone", + 13: "note.xylophone", + 14: "note.chime", + 15: "note.banjo", + 16: "note.xylophone", + 17: "note.iron_xylophone", + 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.guitar", + 28: "note.guitar", + 29: "note.guitar", + 30: "note.guitar", + 31: "note.bass", + 32: "note.bass", + 33: "note.bass", + 34: "note.bass", + 35: "note.bass", + 36: "note.bass", + 37: "note.bass", + 38: "note.bass", + 39: "note.bass", + 40: "note.flute", + 41: "note.flute", + 42: "note.flute", + 43: "note.bass", + 44: "note.flute", + 45: "note.iron_xylophone", + 46: "note.harp", + 47: "note.snare", + 48: "note.flute", + 49: "note.flute", + 50: "note.flute", + 51: "note.flute", + 52: "note.didgeridoo", + 53: "note.flute", + 54: "note.flute", + 55: "mob.zombie.wood", + 56: "note.flute", + 57: "note.flute", + 58: "note.flute", + 59: "note.flute", + 60: "note.flute", + 61: "note.flute", + 62: "note.flute", + 63: "note.flute", + 64: "note.bit", + 65: "note.bit", + 66: "note.bit", + 67: "note.bit", + 68: "note.flute", + 69: "note.bit", + 70: "note.banjo", + 71: "note.flute", + 72: "note.flute", + 73: "note.flute", + 74: "note.flute", + 75: "note.flute", + 76: "note.iron_xylophone", + 77: "note.iron_xylophone", + 78: "note.flute", + 79: "note.flute", + 80: "note.bit", + 81: "note.bit", + 82: "note.flute", + 83: "note.flute", + 84: "note.guitar", + 85: "note.flute", + 86: "note.bass", + 87: "note.bass", + 88: "note.bit", + 89: "note.flute", + 90: "note.bit", + 91: "note.flute", + 92: "note.bell", + 93: "note.guitar", + 94: "note.flute", + 95: "note.bit", + 96: "note.bit", + 97: "note.flute", + 98: "note.bell", + 99: "note.bit", + 100: "note.bit", + 101: "note.bit", + 102: "note.bit", + 103: "note.bit", + 104: "note.iron_xylophone", + 105: "note.banjo", + 106: "note.harp", + 107: "note.harp", + 108: "note.bell", + 109: "note.flute", + 110: "note.flute", + 111: "note.flute", + 112: "note.bell", + 113: "note.xylophone", + 114: "note.flute", + 115: "note.hat", + 116: "note.snare", + 117: "note.snare", + 118: "note.bd", + 119: "firework.blast", + 120: "note.guitar", + 121: "note.harp", + 122: "note.harp", + 123: "note.harp", + 124: "note.bit", + 125: "note.hat", + 126: "firework.twinkle", + 127: "mob.zombie.wood", } """“偷吃”乐音乐器对照表""" -MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = { - 34: ("note.hat", -1), - 35: ("note.bd", -1), - 36: ("note.bd", -1), - 37: ("note.snare", -1), - 38: ("note.snare", -1), - 39: ("fire.ignite", 7), - 40: ("note.snare", -1), - 41: ("note.hat", -1), - 42: ("note.hat", -1), - 43: ("firework.blast", -1), - 44: ("note.hat", -1), - 45: ("note.snare", -1), - 46: ("note.snare", -1), - 47: ("note.snare", -1), - 48: ("note.bell", 4), - 49: ("note.hat", -1), - 50: ("note.bell", 4), - 51: ("note.bell", 4), - 52: ("note.bell", 4), - 53: ("note.bell", 4), - 54: ("note.bell", 4), - 55: ("note.bell", 4), - 56: ("note.snare", -1), - 57: ("note.hat", -1), - 58: ("note.chime", 4), - 59: ("note.iron_xylophone", 6), - 60: ("note.bd", -1), - 61: ("note.bd", -1), - 62: ("note.xylophone", 4), - 63: ("note.xylophone", 4), - 64: ("note.xylophone", 4), - 65: ("note.hat", -1), - 66: ("note.bell", 4), - 67: ("note.bell", 4), - 68: ("note.hat", -1), - 69: ("note.hat", -1), - 70: ("note.snare", -1), - 71: ("note.flute", 5), - 72: ("note.hat", -1), - 73: ("note.hat", -1), - 74: ("note.xylophone", 4), - 75: ("note.hat", -1), - 76: ("note.hat", -1), - 77: ("note.xylophone", 4), - 78: ("note.xylophone", 4), - 79: ("note.bell", 4), - 80: ("note.bell", 4), +MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = { + 34: "note.hat", + 35: "note.bd", + 36: "note.bd", + 37: "note.snare", + 38: "note.snare", + 39: "fire.ignite", + 40: "note.snare", + 41: "note.hat", + 42: "note.hat", + 43: "firework.blast", + 44: "note.hat", + 45: "note.snare", + 46: "note.snare", + 47: "note.snare", + 48: "note.bell", + 49: "note.hat", + 50: "note.bell", + 51: "note.bell", + 52: "note.bell", + 53: "note.bell", + 54: "note.bell", + 55: "note.bell", + 56: "note.snare", + 57: "note.hat", + 58: "note.chime", + 59: "note.iron_xylophone", + 60: "note.bd", + 61: "note.bd", + 62: "note.xylophone", + 63: "note.xylophone", + 64: "note.xylophone", + 65: "note.hat", + 66: "note.bell", + 67: "note.bell", + 68: "note.hat", + 69: "note.hat", + 70: "note.snare", + 71: "note.flute", + 72: "note.hat", + 73: "note.hat", + 74: "note.xylophone", + 75: "note.hat", + 76: "note.hat", + 77: "note.xylophone", + 78: "note.xylophone", + 79: "note.bell", + 80: "note.bell", } """“偷吃”打击乐器对照表""" +# 以下是 Dislink “断联” 的音色对照表 +# 包括乐音乐器对照和打击乐器对照 + +MM_DISLINK_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { + 0: "note.harp", + 1: "note.harp", + 2: "note.pling", + 3: "note.harp", + 4: "note.harp", + 5: "note.harp", + 6: "note.harp", + 7: "note.harp", + 8: "note.iron_xylophone", + 9: "note.bell", + 10: "note.iron_xylophone", + 11: "note.iron_xylophone", + 12: "note.iron_xylophone", + 13: "note.iron_xylophone", + 14: "note.chime", + 15: "note.iron_xylophone", + 16: "note.harp", + 17: "note.harp", + 18: "note.harp", + 19: "note.harp", + 20: "note.harp", + 21: "note.harp", + 22: "note.harp", + 23: "note.harp", + 24: "note.guitar", + 25: "note.guitar", + 26: "note.guitar", + 27: "note.guitar", + 28: "note.guitar", + 29: "note.guitar", + 30: "note.guitar", + 31: "note.guitar", + 32: "note.bass", + 33: "note.bass", + 34: "note.bass", + 35: "note.bass", + 36: "note.bass", + 37: "note.bass", + 38: "note.bass", + 39: "note.bass", + 40: "note.harp", + 41: "note.flute", + 42: "note.flute", + 43: "note.flute", + 44: "note.flute", + 45: "note.harp", + 46: "note.harp", + 47: "note.harp", + 48: "note.harp", + 49: "note.harp", + 50: "note.harp", + 51: "note.harp", + 52: "note.harp", + 53: "note.harp", + 54: "note.harp", + 55: "note.harp", + 56: "note.harp", + 57: "note.harp", + 58: "note.harp", + 59: "note.harp", + 60: "note.harp", + 61: "note.harp", + 62: "note.harp", + 63: "note.harp", + 64: "note.harp", + 65: "note.harp", + 66: "note.harp", + 67: "note.harp", + 68: "note.harp", + 69: "note.harp", + 70: "note.harp", + 71: "note.harp", + 72: "note.flute", + 73: "note.flute", + 74: "note.flute", + 75: "note.flute", + 76: "note.flute", + 77: "note.flute", + 78: "note.flute", + 79: "note.flute", + 80: "note.bit", + 81: "note.bit", + 82: "note.harp", + 83: "note.harp", + 84: "note.harp", + 85: "note.harp", + 86: "note.harp", + 87: "note.harp", + 88: "note.harp", + 89: "note.harp", + 90: "note.harp", + 91: "note.harp", + 92: "note.harp", + 93: "note.harp", + 94: "note.harp", + 95: "note.harp", + 96: "note.harp", + 97: "note.harp", + 98: "note.harp", + 99: "note.harp", + 100: "note.harp", + 101: "note.harp", + 102: "note.harp", + 103: "note.harp", + 104: "note.harp", + 105: "note.banjo", + 106: "note.harp", + 107: "note.harp", + 108: "note.harp", + 109: "note.harp", + 110: "note.harp", + 111: "note.harp", + 112: "note.cow_bell", + 113: "note.harp", + 114: "note.harp", + 115: "note.bd", + 116: "note.bd", + 117: "note.bd", + 118: "note.bd", + 119: "note.harp", + 120: "note.harp", + 121: "note.harp", + 122: "note.harp", + 123: "note.harp", + 124: "note.harp", + 125: "note.harp", + 126: "note.harp", + 127: "note.harp", +} +"""“断联”乐音乐器对照表""" + +MM_DISLINK_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = { + 34: "note.bd", + 35: "note.bd", + 36: "note.snare", + 37: "note.snare", + 38: "note.bd", + 39: "note.snare", + 40: "note.bd", + 41: "note.hat", + 42: "note.bd", + 43: "note.hat", + 44: "note.bd", + 45: "note.hat", + 46: "note.bd", + 47: "note.bd", + 48: "note.bd", + 49: "note.bd", + 50: "note.bd", + 51: "note.bd", + 52: "note.bd", + 53: "note.bd", + 54: "note.bd", + 55: "note.cow_bell", + 56: "note.bd", + 57: "note.bd", + 58: "note.bd", + 59: "note.bd", + 60: "note.bd", + 61: "note.bd", + 62: "note.bd", + 63: "note.bd", + 64: "note.bd", + 65: "note.bd", + 66: "note.bd", + 67: "note.bd", + 68: "note.bd", + 69: "note.bd", + 70: "note.bd", + 71: "note.bd", + 72: "note.bd", + 73: "note.bd", + 74: "note.bd", + 75: "note.bd", + 76: "note.bd", + 77: "note.bd", + 78: "note.bd", + 79: "note.bd", + 80: "note.bd", +} +"""“断联”打击乐器对照表""" + + # 即将启用 # height2note = { # 0.5: 0, diff --git a/Musicreater/exceptions.py b/Musicreater/exceptions.py index 4318d04..cf39919 100644 --- a/Musicreater/exceptions.py +++ b/Musicreater/exceptions.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -存放一堆报错类型 +存放一些报错类型 """ """ @@ -50,13 +50,13 @@ class MidiDestroyedError(MSCTBaseException): super().__init__("MIDI文件损坏:无法读取MIDI文件", *args) -class MidiUnboundError(MSCTBaseException): - """未定义Midi对象""" - - def __init__(self, *args): - """未绑定Midi对象""" - super().__init__("未定义MidiFile对象:你甚至没有对象就想要生孩子?", *args) +# class MidiUnboundError(MSCTBaseException): +# """未定义Midi对象(无用)""" +# def __init__(self, *args): +# """未绑定Midi对象""" +# super().__init__("未定义MidiFile对象:你甚至没有对象就想要生孩子?", *args) +# 此错误在本版本内已经不再使用 class CommandFormatError(RuntimeError): """指令格式与目标格式不匹配而引起的错误""" diff --git a/Musicreater/experiment.py b/Musicreater/experiment.py index 375a223..43ef3c5 100644 --- a/Musicreater/experiment.py +++ b/Musicreater/experiment.py @@ -24,6 +24,7 @@ from .main import ( MidiConvert, MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE, + mido, ) from .types import Tuple, List, Dict, ChannelType @@ -122,7 +123,7 @@ class FutureMidiConvertM4(MidiConvert): notes_list: List[SingleNote] = [] # 此处 我们把通道视为音轨 - for channel in self.to_music_note_channels().values(): + for channel in self.channels.values(): for note in channel: note.set_info( note_to_command_parameters( @@ -132,8 +133,11 @@ class FutureMidiConvertM4(MidiConvert): if note.percussive else self.pitched_note_reference_table ), - (max_volume) if note.track_no == 0 else (max_volume * 0.9), - self.volume_processing_function, + deviation=0, + volume_percentage=( + (max_volume) if note.track_no == 0 else (max_volume * 0.9) + ), + volume_processing_method=self.volume_processing_function, ) ) @@ -180,6 +184,7 @@ class FutureMidiConvertM5(MidiConvert): def to_music_channels( self, + midi: mido.MidiFile, ) -> ChannelType: """ 使用金羿的转换思路,将midi解析并转换为频道信息字典 @@ -189,10 +194,11 @@ class FutureMidiConvertM5(MidiConvert): 以频道作为分割的Midi信息字典: Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],] """ - if self.midi is None: - raise MidiUnboundError( - "你是否正在使用的是一个由 copy_important 生成的MidiConvert对象?这是不可复用的。" - ) + + # if self.midi is None: + # raise MidiUnboundError( + # "你是否正在使用的是一个由 copy_important 生成的MidiConvert对象?这是不可复用的。" + # ) # 一个midi中仅有16个通道 我们通过通道来识别而不是音轨 midi_channels: ChannelType = empty_midi_channels() @@ -200,7 +206,7 @@ class FutureMidiConvertM5(MidiConvert): # 我们来用通道统计音乐信息 # 但是是用分轨的思路的 - for track_no, track in enumerate(self.midi.tracks): + for track_no, track in enumerate(midi.tracks): microseconds = 0 if not track: continue @@ -209,7 +215,7 @@ class FutureMidiConvertM5(MidiConvert): for msg in track: if msg.time != 0: - microseconds += msg.time * tempo / self.midi.ticks_per_beat / 1000 + microseconds += msg.time * tempo / midi.ticks_per_beat / 1000 if msg.is_meta: if msg.type == "set_tempo": @@ -257,6 +263,7 @@ class FutureMidiConvertM5(MidiConvert): # 神奇的偏移音 def to_command_list_in_delay( self, + midi: mido.MidiFile, max_volume: float = 1.0, speed: float = 1.0, player_selector: str = "@a", @@ -282,7 +289,9 @@ class FutureMidiConvertM5(MidiConvert): raise ZeroSpeedError("播放速度仅可为正实数") max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume) - self.to_music_channels() + self.to_music_channels( + midi=midi, + ) tracks = {} InstID = -1 @@ -304,12 +313,12 @@ class FutureMidiConvertM5(MidiConvert): InstID = msg[1] elif msg[0] == "NoteS": - soundID, _X = ( - inst_to_sould_with_deviation( + soundID = ( + midi_inst_to_mc_sould( msg[1], MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE ) if SpecialBits - else inst_to_sould_with_deviation( + else midi_inst_to_mc_sould( InstID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE ) ) @@ -324,7 +333,7 @@ class FutureMidiConvertM5(MidiConvert): + ( "" if SpecialBits - else f"{2 ** ((msg[1] - 60 - _X) / 12)}" + else f"{2 ** ((msg[1] - 66) / 12)}" ) ) except KeyError: @@ -334,7 +343,7 @@ class FutureMidiConvertM5(MidiConvert): + ( "" if SpecialBits - else f"{2 ** ((msg[1] - 60 - _X) / 12)}" + else f"{2 ** ((msg[1] - 66) / 12)}" ) ] diff --git a/Musicreater/main.py b/Musicreater/main.py index 7fc7b91..4fa6b74 100644 --- a/Musicreater/main.py +++ b/Musicreater/main.py @@ -70,21 +70,203 @@ tick * tempo / 1000000.0 / ticks_per_beat * 一秒多少游戏刻 """ -VoidMido = Union[mido.MidiFile, None] # void mido -""" -空Midi类类型 -""" +# VoidMido = Union[mido.MidiFile, None] # void mido +# """ +# 空Midi类类型 +# """ +# 已经成为历史了 @dataclass(init=False) -class MidiConvert: +class MusicSequence: + """ + 音乐曲谱序列存储类 + """ + + music_name: str + """Midi乐曲名""" + + channels: NoteChannelType + """频道信息字典""" + + def __init__( + self, + name_of_music: str, + channels_of_notes: NoteChannelType, + ) -> None: + self.music_name = name_of_music + self.channels = channels_of_notes + + @classmethod + def from_mido( + cls, + mido_file: mido.MidiFile, + midi_music_name, + default_tempo: int = mido.midifiles.midifiles.DEFAULT_TEMPO, + mismatch_error_ignorance: bool = True, + ): + + return cls( + midi_music_name, + cls.to_music_note_channels( + midi=mido_file, + default_tempo_value=default_tempo, + ignore_mismatch_error=mismatch_error_ignorance, + ), + ) + + def set_deviation(self, deviation_value: int): + self.music_deviation = deviation_value + + def rename_music(self, new_name: str): + self.music_name = new_name + + @staticmethod + def to_music_note_channels( + midi: mido.MidiFile, + default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO, + ignore_mismatch_error: bool = True, + ) -> NoteChannelType: + """ + 将midi解析并转换为频道音符字典 + + Returns + ------- + 以频道作为分割的Midi音符列表字典: + Dict[int,List[SingleNote,]] + """ + + # if midi is None: + # raise MidiUnboundError( + # "Midi参量为空。你是否正在使用的是一个由 copy_important 生成的MidiConvert对象?这是不可复用的。" + # ) + + # 一个midi中仅有16个通道 我们通过通道来识别而不是音轨 + midi_channels: NoteChannelType = empty_midi_channels(staff=[]) + tempo = default_tempo_value + + # 我们来用通道统计音乐信息 + # 但是是用分轨的思路的 + for track_no, track in enumerate(midi.tracks): + microseconds = 0 + if not track: + continue + + note_queue_A: Dict[ + int, + List[ + Tuple[ + int, + int, + ] + ], + ] = empty_midi_channels(staff=[]) + note_queue_B: Dict[ + int, + List[ + Tuple[ + int, + int, + ] + ], + ] = empty_midi_channels(staff=[]) + + channel_program: Dict[int, int] = empty_midi_channels(staff=-1) + + for msg in track: + if msg.time != 0: + microseconds += msg.time * tempo / midi.ticks_per_beat / 1000 + + if msg.is_meta: + if msg.type == "set_tempo": + tempo = msg.tempo + else: + if msg.type == "program_change": + channel_program[msg.channel] = msg.program + + elif msg.type == "note_on" and msg.velocity != 0: + note_queue_A[msg.channel].append( + (msg.note, channel_program[msg.channel]) + ) + note_queue_B[msg.channel].append((msg.velocity, microseconds)) + + elif (msg.type == "note_off") or ( + msg.type == "note_on" and msg.velocity == 0 + ): + if (msg.note, channel_program[msg.channel]) in note_queue_A[ + msg.channel + ]: + _velocity, _ms = note_queue_B[msg.channel][ + note_queue_A[msg.channel].index( + (msg.note, channel_program[msg.channel]) + ) + ] + note_queue_A[msg.channel].remove( + (msg.note, channel_program[msg.channel]) + ) + note_queue_B[msg.channel].remove((_velocity, _ms)) + midi_channels[msg.channel].append( + SingleNote( + instrument=msg.note, + pitch=channel_program[msg.channel], + velocity=_velocity, + startime=_ms, + lastime=microseconds - _ms, + track_number=track_no, + is_percussion=True, + ) + if msg.channel == 9 + else SingleNote( + instrument=channel_program[msg.channel], + pitch=msg.note, + velocity=_velocity, + startime=_ms, + lastime=microseconds - _ms, + track_number=track_no, + is_percussion=False, + ) + ) + else: + if ignore_mismatch_error: + print( + "[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format( + msg + ) + ) + else: + raise NoteOnOffMismatchError( + "当前的MIDI很可能有损坏之嫌……", + msg, + "无法在上文中找到与之匹配的音符开音消息。", + ) + + """整合后的音乐通道格式 + 每个通道包括若干消息元素其中逃不过这三种: + + 1 切换乐器消息 + ("PgmC", 切换后的乐器ID: int, 距离演奏开始的毫秒) + + 2 音符开始消息 + ("NoteS", 开始的音符ID, 力度(响度), 距离演奏开始的毫秒) + + 3 音符结束消息 + ("NoteE", 结束的音符ID, 距离演奏开始的毫秒)""" + del tempo + channels = dict( + [ + (channel_no, sorted(channel_notes, key=lambda note: note.start_time)) + for channel_no, channel_notes in midi_channels.items() + ] + ) + + return channels + + +class MidiConvert(MusicSequence): """ 将Midi文件转换为我的世界内容 """ - midi: VoidMido - """MidiFile对象""" - pitched_note_reference_table: MidiInstrumentTableType """乐音乐器Midi-MC对照表""" @@ -94,18 +276,12 @@ class MidiConvert: volume_processing_function: FittingFunctionType """音量处理函数""" - midi_music_name: str - """Midi乐曲名""" - enable_old_exe_format: bool """是否启用旧版execute指令格式""" execute_cmd_head: str """execute指令头部""" - channels: Union[ChannelType, NoteChannelType] - """频道信息字典""" - music_command_list: List[SingleCommand] """音乐指令列表""" @@ -115,10 +291,15 @@ class MidiConvert: progress_bar_command: List[SingleCommand] """进度条指令列表""" + music_deviation: int + """音乐音调总偏移""" + def __init__( self, - midi_obj: VoidMido, + midi_obj: mido.MidiFile, midi_name: str, + deviation: Union[int, Literal[None]] = 0, + ignore_mismatch_error: bool = True, enable_old_exe_format: bool = False, pitched_note_rtable: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE, percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE, @@ -141,9 +322,15 @@ class MidiConvert: 打击乐器Midi-MC对照表 """ - self.midi: VoidMido = midi_obj + super(MidiConvert, self).from_mido( + midi_obj, + midi_name, + mismatch_error_ignorance=ignore_mismatch_error, + ) - self.midi_music_name: str = midi_name + self.music_deviation = ( + self.guess_deviation() if deviation is None else deviation + ) self.enable_old_exe_format: bool = enable_old_exe_format @@ -165,6 +352,7 @@ class MidiConvert: def from_midi_file( cls, midi_file_path: str, + ignore_mismatch_error: bool = True, old_exe_format: bool = False, pitched_note_table: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE, percussion_note_table: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE, @@ -192,9 +380,14 @@ class MidiConvert: try: return cls( - mido.MidiFile(midi_file_path, clip=True), + mido.MidiFile( + midi_file_path, + clip=True, + ), midi_music_name, + None, old_exe_format, + ignore_mismatch_error, pitched_note_table, percussion_note_table, vol_processing_func, @@ -207,6 +400,11 @@ class MidiConvert: # ……真的那么重要吗 # 我又几曾何时,知道祂真的会抛下我 + def guess_deviation(self) -> int: + + # 等我想想看 + return 0 + def form_progress_bar( self, max_score: int, @@ -422,7 +620,7 @@ class MidiConvert: npg_stl = ( pgs_style.replace("_", progressbar_style.played_style, i + 1) .replace("_", progressbar_style.to_play_style) - .replace(r"%%N", self.midi_music_name) + .replace(r"%%N", self.music_name) .replace( r"%%s", '"},{"score":{"name":"*","objective":"' @@ -482,145 +680,6 @@ class MidiConvert: self.progress_bar_command = result return result - def to_music_note_channels( - self, - default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO, - ignore_mismatch_error: bool = True, - ) -> NoteChannelType: - """ - 将midi解析并转换为频道音符字典 - - Returns - ------- - 以频道作为分割的Midi音符列表字典: - Dict[int,List[SingleNote,]] - """ - - if self.midi is None: - raise MidiUnboundError( - "你是否正在使用的是一个由 copy_important 生成的MidiConvert对象?这是不可复用的。" - ) - - # 一个midi中仅有16个通道 我们通过通道来识别而不是音轨 - midi_channels: NoteChannelType = empty_midi_channels(staff=[]) - tempo = default_tempo_value - - # 我们来用通道统计音乐信息 - # 但是是用分轨的思路的 - for track_no, track in enumerate(self.midi.tracks): - microseconds = 0 - if not track: - continue - - note_queue_A: Dict[ - int, - List[ - Tuple[ - int, - int, - ] - ], - ] = empty_midi_channels(staff=[]) - note_queue_B: Dict[ - int, - List[ - Tuple[ - int, - int, - ] - ], - ] = empty_midi_channels(staff=[]) - - channel_program: Dict[int, int] = empty_midi_channels(staff=-1) - - for msg in track: - if msg.time != 0: - microseconds += msg.time * tempo / self.midi.ticks_per_beat / 1000 - - if msg.is_meta: - if msg.type == "set_tempo": - tempo = msg.tempo - else: - if msg.type == "program_change": - channel_program[msg.channel] = msg.program - - elif msg.type == "note_on" and msg.velocity != 0: - note_queue_A[msg.channel].append( - (msg.note, channel_program[msg.channel]) - ) - note_queue_B[msg.channel].append((msg.velocity, microseconds)) - - elif (msg.type == "note_off") or ( - msg.type == "note_on" and msg.velocity == 0 - ): - if (msg.note, channel_program[msg.channel]) in note_queue_A[ - msg.channel - ]: - _velocity, _ms = note_queue_B[msg.channel][ - note_queue_A[msg.channel].index( - (msg.note, channel_program[msg.channel]) - ) - ] - note_queue_A[msg.channel].remove( - (msg.note, channel_program[msg.channel]) - ) - note_queue_B[msg.channel].remove((_velocity, _ms)) - midi_channels[msg.channel].append( - SingleNote( - instrument=msg.note, - pitch=channel_program[msg.channel], - velocity=_velocity, - startime=_ms, - lastime=microseconds - _ms, - track_number=track_no, - is_percussion=True, - ) - if msg.channel == 9 - else SingleNote( - instrument=channel_program[msg.channel], - pitch=msg.note, - velocity=_velocity, - startime=_ms, - lastime=microseconds - _ms, - track_number=track_no, - is_percussion=False, - ) - ) - else: - if ignore_mismatch_error: - print( - "[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format( - msg - ) - ) - else: - raise NoteOnOffMismatchError( - "当前的MIDI很可能有损坏之嫌……", - msg, - "无法在上文中找到与之匹配的音符开音消息。", - ) - - """整合后的音乐通道格式 - 每个通道包括若干消息元素其中逃不过这三种: - - 1 切换乐器消息 - ("PgmC", 切换后的乐器ID: int, 距离演奏开始的毫秒) - - 2 音符开始消息 - ("NoteS", 开始的音符ID, 力度(响度), 距离演奏开始的毫秒) - - 3 音符结束消息 - ("NoteE", 结束的音符ID, 距离演奏开始的毫秒)""" - del tempo, self.channels - self.channels = dict( - [ - (channel_no, sorted(channel_notes, key=lambda note: note.start_time)) - for channel_no, channel_notes in midi_channels.items() - ] - ) - - return self.channels - def to_command_list_in_score( self, scoreboard_name: str = "mscplay", @@ -653,7 +712,7 @@ class MidiConvert: max_score = 0 # 此处 我们把通道视为音轨 - for channel in self.to_music_note_channels().values(): + for channel in self.channels.values(): # 如果当前通道为空 则跳过 if not channel: continue @@ -676,8 +735,11 @@ class MidiConvert: if note.percussive else self.pitched_note_reference_table ), - (max_volume) if note.track_no == 0 else (max_volume * 0.9), - self.volume_processing_function, + deviation=self.music_deviation, + volume_percentage=( + (max_volume) if note.track_no == 0 else (max_volume * 0.9) + ), + volume_processing_method=self.volume_processing_function, ) this_channel.append( @@ -756,7 +818,7 @@ class MidiConvert: notes_list: List[SingleNote] = [] # 此处 我们把通道视为音轨 - for channel in self.to_music_note_channels().values(): + for channel in self.channels.values(): notes_list.extend(channel) notes_list.sort(key=lambda a: a.start_time) @@ -785,8 +847,11 @@ class MidiConvert: if note.percussive else self.pitched_note_reference_table ), - (max_volume) if note.track_no == 0 else (max_volume * 0.9), - self.volume_processing_function, + deviation=self.music_deviation, + volume_percentage=( + (max_volume) if note.track_no == 0 else (max_volume * 0.9) + ), + volume_processing_method=self.volume_processing_function, ) self.music_command_list.append( @@ -829,8 +894,8 @@ class MidiConvert: def copy_important(self): dst = MidiConvert( - midi_obj=None, - midi_name=self.midi_music_name, + midi_obj=mido.MidiFile(), + midi_name=self.music_name, enable_old_exe_format=self.enable_old_exe_format, ) dst.music_command_list = [i.copy() for i in self.music_command_list] diff --git a/Musicreater/plugin/addonpack/main.py b/Musicreater/plugin/addonpack/main.py index 75b2a04..f4655b6 100644 --- a/Musicreater/plugin/addonpack/main.py +++ b/Musicreater/plugin/addonpack/main.py @@ -68,8 +68,8 @@ def to_addon_pack_in_score( with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f: json.dump( behavior_mcpack_manifest( - pack_description=f"{midi_cvt.midi_music_name} 音乐播放包,MCFUNCTION(MCPACK) 计分播放器 - 由 音·创 生成", - pack_name=midi_cvt.midi_music_name + "播放", + pack_description=f"{midi_cvt.music_name} 音乐播放包,MCFUNCTION(MCPACK) 计分播放器 - 由 音·创 生成", + pack_name=midi_cvt.music_name + "播放", modules_description=f"无 - 由 音·创 生成", ), fp=f, @@ -136,11 +136,11 @@ def to_addon_pack_in_score( index_file.close() - if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack"): - os.remove(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack") + if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack"): + os.remove(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack") compress_zipfile( f"{data_cfg.dist_path}/temp/", - f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack", + f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack", ) shutil.rmtree(f"{data_cfg.dist_path}/temp/") @@ -198,8 +198,8 @@ def to_addon_pack_in_delay( with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f: json.dump( behavior_mcpack_manifest( - pack_description=f"{midi_cvt.midi_music_name} 音乐播放包,MCSTRUCTURE(MCPACK) 延迟播放器 - 由 音·创 生成", - pack_name=midi_cvt.midi_music_name + "播放", + pack_description=f"{midi_cvt.music_name} 音乐播放包,MCSTRUCTURE(MCPACK) 延迟播放器 - 由 音·创 生成", + pack_name=midi_cvt.music_name + "播放", modules_description=f"无 - 由 音·创 生成", ), fp=f, @@ -229,7 +229,7 @@ def to_addon_pack_in_delay( os.path.join( data_cfg.dist_path, "temp/structures/", - f"{midi_cvt.midi_music_name}_main.mcstructure", + f"{midi_cvt.music_name}_main.mcstructure", ) ), "wb+", @@ -239,7 +239,7 @@ def to_addon_pack_in_delay( del struct if data_cfg.progressbar_style: - scb_name = midi_cvt.midi_music_name[:3] + "Pgb" + scb_name = midi_cvt.music_name[:3] + "Pgb" index_file.write("scoreboard objectives add {0} dummy {0}计\n".format(scb_name)) struct_a = Structure((1, 1, 1), compability_version=compability_ver) @@ -261,14 +261,14 @@ def to_addon_pack_in_delay( os.path.join( data_cfg.dist_path, "temp/structures/", - f"{midi_cvt.midi_music_name}_start.mcstructure", + f"{midi_cvt.music_name}_start.mcstructure", ) ), "wb+", ) as f: struct_a.dump(f) - index_file.write(f"structure load {midi_cvt.midi_music_name}_start ~ ~ ~1\n") + index_file.write(f"structure load {midi_cvt.music_name}_start ~ ~ ~1\n") pgb_struct, pgbSize, pgbNowPos = commands_to_structure( midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style), @@ -281,14 +281,14 @@ def to_addon_pack_in_delay( os.path.join( data_cfg.dist_path, "temp/structures/", - f"{midi_cvt.midi_music_name}_pgb.mcstructure", + f"{midi_cvt.music_name}_pgb.mcstructure", ) ), "wb+", ) as f: pgb_struct.dump(f) - index_file.write(f"structure load {midi_cvt.midi_music_name}_pgb ~ ~1 ~1\n") + index_file.write(f"structure load {midi_cvt.music_name}_pgb ~ ~1 ~1\n") struct_a = Structure( (1, 1, 1), @@ -311,7 +311,7 @@ def to_addon_pack_in_delay( os.path.join( data_cfg.dist_path, "temp/structures/", - f"{midi_cvt.midi_music_name}_reset.mcstructure", + f"{midi_cvt.music_name}_reset.mcstructure", ) ), "wb+", @@ -321,23 +321,23 @@ def to_addon_pack_in_delay( del struct_a, pgb_struct index_file.write( - f"structure load {midi_cvt.midi_music_name}_reset ~{pgbSize[0] + 2} ~ ~1\n" + f"structure load {midi_cvt.music_name}_reset ~{pgbSize[0] + 2} ~ ~1\n" ) index_file.write( - f"structure load {midi_cvt.midi_music_name}_main ~{pgbSize[0] + 2} ~1 ~1\n" + f"structure load {midi_cvt.music_name}_main ~{pgbSize[0] + 2} ~1 ~1\n" ) else: - index_file.write(f"structure load {midi_cvt.midi_music_name}_main ~ ~ ~1\n") + index_file.write(f"structure load {midi_cvt.music_name}_main ~ ~ ~1\n") index_file.close() - if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack"): - os.remove(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack") + if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack"): + os.remove(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack") compress_zipfile( f"{data_cfg.dist_path}/temp/", - f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack", + f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack", ) shutil.rmtree(f"{data_cfg.dist_path}/temp/") @@ -395,8 +395,8 @@ def to_addon_pack_in_repeater( with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f: json.dump( behavior_mcpack_manifest( - pack_description=f"{midi_cvt.midi_music_name} 音乐播放包,MCSTRUCTURE(MCPACK) 中继器播放器 - 由 音·创 生成", - pack_name=midi_cvt.midi_music_name + "播放", + pack_description=f"{midi_cvt.music_name} 音乐播放包,MCSTRUCTURE(MCPACK) 中继器播放器 - 由 音·创 生成", + pack_name=midi_cvt.music_name + "播放", modules_description=f"无 - 由 音·创 生成", ), fp=f, @@ -427,7 +427,7 @@ def to_addon_pack_in_repeater( os.path.join( data_cfg.dist_path, "temp/structures/", - f"{midi_cvt.midi_music_name}_main.mcstructure", + f"{midi_cvt.music_name}_main.mcstructure", ) ), "wb+", @@ -437,7 +437,7 @@ def to_addon_pack_in_repeater( del struct if data_cfg.progressbar_style: - scb_name = midi_cvt.midi_music_name[:3] + "Pgb" + scb_name = midi_cvt.music_name[:3] + "Pgb" index_file.write("scoreboard objectives add {0} dummy {0}计\n".format(scb_name)) struct_a = Structure((1, 1, 1), compability_version=compability_ver) @@ -459,14 +459,14 @@ def to_addon_pack_in_repeater( os.path.join( data_cfg.dist_path, "temp/structures/", - f"{midi_cvt.midi_music_name}_start.mcstructure", + f"{midi_cvt.music_name}_start.mcstructure", ) ), "wb+", ) as f: struct_a.dump(f) - index_file.write(f"structure load {midi_cvt.midi_music_name}_start ~ ~ ~1\n") + index_file.write(f"structure load {midi_cvt.music_name}_start ~ ~ ~1\n") pgb_struct, pgbSize, pgbNowPos = commands_to_structure( midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style), @@ -479,14 +479,14 @@ def to_addon_pack_in_repeater( os.path.join( data_cfg.dist_path, "temp/structures/", - f"{midi_cvt.midi_music_name}_pgb.mcstructure", + f"{midi_cvt.music_name}_pgb.mcstructure", ) ), "wb+", ) as f: pgb_struct.dump(f) - index_file.write(f"structure load {midi_cvt.midi_music_name}_pgb ~ ~1 ~1\n") + index_file.write(f"structure load {midi_cvt.music_name}_pgb ~ ~1 ~1\n") struct_a = Structure( (1, 1, 1), @@ -509,7 +509,7 @@ def to_addon_pack_in_repeater( os.path.join( data_cfg.dist_path, "temp/structures/", - f"{midi_cvt.midi_music_name}_reset.mcstructure", + f"{midi_cvt.music_name}_reset.mcstructure", ) ), "wb+", @@ -519,23 +519,23 @@ def to_addon_pack_in_repeater( del struct_a, pgb_struct index_file.write( - f"structure load {midi_cvt.midi_music_name}_reset ~{pgbSize[0] + 2} ~ ~1\n" + f"structure load {midi_cvt.music_name}_reset ~{pgbSize[0] + 2} ~ ~1\n" ) index_file.write( - f"structure load {midi_cvt.midi_music_name}_main ~{pgbSize[0] + 2} ~1 ~1\n" + f"structure load {midi_cvt.music_name}_main ~{pgbSize[0] + 2} ~1 ~1\n" ) else: - index_file.write(f"structure load {midi_cvt.midi_music_name}_main ~ ~ ~1\n") + index_file.write(f"structure load {midi_cvt.music_name}_main ~ ~ ~1\n") index_file.close() - if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack"): - os.remove(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack") + if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack"): + os.remove(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack") compress_zipfile( f"{data_cfg.dist_path}/temp/", - f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack", + f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack", ) shutil.rmtree(f"{data_cfg.dist_path}/temp/") diff --git a/Musicreater/plugin/bdxfile/main.py b/Musicreater/plugin/bdxfile/main.py index 41db20b..51c2e72 100644 --- a/Musicreater/plugin/bdxfile/main.py +++ b/Musicreater/plugin/bdxfile/main.py @@ -69,7 +69,7 @@ def to_BDX_file_in_score( with open( os.path.abspath( - os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx") + os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.bdx") ), "w+", ) as f: @@ -119,7 +119,7 @@ def to_BDX_file_in_score( with open( os.path.abspath( - os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx") + os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.bdx") ), "ab+", ) as f: @@ -167,7 +167,7 @@ def to_BDX_file_in_delay( with open( os.path.abspath( - os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx") + os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.bdx") ), "w+", ) as f: @@ -180,7 +180,7 @@ def to_BDX_file_in_delay( cmdBytes, size, finalPos = commands_to_BDX_bytes(cmdlist, max_height - 1) if data_cfg.progressbar_style: - scb_name = midi_cvt.midi_music_name[:3] + "Pgb" + scb_name = midi_cvt.music_name[:3] + "Pgb" _bytes += form_command_block_in_BDX_bytes( r"scoreboard objectives add {} dummy {}计".replace(r"{}", scb_name), 1, @@ -217,7 +217,7 @@ def to_BDX_file_in_delay( with open( os.path.abspath( - os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx") + os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.bdx") ), "ab+", ) as f: diff --git a/Musicreater/plugin/mcstructfile/main.py b/Musicreater/plugin/mcstructfile/main.py index 9b8f29a..ad10ffb 100644 --- a/Musicreater/plugin/mcstructfile/main.py +++ b/Musicreater/plugin/mcstructfile/main.py @@ -72,7 +72,7 @@ def to_mcstructure_file_in_delay( with open( os.path.abspath( - os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.mcstructure") + os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.mcstructure") ), "wb+", ) as f: @@ -144,7 +144,7 @@ def to_mcstructure_file_in_score( with open( os.path.abspath( - os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.mcstructure") + os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.mcstructure") ), "wb+", ) as f: @@ -207,7 +207,7 @@ def to_mcstructure_file_in_repeater( with open( os.path.abspath( - os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.mcstructure") + os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.mcstructure") ), "wb+", ) as f: diff --git a/Musicreater/subclass.py b/Musicreater/subclass.py index f77790c..bdd3d59 100644 --- a/Musicreater/subclass.py +++ b/Musicreater/subclass.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -存储许多非主要的相关类 +存储音·创附属子类 """ """ diff --git a/Musicreater/types.py b/Musicreater/types.py index c3670df..0e354d2 100644 --- a/Musicreater/types.py +++ b/Musicreater/types.py @@ -36,7 +36,7 @@ MidiNoteNameTableType = Mapping[int, Tuple[str, ...]] Midi音符名称对照表类型 """ -MidiInstrumentTableType = Mapping[int, Tuple[str, int]] +MidiInstrumentTableType = Mapping[int, str] """ Midi乐器对照表类型 """ diff --git a/Musicreater/utils.py b/Musicreater/utils.py index cc1bbf7..7deacf3 100644 --- a/Musicreater/utils.py +++ b/Musicreater/utils.py @@ -18,7 +18,7 @@ Terms & Conditions: License.md in the root directory import math import random -from .constants import MM_INSTRUMENT_DEVIATION_TABLE, MC_INSTRUMENT_BLOCKS_TABLE +from .constants import MM_INSTRUMENT_RANGE_TABLE, MC_INSTRUMENT_BLOCKS_TABLE from .subclass import SingleNote from .types import ( @@ -58,18 +58,10 @@ def inst_to_sould_with_deviation( instrumentID: int, reference_table: MidiInstrumentTableType, default_instrument: str = "note.flute", - default_deviation: Optional[int] = 5, ) -> Tuple[str, int]: """ 返回midi的乐器ID对应的我的世界乐器名,对于音域转换算法,如下: - 2**( ( msg.note - 60 - X ) / 12 ) 即为MC的音高,其中 - X的取值随乐器不同而变化: - 竖琴harp、电钢琴pling、班卓琴banjo、方波bit、颤音琴iron_xylophone 的时候为6 - 吉他的时候为7 - 贝斯bass、迪吉里杜管didgeridoo的时候为8 - 长笛flute、牛铃cou_bell的时候为5 - 钟琴bell、管钟chime、木琴xylophone的时候为4 - 而存在一些打击乐器bd(basedrum)、hat、snare,没有音域,则没有X,那么我们返回7即可 + 2**( ( msg.note - 66 ) / 12 ) 即为MC的音高 Parameters ---------- @@ -84,20 +76,43 @@ def inst_to_sould_with_deviation( """ return reference_table.get( instrumentID, - ( - default_instrument, - ( - default_deviation - if default_deviation - else MM_INSTRUMENT_DEVIATION_TABLE.get(default_instrument, -1) - ), - ), + default=default_instrument, + ), 6 + + # 明明已经走了 + # 凭什么还要在我心里留下缠绵缱绻 + + +def midi_inst_to_mc_sould( + instrumentID: int, + reference_table: MidiInstrumentTableType, + default_instrument: str = "note.flute", +) -> str: + """ + 返回midi的乐器ID对应的我的世界乐器名,对于音域转换算法,如下: + 2**( ( msg.note - 66 ) / 12 ) 即为MC的音高 + + Parameters + ---------- + instrumentID: int + midi的乐器ID + reference_table: Dict[int, Tuple[str, int]] + 转换乐器参照表 + + Returns + ------- + tuple(str我的世界乐器名, int转换算法中的X) + """ + return reference_table.get( + instrumentID, + default=default_instrument, ) # 明明已经走了 # 凭什么还要在我心里留下缠绵缱绻 + def natural_curve( vol: float, ) -> float: @@ -145,6 +160,7 @@ def straight_line(vol: float) -> float: def note_to_command_parameters( note_: SingleNote, reference_table: MidiInstrumentTableType, + deviation: int = 0, volume_percentage: float = 1, volume_processing_method: Callable[[float], float] = natural_curve, ) -> Tuple[ @@ -156,20 +172,21 @@ def note_to_command_parameters( """ 将音符转为播放的指令 :param note_:int 音符对象 - :param reference_table:Dict[int, Tuple[str, int]] 转换对照表 + :param reference_table:Dict[int, str] 转换对照表 + :param deviation:int 音调偏移量 :param volume_percentage:int 音量占比(0,1] - :param volume_proccessing_method:Callable[[float], float]: 音量处理函数 + :param volume_proccessing_method:Callable[[float], float] 音量处理函数 :return str[我的世界音符ID], float[播放距离], float[指令音量参数], float[指令音调参数] """ - mc_sound_ID, deviation = inst_to_sould_with_deviation( + mc_sound_ID = midi_inst_to_mc_sould( note_.inst, reference_table, "note.bd" if note_.percussive else "note.flute", ) # delaytime_now = round(self.start_time / float(speed) / 50) - mc_pitch = None if note_.percussive else 2 ** ((note_.note - 60 - deviation) / 12) + mc_pitch = None if note_.percussive else 2 ** ((note_.note - 66 + deviation) / 12) mc_distance_volume = volume_processing_method(note_.velocity * volume_percentage) diff --git a/README.md b/README.md index 4ff1ac2..0d3b7da 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

-

一款免费开源的《我的世界》数字音频转换库。

+

一款免费开源的《我的世界》数字音频库。

@@ -46,6 +46,7 @@ ``` - 如果无法更新最新,可以尝试: + ```bash pip install --upgrade -i https://pypi.python.org/simple Musicreater ``` diff --git a/README_EN.md b/README_EN.md index ebba876..4538424 100644 --- a/README_EN.md +++ b/README_EN.md @@ -7,7 +7,7 @@

-

A free open-source library of converting digital music files into Minecraft formats.

+

A free open-source library of Minecraft digital music.

@@ -45,6 +45,7 @@ Welcome to join our QQ group: [861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr ``` - If above command cannot fetch latest version, try: + ```bash pip install -i https://pypi.python.org/simple Musicreater --upgrade ``` @@ -58,7 +59,6 @@ Welcome to join our QQ group: [861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr Commands such as `python`、`pip` could be changed to some like `python3` or `pip3` according to the difference of platforms. - ## Documentation 📄 (Not in English yet) @@ -121,7 +121,6 @@ NOT APPROVED BY OR ASSOCIATED WITH NETEASE. - 上文提及的 网易 公司,指代的是在中国大陆运营《我的世界:中国版》的上海网之易网络科技发展有限公司 - [Bilibili: Eilles]: https://img.shields.io/badge/Bilibili-%E5%87%8C%E4%BA%91%E9%87%91%E7%BE%BF-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 diff --git a/clean_update.py b/clean_update.py index 410261a..ec1475d 100644 --- a/clean_update.py +++ b/clean_update.py @@ -13,7 +13,9 @@ def main(): if file.endswith(".egg-info"): egg_info.append(file) console.print(file) - for file in track(["build", "dist", "logs", *egg_info], description="Deleting files"): + for file in track( + ["build", "dist", "logs", *egg_info], description="Deleting files" + ): if os.path.isdir(file) and os.access(file, os.W_OK): shutil.rmtree(file) diff --git a/example.py b/example.py index e78f567..f3c3cfc 100644 --- a/example.py +++ b/example.py @@ -189,10 +189,12 @@ print( ) ) if fileFormat == 1 - else (" 结构大小:{},延迟总数:{},指令数量:{}".format( + else ( + " 结构大小:{},延迟总数:{},指令数量:{}".format( *(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore - ) if playerFormat == 2 else - " 结构大小:{},延迟总数:{}".format( + ) + if playerFormat == 2 + else " 结构大小:{},延迟总数:{}".format( *(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore ) )