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

Knife Tool for Sample Clips #4998

Merged
merged 20 commits into from
Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added data/themes/classic/edit_knife.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/themes/default/edit_knife.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions include/SampleTrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ class SampleTCO : public TrackContentObject
bool isPlaying() const;
void setIsPlaying(bool isPlaying);

void inline setMarkerPos( int x )
{
m_markerPos = x;
}
void inline setMarkerEnabled( bool e )
{
m_marker = e;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's remove the spaces inside the brackets in new code.

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought policy was to match surrounding code until an autoformatter fixes this stuff.

Copy link
Member

Choose a reason for hiding this comment

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

match surrounding code

setIsPlaying() doesn't have spaces in the parentheses, but setSampleBuffer() does. Neither way will match the surrounding code perfectly.

Copy link
Member Author

Choose a reason for hiding this comment

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

The vast majority of the file uses spaces, but fair point.

public slots:
void setSampleBuffer( SampleBuffer* sb );
void setSampleFile( const QString & _sf );
Expand All @@ -87,6 +96,8 @@ public slots:
BoolModel m_recordModel;
bool m_isPlaying;

bool m_marker = false;
int m_markerPos = 0;

friend class SampleTCOView;

Expand Down
4 changes: 4 additions & 0 deletions include/SongEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class SongEditor : public TrackContainerView
enum EditMode
{
DrawMode,
KnifeMode,
SelectMode
};

Expand All @@ -82,6 +83,7 @@ public slots:

void setEditMode( EditMode mode );
void setEditModeDraw();
void setEditModeKnife();
void setEditModeSelect();
void toggleProportionalSnap();

Expand Down Expand Up @@ -114,6 +116,7 @@ private slots:
virtual void wheelEvent( QWheelEvent * we );

virtual bool allowRubberband() const;
virtual bool knifeMode() const;


Song * m_song;
Expand Down Expand Up @@ -192,6 +195,7 @@ protected slots:

ActionGroup * m_editModeGroup;
QAction* m_drawModeAction;
QAction* m_knifeModeAction;
QAction* m_selectModeAction;
QAction* m_crtlAction;

Expand Down
4 changes: 4 additions & 0 deletions include/Track.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ protected slots:
MoveSelection,
Resize,
ResizeLeft,
Split,
CopySelection,
ToggleSelected
} ;
Expand Down Expand Up @@ -324,7 +325,10 @@ protected slots:
void setInitialOffsets();

bool mouseMovedDistance( QMouseEvent * me, int distance );
bool unquantizedModHeld( QMouseEvent * me );
MidiTime draggedTCOPos( QMouseEvent * me );
int knifeMarkerPos( QMouseEvent * me );
MidiTime quantizeMarkerPos( MidiTime, bool shiftMode );
} ;


Expand Down
1 change: 1 addition & 0 deletions include/TrackContainerView.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class TrackContainerView : public QWidget, public ModelView,
const TrackView * trackViewAt( const int _y ) const;

virtual bool allowRubberband() const;
virtual bool knifeMode() const;

inline bool rubberBandActive() const
{
Expand Down
153 changes: 140 additions & 13 deletions src/core/Track.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -716,9 +716,37 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me )
{
setInitialPos( me->pos() );
setInitialOffsets();

if( !fixedTCOs() && me->button() == Qt::LeftButton )
{
if( me->modifiers() & Qt::ControlModifier )
if( m_trackView->trackContainerView()->knifeMode() )
{
SampleTCO * sTco = dynamic_cast<SampleTCO*>( m_tco );

if( me->x() < RESIZE_GRIP_WIDTH && sTco
&& !m_tco->getAutoResize() )
{
m_action = ResizeLeft;
setCursor( Qt::SizeHorCursor );
}
else if( me->x() >= width() - RESIZE_GRIP_WIDTH )
{
m_action = Resize;
setCursor( Qt::SizeHorCursor );
}
else if (sTco)
{
m_action = Split;
sTco->setMarkerPos( knifeMarkerPos( me ) );
sTco->setMarkerEnabled( true );
update();
}
// We can't split anything except samples right now, so disable the
// action to avoid entering if statements we don't need to. This
// also saves us a few 'if(sTco)' checks
else { m_action = NoAction; }
}
else if ( me->modifiers() & Qt::ControlModifier )
{
if( isSelected() )
{
Expand Down Expand Up @@ -812,6 +840,16 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me )
{
remove();
}
if (m_action == Split)
{
m_action = NoAction;
SampleTCO * sTco = dynamic_cast<SampleTCO*>( m_tco );
if (sTco)
{
sTco->setMarkerEnabled( false );
update();
}
}
}
else if( me->button() == Qt::MidButton )
{
Expand Down Expand Up @@ -941,8 +979,6 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
}
else if( m_action == Resize || m_action == ResizeLeft )
{
// If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize
const bool unquantized = (me->modifiers() & Qt::ControlModifier) || (me->modifiers() & Qt::AltModifier);
const float snapSize = gui->songEditor()->m_editor->getSnapSize();
// Length in ticks of one snap increment
const MidiTime snapLength = MidiTime( (int)(snapSize * MidiTime::ticksPerTact()) );
Expand All @@ -952,7 +988,8 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
// The clip's new length
MidiTime l = static_cast<int>( me->x() * MidiTime::ticksPerTact() / ppt );

if ( unquantized )
// If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize
if ( unquantizedModHeld(me) )
{ // We want to preserve this adjusted offset,
// even if the user switches to snapping later
setInitialPos( m_initialMousePos );
Expand Down Expand Up @@ -988,7 +1025,7 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
m_trackView->trackContainerView()->currentPosition()+
static_cast<int>( x * MidiTime::ticksPerTact() / ppt ) );

if( unquantized )
if( unquantizedModHeld(me) )
{ // We want to preserve this adjusted offset,
// even if the user switches to snapping later
setInitialPos( m_initialMousePos );
Expand Down Expand Up @@ -1034,6 +1071,12 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
MidiTime::ticksPerTact() ) );
s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) );
}
else if( m_action == Split )
{
SampleTCO * sTco = dynamic_cast<SampleTCO*>( m_tco );
if (sTco) { sTco->setMarkerPos( knifeMarkerPos( me ) ); }
update();
}
else
{
SampleTCO * sTco = dynamic_cast<SampleTCO*>( m_tco );
Expand Down Expand Up @@ -1070,12 +1113,48 @@ void TrackContentObjectView::mouseReleaseEvent( QMouseEvent * me )
{
setSelected( !isSelected() );
}

if( m_action == Move || m_action == Resize || m_action == ResizeLeft )
else if( m_action == Move || m_action == Resize || m_action == ResizeLeft )
{
// TODO: Fix m_tco->setJournalling() consistency
m_tco->setJournalling( true );
}
else if( m_action == Split )
{
SampleTCO * leftTCO = dynamic_cast<SampleTCO*>( m_tco );

if (leftTCO)
{
leftTCO->setMarkerEnabled( false );

const int relativePixelPos = me->pos().x();
const float ppt = m_trackView->trackContainerView()->pixelsPerTact();
MidiTime splitPos = relativePixelPos * MidiTime::ticksPerTact() / ppt;

if ( !unquantizedModHeld(me) )
{
splitPos = quantizeMarkerPos( splitPos, me->modifiers() & Qt::ShiftModifier );
}

splitPos += m_initialTCOPos;

//Don't split if we slid off the TCO or if we're on the clip's start/end
//Cutting at exactly the start/end position would create a zero length
//clip (bad), and a clip the same length as the original one (pointless).
if ( splitPos > m_initialTCOPos && splitPos < m_initialTCOEnd )
{
leftTCO->copy();
SampleTCO * rightTCO = new SampleTCO ( leftTCO->getTrack() );
rightTCO->paste();

leftTCO->changeLength( splitPos - m_initialTCOPos );

rightTCO->movePosition( splitPos );
rightTCO->changeLength( m_initialTCOEnd - splitPos );
rightTCO->setStartTimeOffset( leftTCO->startTimeOffset() - leftTCO->length() );
}
PhysSong marked this conversation as resolved.
Show resolved Hide resolved
}
}

m_action = NoAction;
delete m_hint;
m_hint = NULL;
Expand Down Expand Up @@ -1174,6 +1253,14 @@ bool TrackContentObjectView::mouseMovedDistance( QMouseEvent * me, int distance




bool TrackContentObjectView::unquantizedModHeld( QMouseEvent * me )
{
return me->modifiers() & Qt::ControlModifier || me->modifiers() & Qt::AltModifier;
}



/*! \brief Calculate the new position of a dragged TCO from a mouse event
*
*
Expand All @@ -1188,12 +1275,8 @@ MidiTime TrackContentObjectView::draggedTCOPos( QMouseEvent * me )
MidiTime newPos = m_initialTCOPos + mouseOff * MidiTime::ticksPerTact() / ppt;
MidiTime offset = newPos - m_initialTCOPos;
// If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize
if ( me->button() != Qt::NoButton
|| (me->modifiers() & Qt::ControlModifier)
|| (me->modifiers() & Qt::AltModifier) )
{
// We want to preserve this adjusted offset,
// even if the user switches to snapping
if ( me->button() != Qt::NoButton || unquantizedModHeld(me) )
{ // We want to preserve this adjusted offset, even if the user switches to snapping
setInitialPos( m_initialMousePos );
}
else if ( me->modifiers() & Qt::ShiftModifier )
Expand All @@ -1218,6 +1301,50 @@ MidiTime TrackContentObjectView::draggedTCOPos( QMouseEvent * me )



int TrackContentObjectView::knifeMarkerPos( QMouseEvent * me )
{
//Position relative to start of clip
const int markerPos = me->pos().x();

//In unquantized mode, we don't have to mess with the position at all
if ( unquantizedModHeld(me) ) { return markerPos; }
else
{ //Otherwise we...
//1: Convert the position to a MidiTime
const float ppt = m_trackView->trackContainerView()->pixelsPerTact();
MidiTime midiPos = markerPos * MidiTime::ticksPerTact() / ppt;
//2: Snap to the correct position, based on modifier keys
midiPos = quantizeMarkerPos( midiPos, me->modifiers() & Qt::ShiftModifier );
//3: Convert back to a pixel position
return midiPos * ppt / MidiTime::ticksPerTact();
}
}




MidiTime TrackContentObjectView::quantizeMarkerPos( MidiTime midiPos, bool shiftMode )
{
const float snapSize = gui->songEditor()->m_editor->getSnapSize();
if ( shiftMode )
{ //If shift is held we quantize the length of the new left clip...
const MidiTime leftPos = midiPos.quantize( snapSize );
//...or right clip...
const MidiTime rightOff = m_tco->length() - midiPos;
const MidiTime rightPos = m_tco->length() - rightOff.quantize( snapSize );
//...whichever gives a position closer to the cursor
if ( abs(leftPos - midiPos) < abs(rightPos - midiPos) ) { return leftPos; }
else { return rightPos; }
}
else
{
return (MidiTime(midiPos + m_initialTCOPos).quantize( snapSize ) - m_initialTCOPos);
}
}




// ===========================================================================
// trackContentWidget
// ===========================================================================
Expand Down
10 changes: 9 additions & 1 deletion src/gui/TrackContainerView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,14 @@ bool TrackContainerView::allowRubberband() const



bool TrackContainerView::knifeMode() const
{
return false;
}




void TrackContainerView::setPixelsPerTact( int _ppt )
{
m_ppt = _ppt;
Expand Down Expand Up @@ -377,7 +385,7 @@ void TrackContainerView::dropEvent( QDropEvent * _de )
//it->toggledInstrumentTrackButton( true );
_de->accept();
}
else if( type == "samplefile" || type == "pluginpresetfile"
else if( type == "samplefile" || type == "pluginpresetfile"
|| type == "soundfontfile" || type == "vstpluginfile"
|| type == "patchfile" )
{
Expand Down
16 changes: 16 additions & 0 deletions src/gui/editors/SongEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ void SongEditor::setEditModeDraw()
setEditMode(DrawMode);
}

void SongEditor::setEditModeKnife()
{
setEditMode(KnifeMode);
}

void SongEditor::setEditModeSelect()
{
setEditMode(SelectMode);
Expand Down Expand Up @@ -713,6 +718,14 @@ bool SongEditor::allowRubberband() const



bool SongEditor::knifeMode() const
{
return m_mode == KnifeMode;
}




ComboBoxModel *SongEditor::zoomingModel() const
{
return m_zoomingModel;
Expand Down Expand Up @@ -777,13 +790,16 @@ SongEditorWindow::SongEditorWindow(Song* song) :

m_editModeGroup = new ActionGroup(this);
m_drawModeAction = m_editModeGroup->addAction(embed::getIconPixmap("edit_draw"), tr("Draw mode"));
m_knifeModeAction = m_editModeGroup->addAction(embed::getIconPixmap("edit_knife"), tr("Knife mode (split sample clips)"));
m_selectModeAction = m_editModeGroup->addAction(embed::getIconPixmap("edit_select"), tr("Edit mode (select and move)"));
m_drawModeAction->setChecked(true);

connect(m_drawModeAction, SIGNAL(triggered()), m_editor, SLOT(setEditModeDraw()));
connect(m_knifeModeAction, SIGNAL(triggered()), m_editor, SLOT(setEditModeKnife()));
connect(m_selectModeAction, SIGNAL(triggered()), m_editor, SLOT(setEditModeSelect()));

editActionsToolBar->addAction( m_drawModeAction );
editActionsToolBar->addAction( m_knifeModeAction );
editActionsToolBar->addAction( m_selectModeAction );

DropToolBar *timeLineToolBar = addDropToolBarToTop(tr("Timeline controls"));
Expand Down
4 changes: 4 additions & 0 deletions src/tracks/SampleTrack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,10 @@ void SampleTCOView::paintEvent( QPaintEvent * pe )
embed::getIconPixmap( "muted", size, size ) );
}

if ( m_tco->m_marker )
{
p.drawLine(m_tco->m_markerPos, rect().bottom(), m_tco->m_markerPos, rect().top());
}
// recording sample tracks is not possible at the moment

/* if( m_tco->isRecord() )
Expand Down