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

Joh*_*ick 5 ruby python perl midi

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

从我在文档中看到的,仪器被一个program_changepatch_change指令改变,但我找不到任何已经存在的MIDI文件中的库.他们似乎只支持它从头开始创建的MIDI文件.

Bor*_*din 5

MIDI软件包将做到这一点给你,但确切的方法取决于MIDI文件的原始内容.

midi文件由一个或多个轨道组成,每个轨道是16个通道中任意一个的事件序列,例如Note Off,Note On,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');
Run Code Online (Sandbox Code Playgroud)


Mic*_*ert 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')
Run Code Online (Sandbox Code Playgroud)

或者如果当前没有定义补丁更改:

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')
Run Code Online (Sandbox Code Playgroud)