This commit is contained in:
Raine M. Ekman
2014-02-02 20:41:59 +02:00
61 changed files with 411 additions and 140 deletions

View File

@@ -84,8 +84,6 @@ AutomationPattern::~AutomationPattern()
void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup )
{
bool addIt = true;
if( _search_dup )
{
for( objectVector::iterator it = m_objects.begin();
@@ -95,27 +93,26 @@ void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup )
{
// Already exists
// TODO: Maybe let the user know in some non-annoying way
addIt = false;
break;
return;
}
}
}
if( addIt )
// the automation track is unconnected and there is nothing in the track
if( m_objects.isEmpty() && hasAutomation() == false )
{
// been empty before and model's current value is not its init value?
if( m_objects.isEmpty() && hasAutomation() == false && _obj->isAtInitValue() == false )
{
// then initialize first value
putValue( 0, _obj->value<float>(), false );
}
m_objects += _obj;
connect( _obj, SIGNAL( destroyed( jo_id_t ) ),
this, SLOT( objectDestroyed( jo_id_t ) ),
Qt::DirectConnection );
// then initialize first value
putValue( MidiTime(0), _obj->value<float>(), false );
}
m_objects += _obj;
connect( _obj, SIGNAL( destroyed( jo_id_t ) ),
this, SLOT( objectDestroyed( jo_id_t ) ),
Qt::DirectConnection );
emit dataChanged();
}
@@ -158,7 +155,7 @@ const AutomatableModel * AutomationPattern::firstObject() const
return m;
}
static FloatModel _fm( 0, 0, 1, 0.001 );
static FloatModel _fm( 0, DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, 0.001 );
return &_fm;
}
@@ -584,18 +581,18 @@ void AutomationPattern::resolveAllIDs()
AutomationPattern * a = dynamic_cast<AutomationPattern *>( *j );
if( a )
{
for( QVector<jo_id_t>::Iterator k = a->m_idsToResolve.begin();
k != a->m_idsToResolve.end(); ++k )
{
JournallingObject * o = engine::projectJournal()->
journallingObject( *k );
if( o && dynamic_cast<AutomatableModel *>( o ) )
{
a->addObject( dynamic_cast<AutomatableModel *>( o ), false );
}
}
a->m_idsToResolve.clear();
a->dataChanged();
for( QVector<jo_id_t>::Iterator k = a->m_idsToResolve.begin();
k != a->m_idsToResolve.end(); ++k )
{
JournallingObject * o = engine::projectJournal()->
journallingObject( *k );
if( o && dynamic_cast<AutomatableModel *>( o ) )
{
a->addObject( dynamic_cast<AutomatableModel *>( o ), false );
}
}
a->m_idsToResolve.clear();
a->dataChanged();
}
}
}
@@ -639,6 +636,20 @@ void AutomationPattern::objectDestroyed( jo_id_t _id )
// case we had to remove ourselves if we're the global automation
// pattern of the destroyed object
m_idsToResolve += _id;
for( objectVector::Iterator objIt = m_objects.begin();
objIt != m_objects.end(); objIt++ )
{
Q_ASSERT( !(*objIt).isNull() );
if( (*objIt)->id() == _id )
{
//Assign to objIt so that this loop work even break; is removed.
objIt = m_objects.erase( objIt );
break;
}
}
emit dataChanged();
}
@@ -703,4 +714,6 @@ void AutomationPattern::generateTangents( timeMap::const_iterator it,
}
#include "moc_AutomationPattern.cxx"

View File

@@ -177,12 +177,13 @@ Controller * Controller::create( ControllerTypes _ct, Model * _parent )
QString() );
break;
case Controller::LfoController:
c = new ::LfoController( _parent );
case Controller::LfoController:
c = new ::LfoController( _parent );
break;
case Controller::PeakController:
c = new ::PeakController( _parent );
//Already instantiated in EffectChain::loadSettings()
Q_ASSERT( false );
break;
case Controller::MidiController:
@@ -200,9 +201,18 @@ Controller * Controller::create( ControllerTypes _ct, Model * _parent )
Controller * Controller::create( const QDomElement & _this, Model * _parent )
{
Controller * c = create(
static_cast<ControllerTypes>( _this.attribute( "type" ).toInt() ),
_parent );
Controller * c;
if( _this.attribute( "type" ).toInt() == Controller::PeakController )
{
c = PeakController::getControllerBySetting( _this );
}
else
{
c = create(
static_cast<ControllerTypes>( _this.attribute( "type" ).toInt() ),
_parent );
}
if( c != NULL )
{
c->restoreState( _this );

View File

@@ -198,6 +198,7 @@ void LfoController::saveSettings( QDomDocument & _doc, QDomElement & _this )
m_phaseModel.saveSettings( _doc, _this, "phase" );
m_waveModel.saveSettings( _doc, _this, "wave" );
m_multiplierModel.saveSettings( _doc, _this, "multiplier" );
_this.setAttribute( "userwavefile" , m_userDefSampleBuffer->audioFile() );
}
@@ -212,6 +213,7 @@ void LfoController::loadSettings( const QDomElement & _this )
m_phaseModel.loadSettings( _this, "phase" );
m_waveModel.loadSettings( _this, "wave" );
m_multiplierModel.loadSettings( _this, "multiplier" );
m_userDefSampleBuffer->setAudioFile( _this.attribute("userwavefile" ) );
updateSampleFunction();
}

View File

@@ -28,6 +28,7 @@
#include <QtXml/QDomElement>
#include <QtCore/QObject>
#include <QtCore/QVector>
#include <QMessageBox>
#include "song.h"
@@ -37,8 +38,12 @@
#include "EffectChain.h"
#include "ControllerDialog.h"
#include "plugins/peak_controller_effect/peak_controller_effect.h"
#include "PresetPreviewPlayHandle.h"
PeakControllerEffectVector PeakController::s_effects;
int PeakController::m_getCount;
int PeakController::m_loadCount;
bool PeakController::m_buggedFile;
PeakController::PeakController( Model * _parent,
@@ -58,7 +63,11 @@ PeakController::PeakController( Model * _parent,
PeakController::~PeakController()
{
if( m_peakEffect != NULL && m_peakEffect->effectChain() != NULL )
//EffectChain::loadSettings() appends effect to EffectChain::m_effects
//When it's previewing, EffectChain::loadSettings(<Controller Fx XML>) is not called
//Therefore, we shouldn't call removeEffect() as it is not even appended.
//NB: Most XML setting are loaded on preview, except controller fx.
if( m_peakEffect != NULL && m_peakEffect->effectChain() != NULL && PresetPreviewPlayHandle::isPreviewing() == false )
{
m_peakEffect->effectChain()->removeEffect( m_peakEffect );
}
@@ -103,7 +112,13 @@ void PeakController::saveSettings( QDomDocument & _doc, QDomElement & _this )
void PeakController::loadSettings( const QDomElement & _this )
{
Controller::loadSettings( _this );
int effectId = _this.attribute( "effectId" ).toInt();
if( m_buggedFile == true )
{
effectId = m_loadCount++;
}
PeakControllerEffectVector::Iterator i;
for( i = s_effects.begin(); i != s_effects.end(); ++i )
@@ -118,6 +133,76 @@ void PeakController::loadSettings( const QDomElement & _this )
//Backward compatibility function for bug in <= 0.4.15
void PeakController::initGetControllerBySetting()
{
m_loadCount = 0;
m_getCount = 0;
m_buggedFile = false;
}
PeakController * PeakController::getControllerBySetting(const QDomElement & _this )
{
int effectId = _this.attribute( "effectId" ).toInt();
PeakControllerEffectVector::Iterator i;
//Backward compatibility for bug in <= 0.4.15 . For >= 1.0.0 ,
//foundCount should always be 1 because m_effectId is initialized with rand()
int foundCount = 0;
if( m_buggedFile == false )
{
for( i = s_effects.begin(); i != s_effects.end(); ++i )
{
if( (*i)->m_effectId == effectId )
{
foundCount++;
}
}
if( foundCount >= 2 )
{
m_buggedFile = true;
int newEffectId = 0;
for( i = s_effects.begin(); i != s_effects.end(); ++i )
{
(*i)->m_effectId = newEffectId++;
}
QMessageBox msgBox;
msgBox.setIcon( QMessageBox::Information );
msgBox.setWindowTitle( tr("Peak Controller Bug") );
msgBox.setText( tr("Due to a bug in older version of LMMS, the peak "
"controllers may not be connect properly. "
"Please ensure that peak controllers are connected "
"properly and re-save this file. "
"Sorry for any inconvenience caused.") );
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
}
}
if( m_buggedFile == true )
{
effectId = m_getCount;
}
m_getCount++; //NB: m_getCount should be increased even m_buggedFile is false
for( i = s_effects.begin(); i != s_effects.end(); ++i )
{
if( (*i)->m_effectId == effectId )
{
return (*i)->controller();
}
}
return NULL;
}
QString PeakController::nodeName() const
{
return( "Peakcontroller" );

View File

@@ -88,8 +88,8 @@ public:
bool isPreviewing()
{
bool ret = m_dataMutex.tryLock();
if( ret == true )
bool ret = !m_dataMutex.tryLock();
if( ret == false )
{
m_dataMutex.unlock();
}

View File

@@ -908,6 +908,27 @@ QString SampleBuffer::openAndSetAudioFile()
}
QString SampleBuffer::openAndSetWaveformFile()
{
if( m_audioFile.isEmpty() )
{
m_audioFile = configManager::inst()->factorySamplesDir() + "waveforms/10saw.flac";
}
QString fileName = this->openAudioFile();
if(!fileName.isEmpty())
{
this->setAudioFile( fileName );
}
else
{
m_audioFile = "";
}
return fileName;
}
#undef LMMS_HAVE_FLAC_STREAM_ENCODER_H /* not yet... */

View File

@@ -61,6 +61,7 @@
#include "templates.h"
#include "text_float.h"
#include "timeline.h"
#include "PeakController.h"
#ifdef LMMS_BUILD_WIN32
#ifndef USE_QT_SHMEM
@@ -1141,6 +1142,10 @@ void song::loadProject( const QString & _file_name )
m_globalAutomationTrack->restoreState( mmp.content().
firstChildElement( "track" ) );
}
//Backward compatibility for LMMS <= 0.4.15
PeakController::initGetControllerBySetting();
QDomNode node = mmp.content().firstChild();
while( !node.isNull() )
{
@@ -1354,7 +1359,16 @@ void song::restoreControllerStates( const QDomElement & _this )
QDomNode node = _this.firstChild();
while( !node.isNull() )
{
addController( Controller::create( node.toElement(), this ) );
Controller * c = Controller::create( node.toElement(), this );
Q_ASSERT( c != NULL );
/* For PeakController, addController() was called in
* PeakControllerEffect::PeakControllerEffect().
* This line removes the previously added controller for PeakController
* without affecting the order of controllers in Controller Rack
*/
engine::getSong()->removeController( c );
addController( c );
node = node.nextSibling();
}

View File

@@ -283,14 +283,14 @@ void timeLine::mousePressEvent( QMouseEvent* event )
m_moveXOff = s_posMarkerPixmap->width() / 2;
}
}
else if( event->button() == Qt::RightButton )
else if( event->button() == Qt::RightButton || event->button() == Qt::MiddleButton )
{
const MidiTime t = m_begin + static_cast<int>( event->x() * MidiTime::ticksPerTact() / m_ppt );
if( m_loopPos[0] > m_loopPos[1] )
{
qSwap( m_loopPos[0], m_loopPos[1] );
}
if( event->modifiers() & Qt::ShiftModifier )
if( ( event->modifiers() & Qt::ShiftModifier ) || event->button() == Qt::MiddleButton )
{
m_action = MoveLoopBegin;
}

View File

@@ -72,6 +72,10 @@ QPixmap * AutomationEditor::s_toolSelect = NULL;
QPixmap * AutomationEditor::s_toolMove = NULL;
const QColor DRAGGABLE_PIN_COLOR = QColor( 0xFF, 0x00, 0x00 );
const QColor DRAGGABLE_PIN_BORDER_COLOR = QColor( 0xFF, 0xFF, 0xFF );
AutomationEditor::AutomationEditor() :
QWidget(),
m_zoomingXModel(),
@@ -1348,6 +1352,22 @@ inline void AutomationEditor::drawCross( QPainter & _p )
inline void AutomationEditor::drawAutomationPoint( QPainter & p, timeMap::iterator it )
{
int x = xCoordOfTick( it.key() );
int y = yCoordOfLevel( it.value() );
int outerRadius = qMin( 8, m_ppt/quantization() );
int innerRadius = qMax( 0, outerRadius-2 );
p.setBrush( QBrush( DRAGGABLE_PIN_BORDER_COLOR ) );
p.drawEllipse( x-outerRadius/2, y-outerRadius/2, outerRadius, outerRadius );
p.setBrush( QBrush( DRAGGABLE_PIN_COLOR ) );
p.drawEllipse( x-innerRadius/2, y-innerRadius/2, innerRadius, innerRadius );
p.setBrush( QBrush() );
}
void AutomationEditor::paintEvent( QPaintEvent * _pe )
{
QMutexLocker m( &m_patternMutex );
@@ -1526,71 +1546,75 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
{
int len_ticks = 4;
timeMap & time_map = m_pattern->getTimeMap();
timeMap::iterator it = time_map.begin();
p.setPen( QColor( 0xCF, 0xD9, 0xFF ) );
while( it+1 != time_map.end() )
//Don't bother doing/rendering anything if there is no automation points
if( time_map.size() > 0 )
{
// skip this section if it occurs completely before the
// visible area
int next_x = xCoordOfTick( (it+1).key() );
if( next_x < 0 )
timeMap::iterator it = time_map.begin();
p.setPen( QColor( 0xCF, 0xD9, 0xFF ) );
while( it+1 != time_map.end() )
{
++it;
continue;
}
// skip this section if it occurs completely before the
// visible area
int next_x = xCoordOfTick( (it+1).key() );
if( next_x < 0 )
{
++it;
continue;
}
int x = xCoordOfTick( it.key() );
if( x > width() )
{
break;
}
int x = xCoordOfTick( it.key() );
if( x > width() )
{
break;
}
bool is_selected = FALSE;
// if we're in move-mode, we may only draw
// values in selected area, that have originally
// been selected and not values that are now in
// selection because the user moved it...
if( m_editMode == MOVE )
{
if( m_selValuesForMove.contains( it.key() ) )
bool is_selected = FALSE;
// if we're in move-mode, we may only draw
// values in selected area, that have originally
// been selected and not values that are now in
// selection because the user moved it...
if( m_editMode == MOVE )
{
if( m_selValuesForMove.contains( it.key() ) )
{
is_selected = TRUE;
}
}
else if( it.value() >= selLevel_start &&
it.value() <= selLevel_end &&
it.key() >= sel_pos_start &&
it.key() + len_ticks <= sel_pos_end )
{
is_selected = TRUE;
}
float *values = m_pattern->valuesAfter( it.key() );
for( int i = 0; i < (it+1).key() - it.key(); i++ )
{
drawLevelTick( p, it.key() + i, values[i],
is_selected );
}
delete [] values;
// Draw circle
drawAutomationPoint(p, it);
++it;
}
else if( it.value() >= selLevel_start &&
it.value() <= selLevel_end &&
it.key() >= sel_pos_start &&
it.key() + len_ticks <= sel_pos_end )
Q_ASSERT( it == time_map.end()-1 );
for( int i = it.key(), x = xCoordOfTick( i ); x <= width();
i++, x = xCoordOfTick( i ) )
{
is_selected = TRUE;
// TODO: Find out if the section after the last control
// point is able to be selected and if so set this
// boolean correctly
drawLevelTick( p, i, it.value(), false );
}
float *values = m_pattern->valuesAfter( it.key() );
for( int i = 0; i < (it+1).key() - it.key(); i++ )
{
drawLevelTick( p, it.key() + i, values[i],
is_selected );
}
delete [] values;
// Draw cross
int y = yCoordOfLevel( it.value() );
p.drawLine( x - 1, y, x + 1, y );
p.drawLine( x, y - 1, x, y + 1 );
// _p.setPen( QColor( 0xFF, 0x9F, 0x00 ) );
// _p.setPen( QColor( 0xFF, 0xFF, 0x40 ) );
++it;
}
for( int i = it.key(), x = xCoordOfTick( i ); x <= width();
i++, x = xCoordOfTick( i ) )
{
// TODO: Find out if the section after the last control
// point is able to be selected and if so set this
// boolean correctly
drawLevelTick( p, i, it.value(), false );
// Draw circle(the last one)
drawAutomationPoint(p, it);
}
}
else

View File

@@ -107,10 +107,26 @@ void AutomationPatternView::disconnectObject( QAction * _a )
journallingObject( _a->data().toInt() );
if( j && dynamic_cast<AutomatableModel *>( j ) )
{
float oldMin = m_pat->getMin();
float oldMax = m_pat->getMax();
m_pat->m_objects.erase( qFind( m_pat->m_objects.begin(),
m_pat->m_objects.end(),
dynamic_cast<AutomatableModel *>( j ) ) );
update();
//If automation editor is opened, update its display after disconnection
if( engine::automationEditor() )
{
engine::automationEditor()->updateAfterPatternChange();
}
//if there is no more connection connected to the AutomationPattern
if( m_pat->m_objects.size() == 0 )
{
//scale the points to fit the new min. and max. value
this->scaleTimemapToFit( oldMin, oldMax );
}
}
}
@@ -333,6 +349,14 @@ void AutomationPatternView::dropEvent( QDropEvent * _de )
{
engine::automationEditor()->setCurrentPattern( m_pat );
}
//This is the only model that's just added to AutomationPattern.
if( m_pat->m_objects.size() == 1 )
{
//scale the points to fit the new min. and max. value
this->scaleTimemapToFit( AutomationPattern::DEFAULT_MIN_VALUE,
AutomationPattern::DEFAULT_MAX_VALUE );
}
}
else
{
@@ -343,5 +367,37 @@ void AutomationPatternView::dropEvent( QDropEvent * _de )
/**
* @brief Preserves the auto points over different scale
*/
void AutomationPatternView::scaleTimemapToFit( float oldMin, float oldMax )
{
float newMin = m_pat->getMin();
float newMax = m_pat->getMax();
if( oldMin == newMin && oldMax == newMax )
{
return;
}
for( AutomationPattern::timeMap::iterator it = m_pat->m_timeMap.begin();
it != m_pat->m_timeMap.end(); ++it )
{
if( *it < oldMin )
{
*it = oldMin;
}
else if( *it > oldMax )
{
*it = oldMax;
}
*it = (*it-oldMin)*(newMax-newMin)/(oldMax-oldMin)+newMin;
}
m_pat->generateTangents();
}
#include "moc_AutomationPatternView.cxx"

View File

@@ -252,7 +252,7 @@ void LfoControllerDialog::askUserDefWave()
{
SampleBuffer * sampleBuffer = dynamic_cast<LfoController*>(this->model())->
m_userDefSampleBuffer;
QString fileName = sampleBuffer->openAndSetAudioFile();
QString fileName = sampleBuffer->openAndSetWaveformFile();
if( fileName.isEmpty() == false )
{
// TODO:

View File

@@ -68,12 +68,12 @@ bbEditor::bbEditor( bbTrackContainer* tc ) :
"compacttrackbuttons" ).toInt() )
{
setMinimumWidth( TRACK_OP_WIDTH_COMPACT + DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT
+ 2 * TCO_BORDER_WIDTH + 192 );
+ 2 * TCO_BORDER_WIDTH + 264 );
}
else
{
setMinimumWidth( TRACK_OP_WIDTH + DEFAULT_SETTINGS_WIDGET_WIDTH
+ 2 * TCO_BORDER_WIDTH + 192 );
+ 2 * TCO_BORDER_WIDTH + 264 );
}

View File

@@ -485,7 +485,7 @@ void graphModel::setWaveToNoise()
QString graphModel::setWaveToUser()
{
SampleBuffer * sampleBuffer = new SampleBuffer;
QString fileName = sampleBuffer->openAndSetAudioFile();
QString fileName = sampleBuffer->openAndSetWaveformFile();
if( fileName.isEmpty() == false )
{
for( int i = 0; i < length(); i++ )

View File

@@ -1332,6 +1332,7 @@ void InstrumentTrackWindow::modelChanged()
{
m_pitchKnob->hide();
m_pitchKnob->setModel( NULL );
m_pitchRangeSpinBox->hide();
}
m_ssView->setModel( &m_track->m_soundShaping );