chanio has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I am trying to make a detailed list of my midi collection. And I would like to add the time elapsed of each midi (the time that plays). Is there a way of getting that value without hurting anybody (not even myself) in the process? :)

Is there a module that does that without having to know how to sum all the sound and silence periods?

I was first thinking that it was only a matter of translating the size of the file into seconds, but it is much more complex!

Thank you for always being here!

PS: Thank you for your answers :). I was first afraid of asking this without not having searched a lot. But I guess that there is no clean perl way of doing it (at least in a multiplatform way, just LINUX & Wine).-- Please, read the answers.

Googling I found that the elapsed time of a midi depends a lot on the program that reads it. I read an example of a midi with the elapsed time as a title that was measured by a program with the double of it's correct elapsed time.

.{\('v')/}
_`(___)' __________________________
Wherever I lay my KNOPPIX disk, a new FREE LINUX nation could be established.

Replies are listed 'Best First'.
Re: MIDI time elapsed stat
by zentara (Cardinal) on Nov 27, 2004 at 15:27 UTC
    Well I went and looked at the gnmidi set of utilities, and there is a way to do it, with the free utility mididir.exe. It runs under wine like this:
    wine -- mididir.exe -length
    That will give a print out like this for each midi file in the directory:
    Y:\ZLIG~MY.MID: (33 kb, format 1, 11 tracks, GS, 3:00)
    Where the MM::SS is at the end after GS,

    Now you have 2 problems, working out a way to translate the crappy 8.3 notation output for the filename to the real filename, and extracting the MM::SS info and probably throwing it in a hash for saving in a database.

    Heres an UPDATE: If you go to the free utilities section of gnmidi.com, there is the miditime utility. You can run it on an individual file like this:

    #!/usr/bin/perl my $midifile = shift; system("wine -- midifade -songtime $midifile");
    and you will get output like this
    song: units 0 - 34475 time 0:00.000000 - 2:59.545800
    So run it with backticks, and grab the last set of digits.

    I'm not really a human, but I play one on earth. flash japh
      Thank you a lot, zentara! It seems the simplest solution. At least, until I study more about the use of the MIDI.pms.

      I promise to publish a better solution if I am able to find it, some day :).

      .{\('v')/}
      _`(___)' __________________________
      Wherever I lay my KNOPPIX disk, a new FREE LINUX nation could be established.
Re: MIDI time elapsed stat
by Jaap (Curate) on Nov 27, 2004 at 11:16 UTC
    Perhaps you can use the score method of MIDI.
    score: a structure of notes like an event structure, but where notes are represented as single items, and where timing of items is absolute from the beginning of the track, instead of being represented in delta-times.
Re: MIDI time elapsed stat
by zentara (Cardinal) on Nov 27, 2004 at 14:39 UTC
    I would think that dumping the midi into a wav file, then getting the time length of the wav would be the easiest. Of of course it is a "clunky approach", but easy. It would be sweeter to loop thru the midis, and summing the note durations, but there is the problem of overlapping notes. So maybe it can't be done that way? Anyways, you could use timidity to create a temp.wav with
    system("timidity -Ow -o temp.wav $input.mid")
    Then you can get the length of the wav with:
    #!/usr/bin/perl -w use strict; use Fcntl; my $fnm = shift; sysopen WAV,$fnm,O_RDONLY; my $riff; sysread WAV,$riff,12; my $fmt; sysread WAV,$fmt,24; my $data; sysread WAV,$data,8; close WAV; # RIFF header: 'RIFF', long length, type='WAVE' my ($r1,$r2,$r3) = unpack "A4VA4", $riff; # WAV header, 'fmt ', long length, short unused, short channels, # long samples/second, long bytes per second, short bytes per sample, # short bits per sample my ($f1,$f2,$f3,$f4,$f5,$f6,$f7,$f8) = unpack "A4VvvVVvv",$fmt; # DATA header, 'DATA', long length my ($d1,$d2) = unpack "A4V", $data; my $playlength = $d2/$f6; print << "EOF"; RIFF header: $r1, length $r2, type $r3 Format: $f1, length $f2, always $f3, channels $f4, sample rate $f5, bytes per second $f6, bytes per sample $f7, bits per sample $f8 Data: $d1, length $d2 Playlength: $playlength seconds EOF
    The drawback to using this method, is that timidity takes the exact playlength of the midi to do the conversion, which could take a long time to process thousands of midis. But you would only have to do it once, and save the lengths in a file of your choice, then you could select midis from the list to give you an exact program of length "$x seconds". Maybe there are better midi-2-wav dumpers out there, that do it faster?

    I know on the midi newsgroups, they always refer to the win32 gnmidi set of utilities to do this type of thing. gnmidi does run under Wine, so it may be worth investigating. gnmidi

    Another thought is to determine the BPM(beats per minute) of the midi, then count the number of beats, and the duration of a beat?


    I'm not really a human, but I play one on earth. flash japh
Re: MIDI time elapsed stat
by zentara (Cardinal) on Nov 28, 2004 at 17:16 UTC
      You're the best!

      Perhaps it might help to also read this tip that I found yesterday.

      I can't judge your recent code until I study a little more about Midi. I've also got the MIDI.PM family. And I guess that most might be obtained with MIDI::Score. But it's all my guessing.

      Besides, I managed to use Midifade by providing a Win-DOS short midi filename (but keeping also the long filename). I used dir /X instead of opendir. Then, some splitting whitespaces would provide both: short and long filename to do all the job.

      I had some complains, I guess, with the speed of the qx commands but I have overcome that difficult part.

      Anyway, I am interested in studying a bit more about handling MIDI files and your code would be of great help.

      Lot's of thanks, zentara!

      .{\('v')/}
      _`(___)' __________________________
      Wherever I lay my KNOPPIX disk, a new FREE LINUX nation could be established.
        Yes, I have the information from your tip, but just couldn't seem to "get my head around it". :-) I tried testing on midi files ranging from 96 ticks/qn to 480 ticks/qn, and could only get good results with perl if I used a different equation for each range...sort of a lookup table. However, the gnmidi utility was always correct, and the various midi players also report correct time. The gnmidi source is not open source, so I can't see what he does, but I may look at the open source midi players, and see how they compute it in C. It's got to be a simple equation, yet it's implementation eludes me. :-(

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