如何读取midi文件、更改其乐器并将其写回?

2024-05-16 04:14:43 发布

您现在位置:Python中文网/ 问答频道 /正文

我想解析一个已经存在的.mid文件,将它的乐器从“原声大钢琴”更改为“小提琴”,并将其保存回或另存为一个.mid文件。

从我在文档中看到的情况来看,该乐器使用program_changepatch_change指令进行了更改,但在已经存在的MIDI文件中找不到任何这样做的库。它们似乎都只支持从头开始创建的MIDI文件。


Tags: 文件文档指令情况programchangemidipatch
2条回答

^{}包将为您执行此操作,但具体方法取决于midi文件的原始内容。

midi文件由一个或多个曲目组成,每个曲目是16个频道中任何一个频道上的事件序列,例如注释关闭注释打开程序更改等。最后一个曲目将更改分配给频道的乐器,这是您需要更改或添加的。

如果没有任何程序更改事件,频道将使用程序编号(语音编号)0,这是一架原声大钢琴。如果您想更改这样一个频道的乐器,那么您只需要在曲目的开头为这个频道添加一个新的节目更改事件。

但是,如果一个频道已经有一个程序更改事件,那么在开头添加一个新的将不会有任何效果,因为它会立即被先前存在的事件覆盖。在这种情况下,必须更改现有事件的参数才能使用所需的工具。

如果一个频道最初有几个程序更改事件,这意味着乐器在整个音轨中都会更改,那么事情可能会更复杂。这是不寻常的,但如果你遇到这样的文件,你将不得不决定如何改变它。

假设您有一个非常简单的midi文件,其中包含一个单轨、一个通道,并且没有现有的程序更改事件。这个程序从文件中创建一个新的MIDI::Opus对象,访问曲目列表(只有一个成员),并引用第一个曲目的事件列表。然后通道0的一个新的程序更改事件(这个模块称之为patch_change)被取消将移到事件列表的开头。新的活动有40个节目-小提琴-所以这个频道现在将用小提琴而不是钢琴演奏。

有了多个轨迹、多个通道和现有的程序更改事件,任务变得更加复杂,但原理是一样的——决定需要做什么,并根据需要更改事件列表。

use strict;
use warnings;

use MIDI;

my $opus = MIDI::Opus->new( { from_file => 'song.mid' } );

my $tracks = $opus->tracks_r;
my $track0_events = $tracks->[0]->events_r;

unshift @$track0_events, ['patch_change', 0, 0, 40];
$opus->write_to_file('newsong.mid');

使用music21 library(插入我自己的系统,希望没问题)。如果零件中定义了修补程序,请执行以下操作:

from music21 import converter,instrument # or import *
s = converter.parse('/Users/cuthbert/Desktop/oldfilename.mid')

for el in s.recurse():
    if 'Instrument' in el.classes: # or 'Piano'
        el.activeSite.replace(el, instrument.Violin())

s.write('midi', '/Users/cuthbert/Desktop/newfilename.mid')

或者如果当前未定义修补程序更改:

from music21 import converter,instrument # or import *
s = converter.parse('/Users/cuthbert/Desktop/oldfilename.mid')

for p in s.parts:
    p.insert(0, instrument.Violin())

s.write('midi', '/Users/cuthbert/Desktop/newfilename.mid')

相关问题 更多 >