diff --git a/Musicreater/constants.py b/Musicreater/constants.py index 50a72af..c29c089 100644 --- a/Musicreater/constants.py +++ b/Musicreater/constants.py @@ -41,6 +41,8 @@ DEFAULT_PROGRESSBAR_STYLE = ( 默认的进度条样式组 """ +# Midi用对照表 + MIDI_PITCH_NAME_TABLE: Dict[int, str] = { 0: "C", 1: "C#", @@ -171,12 +173,468 @@ MIDI_PITCH_NAME_TABLE: Dict[int, str] = { 126: "F#", 127: "G", } +"""Midi音高名称对照表""" +MIDI_PITCHED_NOTE_NAME_GROUP: Dict[int, Tuple[str, str]] = { + 1: ("钢琴", "Piano"), + 9: ("半音阶打击乐器", "Chromatic Percussion"), + 17: ("风琴", "Organ"), + 25: ("吉他", "Guitar"), + 33: ("贝斯", "Bass"), + 41: ("弦乐器", "Strings"), + 49: ("合奏乐器", "Ensemble"), + 57: ("铜管乐器", "Brass"), + 65: ("簧乐器", "Reed"), + 73: ("吹管乐器", "Pipe"), + 81: ("合成主旋律", "Synth Lead"), + 89: ("合成和弦", "Synth Pad"), + 97: ("合成声效", "Synth Effects"), + 105: ("民族乐器", "Ethnic"), + 113: ("打击乐器", "Percussive"), + 121: ("特殊音效", "Sound Effects"), +} +"""Midi乐音乐器分组名称对照表""" + +MIDI_PITCHED_NOTE_NAME_TABLE: Dict[int, Tuple[str, str]] = { + 1: ("原声平台钢琴", "Acoustic Grand Piano"), + 2: ("亮音原声钢琴", "Bright Acoustic Piano"), + 3: ("数码电钢琴", "Electric Grand Piano"), + 4: ("酒吧钢琴", "Honky-tonk Piano"), + 5: ("电气电钢琴", "Electric Piano 1(Rhodes Piano)"), + 6: ("合唱效果电钢琴", "Electric Piano 2(Chorused Piano)"), + 7: ("拨弦古钢琴(羽管键琴)", "Harpsichord"), + 8: ("古钢琴", "Clavi"), + 9: ("钢片琴", "Celesta"), + 10: ("钟琴", "Glockenspiel"), + 11: ("八音盒", "Music box"), + 12: ("颤音琴", "Vibraphone"), + 13: ("马林巴琴", "Marimba"), + 14: ("木琴", "Xylophone"), + 15: ("管钟", "Tubular Bells"), + 16: ("扬琴", "Dulcimer"), + 17: ("音栓风琴(击杆风琴)", "Drawbar Organ (Hammond Organ)"), + 18: ("打击风琴", "Percussive Organ"), + 19: ("摇滚管风琴", "Rock Organ"), + 20: ("教堂管风琴", "Church Organ"), + 21: ("簧风琴", "Reed Organ"), + 22: ("手风琴", "Accordion"), + 23: ("口琴", "Harmonica"), + 24: ("探戈手风琴", "Tango Accordion"), + 25: ("尼龙弦吉他", "Acoustic Guitar (nylon)"), + 26: ("钢弦吉他", "Acoustic Guitar (steel)"), + 27: ("爵士电吉他", "Electric Guitar (jazz)"), + 28: ("清音电吉他", "Electric Guitar (clean)"), + 29: ("弱音电吉他", "Electric Guitar (muted)"), + 30: ("过驱电吉他", "Overdriven Guitar"), + 31: ("失真电吉他", "Distortion Guitar"), + 32: ("吉他泛音", "Guitar harmonics"), + 33: ("原声贝斯", "Acoustic Bass"), + 34: ("指奏电贝斯", "Electric Bass (finger)"), + 35: ("拨奏电贝斯", "Electric Bass (pick)"), + 36: ("无品贝斯", "Fretless Bass"), + 37: ("击弦贝斯 1", "Slap Bass 1"), + 38: ("击弦贝斯 2", "Slap Bass 2"), + 39: ("合成贝斯 1", "Synth Bass 1"), + 40: ("合成贝斯 2", "Synth Bass 2"), + 41: ("小提琴", "Violin"), + 42: ("中提琴", "Viola"), + 43: ("大提琴", "Cello"), + 44: ("低音提琴", "Contrabass"), + 45: ("颤弓弦乐(弦乐震音)", "Tremolo Strings"), + 46: ("弹拨弦乐(弦乐拨奏)", "Pizzicato Strings"), + 47: ("竖琴", "Orchestral Harp"), + 48: ("定音鼓", "Timpani"), + 49: ("弦乐合奏 1", "String Ensemble 1"), + 50: ("弦乐合奏 2", "String Ensemble 2"), + 51: ("合成弦乐 1", "Synth Strings 1"), + 52: ("合成弦乐 2", "Synth Strings 2"), + 53: ("合唱“啊”音", "Choir Aahs"), + 54: ("人声“嘟”音", "Voice Oohs"), + 55: ("合成人声", "Synth Voice"), + 56: ("交响打击乐", "Orchestra Hit"), + 57: ("小号", "Trumpet"), + 58: ("长号", "Trombone"), + 59: ("大号", "Tuba"), + 60: ("弱音小号", "Muted Trumpet"), + 61: ("圆号(法国号)", "French Horn"), + 62: ("铜管乐组", "Brass Section"), + 63: ("合成铜管 1", "Synth Brass 1"), + 64: ("合成铜管 2", "Synth Brass 2"), + 65: ("高音萨克斯风", "Soprano Sax"), + 66: ("中音萨克斯风", "Alto Sax"), + 67: ("次中音萨克斯风", "Tenor Sax"), + 68: ("上低音萨克斯风", "Baritone Sax"), + 69: ("双簧管", "Oboe"), + 70: ("英国管", "English Horn"), + 71: ("大管(巴松管)", "Bassoon"), + 72: ("单簧管(黑管)", "Clarinet"), + 73: ("短笛", "Piccolo"), + 74: ("长笛", "Flute"), + 75: ("竖笛", "Recorder"), + 76: ("排笛", "Pan Flute"), + 77: ("瓶笛", "Blown Bottle"), + 78: ("尺八", "Shakuhachi"), + 79: ("哨子", "Whistle"), + 80: ("陶笛", "Ocarina"), + 81: ("方波", "Lead 1 (square)"), + 82: ("锯齿波", "Lead 2 (sawtooth)"), + 83: ("汽笛风琴", "Lead 3 (calliope)"), + 84: ("合成吹管", "Lead 4 (chiff)"), + 85: ("合成电吉他", "Lead 5 (charang)"), + 86: ("人声键盘", "Lead 6 (voice)"), + 87: ("五度音", "Lead 7 (fifths)"), + 88: ("低音+主音", "Lead 8 (bass+lead)"), + 89: ("新纪元", "Pad 1 (new age)"), + 90: ("暖温", "Pad 2 (warm)"), + 91: ("复合成音", "Pad 3 (polysynth)"), + 92: ("人声合唱", "Pad 4 (choir)"), + 93: ("弓弦", "Pad 5 (bowed)"), + 94: ("银铃", "Pad 6 (metallic)"), + 95: ("荣光", "Pad 7 (halo)"), + 96: ("轻扫", "Pad 8 (sweep)"), + 97: ("夏雨", "FX 1 (rain)"), + 98: ("音轨", "FX 2 (soundtrack)"), + 99: ("水晶", "FX 3 (crystal)"), + 100: ("大气", "FX 4 (atmosphere)"), + 101: ("轻曼", "FX 5 (light)"), + 102: ("魅影", "FX 6 (goblins)"), + 103: ("回响", "FX 7 (echoes)"), + 104: ("科幻", "FX 8 (sci-fi)"), + 105: ("西塔琴", "Sitar"), + 106: ("五弦琴(班卓琴)", "Banjo"), + 107: ("三味线", "Shamisen"), + 108: ("日本筝", "Koto"), + 109: ("卡林巴铁片琴", "Kalimba"), + 110: ("苏格兰风笛", "Bagpipe"), + 111: ("古提琴", "Fiddle"), + 112: ("唢呐", "Shanai"), + 113: ("铃铛", "Tinkle Bell"), + 114: ("拉丁打铃", "Agogo"), + 115: ("钢鼓", "Steel Drums"), + 116: ("木块", "Woodblock"), + 117: ("太鼓", "Taiko Drum"), + 118: ("古式高音鼓", "Melodic Tom"), + 119: ("合成鼓", "Synth Drum"), + 120: ("铜钹", "Reverse Cymbal"), + 121: ("吉他品格杂音", "Guitar Fret Noise"), + 122: ("呼吸杂音", "Breath Noise"), + 123: ("浪潮", "Seashore"), + 124: ("鸟鸣", "Bird Tweet"), + 125: ("电话", "Telephone"), + 126: ("直升机", "Helicopter"), + 127: ("鼓掌", "Applause"), + 128: ("射击", "Gunshot"), +} +"""Midi乐音乐器名称对照表""" + +MIDI_PERCUSSION_NOTE_NAME_TABLE: Dict[int, Tuple[str, str]] = { + 35: ("原声大鼓", "Acoustic Bass Drum"), + 36: ("大鼓", "Bass Drum 1"), + 37: ("小鼓鼓边", "Side Stick"), + 38: ("原声小军鼓", "Acoustic Snare"), + 39: ("拍手", "Hand Clap"), + 40: ("电子小军鼓", "Electric Snare"), + 41: ("低音落地桶鼓", "Low Floor Tom"), + 42: ("闭镲", "Closed Hi-Hat"), + 43: ("高音落地桶鼓", "High Floor Tom"), + 44: ("脚踏踩镲", "Pedal Hi-Hat"), + 45: ("低桶鼓", "Low Tom"), + 46: ("开镲", "Open Hi-Hat"), + 47: ("低音中桶鼓", "Low-Mid Tom"), + 48: ("高音中桶鼓", "Hi Mid Tom 2"), + 49: ("强音钹 1", "Crash Cymbal 1"), + 50: ("高桶鼓", "High Tom"), + 51: ("打点钹 1", "Ride Cymbal 1"), + 52: ("钹", "Chinese Cymbal"), + 53: ("圆铃", "Ride Bell"), + 54: ("铃鼓", "Tambourine"), + 55: ("小钹铜钹", "Splash Cymbal"), + 56: ("牛铃", "Cowbell"), + 57: ("强音钹 2", "Crash Cymbal 2"), + 58: ("颤音器", "Vibra-Slap"), + 59: ("打点钹 2", "Ride Cymbal 2"), + 60: ("高音邦加鼓", "Hi Bongo"), + 61: ("低音邦加鼓", "Low Bongo"), + 62: ("弱音高音康加鼓", "Mute Hi Conga"), + 63: ("强音高音康加鼓", "Open Hi Conga"), + 64: ("低音康加鼓", "Low Conga"), + 65: ("高音天巴鼓", "High Timbale"), + 66: ("低音天巴鼓", "Low Timbale"), + 67: ("高音阿哥哥", "High Agogo"), + 68: ("低音阿哥哥", "Low Agogo"), + 69: ("串珠", "Cabasa"), + 70: ("沙铃", "Maracas"), + 71: ("短口哨", "Short Whistle"), + 72: ("长口哨", "Long Whistle"), + 73: ("短刮壶", "Short Guiro"), + 74: ("长刮壶", "Long Guiro"), + 75: ("梆子", "Claves"), + 76: ("高音木块", "Hi Wood Block"), + 77: ("低音木块", "Low Wood Block"), + 78: ("弱音锯加鼓", "Mute Cuica"), + 79: ("开音锯加鼓", "Open Cuica"), + 80: ("弱音三角铁", "Mute Triangle"), + 81: ("强音三角铁", "Open Triangle"), +} +"""Midi打击乐器名称对照表""" + +# Minecraft用对照表 + +MC_PERCUSSION_INSTRUMENT_LIST: List[str] = [ + "note.snare", + "note.bd", + "note.hat", + "note.basedrum", + "firework.blast", + "firework.twinkle", + "fire.ignite", + "mob.zombie.wood", +] +"""打击乐器列表""" + +MC_INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str, ...]] = { + "note.bass": ("planks",), + "note.snare": ("sand",), + "note.hat": ("glass",), + "note.bd": ("stone",), + "note.basedrum": ("stone",), + "note.bell": ("gold_block",), + "note.flute": ("clay",), + "note.chime": ("packed_ice",), + "note.guitar": ("wool",), + "note.xylobone": ("bone_block",), + "note.iron_xylophone": ("iron_block",), + "note.cow_bell": ("soul_sand",), + "note.didgeridoo": ("pumpkin",), + "note.bit": ("emerald_block",), + "note.banjo": ("hay_block",), + "note.pling": ("glowstone",), + "note.bassattack": ("stone",), # 无法找到此音效 + "note.harp": ("dirt",), + # 呃…… + "firework.blast": ("sandstone",), + "firework.twinkle": ("red_sandstone",), + "fire.ignite": ("concrete_powder",), + "mob.zombie.wood": ("sand",), +} +"""MC乐器对音符盒下垫方块对照表""" + +# 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, +} +"""不同乐器的音调偏离对照表""" + +# 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_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.flute", 5), + 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), +} +"""“经典”打击乐器对照表""" # 以下是由 Touch “偷吃” 带来的高准确率音效对照表 # 包括乐音乐器对照和打击乐器对照 -PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = { +MM_TOUCH_PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = { 0: ("note.harp", 6), 1: ("note.harp", 6), 2: ("note.pling", 6), @@ -224,7 +682,7 @@ PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = { 44: ("note.flute", 5), 45: ("note.iron_xylophone", 6), 46: ("note.harp", 6), - 47: ("note.snare", 7), + 47: ("note.snare", -1), 48: ("note.flute", 5), 49: ("note.flute", 5), 50: ("note.flute", 5), @@ -232,7 +690,7 @@ PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = { 52: ("note.didgeridoo", 5), 53: ("note.flute", 5), # 合唱“啊”音 54: ("note.flute", 5), # 人声“嘟”音 - 55: ("mob.zombie.wood", 7), # 合成人声 + 55: ("mob.zombie.wood", -1), # 合成人声 56: ("note.flute", 5), 57: ("note.flute", 5), 58: ("note.flute", 5), @@ -292,136 +750,100 @@ PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = { 112: ("note.bell", 4), 113: ("note.xylophone", 4), 114: ("note.flute", 5), - 115: ("note.hat", 7), # 打击乐器无音域 - 116: ("note.snare", 7), # 打击乐器无音域 - 117: ("note.snare", 7), # 打击乐器无音域 - 118: ("note.bd", 7), # 打击乐器无音域 - 119: ("firework.blast", 7), # 打击乐器无音域 + 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", 7), # 直升机 - 126: ("firework.twinkle", 7), # 打击乐器无音域 - 127: ("mob.zombie.wood", 7), # 打击乐器无音域 + 125: ("note.hat", -1), # 直升机 + 126: ("firework.twinkle", -1), # 打击乐器无音域 + 127: ("mob.zombie.wood", -1), # 打击乐器无音域 } +"""“偷吃”乐音乐器对照表""" -PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = { - 34: ("note.hat", 7), - 35: ("note.bd", 7), - 36: ("note.bd", 7), - 37: ("note.snare", 7), - 38: ("note.snare", 7), +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", 7), - 41: ("note.hat", 7), - 42: ("note.hat", 7), - 43: ("firework.blast", 7), - 44: ("note.hat", 7), - 45: ("note.snare", 4), - 46: ("note.snare", 7), - 47: ("note.snare", 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", 7), + 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", 7), - 57: ("note.hat", 7), + 56: ("note.snare", -1), + 57: ("note.hat", -1), 58: ("note.chime", 4), 59: ("note.iron_xylophone", 6), - 60: ("note.bd", 7), - 61: ("note.bd", 7), + 60: ("note.bd", -1), + 61: ("note.bd", -1), 62: ("note.xylophone", 4), 63: ("note.xylophone", 4), 64: ("note.xylophone", 4), - 65: ("note.hat", 7), + 65: ("note.hat", -1), 66: ("note.bell", 4), 67: ("note.bell", 4), - 68: ("note.hat", 7), - 69: ("note.hat", 7), + 68: ("note.hat", -1), + 69: ("note.hat", -1), 70: ("note.flute", 5), 71: ("note.flute", 5), - 72: ("note.hat", 7), - 73: ("note.hat", 7), + 72: ("note.hat", -1), + 73: ("note.hat", -1), 74: ("note.xylophone", 4), - 75: ("note.hat", 7), - 76: ("note.hat", 7), + 75: ("note.hat", -1), + 76: ("note.hat", -1), 77: ("note.xylophone", 4), 78: ("note.xylophone", 4), 79: ("note.bell", 4), 80: ("note.bell", 4), } - -PERCUSSION_INSTRUMENT_LIST: List[str] = [ - "note.snare", - "note.bd", - "note.hat", - "note.basedrum", - "firework.blast", - "firework.twinkle", - "fire.ignite", - "mob.zombie.wood", -] - -INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str, ...]] = { - "note.bass": ("planks",), - "note.snare": ("sand",), - "note.hat": ("glass",), - "note.bd": ("stone",), - "note.basedrum": ("stone",), - "note.bell": ("gold_block",), - "note.flute": ("clay",), - "note.chime": ("packed_ice",), - "note.guitar": ("wool",), - "note.xylobone": ("bone_block",), - "note.iron_xylophone": ("iron_block",), - "note.cow_bell": ("soul_sand",), - "note.didgeridoo": ("pumpkin",), - "note.bit": ("emerald_block",), - "note.banjo": ("hay_block",), - "note.pling": ("glowstone",), - "note.bassattack": ("command_block",), # 无法找到此音效 - "note.harp": ("dirt",), - # 呃…… - "firework.blast": ("sandstone",), - "firework.twinkle": ("red_sandstone",), - "fire.ignite": ("concrete_powder",), - "mob.zombie.wood": ("sand",), -} - +"""“偷吃”打击乐器对照表""" # 即将启用 -height2note = { - 0.5: 0, - 0.53: 1, - 0.56: 2, - 0.6: 3, - 0.63: 4, - 0.67: 5, - 0.7: 6, - 0.75: 7, - 0.8: 8, - 0.84: 9, - 0.9: 10, - 0.94: 11, - 1.0: 12, - 1.05: 13, - 1.12: 14, - 1.2: 15, - 1.25: 16, - 1.33: 17, - 1.4: 18, - 1.5: 19, - 1.6: 20, - 1.7: 21, - 1.8: 22, - 1.9: 23, - 2.0: 24, -} -"""音高对照表\n -MC音高:音符盒音调""" +# height2note = { +# 0.5: 0, +# 0.53: 1, +# 0.56: 2, +# 0.6: 3, +# 0.63: 4, +# 0.67: 5, +# 0.7: 6, +# 0.75: 7, +# 0.8: 8, +# 0.84: 9, +# 0.9: 10, +# 0.94: 11, +# 1.0: 12, +# 1.05: 13, +# 1.12: 14, +# 1.2: 15, +# 1.25: 16, +# 1.33: 17, +# 1.4: 18, +# 1.5: 19, +# 1.6: 20, +# 1.7: 21, +# 1.8: 22, +# 1.9: 23, +# 2.0: 24, +# } +# """音高对照表\n +# MC音高:音符盒音调""" diff --git a/Musicreater/experiment.py b/Musicreater/experiment.py index 8d1da2b..9fd799e 100644 --- a/Musicreater/experiment.py +++ b/Musicreater/experiment.py @@ -16,14 +16,12 @@ Terms & Conditions: License.md in the root directory # Email TriM-Organization@hotmail.com # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md -import random -from .constants import INSTRUMENT_BLOCKS_TABLE from .exceptions import * -from .main import MidiConvert from .subclass import * from .utils import * -from .types import Tuple, List, Dict +from .main import MidiConvert, MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE +from .types import Tuple, List, Dict, ChannelType class FutureMidiConvertRSNB(MidiConvert): @@ -34,30 +32,6 @@ class FutureMidiConvertRSNB(MidiConvert): music_command_list: Dict[int, SingleNoteBox] """音乐指令列表""" - @staticmethod - def soundID_to_block(sound_id: str, random_select: bool = False) -> str: - """ - 将我的世界乐器名改作音符盒所需的对应方块名称 - - Parameters - ---------- - sound_id: str - 将我的世界乐器名 - random_select: bool - 是否随机选取对应方块 - - Returns - ------- - str方块名称 - """ - try: - if random_select: - return random.choice(INSTRUMENT_BLOCKS_TABLE[sound_id]) - else: - return INSTRUMENT_BLOCKS_TABLE[sound_id][0] - except KeyError: - return "air" - class FutureMidiConvertM4(MidiConvert): """ @@ -101,6 +75,7 @@ class FutureMidiConvertM4(MidiConvert): lastime=int(_note.duration / totalCount), track_number=_note.track_no, is_percussion=_note.percussive, + extra_information=_note.extra_info ) # ( # _note.start_time + _i * _apply_time_division, @@ -145,8 +120,11 @@ class FutureMidiConvertM4(MidiConvert): # 此处 我们把通道视为音轨 for channel in self.to_music_note_channels().values(): for note in channel: + note.set_info(note_to_command_parameters(note,self.percussion_note_referrence_table 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,)) + + if not note.percussive: - notes_list.extend(self._linear_note(note, note.get_mc_pitch * 500)) + notes_list.extend(self._linear_note(note, note.extra_info[3] * 500)) else: notes_list.append(note) @@ -166,12 +144,12 @@ class FutureMidiConvertM4(MidiConvert): self.music_command_list.append( SingleCommand( self.execute_cmd_head.format(player_selector) - + note.to_command(max_volume), + + r"playsound {} @s ^ ^ ^{} {} {}".format(*note.extra_info), tick_delay=tickdelay, annotation="在{}播放{}%的{}音".format( mctick2timestr(delaytime_now), max_volume * 100, - "{}:{}".format(note.mc_sound_ID, note.mc_pitch), + "{}:{}".format(note.extra_info[0], note.extra_info[3]), ), ) ) @@ -186,6 +164,82 @@ class FutureMidiConvertM5(MidiConvert): 加入同刻偏移算法优化音感 """ + def to_music_channels( + self, + ) -> ChannelType: + """ + 使用金羿的转换思路,将midi解析并转换为频道信息字典 + + Returns + ------- + 以频道作为分割的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对象?这是不可复用的。" + ) + + # 一个midi中仅有16个通道 我们通过通道来识别而不是音轨 + midi_channels: ChannelType = empty_midi_channels() + tempo = 500000 + + # 我们来用通道统计音乐信息 + # 但是是用分轨的思路的 + for track_no, track in enumerate(self.midi.tracks): + microseconds = 0 + if not track: + continue + + note_queue = empty_midi_channels(staff=[]) + + 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: + try: + if not track_no in midi_channels[msg.channel].keys(): + midi_channels[msg.channel][track_no] = [] + except AttributeError as E: + print(msg, E) + + if msg.type == "program_change": + midi_channels[msg.channel][track_no].append( + ("PgmC", msg.program, microseconds) + ) + + elif msg.type == "note_on" and msg.velocity != 0: + midi_channels[msg.channel][track_no].append( + ("NoteS", msg.note, msg.velocity, microseconds) + ) + + elif (msg.type == "note_on" and msg.velocity == 0) or ( + msg.type == "note_off" + ): + midi_channels[msg.channel][track_no].append( + ("NoteE", msg.note, microseconds) + ) + + """整合后的音乐通道格式 + 每个通道包括若干消息元素其中逃不过这三种: + + 1 切换乐器消息 + ("PgmC", 切换后的乐器ID: int, 距离演奏开始的毫秒) + + 2 音符开始消息 + ("NoteS", 开始的音符ID, 力度(响度), 距离演奏开始的毫秒) + + 3 音符结束消息 + ("NoteE", 结束的音符ID, 距离演奏开始的毫秒)""" + del tempo, self.channels + self.channels: ChannelType = midi_channels + # [print([print(no,tno,sum([True if i[0] == 'NoteS' else False for i in track])) for tno,track in cna.items()]) if cna else False for no,cna in midi_channels.items()] + return midi_channels + # 神奇的偏移音 def to_command_list_in_delay( self, @@ -237,9 +291,9 @@ class FutureMidiConvertM5(MidiConvert): elif msg[0] == "NoteS": soundID, _X = ( - self.perc_inst_to_soundID_withX(msg[1]) + inst_to_sould_with_deviation(msg[1],MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE) if SpecialBits - else self.inst_to_souldID_withX(InstID) + else inst_to_sould_with_deviation(InstID,MM_CLASSIC_PITCHED_INSTRUMENT_TABLE) ) score_now = round(msg[-1] / float(speed) / 50) diff --git a/Musicreater/main.py b/Musicreater/main.py index a9e3070..9dddbfb 100644 --- a/Musicreater/main.py +++ b/Musicreater/main.py @@ -77,6 +77,15 @@ class MidiConvert: midi: VoidMido """MidiFile对象""" + pitched_note_reference_table: Dict[int, Tuple[str, int]] + """乐音乐器Midi-MC对照表""" + + percussion_note_referrence_table: Dict[int, Tuple[str, int]] + """打击乐器Midi-MC对照表""" + + volume_processing_function: Callable[[float], float] + """音量处理函数""" + midi_music_name: str """Midi乐曲名""" @@ -103,6 +112,13 @@ class MidiConvert: midi_obj: VoidMido, midi_name: str, enable_old_exe_format: bool = False, + pitched_note_rtable: Dict[ + int, Tuple[str, int] + ] = MM_TOUCH_PITCHED_INSTRUMENT_TABLE, + percussion_note_rtable: Dict[ + int, Tuple[str, int] + ] = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE, + vol_processing_function: Callable[[float], float] = natural_curve, ): """ 简单的midi转换类,将midi对象转换为我的世界结构或者包 @@ -115,6 +131,10 @@ class MidiConvert: 此音乐之名 enable_old_exe_format: bool 是否启用旧版(≤1.19)指令格式,默认为否 + pitched_note_rtable: Dict[int, Tuple[str, int]] + 乐音乐器Midi-MC对照表 + percussion_note_rtable: Dict[int, Tuple[str, int]] + 打击乐器Midi-MC对照表 """ self.midi: VoidMido = midi_obj @@ -129,6 +149,10 @@ class MidiConvert: else "execute as {} at @s positioned ~ ~ ~ run " ) + self.pitched_note_reference_table = pitched_note_rtable + self.percussion_note_referrence_table = percussion_note_rtable + self.volume_processing_function = vol_processing_function + self.progress_bar_command = self.music_command_list = [] self.channels = {} self.music_tick_num = 0 @@ -138,6 +162,13 @@ class MidiConvert: cls, midi_file_path: str, old_exe_format: bool = False, + pitched_note_table: Dict[ + int, Tuple[str, int] + ] = MM_TOUCH_PITCHED_INSTRUMENT_TABLE, + percussion_note_table: Dict[ + int, Tuple[str, int] + ] = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE, + vol_processing_func: Callable[[float], float] = natural_curve, ): """ 直接输入文件地址,将midi文件读入 @@ -148,6 +179,10 @@ class MidiConvert: midi文件地址 enable_old_exe_format: bool 是否启用旧版(≤1.19)指令格式,默认为否 + pitched_note_table: Dict[int, Tuple[str, int]] + 乐音乐器Midi-MC对照表 + percussion_note_table: Dict[int, Tuple[str, int]] + 打击乐器Midi-MC对照表 """ midi_music_name = os.path.splitext(os.path.basename(midi_file_path))[0].replace( @@ -160,6 +195,9 @@ class MidiConvert: mido.MidiFile(midi_file_path, clip=True), midi_music_name, old_exe_format, + pitched_note_table, + percussion_note_table, + vol_processing_func, ) except (ValueError, TypeError) as E: raise MidiDestroyedError(f"文件{midi_file_path}损坏:{E}") @@ -447,6 +485,7 @@ class MidiConvert: def to_music_note_channels( self, + default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO, ignore_mismatch_error: bool = True, ) -> NoteChannelType: """ @@ -465,7 +504,7 @@ class MidiConvert: # 一个midi中仅有16个通道 我们通过通道来识别而不是音轨 midi_channels: NoteChannelType = empty_midi_channels(staff=[]) - tempo = mido.midifiles.midifiles.DEFAULT_TEMPO + tempo = default_tempo_value # 我们来用通道统计音乐信息 # 但是是用分轨的思路的 @@ -624,6 +663,20 @@ class MidiConvert: score_now = round(note.start_time / float(speed) / 50) max_score = max(max_score, score_now) + ( + mc_sound_ID, + mc_distance_volume, + volume_percentage, + mc_pitch, + ) = note_to_command_parameters( + note, + self.percussion_note_referrence_table + 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, + ) + this_channel.append( SingleCommand( self.execute_cmd_head.format( @@ -631,13 +684,13 @@ class MidiConvert: .replace("(", r"{") .replace(")", r"}") ) - + note.to_command( - (max_volume) if note.track_no == 0 else (max_volume * 0.9) + + r"playsound {} @s ^ ^ ^{} {} {}".format( + mc_sound_ID, mc_distance_volume, volume_percentage, mc_pitch ), annotation="在{}播放{}%的{}音".format( mctick2timestr(score_now), max_volume * 100, - "{}:{}".format(note.mc_sound_ID, note.mc_pitch), + "{}:{:.2f}".format(mc_sound_ID, mc_pitch), ), ), ) @@ -697,17 +750,35 @@ class MidiConvert: else: max_multi = max(max_multi, multi) multi = 0 + + ( + mc_sound_ID, + mc_distance_volume, + volume_percentage, + mc_pitch, + ) = note_to_command_parameters( + note, + self.percussion_note_referrence_table + 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, + ) + self.music_command_list.append( SingleCommand( self.execute_cmd_head.format(player_selector) - + note.to_command( - (max_volume) if note.track_no == 0 else (max_volume * 0.9) + + r"playsound {} @s ^ ^ ^{} {} {}".format( + mc_sound_ID, + mc_distance_volume, + volume_percentage, + mc_pitch, ), tick_delay=tickdelay, annotation="在{}播放{}%的{}音".format( mctick2timestr(delaytime_now), max_volume * 100, - "{}:{}".format(note.mc_sound_ID, note.mc_pitch), + "{}:{:.2f}".format(mc_sound_ID, mc_pitch), ), ) ) diff --git a/Musicreater/plugin/noteblock.py b/Musicreater/plugin/noteblock.py index 14c05b7..aaf0196 100644 --- a/Musicreater/plugin/noteblock.py +++ b/Musicreater/plugin/noteblock.py @@ -19,7 +19,7 @@ Terms & Conditions: License.md in the root directory from ..exceptions import NotDefineProgramError, ZeroSpeedError from ..main import MidiConvert from ..subclass import SingleCommand -from ..utils import inst_to_souldID_withX, perc_inst_to_soundID_withX +from ..utils import inst_to_sould_with_deviation, perc_inst_to_soundID_withX # 你以为写完了吗?其实并没有 @@ -71,13 +71,13 @@ def to_note_list( soundID, _X = ( perc_inst_to_soundID_withX(InstID) if SpecialBits - else inst_to_souldID_withX(InstID) + else inst_to_sould_with_deviation(InstID) ) except UnboundLocalError as E: soundID, _X = ( perc_inst_to_soundID_withX(-1) if SpecialBits - else inst_to_souldID_withX(-1) + else inst_to_sould_with_deviation(-1) ) score_now = round(msg[-1] / float(speed) / 50) # print(score_now) diff --git a/Musicreater/previous.py b/Musicreater/previous.py index 5c48fee..0c8b5cf 100644 --- a/Musicreater/previous.py +++ b/Musicreater/previous.py @@ -20,6 +20,7 @@ from .main import MidiConvert, mido from .subclass import * from .types import ChannelType from .utils import * +from .constants import * class ObsoleteMidiConvert(MidiConvert): @@ -153,9 +154,9 @@ class ObsoleteMidiConvert(MidiConvert): ) maxscore = max(maxscore, nowscore) if msg.channel == 9: - soundID, _X = perc_inst_to_soundID_withX(instrumentID) + soundID, _X = inst_to_sould_with_deviation(instrumentID,MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE) else: - soundID, _X = inst_to_souldID_withX(instrumentID) + soundID, _X = inst_to_sould_with_deviation(instrumentID,MM_CLASSIC_PITCHED_INSTRUMENT_TABLE) singleTrack.append( "execute @a[scores={" @@ -212,7 +213,10 @@ class ObsoleteMidiConvert(MidiConvert): (ticks * tempo) / ((self.midi.ticks_per_beat * float(speed)) * 50000) # type: ignore ) maxscore = max(maxscore, nowscore) - soundID, _X = inst_to_souldID_withX(instrumentID) + if msg.channel == 9: + soundID, _X = inst_to_sould_with_deviation(instrumentID,MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE) + else: + soundID, _X = inst_to_sould_with_deviation(instrumentID,MM_CLASSIC_PITCHED_INSTRUMENT_TABLE) singleTrack.append( "execute @a[scores={" + str(scoreboardname) @@ -273,9 +277,9 @@ class ObsoleteMidiConvert(MidiConvert): elif msg[0] == "NoteS": soundID, _X = ( - perc_inst_to_soundID_withX(msg[1]) + inst_to_sould_with_deviation(msg[1],MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE) if SpecialBits - else inst_to_souldID_withX(InstID) + else inst_to_sould_with_deviation(InstID,MM_CLASSIC_PITCHED_INSTRUMENT_TABLE) ) score_now = round(msg[-1] / float(speed) / 50) maxScore = max(maxScore, score_now) @@ -339,7 +343,11 @@ class ObsoleteMidiConvert(MidiConvert): (ticks * tempo) / ((self.midi.ticks_per_beat * float(speed)) * 50000) ) - soundID, _X = inst_to_souldID_withX(instrumentID) + + if msg.channel == 9: + soundID, _X = inst_to_sould_with_deviation(instrumentID,MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE) + else: + soundID, _X = inst_to_sould_with_deviation(instrumentID,MM_CLASSIC_PITCHED_INSTRUMENT_TABLE) try: tracks[now_tick].append( self.execute_cmd_head.format(player) @@ -413,9 +421,9 @@ class ObsoleteMidiConvert(MidiConvert): elif msg[0] == "NoteS": soundID, _X = ( - perc_inst_to_soundID_withX(msg[1]) + inst_to_sould_with_deviation(msg[1],MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE) if SpecialBits - else inst_to_souldID_withX(InstID) + else inst_to_sould_with_deviation(InstID,MM_CLASSIC_PITCHED_INSTRUMENT_TABLE) ) score_now = round(msg[-1] / float(speed) / 50) diff --git a/Musicreater/subclass.py b/Musicreater/subclass.py index 0ca4504..c6c7efe 100644 --- a/Musicreater/subclass.py +++ b/Musicreater/subclass.py @@ -18,10 +18,9 @@ Terms & Conditions: License.md in the root directory from dataclasses import dataclass -from typing import Optional +from typing import Optional, Any -from .constants import PERCUSSION_INSTRUMENT_LIST -from .utils import inst_to_souldID_withX, perc_inst_to_soundID_withX, volume2distance +from .constants import MC_PERCUSSION_INSTRUMENT_LIST @dataclass(init=False) @@ -49,6 +48,9 @@ class SingleNote: percussive: bool """是否为打击乐器""" + extra_info: Any + """你觉得放什么好?""" + def __init__( self, instrument: int, @@ -58,6 +60,7 @@ class SingleNote: lastime: int, track_number: int = 0, is_percussion: Optional[bool] = None, + extra_information: Any = None, ): """用于存储单个音符的类 :param instrument 乐器编号 @@ -80,12 +83,14 @@ class SingleNote: """音符所处的音轨""" self.percussive = ( - (is_percussion in PERCUSSION_INSTRUMENT_LIST) + (is_percussion in MC_PERCUSSION_INSTRUMENT_LIST) if (is_percussion is None) else is_percussion ) """是否为打击乐器""" + self.extra_info = extra_information + @property def inst(self) -> int: """乐器编号""" @@ -100,14 +105,14 @@ class SingleNote: """音符编号""" return self.note - @property - def get_mc_pitch(self) -> float: - self.mc_sound_ID, _X = ( - perc_inst_to_soundID_withX(self.inst) - if self.percussive - else inst_to_souldID_withX(self.inst) - ) - return -1 if self.percussive else 2 ** ((self.note - 60 - _X) / 12) + # @property + # def get_mc_pitch(self,table: Dict[int, Tuple[str, int]]) -> float: + # self.mc_sound_ID, _X = inst_to_sould_with_deviation(self.inst,table,"note.bd" if self.percussive else "note.flute",) + # return -1 if self.percussive else 2 ** ((self.note - 60 - _X) / 12) + + def set_info(self, sth: Any): + """设置附加信息""" + self.extra_info = sth def __str__(self, is_track: bool = False): return "{}Note(Instrument = {}, {}Velocity = {}, StartTime = {}, Duration = {}{})".format( @@ -168,31 +173,6 @@ class SingleNote: return False return self.__str__() == other.__str__() - def to_command(self, volume_percentage: float = 1) -> str: - """ - 将音符转为播放的指令 - :param volume_percentage:int 音量占比(0,1] - - :return str指令 - """ - self.mc_sound_ID, _X = ( - perc_inst_to_soundID_withX(self.inst) - if self.percussive - else inst_to_souldID_withX(self.inst) - ) - - # delaytime_now = round(self.start_time / float(speed) / 50) - self.mc_pitch = "" if self.percussive else 2 ** ((self.note - 60 - _X) / 12) - - self.mc_distance_volume = volume2distance(self.velocity * volume_percentage) - - return "playsound {} @s ^ ^ ^{} {} {}".format( - self.mc_sound_ID, - self.mc_distance_volume, - volume_percentage, - self.mc_pitch, - ) - @dataclass(init=False) class SingleCommand: @@ -304,7 +284,7 @@ class SingleNoteBox: self.annotation_text = annotation """音符注释""" if percussion is None: - self.is_percussion = percussion in PERCUSSION_INSTRUMENT_LIST + self.is_percussion = percussion in MC_PERCUSSION_INSTRUMENT_LIST else: self.is_percussion = percussion diff --git a/Musicreater/utils.py b/Musicreater/utils.py index 277b910..fdebdc7 100644 --- a/Musicreater/utils.py +++ b/Musicreater/utils.py @@ -16,9 +16,12 @@ Terms & Conditions: License.md in the root directory # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md import math +import random -from .constants import PERCUSSION_INSTRUMENT_TABLE, PITCHED_INSTRUMENT_TABLE -from typing import Any, Dict, Tuple +from .constants import MM_INSTRUMENT_DEVIATION_TABLE, MC_INSTRUMENT_BLOCKS_TABLE +from .subclass import SingleNote + +from typing import Any, Dict, Tuple, Optional, Callable, Literal, Union def mctick2timestr(mc_tick: int) -> str: @@ -42,8 +45,11 @@ def empty_midi_channels(channel_count: int = 17, staff: Any = {}) -> Dict[int, A ) -def inst_to_souldID_withX( +def inst_to_sould_with_deviation( instrumentID: int, + reference_table: Dict[int, Tuple[str, int]], + default_instrument: str = "note.flute", + default_deviation: Optional[int] = 5, ) -> Tuple[str, int]: """ 返回midi的乐器ID对应的我的世界乐器名,对于音域转换算法,如下: @@ -60,40 +66,30 @@ def inst_to_souldID_withX( ---------- instrumentID: int midi的乐器ID + reference_table: Dict[int, Tuple[str, int]] + 转换乐器参照表 Returns ------- tuple(str我的世界乐器名, int转换算法中的X) """ - try: - return PITCHED_INSTRUMENT_TABLE[instrumentID] - except KeyError: - return "note.flute", 5 - - -def perc_inst_to_soundID_withX(instrumentID: int) -> Tuple[str, int]: - """ - 对于Midi第10通道所对应的打击乐器,返回我的世界乐器名 - - Parameters - ---------- - instrumentID: int - midi的乐器ID - - Returns - ------- - tuple(str我的世界乐器名, int转换算法中的X) - """ - try: - return PERCUSSION_INSTRUMENT_TABLE[instrumentID] - except KeyError: - return "note.bd", 7 + return reference_table.get( + instrumentID, + ( + default_instrument, + default_deviation + if default_deviation + else MM_INSTRUMENT_DEVIATION_TABLE.get(default_instrument, -1), + ), + ) # 明明已经走了 # 凭什么还要在我心里留下缠绵缱绻 -def volume2distance(vol: float) -> float: +def natural_curve( + vol: float, +) -> float: """ midi力度值拟合成的距离函数 @@ -117,3 +113,97 @@ def volume2distance(vol: float) -> float: + -6.313841334963396 * (vol + 2592.272889454798) + 4558.496367823575 ) + + +def straight_line(vol: float) -> float: + """ + midi力度值拟合成的距离函数 + + Parameters + ---------- + vol: int + midi音符力度值 + + Returns + ------- + float播放中心到玩家的距离 + """ + return vol / -8 + 16 + + +def note_to_command_parameters( + note_: SingleNote, + reference_table: Dict[int, Tuple[str, int]], + volume_percentage: float = 1, + volume_processing_method: Callable[[float], float] = natural_curve, +) -> Tuple[str, float, float, Union[float, Literal[None]],]: + """ + 将音符转为播放的指令 + :param note_:int 音符对象 + :param reference_table:Dict[int, Tuple[str, int]] 转换对照表 + :param volume_percentage:int 音量占比(0,1] + :param volume_proccessing_method:Callable[[float], float]: 音量处理函数 + + :return str[我的世界音符ID], float[播放距离], float[指令音量参数], float[指令音调参数] + """ + mc_sound_ID, deviation = inst_to_sould_with_deviation( + 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_distance_volume = volume_processing_method(note_.velocity * volume_percentage) + + return mc_sound_ID, mc_distance_volume, volume_percentage, mc_pitch + + +def from_single_note( + note_: SingleNote, random_select: bool = False, default_block: str = "air" +): + """ + 将我的世界乐器名改作音符盒所需的对应方块名称 + + Parameters + ---------- + note_: SingleNote + 音符类 + random_select: bool + 是否随机选取对应方块 + default_block: str + 查表查不到怎么办?默认一个! + + Returns + ------- + str方块名称 + """ + pass + # return SingleNoteBox() # TO-DO + + +@staticmethod +def soundID_to_blockID( + sound_id: str, random_select: bool = False, default_block: str = "air" +) -> str: + """ + 将我的世界乐器名改作音符盒所需的对应方块名称 + + Parameters + ---------- + sound_id: str + 将我的世界乐器名 + random_select: bool + 是否随机选取对应方块 + default_block: str + 查表查不到怎么办?默认一个! + + Returns + ------- + str方块名称 + """ + if random_select: + return random.choice(MC_INSTRUMENT_BLOCKS_TABLE.get(sound_id, (default_block,))) + else: + return MC_INSTRUMENT_BLOCKS_TABLE.get(sound_id, (default_block,))[0]