You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 18:53:22