-
-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Refactor to move positionChanged signal to Timeline #7454
base: master
Are you sure you want to change the base?
Conversation
src/gui/editors/PianoRoll.cpp
Outdated
@@ -290,7 +290,7 @@ PianoRoll::PianoRoll() : | |||
this, SLOT( updatePositionStepRecording( const lmms::TimePos& ) ) ); | |||
|
|||
// update timeline when in record-accompany mode | |||
connect(m_timeLine, &TimeLineWidget::positionChanged, this, &PianoRoll::updatePositionAccompany); | |||
connect(&Engine::getSong()->getTimeline(Song::PlayMode::Song), SIGNAL(positionChanged(const lmms::TimePos&)), this, SLOT(updatePositionAccompany(const lmms::TimePos&))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
connect(&Engine::getSong()->getTimeline(Song::PlayMode::Song), SIGNAL(positionChanged(const lmms::TimePos&)), this, SLOT(updatePositionAccompany(const lmms::TimePos&))); | |
connect(&Engine::getSong()->getTimeline(Song::PlayMode::Song), &Timeline::positionChanged, this, &PianoRoll::updatePositionAccompany); |
We're trying to avoid using the SIGNAL
and SLOT
macros in new code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, there may be a better approach than adding a signal to Timeline
... Let me think this over some more
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really like how Timeline
and TimeLineWidget
have implemented their signals and slots for changes in position. I think ideally Timeline
should be the model and only it should emit any positionChanged
signals, while TimeLineWidget
should be the view and its updatePosition
slot should not emit any signals. The way it is currently, the TimeLineWidget
is trying to do the job of both a model and a view, and that complicates things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DomClark Do you agree with my assessment? It looks to me like a proper solution to this bug may require rethinking how the timeline position signals/slots are implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have now updated to PR to refactor everything in order to move the positionChanged
signal from TimeLineWidget
to Timeline
.
Co-authored-by: Dalton Messmer <[email protected]>
Fix looks simple. @messmerd did this fix your issue? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I ran it, I saw this output in the console:
QObject::connect: Cannot queue arguments of type 'lmms::TimePos'
(Make sure 'lmms::TimePos' is registered using qRegisterMetaType().)
src/core/Song.cpp
Outdated
@@ -121,6 +121,7 @@ Song::Song() : | |||
|
|||
for (auto& scale : m_scales) {scale = std::make_shared<Scale>();} | |||
for (auto& keymap : m_keymaps) {keymap = std::make_shared<Keymap>();} | |||
for (auto& timeline : m_timelines) { connect(this, &Song::bufferProcessed, &timeline, &Timeline::updatePosition, Qt::QueuedConnection); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This passes the Song's bufferProcessed
signal to Timeline's updatePosition
slot which does nothing except emit a positionChanged
signal containing the play position which editors connect to for updating their position.
It seems to me that Timeline::updatePosition
should not exist. When the setters of Timeline are called and its position changes, they would emit the positionChanged
signal. Then any views connected to that signal would update as needed.
Maybe it would be a good idea to sketch out a tree diagram of the signal/slot connections between the different objects - might help with understanding and simplifying this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, I think the bufferProcessed
signal and updatePosition
slot can probably be removed.
Except that, when I remove them and instead rely on setTicks(ticks)
to emit the signal, the processNextBuffer
function in Song
does not update the playhead position, so it stays still during playback. I believe this may be due to processNextBuffer
being on a different thread?
But then it somehow fixes itself when I specify Qt::DirectConnection
in all the connect()
s. I'm not sure why.
Co-authored-by: Dalton Messmer <[email protected]>
Co-authored-by: Dalton Messmer <[email protected]>
Co-authored-by: Dalton Messmer <[email protected]>
The |
I found a solution to the warnings by simply removing the parameter from the signal and instead having each editor query the current position of the timeline themselves. The connections are all now |
Okay, this is difficult. Originally, I had it so that So then sakertooth had the idea of routing all the connections to the gui editors through Another potential solution which sakertooth suggested is using What should be done? |
Coming up with sound designs is probably my biggest weakness. The idea I mentioned could work, but as more time passes I continue to fail to see a critical issue using queued connections that emit the |
include/PlayPos.h
Outdated
/* | ||
* PlayPos.h - declaration of class PlayPos, a TimePos with additional features useful for Timelines | ||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest removing PlayPos
and using TimePos
directly in Timeline
. This class mostly is just a container for data and does not do anything special really.
TimePos
can be refactored to store frames as its smallest unit of time (or just store the frame position maybe? do we even need a class just to store the frame position within the tick?). The member functions should then be changed accordingly to account for that as necessary. This might be okay in a separate PR if you feel its out of scope, but I think its minor enough that it shouldn't matter too much to do it in here. Up to you.
Then for the jumped variable, from what I can tell, this just lets us know if the timeline didn't change through time progressively but instead jumped in time. What would it mean for the jumped variable to be true
even when the timeline is not jumping? This should be a parameter for the positionChanged
signal. You might have to refactor the VST sync controller code for its invocation to be event-based and not executed within Song::processNextBuffer
though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure TimePos
can easily be refactored to store frames as its smallest unit of time, unless we allow for fractional frames. If the number of frames in a bar is not a whole number, then it will have to be rounded. That's fine, but if the bpm is changed, then every single TimePos will have to be updated at once so that the number of frames matches the correct tick amount. I feel like a better long-term solution would be to allow float tick values, but that's just me.
But I agree, the jumped variable can probably be combined with the positionChanged
signal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about just storing the frame position directly and still removing PlayPos
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have removed PlayPos
. Now each Timeline
stores it's current frame offset and jumped state. I will see if I can get jumped
to be passed in the positionChanged
signal instead.
@@ -273,23 +270,24 @@ void AutomationEditor::keyPressEvent(QKeyEvent * ke ) | |||
break; | |||
|
|||
case Qt::Key_Left: | |||
if( ( m_timeLine->pos() -= 16 ) < 0 ) | |||
if(m_timeLine->model()->getTicks() - 16 > 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(m_timeLine->model()->getTicks() - 16 > 0) | |
if ((m_timeLine->model()->getTicks() -= 16) < 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Timeline
does not currently implement the -=
operator. I suppose I could add it.
ke->accept(); | ||
break; | ||
|
||
case Qt::Key_Right: | ||
m_timeLine->pos() += 16; | ||
m_timeLine->updatePosition(); | ||
m_timeLine->model()->setTicks(m_timeLine->model()->getTicks() + 16); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to leave it as is?
m_timeLine->model()->setTicks(m_timeLine->model()->getTicks() + 16); | |
m_timeLine->model() += 16; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure it makes sense to +=
an integer to a Timeline*
? I get that it would just add to the internal TimePos
, but it just seems weird to add a number to a timeline object. It would definitely make the code simpler in this situation, but idk.
Co-authored-by: saker <[email protected]>
Co-authored-by: saker <[email protected]>
Co-authored-by: saker <[email protected]>
Co-authored-by: saker <[email protected]>
Partially following sakertooth's suggestion, I have removed I have also removed I could not find any code referencing
sakertooth also suggested getting rid of the |
Previously, this PR simply added a new signal to the
Timeline
class and had it emitted by theTimeLineWidget
class in order to solve #7351.However, as messmerd pointed out in his review comments, that was quite a hacky solution, and ideally the
positionChanged
signal would be solely managed by the backendTimeline
class, while the frontendTimeLineWidget
class would connect to those signals, instead of the other way around.This PR is no longer a simple bugfix, but instead a refactoring of the
Timeline
/TimeLineWidget
signal/slots.Changes
PlayPos
to its own file,PlayPos.h
.positionChanged
signal andupdatePosition
slot were removed fromTimeLineWidget
and moved toTimeline
.Timeline::positionChanged
were made intoQt::QueuedConnections
to prevent errors when the audio thread changes thePlayPos
which would immediately cause gui code to run if it were a direct connection.pos()
method andPlayPos m_pos
were removed fromTimeLineWidget
;PlayPos m_pos
was added toTimeline
, along with getter and setter methods which emitpositionChanged
.TimeLineWidget
no longer requires aPlayPos
reference.TimeLineWidget
stores a reference to itsTimeline
, a new method was added,model()
, for other classes to access it.PlayPos
es inSong
. Now eachTimeline
holds their respectivePlayPos
.Song
's methods forgetPlayPos
were changed to return thePlayPos
of the respectiveTimeline
. The non-const versions of the methods were removed becauseTimeline
does not expose itsPlayPos
to write.Timeline
s are used were updated, along with their calls to access/modify thePlayPos
. For example, occurrences ofm_timeline->pos()
were replaced withm_timeline->model()->getPlayPos()
, and calls tom_timeline->pos().setTicks(ticks)
were changed tom_timeline->model()->setTicks(ticks)
.Minor note: The members
m_timeline
orm_timeLine
in places likePianoRoll
orSongEditor
should honestly really be renamed something likem_timeLineWidget
, as they are notTimeline
objects.Old PR Description
This pull request fixes #7351 by changing the PianoRoll's connection from `m_timeline` to `&Engine::getSong()->getTimeline(Song::PlayMode::Song)`, and additionally adding the signal `positionChanged` to the `Timeline` class.The new signal is necessary because
getTimeline()
returns aTimeline
, which does not have apositionChanged
signal, whilem_timeline
is aTimeLineWidget
which does.This pr is marked as a draft, since after fixing the bug I found another one: When pressing stop after record-play, the piano roll play head does not go back to the start, but instead stops at its current position.Edit: I have decided to leave fixing that bug to a future pr, as it appears that it existed prior to the regression. Instead, this pr simply fixes the pianoroll playhead movement.