6

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

从我在文档中看到的内容来看,该乐器使用program_changeorpatch_change指令进行了更改,但我在已经存在的 MIDI 文件中找不到任何可以执行此操作的库。他们似乎都只支持从头开始创建的 MIDI 文件。

4

2 回答 2

5

MIDI软件包将为您执行此操作,但确切的方法取决于 MIDI 文件的原始内容。

一个 midi 文件由一个或多个轨道组成,每个轨道是 16 个通道中任何一个通道上的一系列事件,例如Note OffNote OnProgram Change等。最后一个将更改分配给通道的乐器,并且这就是您需要更改或添加的内容。

根本没有任何 Program Change 事件,通道将使用程序编号(音色编号)零,这是一台原声三角钢琴。如果你想改变这样一个通道的乐器,那么你需要做的就是在轨道的开头为这个通道添加一个新的 Program Change 事件。

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

如果一个通道最初有多个 Program Change 事件,则事情可能会更加复杂,这意味着乐器会在整个轨道中发生变化。这是不寻常的,但如果您遇到这样的文件,您将不得不决定如何更改它。

假设您有一个非常简单的 MIDI 文件,它只有一个轨道、一个通道,并且没有现有的 Program Change 事件。该程序从文件中创建一个新MIDI::Opus对象,访问轨道列表(只有一个成员),并获取对第一个轨道事件列表的引用。patch_change然后,通道 0的新程序更改事件(此模块称为它)未移动到事件列表的开头。新活动的节目编号为 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');
于 2013-01-28T09:29:24.980 回答
4

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

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')
于 2013-01-30T19:11:40.070 回答