Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I read a midi file, change its instrument, and write it back?

I want to parse an already existing .mid file, change its instrument, from 'acoustic grand piano' to 'violin' for example, and save it back or as another .mid file.

From what I saw in the documentation, the instrument gets altered with a program_change or patch_change directive but I cannot find any library that does this in MIDI files that exist already. They all seem to support it only MIDI files created from scratch.

like image 212
John Kornick Avatar asked Jan 21 '13 22:01

John Kornick


People also ask

How do I change the instrument in a MIDI file?

Press in the channel list to change the instrument. This will open up the following dialog: You can specify the instrument from the given list. Accepting the dialog will set the channel's instrument in the beginning of the file to the instrument from the list.

How do you separate instruments in a MIDI file?

Control-click the MIDI region, then choose MIDI > Separate by MIDI Channel from the shortcut menu. A separate MIDI region is created for every MIDI channel found, containing all matching events. Each region is created on a track with an instrument channel that matches the MIDI channel.

What information is stored in a MIDI file?

MIDI Files contain one or more MIDI streams, with time information for each event. Song, sequence, and track structures, tempo and time signature information, are all supported. Track names and other descriptive information may be stored with the MIDI data.


1 Answers

The MIDI package will do this for you, but the exact approach depends on the original contents of the midi file.

A midi file consists of one or more tracks, and each track is a sequence of events on any of sixteen channels, such as Note Off, Note On, Program Change etc. The last of these will change the instrument assigned to a channel, and that is what you need to change or add.

Without any Program Change events at all, a channel will use program number (voice number) zero, which is an acoustic grand piano. If you want to change the instrument for such a channel then all you need to do is add a new Program Change event for this channel at the beginning of the track.

However if a channel already has a Program Change event then adding a new one at the beginning will have no effect because it is immediately overridden by the pre-existing one. In this case you will have to change the parameters of the existing event to use the instrument that you want.

Things could be even more complicated if there are originally several Program Change events for a channel, meaning that the instrument changes throughout the track. This is unusual, but if you come across a file like this you will have to decide how you want to change it.

Supposing you have a very simple midi file with a single track, one channel, and no existing Program Change events. This program creates a new MIDI::Opus object from the file, accesses the list of tracks (with only a single member), and takes a reference to the list of the first track's events. Then a new Program Change event (this module calls it patch_change) for channel 0 is unshifted onto the beginning of the event list. The new event has a program number of 40 - violin - so this channel will now be played with a violin instead of a piano.

With multiple tracks, multiple channels, and existing Program Change events the task becomes more complex, but the principle is the same - decide what needs to be done and alter the list of events as necessary.

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');
like image 130
Borodin Avatar answered Sep 18 '22 10:09

Borodin