implemented automation recording

This commit is contained in:
Andrew Kelley
2009-04-26 19:39:15 -07:00
parent 12d26ed0aa
commit f40b61045a
11 changed files with 250 additions and 5 deletions

3
TODO
View File

@@ -52,6 +52,7 @@
- add FLAC as export-format?
Andrew Kelley's todo:
- make knobs easier to tune (less sensitive)
- add a Modulator class and a Humanizer tool to the piano roll
- when a song goes past the end of the song, make it stop or loop
- when looking at a piano roll, if the song is playing that pattern, move the position ticker to where it should be
@@ -75,8 +76,6 @@ Andrew Kelley's todo:
- don't end mouse selection when control is released
- mouse cursor isn't updated correctly in selection mode (from resizing note edit area)
- recording automation
- make knobs easier to tune (less sensitive)
- make the menu for a channel happen when you right click, instead of renaming, and make the midi input a top-level menu item
- add a "Set exact value" to a right clicked automation menu
- enable "auto detect" by default when you bring up the "connect to controller" window

View File

@@ -0,0 +1,78 @@
/*
* automation_recorder.h - declaration of class AutomationRecorder
* which accepts a controllerEvent call from midi
* controllers and creates automation TCOs if automation
* recording is on.
*
* Copyright (c) 2008-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2009-2009 Andrew Kelley <superjoe30/at/gmail.com>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef _AUTOMATION_RECORDER_H
#define _AUTOMATION_RECORDER_H
#include <QObject>
#include "controller.h"
#include "automation_track.h"
#include "automation_pattern.h"
class AutomationRecorder : public QObject
{
Q_OBJECT
public:
typedef struct
{
// during this recording, have we seen this controller change?
bool seen;
// the track that contains the tco
automationTrack* auto_track;
// the tco that we're putting this automation in
automationPattern* pat;
} ControllerMetaData;
typedef QMap<const controller *, ControllerMetaData> ControllerMap;
AutomationRecorder();
~AutomationRecorder();
// midi controllers call this
void controllerEvent( const controller * _controller, float _val );
// must be called at some point between a recording ending and a new
// one beginning
void initRecord( void );
inline bool recording( void ) const { return m_recording; }
inline void setRecording( bool _recording ){ m_recording = _recording; }
private:
bool m_recording; // while the song is playing, should we record automation?
ControllerMap m_controllers; // remember state during recording
friend class engine;
} ;
#endif

View File

@@ -34,6 +34,7 @@
#include "lmms_style.h"
class automationEditor;
class AutomationRecorder;
class bbEditor;
class bbTrackContainer;
class dummyTrackContainer;
@@ -139,6 +140,11 @@ public:
return s_automationEditor;
}
static AutomationRecorder * getAutomationRecorder( void )
{
return s_automationRecorder;
}
static ladspa2LMMS * getLADSPAManager( void )
{
return s_ladspaManager;
@@ -195,6 +201,7 @@ private:
static fxMixerView * s_fxMixerView;
static songEditor * s_songEditor;
static automationEditor * s_automationEditor;
static AutomationRecorder * s_automationRecorder;
static bbEditor * s_bbEditor;
static pianoRoll * s_pianoRoll;
static projectNotes * s_projectNotes;

View File

@@ -136,6 +136,7 @@ public slots:
void undo( void );
void redo( void );
void toggleRecordAutomation( bool );
protected:
virtual void closeEvent( QCloseEvent * _ce );

View File

@@ -49,7 +49,7 @@ public:
virtual void processOutEvent( const midiEvent& _me,
const midiTime & _time)
{
// No output yet
// No output yet (TODO?)
}
virtual void saveSettings( QDomDocument & _doc, QDomElement & _this );

View File

@@ -0,0 +1,139 @@
/*
* automation_recorder.cpp - declaration of class AutomationRecorder
* which handles the valueChanged signal of every
* controller and records automation if automation
* recording is on.
*
* Copyright (c) 2008-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2009-2009 Andrew Kelley <superjoe30/at/gmail.com>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include "automation_recorder.h"
#include "controller.h"
#include "song.h"
AutomationRecorder::AutomationRecorder() :
m_recording( false ),
m_controllers( ControllerMap() )
{
}
AutomationRecorder::~AutomationRecorder()
{
}
void AutomationRecorder::controllerEvent(
const controller * _controller, float _val )
{
if( 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
track * inside_track = NULL;
for( int i=0; i < s->trackList.size(); ++i){
track * t = s->trackList.at(i);
if( t->type == track::AutomationTrack &&
song_pos >= t->startPosition() &&
song_pos <= t->endPosition() )
{
inside_track = t;
break;
}
}
if( inside_track != NULL )
{
// edit the value of the automation TCO at this tick
}
else
{
// create a new automation TCO for this automatable model
// and set the value
}
*/
// check if we've seen this controller change yet
if( m_controllers.contains( _controller ) &&
m_controllers[_controller].seen )
{
ControllerMetaData data = m_controllers[_controller];
// 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, _val );
}
else
{
// new entry in controller map
ControllerMetaData data;
// create a new automation track in the song
engine::getMixer()->lock();
data.auto_track = (automationTrack *)
track::create( track::AutomationTrack, engine::getSong() );
engine::getMixer()->unlock();
// put a tco in the automation track which contains the automation
// turn the tco into an automation pattern
data.pat = dynamic_cast<automationPattern *>(
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) ) );
}
// add first value TODO: make sure this is absolute
data.pat->putValue( song_pos, _val );
// insert into map
data.seen = true;
m_controllers.insert(_controller, data);
}
}
}
void AutomationRecorder::initRecord( void )
{
// starting a new recording, clear map
m_controllers.clear();
}
#include "moc_automation_recorder.cxx"

View File

@@ -28,6 +28,7 @@
#include "engine.h"
#include "automation_editor.h"
#include "automation_recorder.h"
#include "bb_editor.h"
#include "bb_track_container.h"
#include "config_mgr.h"
@@ -63,6 +64,7 @@ song * engine::s_song = NULL;
UnifiedResourcesProvider * engine::s_resourcesProvider = NULL;
songEditor * engine::s_songEditor = NULL;
automationEditor * engine::s_automationEditor = NULL;
AutomationRecorder * engine::s_automationRecorder = NULL;
bbEditor * engine::s_bbEditor = NULL;
pianoRoll * engine::s_pianoRoll = NULL;
projectNotes * engine::s_projectNotes = NULL;
@@ -125,6 +127,7 @@ void engine::init( const bool _has_gui )
s_bbEditor = new bbEditor( s_bbTrackContainer );
s_pianoRoll = new pianoRoll;
s_automationEditor = new automationEditor;
s_automationRecorder = new AutomationRecorder;
s_mainWindow->finalize();
}
@@ -152,6 +155,8 @@ void engine::destroy( void )
s_pianoRoll = NULL;
delete s_automationEditor;
s_automationEditor = NULL;
delete s_automationRecorder;
s_automationRecorder = NULL;
delete s_fxMixerView;
s_fxMixerView = NULL;

View File

@@ -35,7 +35,7 @@
#include "mixer.h"
#include "midi_client.h"
#include "midi_controller.h"
#include "automation_recorder.h"
midiController::midiController( model * _parent ) :
@@ -96,6 +96,10 @@ 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;

View File

@@ -33,6 +33,7 @@
#include "song.h"
#include "automation_track.h"
#include "automation_editor.h"
#include "automation_recorder.h"
#include "bb_editor.h"
#include "bb_track.h"
#include "bb_track_container.h"
@@ -594,6 +595,8 @@ void song::setPlayPos( tick_t _ticks, PlayModes _play_mode )
void song::stop( void )
{
m_actions.push_back( ActionStop );
engine::getAutomationRecorder()->initRecord();
}

View File

@@ -304,7 +304,6 @@ QPalette CusisStyle::standardPalette( void ) const
void CusisStyle::hoverColors( bool sunken, bool hover, bool active,
QColor & color, QColor & blend ) const
{
printf("SB: %d %d %d\n", sunken, hover, active );
if( active )
{
if( sunken )

View File

@@ -48,6 +48,7 @@
#include "main_window.h"
#include "bb_editor.h"
#include "song_editor.h"
#include "automation_recorder.h"
#include "song.h"
#include "piano_roll.h"
#include "embed.h"
@@ -759,6 +760,8 @@ void mainWindow::finalize( void )
m_chkrAutomation = new QCheckBox( tr( "Automation" ), gr_w );
m_chkrMidi = new QCheckBox( tr( "MIDI" ), gr_w );
connect(m_chkrAutomation, SIGNAL(toggled(bool)), SLOT(toggleRecordAutomation(bool)));
grw_layout->addWidget( new QLabel( tr( "RECORD" ), gr_w ) );
grw_layout->addWidget( m_chkrAudio );
grw_layout->addWidget( m_chkrAutomation );
@@ -1554,6 +1557,13 @@ void mainWindow::masterPitchReleased( void )
}
void mainWindow::toggleRecordAutomation( bool _recording )
{
engine::getAutomationRecorder()->setRecording( _recording );
}
#include "moc_main_window.cxx"