Go to English page

ViaThinkSoft CodeLib

Dieser Artikel befindet sich in der Kategorie:
CodeLibProgrammierhilfenPerl

Im SMF-0-Format von MIDI sind alle MIDI-Events - sowohl globale Events als auch die Ereignisse für die einzelnen Instrumente - in einem Track zusammengefasst.
Möchte man nun Ereignisse eines Instruments herausfiltern, stellt sich das durchaus schwierig dar, da die Zeit, bei der ein bestimmtes Ereignis eintreten soll, relativ angegeben ist (relativ zum vorhergehenden Ereignis).
Im SMF-1 MIDI-Format hingegen wird jedem Instrument ein Track zugeordnet, auf dem nur Ereignisse für dieses spezielle Instrument verzeichnet sind. Die Zeit, wann welches Ereignis eintreten soll, ist hierbei ebenfalls relativ angegeben - jedoch nur relativ zu anderen Events, die diesem Track zugeordnet sind und somit für das gleiche Instrument bestimmt sind. Somit können sich beispielsweise einfacher Melodien herausfiltern.

Hier finden Sie eine Perl-Funktion, die eine MIDI SMF-0 - Datei in eine MIDI SMF-1 - Datei umwandelt.
Diese Funktion wird von uns bereits im myMoozle Web Crawler eingesetzt. MyMoozle ist unsere semantische Musiksuchmaschine.

Wir verwenden das MIDI - Perl-Modul von der Seite http://search.cpan.org/~conklin/MIDI-Perl-0.82

http://www.mymoozle.com
sub MIDI_SMF0toSMF1 {
        #
        # MIDI_SMF0toSMF1
        # (c) Copyright 2010 ViaThinkSoft - intelligent software for everyone
        #
        # @param $opus - a MIDI Opus in format 0 (http://search.cpan.org/~conklin/MIDI-Perl-0.82/lib/MIDI/Opus.pm)
        # @return a MIDI Opus in format 1
        #
        # This function converts a SMF0 MIDI Opus into a SMF1 MIDI Opus
        my $opus = $_[0];
        
        if ($opus->format ne 0) {
                die('MIDI is not in format 0!');
        }

        my $newOpus = MIDI::Opus->new({ 'format' => 1 , 'ticks' => $opus->ticks});
        my %channelToTrack = (-1 => '0');
        my %addTime = (-1 => 0);
        # Add a NULL track for non-channel events
        push(@{$newOpus->tracks_r}, MIDI::Track->new);
        my @tracks = $opus->tracks;

        foreach my $track (@tracks) {
                foreach my $event ($track->events) {
                        my $e = $event->[0]; # event name
                        my $t = $event->[1]; # delta time
                        my $c; # channel id
                        if  ( ($e eq 'note_off') || ($e eq 'note_on') || ($e eq 'key_after_touch') ||
                                                ($e eq 'control_change') || ($e eq 'patch_change') || ($e eq 'channel_after_touch') ||
                                                ($e eq 'pitch_wheel_change') ) {
                                $c = $event->[2]; # channel
                                # check if there already exists a channel->track assignment for the new opus,
                                # if not, create a new track
                                if (! exists $channelToTrack{$c}) {
                                        push(@{$newOpus->tracks_r}, MIDI::Track->new);
                                        $channelToTrack{$c} = scalar($newOpus->tracks)-1;
                                        my @trackNameEvent = ('track_name', 0, 'Tr. '.($channelToTrack{$c}+1));
                                        push(@{$newOpus->tracks_r->[$channelToTrack{$c}]->events_r}, \@trackNameEvent);
                                        if (!$quiet) {print "(MIDI_SMF0toSMF1) new track, so add from -1: ".$addTime{-1}."\n";}
                                        $addTime{$c} = $addTime{-1};
                                }
                        } else {
                                $c = -1;
                        }
                        # Add the accumulated time interval for the current channel/track
                        $event->[1] += $addTime{$c};
                        # Add the event to the track in the new opus 
                        push(@{$newOpus->tracks_r->[$channelToTrack{$c}]->events_r}, $event);
                        # Update the accumulated times for the other tracks
                        foreach my $key (keys %addTime) {
                                if ( ($c ne $key) || ($c eq -1) ) {
                                        $addTime{$key} += $t; # add delta time of current event
                                }
                        }
                        # Reset the accumulated time of the current channel/track
                        if ($c ne -1) {
                                $addTime{$c} = 0;
                        }
                }
        }
        
        return $newOpus;
}
Victor-Phillip Negoescu
ViaThinkSoft Gründer