I made a free online tool to unpack EZKeys MIDI packs.

Discussion in 'Software' started by kabaragoya, Dec 31, 2023.

  1. DonCaballero

    DonCaballero Producer

    Joined:
    Feb 6, 2016
    Messages:
    166
    Likes Received:
    92
    Adding exceptions for those two files seems like the best solution.

    BPM looks to be working correctly now. :wink:

    I attached two example folders with incorrect key information (including the one in the previous screenshot.) The Cb one should be F, and the Db one should be Eb.

    The only obvious thing I can see in the code is that "Cb" should be "B". They are essentially the same, but "Cb" would only be used in rare contexts.

    If you let me know the name of the pack you originally created this to convert I can see if I have it and list any folders with incorrect keys...
     
    Last edited: Jan 2, 2024

    Attached Files:

  2. Paris ByNight

    Paris ByNight Newbie

    Joined:
    Sep 20, 2023
    Messages:
    2
    Likes Received:
    1
    Just bravo ! very usefull !
     
  3. tvandlover

    tvandlover Producer

    Joined:
    Aug 27, 2016
    Messages:
    283
    Likes Received:
    142
    So you are saying that people have been buying just the EZKeys midi packs which they could do nothing with until now thanks to your converter.
     
  4. El Cycer

    El Cycer Producer

    Joined:
    Sep 9, 2023
    Messages:
    303
    Likes Received:
    135
    I have more than 80 toontrack packs: is there a way to batch convert the whole EZ folder?
     
    Last edited: Jan 2, 2024
  5. kabaragoya

    kabaragoya Ultrasonic

    Joined:
    Dec 31, 2023
    Messages:
    31
    Likes Received:
    25
    Yes, exactly that. For at least half a decade.

    In fairness to us, though... I would describe it less as "been buying" and more as Toontracks "has been selling", because the word emphasis matters. We had every reason to believe that we were buying MIDI packs, because that is how they are advertised.
     
  6. kabaragoya

    kabaragoya Ultrasonic

    Joined:
    Dec 31, 2023
    Messages:
    31
    Likes Received:
    25
    Unfortunately, I'm not being paid enough to implement batch processing at this time. :wink:

    In all seriousness, there are obscure technical reasons why this would be problematic to implement without drastically changing my approach and I would have to move to a new hosting system. That's not happening, especially because this tool is fundamentally "done" as far as my volunteer effort goes.

    That said, at least the results come back instantly, so running through your stash should only take 10-12 minutes of admittedly boring time. Consider that I spent about 80-100x more than that building it, and feel less bad? :)
     
  7. DonCaballero

    DonCaballero Producer

    Joined:
    Feb 6, 2016
    Messages:
    166
    Likes Received:
    92
    Maybe planetdestroyer is willing to share his script?

    If ChatGPT-4 isn't still hungover you could try uploading the files and ask it to give you all the results back, though I have my doubts it would comply.

    You'd likely have better luck asking it to write a script for you. Be sure to tell it you have a physical disability so can't do it yourself and offer to tip it $200 for the task. ;)

    I haven't used it much for programming, but if you stick something like this in the Custom Instructions under settings you might have more success.

     
  8. kabaragoya

    kabaragoya Ultrasonic

    Joined:
    Dec 31, 2023
    Messages:
    31
    Likes Received:
    25
    Wow, that is a wonderful set of meta instructions. Thanks for that!

    Seriously, though... you can definitely power through 80 uploads in 10 minutes or less. Assuming that your browser remembers the current folder, you should be able to get into a groove where each request takes five seconds or so.

    While learning prompt engineering is a worthy objective, I have never in a career of people making claims that a task will take "five minutes" seen anything worth doing take five minutes.
     
  9. bagpipe

    bagpipe Noisemaker

    Joined:
    Jan 3, 2024
    Messages:
    9
    Likes Received:
    5
    Try this https://ttfstool.web.app. May have bugs but I think it fixes some problems with the previous one. It maintains the hierarchy. The packs have the MIDI data in a somewhat arbitrary order. This will keep the variation numbers in line with the product and categorized. The names are ugly but I think it's better than nothing.
    This shouldn't produce duplicate files, as you might note with 000851@SYNTH_POP (every file came out twice). Also it should output all the files, other tool was one short. The key signature should be correct. Finally it accepts zip files of the pack files. The upload is 32 MB so you might still need to do batches.

    Some point I will dump a copy you can run on your system and pastebin the source.

    Key signature from MIDI is based on number of flats and sharps, not the name of the key, so it must be adjusted for minor scales.

    Code:
    def calculate_key_signature(midi_file)
       key_sig_event_identifier = "\xFF\x59\x02".force_encoding('ASCII-8BIT')
       pos = midi_file.index(key_sig_event_identifier)
    
       if pos
         key_sig_data = midi_file[pos + 3, 2].unpack('cC')
         # First table is major keys from 7 flats to 0 (CM) and then up to 7 sharps. Note Cb
         # is enharmonic to B and C# enharmonic to Db, but they are distinct keys representable
         # by MIDI. The second list is the minor keys from 7 flats to 7 sharps.
        [ ["Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#"],
          ["Abm", "Ebm", "Bbm", "Fm", "Cm", "Gm", "Dm", "Am", "Em", "Bm", "F#m", "C#m", "G#m", "D#m", "A#m"] ][key_sig_data[1]][key_sig_data[0] + 7]
       else
         "C" # Default key signature (C major)
       end
      end
    
    I suggest not to use floating at all for BPM calculation, but it must be rounded everything fit easy in 32 bits. The 24 bit quantity in the meta event is number of microseconds per beat, add half the divisor to round.
    Code:
    (60000000 + us_per_beat/2) / us_per_beat
    
     
    Last edited: Jan 9, 2024
  10. Rasputin

    Rasputin Platinum Record

    Joined:
    Jun 29, 2012
    Messages:
    375
    Likes Received:
    239
    Lovely!
     
  11. Ryck

    Ryck Guest

    First of all, I congratulate you on your discovery, and I see that it is very helpful to many people here. However, I find myself thinking, how would it benefit me? Because look, I have EZkeys, Superior Drummer, and EZbass. So, let's say I like a piano, I drag it to the track, and I have the MIDI, so I don't understand what the advantage of this would be. I mean, maybe there's something I'm missing and I'm not realizing. Greetings!
     
  12. kabaragoya

    kabaragoya Ultrasonic

    Joined:
    Dec 31, 2023
    Messages:
    31
    Likes Received:
    25
    That's awesome, @bagpipe!

    The automatic conversion from a float to an integer in Ruby will round .999 to 0, which means that your
    Code:
    us_per_beat/2
    actually introduces a floating point issue. If you'd like to skip that, try:
    Code:
    bpm = (60000000.0 / tempo_data).round
    Likewise, I love how you simplified and corrected my key signature extraction, so I'm going to borrow it.

    I started this project not understanding key signature really at all, and finished knowing... still basically nothing but slightly more, which I'm grateful for. GPT-4 explained it to me as such:

    In wanting to keep things simple, I opted for the more conventional representations of key signatures in my code. However, what I gapped on at the time is that the responsibility is on the library publisher to not label an Ab performance as G#.

    Go team!
     
  13. kabaragoya

    kabaragoya Ultrasonic

    Joined:
    Dec 31, 2023
    Messages:
    31
    Likes Received:
    25
    Well, you actually answered your own question; you own EZKeys, so you probably don't need this tool.

    There are a few people in the thread who said that they enjoyed having the MIDI files even though they own the VST, but I'm like you... if I owned the instrument, I probably wouldn't bother. :)
     
  14. kabaragoya

    kabaragoya Ultrasonic

    Joined:
    Dec 31, 2023
    Messages:
    31
    Likes Received:
    25
    @DonCaballero you should be all set!

    Thank you so much for your help with the troubleshooting.
     
  15. bagpipe

    bagpipe Noisemaker

    Joined:
    Jan 3, 2024
    Messages:
    9
    Likes Received:
    5
    No, no floating point is involved.

    Code:
    irb(main):014> us_per_beat = 393331
    => 393331
    irb(main):015> ((60000000 + us_per_beat / 2) / us_per_beat)
    => 153
    irb(main):017> (us_per_beat / 2).class
    => Integer
    irb(main):017> ((60000000 + us_per_beat / 2) / us_per_beat).class
    => Integer
    
    Unlike python or (JS which don't have ints), ruby does not convert integers to floats, / on integers does truncating integral division. If us_per_beat is the integer value unpacked from the file the above rounds to nearest whole number.
    In python it would be:
    Code:
    ((60000000 + us_per_beat // 2) // us_per_beat)
    
     
    Last edited: Jan 5, 2024
  16. kabaragoya

    kabaragoya Ultrasonic

    Joined:
    Dec 31, 2023
    Messages:
    31
    Likes Received:
    25
    If you run irb and try
    Code:
    3/2
    You will get 1. Call it a "precision concern" if you wish, but if your us_per_beat is an odd number, then your approach introduces a very small error. I'd rather just use the standard technique Rubyists use and keep my accuracy. YMMV!
     
  17. bagpipe

    bagpipe Noisemaker

    Joined:
    Jan 3, 2024
    Messages:
    9
    Likes Received:
    5
    Obvi you get 1 it is truncating integer division. But you are stuck on an irrelevant detail.

    As for introducing error, no it absolutely does not. I showed you with us_per_beat as an odd number for this reason. us_per_beat is always so much larger than the quotient that there is no practical precision error when the intent is to round to the nearest whole number. The bits you are concerned about are long gone after the division.

    And floating point almost always introduces small errors by its very nature, powers of 2 cannot exactly represent most powers of 10. For example even a number like 2.1 cannot be represented by an IEEE 64-bit floating point number.
    There is a reason Ruby has BigDecimal. It is very slow though.

    Yeah you can use floats, at least when not stuck with a cheap 8 bit microcontroller. But the integer math is sound.
     
    Last edited: Jan 3, 2024
  18. kabaragoya

    kabaragoya Ultrasonic

    Joined:
    Dec 31, 2023
    Messages:
    31
    Likes Received:
    25
    Alright, that tracks. Floating point can be the devil, if you're not on your toes. Appreciate the perspective!
     
  19. tvandlover

    tvandlover Producer

    Joined:
    Aug 27, 2016
    Messages:
    283
    Likes Received:
    142
    Thanks for making that clear
     
  20. DonCaballero

    DonCaballero Producer

    Joined:
    Feb 6, 2016
    Messages:
    166
    Likes Received:
    92
    Awesome. It's nice to have multiple options. :wink:
    I did all the packs in one zip, and the factory content in another but it could've all fit in one.

    The original convertor resulted in 17,835 .mid files, 1,104 folders.
    This one was 18,604 files, 5,297 folders. However 456 folders contained only a 0 byte "header" file. The total number of .mid files was 18,147.

    I haven't checked each pack, but it does seem this includes one additional .mid, aside from Synth Pop where this has 320 .mid, and the original convertor 639. Subtracting those 319 additional files takes it's grand total to 17,516 .mid, 631 less.

    I'm giving some thought about a file-naming/structure which maintains as much of the original categorization while being user-friendly and better optimized for search. At the moment my preference is one folder per song with more descriptive filenames rather than many sub-folders and generic names.

    For example, even the maximally descriptive filename including the pack name, section+variation, and song title (aka Family in EZKeys), results in a similar path length.

    D:\EZKeys2\Neo Soul\88_4#4_Am\Neo_Soul_Am_88_Leela_Intro_01_4#4.mid
    D:\EZKeys2\Neo Soul\205@Straight_4#4\088-S051@Intro\01_Am_4#4_88.mid

    Swing time signatures could be written something like 4#4s or s4#4. Starting folder names with the time sig would group all songs in straight time at the top if that's a priority.

    EZKeys automatically transposes everything so doesn't even show the key, but with external MIDI key > tempo for usability.

    Anyway my brain is starting to hurt and this is more than enough for one post.
    :deep_facepalm:
     
    Last edited: Jan 3, 2024
Loading...
Loading...