PatternClips now use startTimeOffset, too, and can be resized on their start [FIX 7]

This commit is contained in:
Proud Electrics Studios
2023-01-16 10:13:35 +01:00
committed by Kevin Zander
parent 534e7edf0f
commit 1a68aee149
4 changed files with 52 additions and 19 deletions

View File

@@ -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();

View File

@@ -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<SampleClip*>(m_clip);
auto pClip = dynamic_cast<PatternClip*>(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<SampleClip*>(m_clip);
auto pClip = dynamic_cast<PatternClip*>(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<SampleClip*>(m_clip);
if( sClip )
auto pClip = dynamic_cast<PatternClip*>(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<int>( 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);
}
}
}
}

View File

@@ -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<int>(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<int>( t * pixelsPerBar() );
x < width() - 2;
x += static_cast<int>( 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 ),

View File

@@ -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<PatternClip*>(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
} // namespace lmms