Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/355.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何读取midi文件、更改其乐器并将其写回?_Python_Ruby_Perl_Midi - Fatal编程技术网

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

Python 如何读取midi文件、更改其乐器并将其写回?,python,ruby,perl,midi,Python,Ruby,Perl,Midi,我想解析一个已经存在的.mid文件,将其乐器从“原声大钢琴”更改为“小提琴”,然后将其保存回或另存为一个.mid文件 从我在文档中看到的情况来看,该乐器使用程序更改或补丁更改指令进行更改,但我在现有的MIDI文件中找不到任何这样做的库。它们似乎都只支持从头开始创建的MIDI文件。该软件包可以为您实现这一点,但具体方法取决于MIDI文件的原始内容 midi文件由一个或多个音轨组成,每个音轨都是十六个声道中任意一个声道上的事件序列,如音符关闭、音符打开、程序更改等。最后一个声道将更改指定给某个声道的

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


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

该软件包可以为您实现这一点,但具体方法取决于MIDI文件的原始内容

midi文件由一个或多个音轨组成,每个音轨都是十六个声道中任意一个声道上的事件序列,如音符关闭、音符打开、程序更改等。最后一个声道将更改指定给某个声道的乐器,这就是您需要更改或添加的

在完全没有任何节目更改事件的情况下,频道将使用节目编号(语音编号)零,这是一种原声大钢琴。如果你想为这样一个频道更换乐器,那么你需要做的就是在曲目的开头为这个频道添加一个新的节目更改事件

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

如果一个频道最初有几个节目更改事件,这意味着乐器在整个音轨中都会发生更改,事情可能会更加复杂。这是不寻常的,但如果您遇到这样的文件,您必须决定如何更改它

假设您有一个非常简单的midi文件,只有一个音轨、一个通道,并且没有现有的程序更改事件。这个程序从文件中创建一个新的
MIDI::Opus
对象,访问曲目列表(只有一个成员),并引用第一首曲目的事件列表。然后,通道0的一个新程序更改事件(此模块称之为
patch\u 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');
使用(插入我自己的系统,希望没问题)。如果零件中定义了补片,请执行以下操作:

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')

到目前为止你试过什么?您只需要吗?我已经在Perl和Python中搜索了任何能够满足我要求的库,但没有找到。看起来比我原来想的要难。至于标题,我希望不必手动编辑二进制文件。“从我在文档中看到的…”什么文档?MIDI的规范和进行MIDI操作/编辑的库的文档。你看过了吗?它似乎提供了你想要的。博罗丁谢谢你的回答,我很感激。midi文件很简单(我认为有两首曲目)。不幸的是,由于某些原因,脚本无法工作。我得到与输出(钢琴)相同的midi文件。这里有两个.mid文件来测试它。你需要多少帮助?你能调试Perl吗?问题很可能是,正如我所说,曲目中已经有一个节目更改,因此在开始添加一个曲目没有什么区别。
sound.mid
有三个曲目。第一个是节奏音轨,不包含任何音符,因此在开始时更改程序不会产生任何效果。第二首和第三首曲目的第一个事件都有一个节目更改(到voice zero-piano),因此您需要更改或删除这些事件,或者将您自己的节目更改放在thwm之后
sound2.mid
只有一首曲目,没有节目更改事件,因此它使用默认的语音零点播放。但是由于某些原因,它使用通道15,因此您需要在通道15上添加程序更改事件。重新匹配频道0无效,因为它从不播放。