I once transcribed a video game title screen tune into a MIDI file for someone who requested it on a message board. A little later they revealed they were playing the MIDI file on a player piano, and if two notes of the same pitch touched each other (one note ended at the same time the next note began), the player piano didn't have time to react between the notes and it effectively combined them into one note. They asked for some kind of script that would add a gap between notes, but only between notes of the same pitch. At the time, I couldn't think of an easy way to do this, so I just suggested shortening the length of all notes by a small amount.
Recently, the developer of the free MIDI sequencer Sekaiju has released a paid add-on called Sekaiju Application Language (SAL). It costs 500 yen (about 5 dollars), but you can try it out for 30 days.
SAL lets you write scripts to edit events in a MIDI file. Here's how it works:
To try out SAL, I revisited the player piano's problem of touching notes. I wrote a SAL script that helps you modify notes in a MIDI file so that:
The script assumes all the notes are in the same track, and writes its output on channel 1. So before you run this script, you will have to do some preparation work to put all the notes into one track and on channel 1. Please follow the steps in the Example section that follows.
A player piano can only play pitched sounds, not percussion, so we want to remove any percussive sounds. In D-Generation.mid, the tracks "Drums", "Melodic percussion" (a woodblock sound) and "Jetpack exhaust" (a helicopter sound) are percussive sounds, so let's get rid of them.
We want all the notes to play with the same sound, so we want to get rid of all the Program Change events. We also don't need multiple instances of Volume events at the beginning, since we are going to eventually put everything on one track and channel. Since a player piano can't change the volume of a note after it's started, it seems okay to remove the Expression controller events also. So for simplicity, let's just delete every event that isn't a note. Exception: We want to keep the tempo, time signature, and key signature events that are in the first track, so we will deselect that track so it isn't affected.
Let's go to the Event List window to help us delete these events.
When you open an Event List window, it initially shows every event in the MIDI file. The top-right section of checkboxes lets you change the tracks shown in the event list. the bottom-right section of checkboxes lets you change the events shown in the event list. Let's change the view to hide the first track and hide Note events.
Now the Event List is showing every event that isn't a Note and isn't in the first track. Let's remove these events.
You'll notice there are still some End of Track events remaining because End of Track events can't be deleted.
Let's deselect everything and go back to the track list.
Except for the first track, the only events that are remaining are Note events. We want to move all these notes into one track and change them to channel 1.
Let's select the all tracks except the first track.
Now, let's change these events to channel 1.
And now, let's move these events to the second track.
Now, let's remove the empty tracks.
The script has to re-insert the last Note event for each pitch back into the track. In the SAL language, when you use the insert command outside of a forEachEvent
loop, SAL inserts the event into the active track. If you leave the third or higher track active, the last note of each pitch will be lost because the active track doesn't exist. So, before running the script, let's click on the second track so the last note for each pitch can be re-inserted properly.
Now we are ready to run the script.
Wait a few seconds for the script to run.
The main feature of the SAL language is the forEachEvent
loop that will let you examine each of the events selected in Sekaiju one by one. SAL will let you modify the current event, insert new events to the same track as the current event, or delete the current event.
Because the player-piano-prep.sal script has to compare two different notes to tell what modifications it needs to do, it can't tell if it needs to modify the current note until later. Because of this, the script saves the current note's values then deletes the current note. After it examines the next note, it re-writes the saved note's values with any needed modifications. This means the script actually deletes all of the existing notes and re-writes them as it proceeds.
(SAL Programming Tip: I discovered that if you want to both insert a new event and delete the current event in the same pass of the forEachEvent
loop, you have to do the delete last. If you try to delete first then insert you will get the error message "Event is lossed" and your script will stop. The SAL documentation mentions that during a forEachEvent
loop, it will insert events on the same track as the current event, so I guess once you have deleted the current event, it can no longer tell which track to use to insert new events.)
My script runs a new forEachEvent
loop for every possible pitch (0 to 127). An outer loop increments a variable indicating witch pitch it will look at.
In the forEachEvent
loop, it only examines the Note events for the current pitch it is looking at.
It keeps comparing the previous note (A) and the current note (B) of the current pitch it is looking at.
If A and B start at the same time, it will combine them into one note, by using the longest duration from A and B and the highest velocity from A and B.
If A overlaps B or if the gap between A and B is too small, the length of A will be shortened so that there is a gap between A and B.
Here are the message board threads that were the inspiration for creating this script.
VGMusic Forums - D/Generation opening [Internet Archive Wayback Machine]
In this thread, Rachie581 requested a transcription of the opening tune in the video game D/Generation so she could convert it to sheet music and play it on piano. Over the next few days, I worked on transcribing it, posting my progress and the completed results. After I finished the transcription, Rachie581 posted a recording of it played on a piano.The VGMusic Forums no longer exist, so the link above is to a copy in the Internet Archive Wayback Machine. Here are the things linked in that thread.
At the time I thought it was odd that the beginning of the recording sounded like a beginner pianist, but the end of the recording sounded like an expert pianist.
VGMusic Forums - MIDI note repetition [Internet Archive Wayback Machine]
In this thread, Rachie581 described she had built a player piano, but when two notes of the same pitch are touching (one note ends at the same time the next note begins), her player piano combines them into one note. She asked how commercial player pianos solve this problem. I found some Yamaha player piano manuals that didn't describe the exact same problem, but explained they add a delay to incoming MIDI data. I suggested the delay time might be used to add any needed gap between notes of the same pitch. For her case, I suggested shortening all the notes in the MIDI file as a workaround.
The VGMusic Forums no longer exist, so the link above is to a copy in the Internet Archive Wayback Machine. Here is a local copy of the attachment from my post: Excerpts from Disklavier manuals; Converting milliseconds into ticks.zip.
After Rachie581 revealed she had built a player piano, I listened to her recording again and realized it was actually from her player piano: The recording matched the timing of the MIDI file exactly, and notes of the same pitch that were touching in the MIDI file were combined into one note in the recording.
Home > Programs
> Sekaiju Customizations > Player Piano Prep Script |
Robert Hart |