diff --git a/src/core/PatternClip.cpp b/src/core/PatternClip.cpp index 9af9bda6ad3..a0752b0aa83 100644 --- a/src/core/PatternClip.cpp +++ b/src/core/PatternClip.cpp @@ -60,6 +60,7 @@ void PatternClip::saveSettings(QDomDocument& doc, QDomElement& element) element.setAttribute( "pos", startPosition() ); } element.setAttribute( "len", length() ); + element.setAttribute("off", startTimeOffset()); element.setAttribute( "muted", isMuted() ); if( usesCustomClipColor() ) { @@ -78,6 +79,7 @@ void PatternClip::loadSettings(const QDomElement& element) movePosition( element.attribute( "pos" ).toInt() ); } changeLength( element.attribute( "len" ).toInt() ); + setStartTimeOffset(element.attribute("off").toInt()); if( element.attribute( "muted" ).toInt() != isMuted() ) { toggleMute(); diff --git a/src/gui/clips/ClipView.cpp b/src/gui/clips/ClipView.cpp index e2da1143948..8c4f704bcea 100644 --- a/src/gui/clips/ClipView.cpp +++ b/src/gui/clips/ClipView.cpp @@ -43,6 +43,8 @@ #include "MidiClip.h" #include "MidiClipView.h" #include "Note.h" +#include "PatternClip.h" +#include "PatternStore.h" #include "SampleClip.h" #include "Song.h" #include "SongEditor.h" @@ -502,10 +504,11 @@ void ClipView::dropEvent( QDropEvent * de ) void ClipView::updateCursor(QMouseEvent * me) { auto sClip = dynamic_cast(m_clip); + auto pClip = dynamic_cast(m_clip); // If we are at the edges, use the resize cursor if (!me->buttons() && !m_clip->getAutoResize() && !isSelected() - && ((me->x() > width() - RESIZE_GRIP_WIDTH) || (me->x() < RESIZE_GRIP_WIDTH && sClip))) + && ((me->x() > width() - RESIZE_GRIP_WIDTH) || (me->x() < RESIZE_GRIP_WIDTH && (sClip || pClip)))) { setCursor(Qt::SizeHorCursor); } @@ -633,6 +636,7 @@ void ClipView::mousePressEvent( QMouseEvent * me ) if( !fixedClips() && me->button() == Qt::LeftButton ) { auto sClip = dynamic_cast(m_clip); + auto pClip = dynamic_cast(m_clip); const bool knifeMode = m_trackView->trackContainerView()->knifeMode(); if ( me->modifiers() & Qt::ControlModifier && !(sClip && knifeMode) ) @@ -677,7 +681,7 @@ void ClipView::mousePressEvent( QMouseEvent * me ) m_action = Resize; setCursor( Qt::SizeHorCursor ); } - else if( me->x() < RESIZE_GRIP_WIDTH && sClip ) + else if( me->x() < RESIZE_GRIP_WIDTH && (sClip || pClip) ) { m_action = ResizeLeft; setCursor( Qt::SizeHorCursor ); @@ -836,7 +840,6 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) if( m_action == Move ) { TimePos newPos = draggedClipPos( me ); - m_clip->movePosition(newPos); newPos = m_clip->startPosition(); // Get the real position the Clip was dragged to for the label m_trackView->getTrackContentWidget()->changePosition(); @@ -916,7 +919,8 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) else { auto sClip = dynamic_cast(m_clip); - if( sClip ) + auto pClip = dynamic_cast(m_clip); + if( sClip || pClip ) { const int x = mapToParent( me->pos() ).x() - m_initialMousePos.x(); @@ -948,12 +952,27 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) t = qMin( m_initialClipEnd - minLength, m_initialClipPos + offset ); } - TimePos oldPos = m_clip->startPosition(); - if( m_clip->length() + ( oldPos - t ) >= 1 ) + TimePos positionOffset = m_clip->startPosition() - t; + if (m_clip->length() + positionOffset >= 1) { - m_clip->movePosition( t ); - m_clip->changeLength( m_clip->length() + ( oldPos - t ) ); - sClip->setStartTimeOffset( sClip->startTimeOffset() + ( oldPos - t ) ); + m_clip->movePosition(t); + m_clip->changeLength(m_clip->length() + positionOffset); + if (sClip) + { + sClip->setStartTimeOffset(sClip->startTimeOffset() + positionOffset); + } + else if (pClip) + { + // Modulus the start time offset as we need it only for offsets + // inside the pattern length. This is done to prevent a value overflow. + // The start time offset may still become larger than the pattern length + // whenever the pattern length decreases without a clip resize following. + // To deal safely with it, always modulus before use. + tick_t patternLength = Engine::patternStore()->lengthOfPattern(pClip->patternIndex()) + * TimePos::ticksPerBar(); + TimePos position = (pClip->startTimeOffset() + positionOffset) % patternLength; + pClip->setStartTimeOffset(position); + } } } } diff --git a/src/gui/clips/PatternClipView.cpp b/src/gui/clips/PatternClipView.cpp index 477a90052e6..bf12440c722 100644 --- a/src/gui/clips/PatternClipView.cpp +++ b/src/gui/clips/PatternClipView.cpp @@ -115,14 +115,18 @@ void PatternClipView::paintEvent(QPaintEvent*) // bar lines const int lineSize = 3; + int pixelsPerPattern = Engine::patternStore()->lengthOfPattern(m_patternClip->patternIndex()) * pixelsPerBar(); + int offset = static_cast(m_patternClip->startTimeOffset() * (pixelsPerBar() / TimePos::ticksPerBar())) + % pixelsPerPattern; + if (offset < 2) { + offset += pixelsPerPattern; + } + p.setPen( c.darker( 200 ) ); - bar_t t = Engine::patternStore()->lengthOfPattern(m_patternClip->patternIndex()); - if (m_patternClip->length() > TimePos::ticksPerBar() && t > 0) + if (pixelsPerPattern > 0) { - for( int x = static_cast( t * pixelsPerBar() ); - x < width() - 2; - x += static_cast( t * pixelsPerBar() ) ) + for (int x = offset; x < width() - 2; x += pixelsPerPattern) { p.drawLine( x, BORDER_WIDTH, x, BORDER_WIDTH + lineSize ); p.drawLine( x, rect().bottom() - ( BORDER_WIDTH + lineSize ), diff --git a/src/tracks/PatternTrack.cpp b/src/tracks/PatternTrack.cpp index 9fcc2c83122..cdcd108ff0c 100644 --- a/src/tracks/PatternTrack.cpp +++ b/src/tracks/PatternTrack.cpp @@ -108,19 +108,27 @@ bool PatternTrack::play( const TimePos & _start, const fpp_t _frames, } TimePos lastPosition; - TimePos lastLen; + TimePos lastLength; + tick_t lastOffset = 0; for (const auto& clip : clips) { if (!clip->isMuted() && clip->startPosition() >= lastPosition) { lastPosition = clip->startPosition(); - lastLen = clip->length(); + lastLength = clip->length(); + tick_t patternLength = Engine::patternStore()->lengthOfPattern(static_cast(clip)->patternIndex()) + * TimePos::ticksPerBar(); + lastOffset = patternLength - (clip->startTimeOffset() % patternLength); + if (lastOffset == patternLength) + { + lastOffset = 0; + } } } - if( _start - lastPosition < lastLen ) + if( _start - lastPosition < lastLength ) { - return Engine::patternStore()->play(_start - lastPosition, _frames, _offset, s_infoMap[this]); + return Engine::patternStore()->play(_start - lastPosition + lastOffset, _frames, _offset, s_infoMap[this]); } return false; } @@ -240,4 +248,4 @@ void PatternTrack::swapPatternTracks(Track* track1, Track* track2) } -} // namespace lmms \ No newline at end of file +} // namespace lmms