A few weeks ago, I made a feeble attempt to calculate midi playlengths in Midi Playlength calculator. It didn't take into account differing midi tempos. So I asked on the perl.midi maillist, and one of the midi gurus Jean Pierre Vidal worked up a solution. Please advise me if you find any midis which this script dosn't give correct times, or if you have code tweaks.
#!/usr/bin/perl use strict; use MIDI; # by Jean-Pierre Vidal on the perl.midi maillist # jeanpierre.vidal@free.fr my $filename = shift; die "unknown file $filename\n" unless -e $filename; die "usage : perl $0 midifile\n" unless defined $filename; my $opus = new MIDI::Opus( { 'from_file' => $filename } ); #$opus->dump({'dump_tracks' => 1}); # ticks per quarter note my $tpqn = $opus->ticks(); my @tracks = $opus->tracks(); # convert tracks to score my @score = (); for my $tr (@tracks) { my @events = $tr->events; my ( $score_r, $ticks ) = MIDI::Score::events_r_to_score_r( \@even +ts ); @score = ( @score, @$score_r ); } my $score_r = MIDI::Score::sort_score_r( \@score ); #MIDI::Score::dump_score( $score_r ); my $duration = 0; my $tempo = .5; #default for midi's with no set_tempo as per spe +cs my $tempo_start = 0; my $last_time = 0; for my $note (@$score_r) { if($$note[0] eq 'set_tempo'){print $$note[0],"\n"} if ( $$note[0] eq 'set_tempo' ) { # if this 'tempo change' is not the last 'note', # we will calculate another $last_time later $last_time = 0; # $duration += ( $$note[1] - $tempo_start ) / $tpqn * $tempo; # tempo in seconds per quarter note $tempo = $$note[2] / 1_000_000; $tempo_start = $$note[1]; } elsif ( $$note[0] eq 'note' ) { # try to get the largest note after last tempo change my $l_t = ( $$note[1] - $tempo_start ) + $$note[2]; $last_time = $l_t if $l_t > $last_time; } } $duration += $last_time / $tpqn * $tempo; my $dmn = int( $duration / 60 ); my $dms = $duration - $dmn * 60; printf "%d mn %.1f s\n", $dmn, $dms; __END__

Replies are listed 'Best First'.
Re: Midi Playlength calculator-II
by saskaqueer (Friar) on Dec 11, 2004 at 23:52 UTC
    @score = ( @score, @$score_r );

    Not that there's anything wrong with this line, it's just more readable to do the following (check out the push manpage for more info).

    push( @score, @$score_r );
Re: Midi Playlength calculator-II
by chanio (Priest) on Jan 07, 2005 at 04:18 UTC
    Good, zentara !

    I was trying a harder aproach by studying the great JAVA SOUND API source code with their midi masterpiece developed by masters in sound. I was helping myself with the JAVA 1.4.2 sound-dev-guide PDF . It was a slow work as I was planning to translate some JAVA classes into perl.

    As you said that you were going to study some C code, I thought that some JAVA code might also help you.

    Besides, the JAVA 1.4.2 sound-dev-guide PDF is a very good introduction to how MIDIS, synthetizers, sequencers and mixers work together. And there are very little descriptions of their JAVA implementation. So, it is a very useful doc.

    Thank you for this script!

    (Update) I am yet building my LINUX Rack to be at least as my Windows one. I am having trouble with my midis. I'll try SDL and your nice script.

    I am also interested in the other aspect of midi code: to command other machines and lights. I found something about this at http://midiox.com/.

      That's a nice pdf. It tempts me to dig out my Java books. :-) Perl needs docs like that. I never would have gotten my SDL sound sampler-mixer going, if I didn't stumble across ActiveState's html doc for SDL::Mixer. Maybe when they hand out money for Perl projects, it should go toowards this kind of documentation.

      I'm not really a human, but I play one on earth. flash japh