From 348315845a787546b4c8eeeb0a5a1189fc648718 Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Sat, 3 May 2014 20:32:58 +0200 Subject: [PATCH 1/6] Add new toolbutton, whatsthis, to MainWindow --- src/gui/MainWindow.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index b5359bbd2..b3c873b68 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -388,6 +388,12 @@ void MainWindow::finalize() SLOT( exportProject() ), m_toolBar ); + toolButton * whatsthis = new toolButton( + embed::getIconPixmap( "whatsthis" ), + tr( "What's this?" ), + this, SLOT( enterWhatsThisMode() ), + m_toolBar ); + m_toolBarLayout->setColumnMinimumWidth( 0, 5 ); m_toolBarLayout->addWidget( project_new, 0, 1 ); @@ -396,7 +402,7 @@ void MainWindow::finalize() m_toolBarLayout->addWidget( project_open_recent, 0, 4 ); m_toolBarLayout->addWidget( project_save, 0, 5 ); m_toolBarLayout->addWidget( project_export, 0, 6 ); - + m_toolBarLayout->addWidget( whatsthis, 0, 7 ); // window-toolbar From d66d5870fd65b5fb2c43027e9e83ea2ac3b81444 Mon Sep 17 00:00:00 2001 From: Vesa Date: Sat, 3 May 2014 23:52:24 +0300 Subject: [PATCH 2/6] PianoRoll: allow shift-right-click in addition to middle-click in vol/pan bars --- src/gui/PianoRoll.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/PianoRoll.cpp b/src/gui/PianoRoll.cpp index bfe20c867..b89799690 100644 --- a/src/gui/PianoRoll.cpp +++ b/src/gui/PianoRoll.cpp @@ -2247,7 +2247,8 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * _me ) } } else if( ( edit_note == true || m_action == ActionChangeNoteProperty ) && - ( _me->buttons() & Qt::LeftButton || _me->buttons() & Qt::MiddleButton ) ) + ( _me->buttons() & Qt::LeftButton || _me->buttons() & Qt::MiddleButton + || ( _me->buttons() & Qt::RightButton && _me->modifiers() & Qt::ShiftModifier ) ) ) { // editing note properties From 4564ed14cd1d8a5115d533fe88f981755b6bc576 Mon Sep 17 00:00:00 2001 From: Vesa Date: Sun, 4 May 2014 21:59:39 +0300 Subject: [PATCH 3/6] Automation Recording --- include/AutomatableModel.h | 9 +++++++ include/AutomationPattern.h | 13 +++++++++- include/AutomationPatternView.h | 4 ++- src/core/AutomationPattern.cpp | 41 ++++++++++++++++++++++--------- src/gui/AutomationPatternView.cpp | 35 +++++++++++++++++++++----- 5 files changed, 83 insertions(+), 19 deletions(-) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 4a1e6d2b5..940ea9168 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -133,6 +133,15 @@ public: float controllerValue( int frameOffset ) const; + // get value always in float - for automation recording + inline float getValue( int frameOffset = 0 ) const + { + if( unlikely( m_hasLinkedModels || m_controllerConnection != NULL ) ) + { + return controllerValue( frameOffset ); + } + return m_value; + } template T initValue() const diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index ca2ff2695..aea64947e 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -155,13 +155,21 @@ public: static AutomationPattern * globalAutomationPattern( AutomatableModel * _m ); static void resolveAllIDs(); + bool isRecording() const + { + return m_isRecording; + } + + void setRecording( const bool b ) + { + m_isRecording = b; + } public slots: void clear(); void openInAutomationEditor(); void objectDestroyed( jo_id_t ); - private: void cleanObjects(); void generateTangents(); @@ -179,6 +187,9 @@ private: ProgressionTypes m_progressionType; bool m_dragging; + + bool m_isRecording; + float m_lastRecordedValue; static const float DEFAULT_MIN_VALUE; static const float DEFAULT_MAX_VALUE; diff --git a/include/AutomationPatternView.h b/include/AutomationPatternView.h index 73b2a7d9e..382c28375 100644 --- a/include/AutomationPatternView.h +++ b/include/AutomationPatternView.h @@ -50,7 +50,7 @@ protected slots: void resetName(); void changeName(); void disconnectObject( QAction * _a ); - + void toggleRecording(); protected: virtual void constructContextMenu( QMenu * ); @@ -69,6 +69,8 @@ private: AutomationPattern * m_pat; QPixmap m_paintPixmap; bool m_needsUpdate; + + static QPixmap * s_pat_rec; void scaleTimemapToFit( float oldMin, float oldMax ); } ; diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 0f38b34d9..188a2dd5f 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -47,7 +47,9 @@ AutomationPattern::AutomationPattern( AutomationTrack * _auto_track ) : m_objects(), m_tension( 1.0 ), m_progressionType( DiscreteProgression ), - m_dragging( false ) + m_dragging( false ), + m_isRecording( false ), + m_lastRecordedValue( 0 ) { changeLength( MidiTime( 1, 0 ) ); } @@ -469,27 +471,44 @@ const QString AutomationPattern::name() const -void AutomationPattern::processMidiTime( const MidiTime & _time ) +void AutomationPattern::processMidiTime( const MidiTime & time ) { - if( _time >= 0 && hasAutomation() ) + if( ! isRecording() ) { - const float val = valueAt( _time ); - for( objectVector::iterator it = m_objects.begin(); - it != m_objects.end(); ++it ) + if( time >= 0 && hasAutomation() ) { - if( *it ) + const float val = valueAt( time ); + for( objectVector::iterator it = m_objects.begin(); + it != m_objects.end(); ++it ) { - ( *it )->setAutomatedValue( val ); - } + if( *it ) + { + ( *it )->setAutomatedValue( val ); + } + } + } + } + else + { + if( time >= 0 && hasAutomation() && ! m_objects.isEmpty() ) + { + const float value = static_cast( firstObject()->getValue() ); + if( value != m_lastRecordedValue ) + { + putValue( time, value, true ); + m_lastRecordedValue = value; + } + else if( valueAt( time ) != value ) + { + removeValue( time, false ); + } } } } - - trackContentObjectView * AutomationPattern::createView( trackView * _tv ) { return new AutomationPatternView( this, _tv ); diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index baa19376c..7f0f22194 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -38,6 +38,7 @@ #include "tooltip.h" +QPixmap * AutomationPatternView::s_pat_rec = NULL; AutomationPatternView::AutomationPatternView( AutomationPattern * _pattern, trackView * _parent ) : @@ -58,6 +59,9 @@ AutomationPatternView::AutomationPatternView( AutomationPattern * _pattern, toolTip::add( this, tr( "double-click to open this pattern in " "automation editor" ) ); setStyle( QApplication::style() ); + + if( s_pat_rec == NULL ) { s_pat_rec = new QPixmap( embed::getIconPixmap( + "pat_rec" ) ); } } @@ -134,6 +138,11 @@ void AutomationPatternView::disconnectObject( QAction * _a ) } +void AutomationPatternView::toggleRecording() +{ + m_pat->setRecording( ! m_pat->isRecording() ); + update(); +} void AutomationPatternView::constructContextMenu( QMenu * _cm ) @@ -156,6 +165,9 @@ void AutomationPatternView::constructContextMenu( QMenu * _cm ) _cm->addAction( embed::getIconPixmap( "edit_rename" ), tr( "Change name" ), this, SLOT( changeName() ) ); + _cm->addAction( embed::getIconPixmap( "record" ), + tr( "Set/clear record" ), + this, SLOT( toggleRecording() ) ); if( !m_pat->m_objects.isEmpty() ) { _cm->addSeparator(); @@ -236,12 +248,6 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) p.setPen( c.lighter( 130 ) ); p.drawRect( 1, 1, width()-3, height()-3 ); - p.setBrush( QBrush() ); - if( engine::automationEditor()->currentPattern() == m_pat ) - p.setPen( c.lighter( 130 ) ); - else - p.setPen( c.darker( 300 ) ); - p.drawRect( 0, 0, width()-1, height()-1 ); const float ppt = fixedTCOs() ? ( parentWidget()->width() - 2 * TCO_BORDER_WIDTH ) @@ -310,6 +316,22 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) } p.resetMatrix(); + + // recording icon for when recording automation + if( m_pat->isRecording() ) + { + p.drawPixmap( 4, 14, *s_pat_rec ); + } + + // outer edge + p.setBrush( QBrush() ); + if( engine::automationEditor()->currentPattern() == m_pat ) + p.setPen( c.lighter( 130 ) ); + else + p.setPen( c.darker( 300 ) ); + p.drawRect( 0, 0, width()-1, height()-1 ); + + // pattern name p.setFont( pointSize<8>( p.font() ) ); QColor text_color = ( m_pat->isMuted() || m_pat->getTrack()->isMuted() ) @@ -327,6 +349,7 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) embed::getIconPixmap( "muted", 16, 16 ) ); } + p.end(); _p.drawPixmap( 0, 0, m_paintPixmap ); From 62068684c28a2ec0da17138a6b9d6215e6bf9e43 Mon Sep 17 00:00:00 2001 From: Vesa Date: Sun, 4 May 2014 22:54:06 +0300 Subject: [PATCH 4/6] Fix to previous - use value() instead of making an unnecessary new function... also accidentally didn't upload the new image last time --- data/themes/default/pat_rec.png | Bin 0 -> 1000 bytes include/AutomatableModel.h | 10 ---------- src/core/AutomationPattern.cpp | 2 +- 3 files changed, 1 insertion(+), 11 deletions(-) create mode 100644 data/themes/default/pat_rec.png diff --git a/data/themes/default/pat_rec.png b/data/themes/default/pat_rec.png new file mode 100644 index 0000000000000000000000000000000000000000..5c0ed94555f7743ca756e10cc707b025c28a2a04 GIT binary patch literal 1000 zcmV>P)e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00U4-L_t(Y$L*D0Xj69-$3MTD zUUM6DT3ahZZE+}@edv>=xM2tdO_vlYOglP-jczLyRAilEVb5a>-9LsDb#6Y?{L_K&XJHauL+5dI=i%~8V2r0gvN0|MZ( z&*$3g@$3;weS#3{QAz?)ZE(<^%TqQdXh z19bi+JXSQx(eO1*O;3UxMmMR_Gq$|#!VTd0C}5{XwYrj=X^huOb& zE#EdZamMRay|-@d`|0uHt4@vxhvx^AGLELb8LY0Re{?ikDyQbya11F~$bP(kA0TS; zK}rrz!wdx8n%%ao zzvLXe+SGLJo#o4e#ZGcK7URJ1Fu;dENcsH=Ktv({VzRk;#Y-5e&~@WTESC6Uc(_*i z{h6Y}Hxdz3EiF~1X+Er6v}kFO_0z!4xfKN79U5xwy?5{TNHWRP^fXh`(?pU<+Hc>^ zi8}&V1KTt$v$5>r#qlvIyD?4nu3ELat-AVXo>OR`mYzTV>|{K?>F0R-#Xrv{NlD4W z$w_{Q#d1Yh5FNlJ;Dv3M^5dmo;5xh0#{0G}V-IXq!g5#*oB*BxY2a_5@AXgq|H8ku WWL|pTAf&_q0000 T initValue() const { diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 188a2dd5f..37a6f7b27 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -493,7 +493,7 @@ void AutomationPattern::processMidiTime( const MidiTime & time ) { if( time >= 0 && hasAutomation() && ! m_objects.isEmpty() ) { - const float value = static_cast( firstObject()->getValue() ); + const float value = static_cast( firstObject()->value() ); if( value != m_lastRecordedValue ) { putValue( time, value, true ); From 6b7a3c0041feaf542eeb09faecdece103b23c42c Mon Sep 17 00:00:00 2001 From: Vesa Date: Mon, 5 May 2014 00:37:39 +0300 Subject: [PATCH 5/6] Automation recording: add option to trackops to switch on/off recording in all TCOs on the track I still had time to implement this convenient little thing, so here you go... The same code can be reused in the future for sampletracks as well. --- include/track.h | 3 ++- src/core/track.cpp | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/track.h b/include/track.h index 07ae52d6e..8139ea869 100644 --- a/include/track.h +++ b/include/track.h @@ -373,7 +373,8 @@ private slots: void cloneTrack(); void removeTrack(); void updateMenu(); - + void recordingOn(); + void recordingOff(); private: static QPixmap * s_grip; diff --git a/src/core/track.cpp b/src/core/track.cpp index d75cc91b4..0c04ff369 100644 --- a/src/core/track.cpp +++ b/src/core/track.cpp @@ -1479,6 +1479,9 @@ void trackOperationsWidget::removeTrack() * * For all track types, we have the Clone and Remove options. * For instrument-tracks we also offer the MIDI-control-menu + * For automation tracks, extra options: turn on/off recording + * on all TCOs (same should be added for sample tracks when + * sampletrack recording is implemented) */ void trackOperationsWidget::updateMenu() { @@ -1497,11 +1500,45 @@ void trackOperationsWidget::updateMenu() to_menu->addMenu( dynamic_cast( m_trackView )->midiMenu() ); } + if( dynamic_cast( m_trackView ) ) + { + to_menu->addAction( tr( "Turn all recording on" ), this, SLOT( recordingOn() ) ); + to_menu->addAction( tr( "Turn all recording off" ), this, SLOT( recordingOff() ) ); + } } +void trackOperationsWidget::recordingOn() +{ + AutomationTrackView * atv = dynamic_cast( m_trackView ); + if( atv ) + { + const track::tcoVector & tcov = atv->getTrack()->getTCOs(); + for( track::tcoVector::const_iterator it = tcov.begin(); it != tcov.end(); it++ ) + { + AutomationPattern * ap = dynamic_cast( *it ); + if( ap ) { ap->setRecording( true ); } + } + atv->update(); + } +} +void trackOperationsWidget::recordingOff() +{ + AutomationTrackView * atv = dynamic_cast( m_trackView ); + if( atv ) + { + const track::tcoVector & tcov = atv->getTrack()->getTCOs(); + for( track::tcoVector::const_iterator it = tcov.begin(); it != tcov.end(); it++ ) + { + AutomationPattern * ap = dynamic_cast( *it ); + if( ap ) { ap->setRecording( false ); } + } + atv->update(); + } +} + // =========================================================================== // track From dbe69c74ecc00e5ef0420280e20b1951d46c06c7 Mon Sep 17 00:00:00 2001 From: Vesa Date: Mon, 5 May 2014 00:57:32 +0300 Subject: [PATCH 6/6] Automation recording: remove unnecessary hasAutomation() - the pattern doesn't need to already have automation in order to record more... --- src/core/AutomationPattern.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 37a6f7b27..49f154732 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -491,7 +491,7 @@ void AutomationPattern::processMidiTime( const MidiTime & time ) } else { - if( time >= 0 && hasAutomation() && ! m_objects.isEmpty() ) + if( time >= 0 && ! m_objects.isEmpty() ) { const float value = static_cast( firstObject()->value() ); if( value != m_lastRecordedValue )