Re: midi re-tempoizer

[email protected] (Jean-Pierre Vidal) Wed, 8 Dec 2004 21:02:54 +0000
Newsgroups perl.midi
Message-ID <[email protected]>
Le vendredi 3 D=E9cembre 2004 10:18, zentara a =E9crit=A0:
> Cool. This is a much asked for utility.
>=20
> I've been wracking my brain trying to figure out how to
> get the total playlength of a midi using Perl.
>=20
> Would anyone know how to do this?

Here is a new one, which use MIDI::Score

#!perl -w
use strict;
use diagnostics;
use MIDI;

my $filename =3D shift;
die "unknown file $filename\n" unless -e $filename;
die "usage : perl $0 midifile\n" unless defined $filename;

my $opus =3D new MIDI::Opus({'from_file' =3D> $filename});
#$opus->dump({'dump_tracks' =3D> 1});

# ticks per quarter note
my $tpqn =3D $opus->ticks();

my @tracks =3D $opus->tracks();

# convert tracks to score
my @score =3D ();
for my $tr (@tracks) {
	my @events =3D $tr->events;
	my ($score_r, $ticks) =3D MIDI::Score::events_r_to_score_r( \@events );
	@score =3D (@score, @$score_r);
}

my $score_r =3D MIDI::Score::sort_score_r( \@score);
#MIDI::Score::dump_score( $score_r );

my $duration =3D 0;
my $tempo =3D 0;
my $tempo_start =3D 0;
my $last_time =3D 0;
for my $note (@$score_r) {
	if ($$note[0] eq 'set_tempo') {
		# if this 'tempo change' is not the last 'note',
		# we will calculate another $last_time later
		$last_time =3D 0;
		#
		$duration +=3D ($$note[1] - $tempo_start) / $tpqn * $tempo;
		# tempo in seconds per quarter note
		$tempo =3D $$note[2] / 1_000_000;
		$tempo_start =3D $$note[1];
	}=20
	elsif ($$note[0] eq 'note') {
		# try to get the largest note after last tempo change
		my $l_t =3D ($$note[1] - $tempo_start) + $$note[2];
		$last_time =3D $l_t if $l_t > $last_time;
	}
}
$duration +=3D $last_time / $tpqn * $tempo;

my $dmn =3D int($duration / 60);
my $dms =3D $duration - $dmn * 60;
printf "%d mn %.1f s\n", $dmn, $dms;
__END__

It works for almost of my midifiles.=20
Unfortunately, it doesnt work for some midifiles, failing with a diagnostic=
=20
like this:

Uncaught exception from user code:
        reading track #18 (of length 1279262804) would exceed file_size_lim=
it=20
at duration.pl line 10
        MIDI::Opus::read_from_handle('MIDI::Opus=3DHASH(0x804cd24)','IO::Ha=
ndle=3DIO(0x8104cdc)','HASH(0x80622b8)',32089)=20
called at /usr/lib/perl5/site_perl/5.8.3/MIDI/Opus.pm line 356
        MIDI::Opus::read_from_file('MIDI::Opus=3DHASH(0x804cd24)','/home/jp=
v/_a_sauver/midi/soul/ipanema.mid','HASH(0x80622b8)')=20
called at /usr/lib/perl5/site_perl/5.8.3/MIDI/Opus.pm line 94
        MIDI::Opus::new('MIDI::Opus','HASH(0x80622b8)') called at duration.=
pl=20
line 10

I'll search what is wrong with these files and MIDI::Opus

Jean-Pierre