Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MusicStudio Midi import bug #5

Open
robin-raymond opened this issue Feb 27, 2024 · 12 comments
Open

MusicStudio Midi import bug #5

robin-raymond opened this issue Feb 27, 2024 · 12 comments

Comments

@robin-raymond
Copy link

I've noticed two midi import bugs:

  1. if you attempt to import midi into multi tracks, e.g. specify tracks 1 + 2 + 3, only the last track is actually imported into the first track (so 3 get imported into track 1)
  2. if you have a note greater than FF in time, the note is imported as an error:
E> DTI:150,14
C-5

instead of something like this:

DTI:AB,14
C-5
+++
  1. If you have a note with greater than FF in duration, it won't split the note:
E> DTI:150,150
C-5

instead of something like this:

DTI:AB,AB
C-5
---
  1. If you don't do the math correctly for the note duration and the note is split across blocks, the note is cut short and silenced in the next block. I would expect the note to be carried into the next block instead of sliced, or to warning/error if there's nothing much that can be done.
@martinpiper
Copy link
Owner

Thanks, yes, the MIDI import has some issues when there are long delays. The MIDI import was really meant for shorter midi files with distinct patterns, to then be composed together into larger pieces.
Can you attach the problem MIDI file so I can test with it?

@robin-raymond
Copy link
Author

Here are the mids. I have a common mid that has all three tracks, and the mids broken down by track. The other file is from LMMS if you want to see the sources. There's gap delays that cause timing issues. I import as 16 (4/4 timed) and 512. I also notice that the import is 1 full octave higher (A-4 becomes A-5). I use "BC" in the track to play at the correct octave as a work around after import. On the per track mids tracks, I added fake "high" notes that I remove to get the timing right as a "work around" to the problem but the main .mid does not have this work around. You can also insert very long notes into the LMMS and export as a .mid as a demonstration too.

claro-de-luna.zip

@robin-raymond
Copy link
Author

Correction, needs to be imported as 16 and 256 to avoid the > $FF block concern.

@robin-raymond
Copy link
Author

If you import track 2 mid as 4/4 timing so 16 / 396 block length (24 bars) and look at block $0A/$0B you'll notice the last note is cut across the boundary but does not play at the correct length (the note across the boundary causes issues). It's not the only way to make this happen. It's not the only place, just an example. Basically any note that is across the block boundary will not play correctly from my observation, but when dealing with small block sizes and a larger song you are very likely to have a note cross a boundary. The work around to minimize this issue is to use a large block size, but then manually move notes into adjacent blocks but it's a lot of human work and prone to error.

@martinpiper
Copy link
Owner

Thanks for the test files, it looks like I've got some MIDI import improvements to make. :)

@martinpiper
Copy link
Owner

I've been improving the MIDI import by adding the option to ignore any MIDI note off events and improve the auto-generated release insertion. Also the "map to track" option now works better so it's possible to import multiple tracks in one go. Lastly added an option to automatically add the HRD command at the start of each block.

Also when using a time multiplier of 20 with block tick length of 480 this synchronises with the music bar length better and produces better editable blocks that align with the timing of the piece better.

image

All of this improves the import quality. See attached:
claro-de-luna.zip

I will be updating the release in itch soon... https://martin-piper.itch.io/musicstudio

@martinpiper
Copy link
Owner

https://martin-piper.itch.io/musicstudio/devlog/691639/musicstudio-v2209-released

@robin-raymond
Copy link
Author

Wonderful! Much improved, thank you.

Q: Does it handle a note crossing the block boundary issue, if not what is the best way to avoid that problem?

For the time duration, I was using a 16 time for 4/4 timing because 4 x 4 = 16, but what multiplier is actually needed on the import? For example 4/6 timing, does that timing value have to be a multiple of 4 or of 6?

I noticed track 3 still has issues, I suspect because of the large timing gaps between the notes? You can see the LMMS only has a few notes in track 3 for harmony. For track 2, there's a gap towards the end of the mid (you can see in the LMMS source), but I don't see that reflected in the imported .mid

FYI - the notes are still one full octave off, A-3 is imported as A-4. Not a big deal because I can use $BC to downscale the notes but just so you know...

@martinpiper
Copy link
Owner

The tracker blocks are fixed length in size. For example using the import block length of 480 results in a tracker block length of 960 frames (480 x 2) (due to the default tempo 2).
The PAL C64 updates the music player at 50 times per second. So this equates to 19.2 seconds (960 / 50).

The note crossing a block boundary should be handled, meaning the blocks and tracks should stay in sync, since the player should add silent rests in the blocks to pad out the timing.

The MIDI time uses the time division. I interpret this as "ticks per beat (quarter note)". There are meta events for setting tempo, but I don't handle those. The time multiplier in the conversion dialog is then applied.
Time in MIDI is one of the hardest things to get right, which is why I added the time multiplier to allow the time to be tweaked and handle all of the ways MIDI time can vary.

However, since the PAL C64 50 frames per second rate may not be exactly in time with the MIDI time tempo, there might be some instances where a note is slightly early or late by a frame.

How long, in seconds, is your 4/4 bar? If you multiply that by 50 that would give you a number of frames, then picking some multiple of that number, should give you the maximum number of notes that fit within the blocks.

@robin-raymond
Copy link
Author

Okay, I think I understand. The issue I was facing might be mistaken but I will clarify with you first. The reason I stuck to multipliers of the timing was because I was concerned about the note being broken across block boundaries. What I was finding was that longer notes that get broken across the block boundary would suddenly shut off mid-playing when the === was hit on the next page which forced me to ensure the timing would align as best as possible to the boundary.

The trouble I faced was that with a block capacity of $FF bytes, that would make it near impossible not to have a note cross the boundary between pages (and thus get cut off). BUT perhaps my understanding of what === does is mistaken. I thought it reset the note into silence (thus thinking a note crossing the block boundary would end the note abruptly). Or perhaps because I didn't do HDR in each block that could also be the issue.

So that's where my concern comes from related to notes crossing the block boundary...

@robin-raymond
Copy link
Author

long-notes-example.zip
Here's an example of a "scales" .mid I created to demonstrate the concern I ad. If you import where a note crosses a boundary the note will end prematurely. Try importing with a timing of 5 for example. I tried different settings and I couldn't get anything to work across the boundary. HRD or no HRD, etc.

BTW do the new settings work independently? I tried HRD without releasing half notes for example and it didn't work.

I really appreciate you taking a look at this! I know it's complicated!

@martinpiper
Copy link
Owner

Thanks for the example file. I need to spend some time to re-think how midi data is imported so solve this. Ideally a block should start on the beginning of a note, or a rest. However this makes the import a lot more complicated since the time period will not be the same for every block.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants