implemented automation recording
This commit is contained in:
3
TODO
3
TODO
@@ -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
|
||||
|
||||
78
include/automation_recorder.h
Normal file
78
include/automation_recorder.h
Normal 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -136,6 +136,7 @@ public slots:
|
||||
void undo( void );
|
||||
void redo( void );
|
||||
|
||||
void toggleRecordAutomation( bool );
|
||||
|
||||
protected:
|
||||
virtual void closeEvent( QCloseEvent * _ce );
|
||||
|
||||
@@ -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 );
|
||||
|
||||
139
src/core/automation_recorder.cpp
Normal file
139
src/core/automation_recorder.cpp
Normal 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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user