diff --git a/TODO b/TODO index 6fda39978..27fa2d617 100644 --- a/TODO +++ b/TODO @@ -52,6 +52,10 @@ - add FLAC as export-format? Andrew Kelley's todo: +- automation recording: + * when you record and there is already an auto clip, it repeats it + * it freezes when you try to do it with the Volume or Panning slider + * crashes when you try to do it with a solo/mute button - when a song goes past the end of the song, make it stop or loop - don't end mouse selection when control is released - add a tools menu to piano roll diff --git a/include/automatable_model.h b/include/automatable_model.h index 2322fbeaf..4509ebbc2 100644 --- a/include/automatable_model.h +++ b/include/automatable_model.h @@ -212,13 +212,16 @@ public: return "0"; } + inline bool armed( void ){ return m_armed; } + inline void setArmed( bool _armed ){ m_armed = _armed; } + public slots: virtual void reset( void ); virtual void copyValue( void ); virtual void pasteValue( void ); void unlinkControllerConnection( void ); - + void handleDataChanged( void ); protected: virtual void redoStep( journalEntry & _je ); @@ -252,6 +255,7 @@ private: controllerConnection * m_controllerConnection; + bool m_armed; // record this model during automation recording? static float __copiedValue; diff --git a/include/automatable_model_view.h b/include/automatable_model_view.h index 04311ea3e..60af262d2 100644 --- a/include/automatable_model_view.h +++ b/include/automatable_model_view.h @@ -104,7 +104,8 @@ public slots: void execConnectionDialog( void ); void removeConnection( void ); void editSongGlobalAutomation( void ); - + void arm( void ); + void deArm( void ); protected: automatableModelView * amv; diff --git a/include/automation_recorder.h b/include/automation_recorder.h index 9434ee3c6..11b5390cd 100644 --- a/include/automation_recorder.h +++ b/include/automation_recorder.h @@ -48,14 +48,14 @@ public: automationTrack* auto_track; // the tco that we're putting this automation in automationPattern* pat; - } ControllerMetaData; - typedef QMap ControllerMap; + } ClipData; + typedef QMap AutoClipMap; AutomationRecorder(); ~AutomationRecorder(); - // midi controllers call this - void controllerEvent( const controller * _controller, float _val ); + // automatable models call this when their data changes + void modelDataEvent( automatableModel * _model ); // must be called at some point between a recording ending and a new // one beginning @@ -67,7 +67,7 @@ public: private: bool m_recording; // while the song is playing, should we record automation? - ControllerMap m_controllers; // remember state during recording + AutoClipMap m_clips; // remember state during recording friend class engine; } ; diff --git a/src/core/automatable_model.cpp b/src/core/automatable_model.cpp index 4e0c1d6d8..aa3dfb318 100644 --- a/src/core/automatable_model.cpp +++ b/src/core/automatable_model.cpp @@ -27,6 +27,7 @@ #include "automatable_model.h" +#include "automation_recorder.h" #include "automation_pattern.h" #include "controller_connection.h" @@ -55,8 +56,13 @@ automatableModel::automatableModel( DataType _type, m_journalEntryReady( false ), m_setValueDepth( 0 ), m_hasLinkedModels( false ), - m_controllerConnection( NULL ) + m_controllerConnection( NULL ), + m_armed( false ) { + // we need to handle our own dataChanged signal so we can + // alert AutomationRecorder and pass a pointer to this + QObject::connect( this, SIGNAL( dataChanged() ), + this, SLOT( handleDataChanged() ) ); } @@ -464,6 +470,13 @@ void automatableModel::unlinkControllerConnection( void ) +void automatableModel::handleDataChanged( void ) +{ + // report the data changed to AutomationRecorder + engine::getAutomationRecorder()->modelDataEvent( this ); +} + + void automatableModel::setInitValue( const float _value ) { diff --git a/src/core/automation_recorder.cpp b/src/core/automation_recorder.cpp index 0e12ea9c1..fc1d7230a 100644 --- a/src/core/automation_recorder.cpp +++ b/src/core/automation_recorder.cpp @@ -1,7 +1,7 @@ /* * automation_recorder.cpp - declaration of class AutomationRecorder - * which handles the valueChanged signal of every - * controller and records automation if automation + * which handles the dataChanged event of every + * automatableModel and records it if automation * recording is on. * * Copyright (c) 2009-2009 Andrew Kelley @@ -32,7 +32,7 @@ AutomationRecorder::AutomationRecorder() : m_recording( false ), - m_controllers( ControllerMap() ) + m_clips( AutoClipMap() ) { } @@ -40,17 +40,17 @@ AutomationRecorder::~AutomationRecorder() { } -void AutomationRecorder::controllerEvent( - const controller * _controller, float _val ) +void AutomationRecorder::modelDataEvent( automatableModel * _model ) { - if( engine::getSong()->isRecording() && + if( _model->armed() && + engine::getSong()->isRecording() && engine::getSong()->isPlaying() && m_recording ) { - // record this controller position at the current tick // determine the current tick song * s = engine::getSong(); midiTime & song_pos = s->getPlayPos( song::Mode_PlaySong ); + /* // if the tick is within an existing automation TCO // for the automatable model that this controller controls @@ -79,23 +79,25 @@ void AutomationRecorder::controllerEvent( } */ - // check if we've seen this controller change yet - if( m_controllers.contains( _controller ) && - m_controllers[_controller].seen ) + // check if we've seen this model change yet + float val = _model->value(); + if( m_clips.contains( _model ) && + m_clips[_model].seen ) { - ControllerMetaData data = m_controllers[_controller]; + ClipData data = m_clips[_model]; // we've seen this controller, add automation to the TCO we added // first make the TCO bigger data.pat->changeLength( song_pos - data.pat->startPosition() ); // now draw a line from the last one to this one // TODO: make it smooth (draw line instead of insert value) - data.pat->putValue( song_pos - data.pat->startPosition(), _val, false ); + data.pat->putValue( + song_pos - data.pat->startPosition(), val, false ); } else { // new entry in controller map - ControllerMetaData data; + ClipData data; // create a new automation track in the song engine::getMixer()->lock(); @@ -109,22 +111,16 @@ void AutomationRecorder::controllerEvent( data.auto_track->createTCO( song_pos ) ); data.pat->movePosition( song_pos ); - // add each automatableModel that the controller controls - // to the automation pattern - QObjectList kids = _controller->children(); - for( int i = 0; i < kids.size(); ++i ) - { - data.pat->addObject( - qobject_cast( kids.at(i) ) ); - } + // connect the model to the automation pattern + data.pat->addObject( _model ); // add first value TODO: make sure this is absolute data.pat->putValue( - song_pos - data.pat->startPosition(), _val, false ); + song_pos - data.pat->startPosition(), val, false ); // insert into map data.seen = true; - m_controllers.insert(_controller, data); + m_clips.insert(_model, data); } } } @@ -132,7 +128,7 @@ void AutomationRecorder::controllerEvent( void AutomationRecorder::initRecord( void ) { // starting a new recording, clear map - m_controllers.clear(); + m_clips.clear(); } #include "moc_automation_recorder.cxx" diff --git a/src/core/midi/midi_controller.cpp b/src/core/midi/midi_controller.cpp index ef6044c79..0543b138d 100644 --- a/src/core/midi/midi_controller.cpp +++ b/src/core/midi/midi_controller.cpp @@ -96,10 +96,6 @@ void midiController::processInEvent( const midiEvent & _me, Uint8 val = _me.m_data.m_bytes[2] & 0x7F; m_lastValue = (float)( val ) / 127.0f; emit valueChanged(); - - // send this event to the automation recorder - engine::getAutomationRecorder()->controllerEvent( - this, m_lastValue ); } break; diff --git a/src/gui/automatable_model_view.cpp b/src/gui/automatable_model_view.cpp index 3c53ac723..26ddb00a6 100644 --- a/src/gui/automatable_model_view.cpp +++ b/src/gui/automatable_model_view.cpp @@ -25,6 +25,7 @@ #include #include + #include "automatable_model_view.h" #include "automation_pattern.h" #include "controller_connection_dialog.h" @@ -127,6 +128,21 @@ void automatableModelView::addDefaultActions( QMenu * _menu ) amvSlots, SLOT( execConnectionDialog() ) ); } + + if( _model->armed() ) + { + _menu->addAction( //embed::getIconPixmap( "controller" ), + automatableModel::tr( "de-arm" ), + amvSlots, + SLOT( deArm() ) ); + } + else + { + _menu->addAction( //embed::getIconPixmap( "controller" ), + automatableModel::tr( "Arm for recording" ), + amvSlots, + SLOT( arm() ) ); + } } @@ -241,6 +257,16 @@ void automatableModelViewSlots::editSongGlobalAutomation( void ) } +void automatableModelViewSlots::arm( void ) +{ + amv->modelUntyped()->setArmed( true ); +} + + +void automatableModelViewSlots::deArm( void ) +{ + amv->modelUntyped()->setArmed( false ); +} #include "moc_automatable_model_view.cxx"