Experimental automation recording support
Allows you to record automation by arming an automatable model and then recording with the RecordAndPlay button. Known bugs: * 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
This commit is contained in:
@@ -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 )
|
||||
{
|
||||
|
||||
@@ -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 <superjoe30/at/gmail.com>
|
||||
@@ -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<float>();
|
||||
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<automatableModel*>( 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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <QtGui/QMenu>
|
||||
#include <QtGui/QMouseEvent>
|
||||
|
||||
#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"
|
||||
|
||||
Reference in New Issue
Block a user