如何使用Music21正确删除小节中的指定节拍
在Music21中删除乐谱首尾的指定节拍片段
Music21没有直接提供some_function_that_removes_first_few_beats这类一键删除指定节拍的函数,但可以通过手动处理乐谱元素的偏移量和时长来实现需求。下面是具体的实现思路和代码示例:
一、删除开头的指定节拍
核心思路是:计算要删除的总时长,筛选出起始位置在删除区间之后的元素,对跨区间的元素截断时长并调整偏移量,最后重新构建乐谱。
实现代码
from music21 import * def remove_first_beats(score, remove_beats): # 获取乐谱拍号(默认用4/4拍,若有多个拍号需额外处理) time_sigs = score.getTimeSignatures() time_sig = time_sigs[0] if time_sigs else meter.TimeSignature('4/4') # 计算单拍对应的四分音符时长(比如4/4拍的单拍就是1个四分音符) beat_quarter_length = time_sig.beatDuration.quarterLength # 要删除的总时长(以四分音符为单位) total_remove_length = beat_quarter_length * remove_beats # 展开乐谱为扁平结构,方便遍历所有元素 flat_elements = score.flat new_stream = stream.Stream() for elem in flat_elements: # 跳过空的小节容器(后续可重新构建小节) if isinstance(elem, stream.Measure): continue elem_start = elem.offset elem_end = elem_start + elem.duration.quarterLength # 元素完全在删除区间之后:直接调整偏移量后保留 if elem_start >= total_remove_length: elem_copy = elem.copy() elem_copy.offset = elem_start - total_remove_length new_stream.append(elem_copy) # 元素跨在删除区间上:截断前半部分,保留剩余部分 elif elem_end > total_remove_length: elem_copy = elem.copy() elem_copy.offset = 0 # 计算剩余时长 remaining_length = elem_end - total_remove_length elem_copy.duration.quarterLength = remaining_length new_stream.append(elem_copy) # 元素完全在删除区间内:直接丢弃 else: continue # 重新构建带小节结构的乐谱(可选,若不需要小节可直接返回new_stream) new_score = stream.Score() new_part = stream.Part() new_part.append(new_stream) # 自动划分小节(基于原拍号) new_part.makeMeasures(timeSignature=time_sig) new_score.append(new_part) return new_score # 使用示例 xml_file_path = "your_musicxml_file.xml" # 读取并截取1-3小节 original_score = converter.parse(xml_file_path).measures(1, 3) # 删除前3个节拍 processed_score = remove_first_beats(original_score, 3) processed_score.show()
二、删除结尾的指定节拍
逻辑和开头类似,先计算乐谱总时长,再保留总时长减去要删除时长范围内的元素:
实现代码
def remove_last_beats(score, remove_beats): time_sigs = score.getTimeSignatures() time_sig = time_sigs[0] if time_sigs else meter.TimeSignature('4/4') beat_quarter_length = time_sig.beatDuration.quarterLength total_remove_length = beat_quarter_length * remove_beats flat_elements = score.flat # 计算乐谱总时长(取最后一个元素的结束位置) total_score_length = max([elem.offset + elem.duration.quarterLength for elem in flat_elements]) # 保留的截止时长 keep_until_length = total_score_length - total_remove_length new_stream = stream.Stream() for elem in flat_elements: if isinstance(elem, stream.Measure): continue elem_start = elem.offset elem_end = elem_start + elem.duration.quarterLength # 元素完全在保留区间内:直接保留 if elem_end <= keep_until_length: new_stream.append(elem.copy()) # 元素跨在保留区间末尾:截断后半部分 elif elem_start < keep_until_length: elem_copy = elem.copy() remaining_length = keep_until_length - elem_start elem_copy.duration.quarterLength = remaining_length new_stream.append(elem_copy) # 元素完全在删除区间内:丢弃 else: continue new_score = stream.Score() new_part = stream.Part() new_part.append(new_stream) new_part.makeMeasures(timeSignature=time_sig) new_score.append(new_part) return new_score # 使用示例 processed_score_end = remove_last_beats(original_score, 2) processed_score_end.show()
注意事项
- 上述代码默认乐谱为单一拍号,如果乐谱存在变拍号,需要遍历每个小节的拍号分段计算时长。
- 对于表情记号、力度标记等非音符元素,代码会自动保留其位置,若有特殊需求可单独调整逻辑。
- 跨小节的音符会被自动截断,确保剩余部分的时长和位置正确。
内容的提问来源于stack exchange,提问作者Yuval Medina
相关产品推荐
相关产品推荐

