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

Add splitting to all types of clips #7477

Open
wants to merge 44 commits into
base: master
Choose a base branch
from

Conversation

regulus79
Copy link
Contributor

@regulus79 regulus79 commented Aug 31, 2024

Makes the knife tool work for all types of clips, not just SampleClips.

Changes

Most of the code is copied from SampleClipView.cpp's splitClip() function, and then modified for each clip type.

  • A couple changes had to be made to ClipView.cpp to allow splitting more than just SampleClips, and to hold off on dragging clips that cannot be resized (MidiClips) when knife mode is enabled.
  • Splitting PatternClips was trivial.
  • Splitting AutomationClips and MidiClips was more involved, as because setStartTimeOffset() appears to do nothing, it required copying the correct nodes over and offsetting them back by the length of the left clip. However, the original clip still had all the notes, which meant that if the user changed some of them, its length would snap back to full. I had difficulty finding a good way to delete certain notes via a loop, so I decided to instead spawn two new clips, one left and one right, populate them with notes, and delete the original clip.

Notes

  • In AutomationClipView.cpp, for some reason when splitting an automation clip, the new clips sometimes sets themselves to record mode. I added a temporary fix for this by setting the recording mode to the mode of the original clip. Fixed
  • Because MidiClips are only drawn in multiples of 1 bar, splitting them between bars led to buggy graphics. Because of this, I forced the split position to be a multiple of a bar. Fixed
  • I am not an expert in how to have objects properly remove themselves without memory issues. I called remove()close() at the end of spltiClip() for AutomationClipView.cpp and MidiClipView.cpp, so I hope that takes care of everything.

Copy link
Contributor

@Rossmaxx Rossmaxx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style review. Do look at fixing the bracket spacing by looking at the diff.

src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/MidiClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/PatternClipView.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@Rossmaxx Rossmaxx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good now

@Rossmaxx
Copy link
Contributor

@szeli1 mind reviewing this?

@szeli1
Copy link
Contributor

szeli1 commented Aug 31, 2024

@szeli1 mind reviewing this?

I will review this soon

Copy link
Contributor

@szeli1 szeli1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make the requested changes for all cases.
You should update this line inside ClipView:

//! Return true iff the clip could be split. Currently only implemented for samples
virtual bool splitClip( const TimePos pos ){ return false; };

And lastly I think using the remove() method is fine.

src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/ClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/ClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/MidiClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/MidiClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
Copy link
Member

@messmerd messmerd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor nitpicks before I test it

src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/MidiClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/PatternClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/MidiClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/MidiClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/PatternClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/PatternClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/SampleClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/MidiClipView.cpp Outdated Show resolved Hide resolved
@messmerd
Copy link
Member

messmerd commented Sep 4, 2024

Here are some things I noticed while testing it:

  • In Knife mode, the knife mouse icon doesn't appear when hovering over midi clips, automation clips, and pattern clips like it does for sample clips.
  • Undo works incorrectly for midi clip and automation clip splits - the old singular clip returns, but the two split clips also remain.
  • The snapping size is different for midi clips - I can't split into anything smaller than a full measure. Nevermind, midi clips don't work like that
  • Double-clicking in Knife mode performs the same action as double-clicking in Draw mode. This problem already existed for sample tracks, but now also exists for other types of tracks too. Double-clicking shouldn't do anything special in Knife mode.
  • When splitting midi clips, if the clip is cut down the middle of a note, the clip size of the resulting left clip is too small and hides the note that passed through the cut point. If the two new clips are ordered correctly, you should be able to make the right clip display over top of the left clip without needing to touch the size of the left clip.

Unrelated bug: In the Piano Roll, the mouse icon when in Pitch Bend mode can disappear if you click on a note, then close the note's automation editor window.

Other than the issues above, everything seems to work fine. I'm pretty excited about this feature and hope we can get it merged soon.

@regulus79
Copy link
Contributor Author

I think I have fixed most of the bugs, however I don't understand how journalling works enough to fix the bug where the new clips don't disappear after undoing. I will continue to look into it, but I may need some help.

Also, I decided to remove the restriction on midi clips needing to be split on the bar, and instead implemented your suggestion on letting the left clip's length be longer and extend behind the right clip. So now they can be split at arbitrary positions!

@regulus79
Copy link
Contributor Author

regulus79 commented Sep 5, 2024

Ah, nevermind, it seems that using close() instead of remove() makes it work! I will have to look into why that happens.

Copy link
Member

@messmerd messmerd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick review before I test it again

src/gui/clips/MidiClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/PatternClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
src/gui/clips/ClipView.cpp Outdated Show resolved Hide resolved
include/ClipView.h Show resolved Hide resolved
@messmerd
Copy link
Member

@regulus79 It seems that when I split a midi clip, the original singular clip still remains though it is invisible. You can hear it when the song plays since it plays twice as loud as before the clip was split, and if you clone the track, it becomes visible in the cloned track.

@regulus79
Copy link
Contributor Author

Okay that's an interesting bug... I think it may have to do with me using close() instead of remove() when removing the old clip. But that's odd, since it doesn't appear to happen for AutomationClips which also use close() (???)

After some testing, it appears that using remove() solves the issue. I also think I found the cause of the old problem when using remove(), which is that I was calling it after m_clip->getTrack()->restoreJournallingState();. Again, I'm not an expert in Journalling, but calling it before appears to fix the problem.

Copy link
Member

@messmerd messmerd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Midi clip splitting seems to work fine now. I wasn't able to find any problems with it while testing.

Pattern clip splitting also seems fine.

However, for automation clips, if you make the split directly on a node with modified tangents or with an outValue, it doesn't split correctly.

src/gui/clips/AutomationClipView.cpp Show resolved Hide resolved
src/gui/clips/MidiClipView.cpp Show resolved Hide resolved
src/gui/clips/PatternClipView.cpp Show resolved Hide resolved
@regulus79
Copy link
Contributor Author

I fixed the problem with out/in values and tangents in automation clip splitting.

@regulus79 regulus79 mentioned this pull request Oct 5, 2024
3 tasks
Copy link
Member

@messmerd messmerd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems pretty good now except for an issue when splitting Cubic Hermite clips, which I explain in a comment below

src/gui/clips/AutomationClipView.cpp Outdated Show resolved Hide resolved
rightClip->clear();
// Remove the nodes past the split point from the left clip. +1 to prevent deleting a node
// right on the split point
leftClip->removeNodes(splitPos + 1, m_initialClipEnd);
Copy link
Member

@messmerd messmerd Oct 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason this doesn't seem to be working, since the nodes to the right of the split point still remain in the left clip after the split. It doesn't make a difference though because the left clip's length is shortened which prevents nodes past the clip length from having an effect.

This might be a good thing, because with Cubic Hermite automations, neighboring nodes can affect the shape of the waveform not just between the node and its neighbors but also between its neighbors and their neighbors. This means that simply chopping off nodes to the right of the split point in a Cubic Hermite automation clip cannot really be done without changing the shape of the waveform to the left of the split point, even if you add a new node at the split point.

Simply shortening the length of the left automation clip without removing any of the nodes seems to solve that problem for Cubic Hermite clips, but the problem remains for the right clip. Unfortunately the length of automation clips cannot be modified from the left side the way sample clips can - only the right side - so the clip to the right of the split point cannot be fixed in the same way.

I think these are our options:

  1. Make automation clip length adjustable from the left side too (similar to sample clips), and use that feature to split Cubic Hermite automations without changing the resulting waveforms
  2. Pretend the problem with splitting Cubic Hermite clips doesn't exist
  3. Return false for Cubic Hermite clips (and possibly print a command-line error message) to disallow splitting them, at least for now

Option 1 is the only genuine solution I can think of, but it would be difficult and beyond the scope of this PR. So that leaves options 2 and 3. Personally I favor option 3 to avoid the strong possibility of messing up automations when splitting them.

What are your thoughts?

Copy link
Contributor Author

@regulus79 regulus79 Oct 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason the nodes to the right are being deleted for me; I believe my branch is up to date, so I'm not sure why it is not working for you.

Regarding splitting Cubic Hermite clips; that was also on my mind, and I agree I don't believe it's possible to accurately reconstruct the curve by inserting a new point in the middle. Option 3 seems a bit inconvenient for the user, since I think an approximate split is better than no split at all. I was originally planning to let it slide with the current implementation, but now that you bring it up, I do think it would be good to take the time to do it correctly and make sure everything is exact. I think I will try and see how hard it is to do option 1 and implement ResizeLeft for automation clips.

Copy link
Contributor Author

@regulus79 regulus79 Oct 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :) That only took about 30 minutes to code, not bad at all!

I added the ability to resize automation clips from the left (I had to add offsets to a few variables in paintEvent; I think I noticed a minor graphical bug with vertical lines sometimes appearing, I will look into that tomorrow), and I changed the splitClip code to essentially match SampleClip and PatternClip. This does however mean that when editing the right split clip, all the previous nodes are in front of you, which may be a bit odd, but it works.

(Now I'm thinking; it might be good to also change the timeline numbering in the AutomationEditor to help users see where exactly the clip starts...)

Copy link
Contributor

@szeli1 szeli1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a style review

src/gui/clips/ClipView.cpp Outdated Show resolved Hide resolved
include/ClipView.h Outdated Show resolved Hide resolved
@szeli1
Copy link
Contributor

szeli1 commented Nov 23, 2024

I'm testing currently and found an issue:

Steps to reproduce:

  1. Open a new project
  2. Make a midi clip that is 2 bar long
  3. Place 3 notes, at 1 bar 1 beat, at 1 bar 4 beat 12 tick, and at 2 bar 3 beat, the 2. one should be wide enough so its end is in the 2. bar
  4. Click on the knife tool
  5. Make a cut exactly at 1 bar 4 beat 24 tick by pressing ctrl + clicking
    The result is 2 clips, the left one is larger and behind the right one. I know (or at least I think) working with TimePos can be difficult, however I think this Issue should / could be fixed.

@szeli1
Copy link
Contributor

szeli1 commented Nov 23, 2024

I think patterns work great, and AutomationClips still need work. If an automation clip is split ant the left side is edited, then it will snap back to the original size.

Co-authored-by: szeli1 <[email protected]>
@regulus79
Copy link
Contributor Author

The result is 2 clips, the left one is larger and behind the right one. I know (or at least I think) working with TimePos can be difficult, however I think this Issue should / could be fixed.

This was considered in previous iterations of the pr, but I believe the current functionality is correct.

The odd behavior you are experiencing is due to the fact that midi clips automatically round their length up to a whole bar. If a note is cut partway through a note which crosses a bar, in order to maintain identical sounds before and after the split, it must let the note extend past the bar in the new left clip which can cause the new length to round back up to two bars. I agree that it does seem odd that the left clip can sometimes be extend further than the right clip, but that is how midi clips currently work in lmms. Additionally, the right clip must start at the split pos so that the split make sense, which means the right clip's length is determined by it's local position on the timeline relative to the split pos, which means it can sometimes not round up to meet a global bar, and thus sometimes it can appear shorter than the left clip in rare circumstances.

If you would like me to change midi clips to have arbitrary sizes, just lmk! I would be happy to do so.

If an automation clip is split ant the left side is edited, then it will snap back to the original size.

Whoops! I forgot about that. I may have to think about what the best solution here is.

Given my previous discussion with messmerd, and how I concluded that adding left-resizing support to AutomationClips would be the best option to accurately split cubic bezier clips, I think it may be beneficial to keep the current setup but simply disable automatic resizing if the automation clips has already been resized once. Or we could revert back to the old setup of erasing the nodes to the right of the split point, but as previously mentioned, this causes cubic bezier clips to be incorrectly split.

I was already planning on doing some work on the automation editor to more clearly convey to the user where the clip starts, so perhaps I can implement a fix for this when I do that.

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

Successfully merging this pull request may close these issues.

4 participants