需要和重新设计音量的配置方式。开始做实验

This commit is contained in:
2026-07-20 07:14:22 +08:00
parent b01fccbf4b
commit 813ffab782
10 changed files with 91 additions and 44 deletions
+11 -13
View File
@@ -66,10 +66,10 @@ class MidiImportConfig(PluginConfig):
default_program_value: int = MIDI_DEFAULT_PROGRAM_VALUE default_program_value: int = MIDI_DEFAULT_PROGRAM_VALUE
default_volume_value: int = MIDI_DEFAULT_VOLUME_VALUE default_volume_value: int = MIDI_DEFAULT_VOLUME_VALUE
default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO
default_midi_charset: str = "latin1" midi_charset: str = "latin1"
default_lyric_encoding: str = "utf-8" # 默认歌词编码 string_encoding: str = "utf-8" # 默认歌词编码
""" """
默认歌词编码,设定为空时不进行二次编码处理 默认字串编码,设定为空时不对原 Midi 中的字符串二次编码
""" """
# 对照表,此处 None 值在下边 post init 函数中有处理 # 对照表,此处 None 值在下边 post init 函数中有处理
@@ -200,9 +200,7 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
if config is None: if config is None:
config = MidiImportConfig() config = MidiImportConfig()
return self.midifile_2_singlemusic( return self.midifile_2_singlemusic(
mido.MidiFile( mido.MidiFile(file=bytes_buffer_in, clip=True, charset=config.midi_charset),
file=bytes_buffer_in, clip=True, charset=config.default_midi_charset
),
config, config,
) )
@@ -213,9 +211,7 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
if config is None: if config is None:
config = MidiImportConfig() config = MidiImportConfig()
return self.midifile_2_singlemusic( return self.midifile_2_singlemusic(
mido.MidiFile( mido.MidiFile(filename=file_path, clip=True, charset=config.midi_charset),
filename=file_path, clip=True, charset=config.default_midi_charset
),
config, config,
) )
@@ -344,7 +340,9 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
midi_copyright_list.append(msg.text) midi_copyright_list.append(msg.text)
elif msg.type == "track_name": elif msg.type == "track_name":
# 检测轨道名称事件 # 检测轨道名称事件
midi_track_name_dict[track_no] = msg.name midi_track_name_dict[track_no] = msg.name.encode("latin1").decode(
config.string_encoding
)
elif msg.type == "note_on" and msg.velocity != 0: elif msg.type == "note_on" and msg.velocity != 0:
# 一个音符开始弹奏 # 一个音符开始弹奏
@@ -428,9 +426,9 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
note_table_replacement=config.note_replacement_table, note_table_replacement=config.note_replacement_table,
lyric_line=( lyric_line=(
_lyric.encode("latin1").decode( _lyric.encode("latin1").decode(
config.default_lyric_encoding config.string_encoding
) )
if config.default_lyric_encoding if config.string_encoding
else _lyric else _lyric
), ),
) )
@@ -498,7 +496,7 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
for track_properties, every_single_track in divided_tracks.items(): for track_properties, every_single_track in divided_tracks.items():
# [音轨编号, 通道编号, 乐器名称, 音量, 声相] # [音轨编号, 通道编号, 乐器名称, 音量, 声相]
if track_properties[0] and ( if track_properties[0] and (
track_name := midi_track_name_dict.get(track_properties[0]) track_name := midi_track_name_dict.get(track_properties[0], "无名音轨")
): # 音轨编号 ): # 音轨编号
every_single_track.name = track_name every_single_track.name = track_name
if track_properties[2]: # 乐器名称 if track_properties[2]: # 乐器名称
@@ -204,7 +204,7 @@ def midi_msgs_to_noteinfo(
return ( return (
SingleNote( SingleNote(
note_pitch=note, note_pitch=note,
note_volume=velocity, note_volume=velocity, # 需要重新设计
start_tick=(tk := int(start_time / float(play_speed) / 50000)), start_tick=(tk := int(start_time / float(play_speed) / 50000)),
keep_tick=round(duration / float(play_speed) / 50000), keep_tick=round(duration / float(play_speed) / 50000),
mass_precision_time=round( mass_precision_time=round(
@@ -113,6 +113,16 @@ class MusicPreview:
if file.is_file(): if file.is_file():
self.res_raw_data[file.stem] = librosa.load(file, sr=None) self.res_raw_data[file.stem] = librosa.load(file, sr=None)
# print(
# file.stem,": { 'AVG':",
# np.average(np.absolute(self.res_raw_data[file.stem][0])),", 'MID' :",
# np.median(np.absolute(self.res_raw_data[file.stem][0])),", 'MAX' :",
# np.max(np.absolute(self.res_raw_data[file.stem][0])),", 'MIN' :",
# np.min(np.absolute(self.res_raw_data[file.stem][0])),", 'VAR' :",
# np.var(np.absolute(self.res_raw_data[file.stem][0])),"}",
# flush=True,
# )
def res_shift( def res_shift(
self, self,
sound_name: str, sound_name: str,
@@ -121,24 +131,28 @@ class MusicPreview:
duration: int, duration: int,
# rate: int # rate: int
): ):
y_orig, sr_orig = self.res_raw_data[sound_name] original_y, original_sample_rate = self.res_raw_data[sound_name]
if not percussive: if not percussive:
if self.mode == 1: if self.mode == 1:
# 变调, 时域压扩, 重采样 mc方法 # 变调, 时域压扩, 重采样 mc方法
self.res_data[sound_name][pitch] = librosa.resample( self.res_data[sound_name][pitch] = librosa.resample(
librosa.effects.time_stretch( librosa.effects.time_stretch(
librosa.effects.pitch_shift(y_orig, sr=sr_orig, n_steps=pitch), librosa.effects.pitch_shift(
original_y, sr=original_sample_rate, n_steps=pitch
),
rate=2 ** (pitch / 12), rate=2 ** (pitch / 12),
), ),
orig_sr=sr_orig, orig_sr=original_sample_rate,
target_sr=self.output_sample_rate, target_sr=self.output_sample_rate,
fix=False, fix=False,
) )
elif self.mode == 0: elif self.mode == 0:
# 重采样, 变调 # 重采样, 变调
self.res_data[sound_name][pitch] = librosa.resample( self.res_data[sound_name][pitch] = librosa.resample(
librosa.effects.pitch_shift(y_orig, sr=sr_orig, n_steps=pitch), librosa.effects.pitch_shift(
orig_sr=sr_orig, original_y, sr=original_sample_rate, n_steps=pitch
),
orig_sr=original_sample_rate,
target_sr=self.output_sample_rate, target_sr=self.output_sample_rate,
fix=False, fix=False,
) )
@@ -146,16 +160,18 @@ class MusicPreview:
# 变调, 时域压扩, 重采样 MIDI-FFT # 变调, 时域压扩, 重采样 MIDI-FFT
if self.overlay_mode_c == 2: if self.overlay_mode_c == 2:
rate = duration / 20 / (len(y_orig[0]) / sr_orig) rate = duration / 20 / (len(original_y[0]) / original_sample_rate)
else: else:
rate = duration / 20 / (len(y_orig) / sr_orig) rate = duration / 20 / (len(original_y) / original_sample_rate)
rate = rate if rate != 0 else 1 rate = rate if rate != 0 else 1
self.res_data[sound_name][pitch] = librosa.resample( self.res_data[sound_name][pitch] = librosa.resample(
librosa.effects.time_stretch( librosa.effects.time_stretch(
librosa.effects.pitch_shift(y_orig, sr=sr_orig, n_steps=pitch), librosa.effects.pitch_shift(
original_y, sr=original_sample_rate, n_steps=pitch
),
rate=rate, rate=rate,
), ),
orig_sr=sr_orig, orig_sr=original_sample_rate,
target_sr=self.output_sample_rate, target_sr=self.output_sample_rate,
fix=False, fix=False,
) )
@@ -163,35 +179,38 @@ class MusicPreview:
# 变调, 时域压扩, 重采样 MIDI-cut # 变调, 时域压扩, 重采样 MIDI-cut
if self.overlay_mode_c == 2: if self.overlay_mode_c == 2:
deal = librosa.effects.pitch_shift( deal = librosa.effects.pitch_shift(
y_orig, sr=sr_orig, n_steps=pitch original_y, sr=original_sample_rate, n_steps=pitch
)[ )[
..., ...,
: ( : (
int(duration / 20 * sr_orig) int(duration / 20 * original_sample_rate)
if duration / 20 * sr_orig > len(y_orig[0]) if duration / 20 * original_sample_rate > len(original_y[0])
else len(y_orig[0]) else len(original_y[0])
), ),
] ]
else: else:
deal = librosa.effects.pitch_shift( deal = librosa.effects.pitch_shift(
y_orig, sr=sr_orig, n_steps=pitch original_y, sr=original_sample_rate, n_steps=pitch
)[ )[
: ( : (
int(duration / 20 * sr_orig) int(duration / 20 * original_sample_rate)
if duration / 20 * sr_orig > len(y_orig) if duration / 20 * original_sample_rate > len(original_y)
else len(y_orig) else len(original_y)
) )
] ]
self.res_data[sound_name][pitch] = librosa.resample( self.res_data[sound_name][pitch] = librosa.resample(
deal, orig_sr=sr_orig, target_sr=self.output_sample_rate, fix=False deal,
orig_sr=original_sample_rate,
target_sr=self.output_sample_rate,
fix=False,
) )
else: else:
# if self.mode == 1: # if self.mode == 1:
# 重采样, 不变调 # 重采样, 不变调
# print(">>", sound_name, pitch) # print(">>", sound_name, pitch)
self.res_data[sound_name][pitch] = librosa.resample( self.res_data[sound_name][pitch] = librosa.resample(
y_orig, original_y,
orig_sr=sr_orig, orig_sr=original_sample_rate,
target_sr=self.output_sample_rate, target_sr=self.output_sample_rate,
fix=False, # 尽管这样不变调,但是也不应将 pitch 与打击乐器联系在一起 fix=False, # 尽管这样不变调,但是也不应将 pitch 与打击乐器联系在一起
) # 已经在下面重新用 0 取代了打击乐器的 pitch ) # 已经在下面重新用 0 取代了打击乐器的 pitch
@@ -354,20 +373,18 @@ class MusicPreview:
if not note.percussive: if not note.percussive:
overlay( overlay(
self.res_data[note.instrument][accurate_pitch] self.res_data[note.instrument][accurate_pitch]
* note.volume * (1 - (note.position.sound_distance / 48)),
/ (note.position.sound_distance + 0.5)
/ 127,
note.start_tick, note.start_tick,
) )
# 上述参数的原顺序应为 # 上述参数的原顺序应为
# x * (音量 / 127) * (1 / (距离 + 0.5)) # x * (音量 / 127) * (1 / (距离 + 0.5))
# 乘法优先是为了提高计算精度,小的数的除法优先同理 # 乘法优先是为了提高计算精度,小的数的除法优先同理
# 下面是后来的注释
# 这种方式是错误的 —— 金羿 20260720
else: else:
overlay( overlay(
self.res_data[note.instrument][accurate_pitch] self.res_data[note.instrument][accurate_pitch]
* note.volume * (1 - (note.position.sound_distance / 48)),
/ (note.position.sound_distance + 0.5)
/ 127,
note.start_tick, note.start_tick,
) )
+35 -3
View File
@@ -409,6 +409,7 @@ MC_PITCHED_INSTRUMENT_LIST: List[str] = [
MC_INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str, ...]] = { MC_INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str, ...]] = {
"note.bass": ("planks",), "note.bass": ("planks",),
"note.bassattack": ("planks",), # 无法找到此音效
"note.snare": ("sand",), "note.snare": ("sand",),
"note.hat": ("glass",), "note.hat": ("glass",),
"note.bd": ("stone",), "note.bd": ("stone",),
@@ -424,12 +425,11 @@ MC_INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str, ...]] = {
"note.bit": ("emerald_block",), "note.bit": ("emerald_block",),
"note.banjo": ("hay_block",), "note.banjo": ("hay_block",),
"note.pling": ("glowstone",), "note.pling": ("glowstone",),
"note.bassattack": ("stone",), # 无法找到此音效
"note.harp": ("dirt",),
"note.trumpet": ("waxed_copper",), "note.trumpet": ("waxed_copper",),
"note.trumpet_exposed": ("waxed_exposed_copper",), "note.trumpet_exposed": ("waxed_exposed_copper",),
"note.trumpet_weathered": ("waxed_weathered_copper",), "note.trumpet_weathered": ("waxed_weathered_copper",),
"note.trumpet_oxidized": ("waxed_oxidized_copper",), "note.trumpet_oxidized": ("waxed_oxidized_copper",),
"note.harp": ("dirt",),
# 呃…… # 呃……
"firework.blast": ("sandstone",), "firework.blast": ("sandstone",),
"firework.twinkle": ("red_sandstone",), "firework.twinkle": ("red_sandstone",),
@@ -438,6 +438,38 @@ MC_INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str, ...]] = {
} }
"""MC乐器对音符盒下垫方块对照表""" """MC乐器对音符盒下垫方块对照表"""
MC_INSTRUMENT_VOLUME_BALANCE_TABLE: Dict[str, float] = {
"note.bass": 0.18975738,
"note.bassattack": 0.19250469,
"note.snare": 0.09636449,
"note.hat": 0.041303635,
"note.bd": 0.25398168,
"note.bell": 0.03190162,
"note.flute": 0.050610494,
"note.chime": 0.011847978,
"note.guitar": 0.03356794,
"note.xylophone": 0.025973769,
"note.iron_xylophone": 0.044438824,
"note.cow_bell": 0.09123069,
"note.didgeridoo": 0.06332747,
"note.bit": 0.028824601,
"note.banjo": 0.02944984,
"note.pling": 0.11315284,
"note.trumpet": 0.069420114,
"note.trumpet_exposed": 0.083734825,
"note.trumpet_oxidized": 0.047252066,
"note.trumpet_weathered": 0.06659823,
"note.harp": 0.12101666,
# 嗯……
"fire.ignite": 0.004931174,
"firework.blast": 0.015445901,
"firework.twinkle": 0.032410737,
"mob.cat.meow-2": 0.0675417,
"mob.cat.meow-4": 0.05923138,
"mob.zombie.wood": 0.14135803,
}
MC_EILLES_RT261_INSTRUMENT_REPLACE_TABLE: Dict[str, str] = { MC_EILLES_RT261_INSTRUMENT_REPLACE_TABLE: Dict[str, str] = {
"note.trumpet": "note.flute", "note.trumpet": "note.flute",
"note.trumpet_exposed": "note.flute", "note.trumpet_exposed": "note.flute",
@@ -505,7 +537,7 @@ MM_INSTRUMENT_RANGE_TABLE: Dict[str, Tuple[Tuple[int, int], int]] = {
} }
""" """
不同乐器的音域偏离对照表 不同乐器的音域偏离对照表
元组里的是范围,后面的整数是中央 C 所在 元组里的是范围,后面的整数是游戏里的默认采样音高
""" """
MM_INSTRUMENT_DEVIATION_TABLE: Dict[str, int] = { MM_INSTRUMENT_DEVIATION_TABLE: Dict[str, int] = {