Merge branch 'stable-1.1' of https://github.com/midi-pascal/lmms into stable-1.1
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "AutomatableModel.h"
|
||||
#include "AutomationPattern.h"
|
||||
#include "ControllerConnection.h"
|
||||
|
||||
#include "lmms_math.h"
|
||||
|
||||
float AutomatableModel::s_copiedValue = 0;
|
||||
|
||||
@@ -39,6 +39,7 @@ AutomatableModel::AutomatableModel( DataType type,
|
||||
Model* parent, const QString & displayName, bool defaultConstructed ) :
|
||||
Model( parent, displayName, defaultConstructed ),
|
||||
m_dataType( type ),
|
||||
m_scaleType( Linear ),
|
||||
m_value( val ),
|
||||
m_initValue( val ),
|
||||
m_minValue( min ),
|
||||
@@ -46,7 +47,7 @@ AutomatableModel::AutomatableModel( DataType type,
|
||||
m_step( step ),
|
||||
m_range( max - min ),
|
||||
m_centerValue( m_minValue ),
|
||||
m_journalEntryReady( false ),
|
||||
m_valueChanged( false ),
|
||||
m_setValueDepth( 0 ),
|
||||
m_hasLinkedModels( false ),
|
||||
m_controllerConnection( NULL )
|
||||
@@ -86,21 +87,28 @@ bool AutomatableModel::isAutomated() const
|
||||
|
||||
void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, const QString& name )
|
||||
{
|
||||
if( isAutomated() )
|
||||
if( isAutomated() || m_scaleType != Linear )
|
||||
{
|
||||
// automation needs tuple of data (name, id, value)
|
||||
// scale type also needs an extra value
|
||||
// => it must be appended as a node
|
||||
QDomElement me = doc.createElement( name );
|
||||
me.setAttribute( "id", id() );
|
||||
me.setAttribute( "value", m_value );
|
||||
me.setAttribute( "scale_type", m_scaleType == Logarithmic ? "log" : "linear" );
|
||||
element.appendChild( me );
|
||||
}
|
||||
else
|
||||
{
|
||||
// non automation, linear scale (default), can be saved as attribute
|
||||
element.setAttribute( name, m_value );
|
||||
}
|
||||
|
||||
if( m_controllerConnection )
|
||||
{
|
||||
QDomElement controllerElement;
|
||||
|
||||
// get "connection" element (and create it if needed)
|
||||
QDomNode node = element.namedItem( "connection" );
|
||||
if( node.isElement() )
|
||||
{
|
||||
@@ -142,9 +150,12 @@ void AutomatableModel::loadSettings( const QDomElement& element, const QString&
|
||||
}
|
||||
return;
|
||||
}
|
||||
// logscales were not existing at this point of time
|
||||
// so they can be ignored
|
||||
}
|
||||
|
||||
QDomNode connectionNode = element.namedItem( "connection" );
|
||||
// reads controller connection
|
||||
if( connectionNode.isElement() )
|
||||
{
|
||||
QDomNode thisConnection = connectionNode.toElement().namedItem( name );
|
||||
@@ -155,14 +166,31 @@ void AutomatableModel::loadSettings( const QDomElement& element, const QString&
|
||||
//m_controllerConnection->setTargetName( displayName() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// models can be stored as elements (port00) or attributes (port10):
|
||||
// <ladspacontrols port10="4.41">
|
||||
// <port00 value="4.41" id="4249278"/>
|
||||
// </ladspacontrols>
|
||||
// element => there is automation data, or scaletype information
|
||||
node = element.namedItem( name );
|
||||
if( node.isElement() )
|
||||
{
|
||||
changeID( node.toElement().attribute( "id" ).toInt() );
|
||||
setValue( node.toElement().attribute( "value" ).toFloat() );
|
||||
changeID( node.toElement().attribute( "id" ).toInt() );
|
||||
setValue( node.toElement().attribute( "value" ).toFloat() );
|
||||
if( node.toElement().hasAttribute( "scale_type" ) )
|
||||
{
|
||||
if( node.toElement().attribute( "scale_type" ) == "linear" )
|
||||
{
|
||||
setScaleType( Linear );
|
||||
}
|
||||
else if( node.toElement().attribute( "scale_type" ) == "log" )
|
||||
{
|
||||
setScaleType( Logarithmic );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( element.hasAttribute( name ) )
|
||||
// attribute => read the element's value from the attribute list
|
||||
{
|
||||
setInitValue( element.attribute( name ).toFloat() );
|
||||
}
|
||||
@@ -184,7 +212,7 @@ void AutomatableModel::setValue( const float value )
|
||||
if( old_val != m_value )
|
||||
{
|
||||
// add changes to history so user can undo it
|
||||
addJournalEntry( JournalEntry( 0, m_value - old_val ) );
|
||||
addJournalCheckPoint();
|
||||
|
||||
// notify linked models
|
||||
for( AutoModelVector::Iterator it = m_linkedModels.begin(); it != m_linkedModels.end(); ++it )
|
||||
@@ -196,6 +224,7 @@ void AutomatableModel::setValue( const float value )
|
||||
(*it)->setJournalling( journalling );
|
||||
}
|
||||
}
|
||||
m_valueChanged = true;
|
||||
emit dataChanged();
|
||||
}
|
||||
else
|
||||
@@ -208,12 +237,74 @@ void AutomatableModel::setValue( const float value )
|
||||
|
||||
|
||||
|
||||
template<class T> T AutomatableModel::logToLinearScale( T value ) const
|
||||
{
|
||||
return castValue<T>( ::logToLinearScale( minValue<float>(), maxValue<float>(), static_cast<float>( value ) ) );
|
||||
}
|
||||
|
||||
|
||||
float AutomatableModel::scaledValue( float value ) const
|
||||
{
|
||||
return m_scaleType == Linear
|
||||
? value
|
||||
: logToLinearScale<float>( ( value - minValue<float>() ) / m_range );
|
||||
}
|
||||
|
||||
|
||||
float AutomatableModel::inverseScaledValue( float value ) const
|
||||
{
|
||||
return m_scaleType == Linear
|
||||
? value
|
||||
: ::linearToLogScale( minValue<float>(), maxValue<float>(), value );
|
||||
}
|
||||
|
||||
|
||||
|
||||
QString AutomatableModel::displayValue( const float val ) const
|
||||
{
|
||||
switch( m_dataType )
|
||||
{
|
||||
case Float: return QString::number( castValue<float>( scaledValue( val ) ) );
|
||||
case Integer: return QString::number( castValue<int>( scaledValue( val ) ) );
|
||||
case Bool: return QString::number( castValue<bool>( scaledValue( val ) ) );
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
|
||||
|
||||
|
||||
//! @todo: this should be moved into a maths header
|
||||
template<class T>
|
||||
void roundAt( T& value, const T& where, const T& step_size )
|
||||
{
|
||||
if( qAbs<float>( value - where )
|
||||
< typeInfo<float>::minEps() * qAbs<float>( step_size ) )
|
||||
{
|
||||
value = where;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
void AutomatableModel::roundAt( T& value, const T& where ) const
|
||||
{
|
||||
::roundAt(value, where, m_step);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AutomatableModel::setAutomatedValue( const float value )
|
||||
{
|
||||
++m_setValueDepth;
|
||||
const float oldValue = m_value;
|
||||
|
||||
m_value = fittedValue( value );
|
||||
const float scaled_value = scaledValue( value );
|
||||
|
||||
m_value = fittedValue( scaled_value );
|
||||
|
||||
if( oldValue != m_value )
|
||||
{
|
||||
// notify linked models
|
||||
@@ -224,9 +315,10 @@ void AutomatableModel::setAutomatedValue( const float value )
|
||||
!(*it)->fittedValue( m_value ) !=
|
||||
(*it)->m_value )
|
||||
{
|
||||
(*it)->setAutomatedValue( m_value );
|
||||
(*it)->setAutomatedValue( value );
|
||||
}
|
||||
}
|
||||
m_valueChanged = true;
|
||||
emit dataChanged();
|
||||
}
|
||||
--m_setValueDepth;
|
||||
@@ -281,17 +373,9 @@ float AutomatableModel::fittedValue( float value ) const
|
||||
value = nearbyintf( value / m_step ) * m_step;
|
||||
}
|
||||
|
||||
// correct rounding error at the border
|
||||
if( qAbs<float>( value - m_maxValue ) < typeInfo<float>::minEps() * qAbs<float>( m_step ) )
|
||||
{
|
||||
value = m_maxValue;
|
||||
}
|
||||
|
||||
// correct rounding error if value = 0
|
||||
if( qAbs<float>( value ) < typeInfo<float>::minEps() * qAbs<float>( m_step ) )
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
roundAt( value, m_maxValue );
|
||||
roundAt( value, m_minValue );
|
||||
roundAt( value, 0.0f );
|
||||
|
||||
if( value < m_minValue )
|
||||
{
|
||||
@@ -309,54 +393,9 @@ float AutomatableModel::fittedValue( float value ) const
|
||||
|
||||
|
||||
|
||||
void AutomatableModel::redoStep( JournalEntry& je )
|
||||
{
|
||||
bool journalling = testAndSetJournalling( false );
|
||||
setValue( value<float>() + (float) je.data().toDouble() );
|
||||
setJournalling( journalling );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AutomatableModel::undoStep( JournalEntry& je )
|
||||
{
|
||||
JournalEntry inv( je.actionID(), -je.data().toDouble() );
|
||||
redoStep( inv );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AutomatableModel::prepareJournalEntryFromOldVal()
|
||||
{
|
||||
m_oldValue = value<float>();
|
||||
saveJournallingState( false );
|
||||
m_journalEntryReady = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AutomatableModel::addJournalEntryFromOldToCurVal()
|
||||
{
|
||||
if( m_journalEntryReady )
|
||||
{
|
||||
restoreJournallingState();
|
||||
if( value<float>() != m_oldValue )
|
||||
{
|
||||
addJournalEntry( JournalEntry( 0, value<float>() - m_oldValue ) );
|
||||
}
|
||||
m_journalEntryReady = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AutomatableModel::linkModel( AutomatableModel* model )
|
||||
{
|
||||
if( !m_linkedModels.contains( model ) )
|
||||
if( !m_linkedModels.contains( model ) && model != this )
|
||||
{
|
||||
m_linkedModels.push_back( model );
|
||||
m_hasLinkedModels = true;
|
||||
@@ -388,8 +427,8 @@ void AutomatableModel::unlinkModel( AutomatableModel* model )
|
||||
|
||||
void AutomatableModel::linkModels( AutomatableModel* model1, AutomatableModel* model2 )
|
||||
{
|
||||
model1->linkModel( model2 );
|
||||
model2->linkModel( model1 );
|
||||
model1->linkModel( model2 );
|
||||
model2->linkModel( model1 );
|
||||
}
|
||||
|
||||
|
||||
@@ -424,17 +463,33 @@ void AutomatableModel::setControllerConnection( ControllerConnection* c )
|
||||
{
|
||||
QObject::connect( m_controllerConnection, SIGNAL( valueChanged() ), this, SIGNAL( dataChanged() ) );
|
||||
QObject::connect( m_controllerConnection, SIGNAL( destroyed() ), this, SLOT( unlinkControllerConnection() ) );
|
||||
m_valueChanged = true;
|
||||
emit dataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
float AutomatableModel::controllerValue( int frameOffset ) const
|
||||
{
|
||||
if( m_controllerConnection )
|
||||
{
|
||||
const float v = minValue<float>() + ( range() * controllerConnection()->currentValue( frameOffset ) );
|
||||
float v = 0;
|
||||
switch(m_scaleType)
|
||||
{
|
||||
case Linear:
|
||||
v = minValue<float>() + ( range() * controllerConnection()->currentValue( frameOffset ) );
|
||||
break;
|
||||
case Logarithmic:
|
||||
v = logToLinearScale(
|
||||
controllerConnection()->currentValue( frameOffset ));
|
||||
break;
|
||||
default:
|
||||
qFatal("AutomatableModel::controllerValue(int)"
|
||||
"lacks implementation for a scale type");
|
||||
break;
|
||||
}
|
||||
if( typeInfo<float>::isEqual( m_step, 1 ) )
|
||||
{
|
||||
return qRound( v );
|
||||
@@ -445,10 +500,10 @@ float AutomatableModel::controllerValue( int frameOffset ) const
|
||||
AutomatableModel* lm = m_linkedModels.first();
|
||||
if( lm->controllerConnection() )
|
||||
{
|
||||
return lm->controllerValue( frameOffset );
|
||||
return fittedValue( lm->controllerValue( frameOffset ) );
|
||||
}
|
||||
|
||||
return lm->m_value;
|
||||
return fittedValue( lm->m_value );
|
||||
}
|
||||
|
||||
|
||||
@@ -495,13 +550,71 @@ void AutomatableModel::copyValue()
|
||||
|
||||
|
||||
|
||||
void AutomatableModel::pasteValue()
|
||||
void AutomatableModel::pasteValue()
|
||||
{
|
||||
setValue( copiedValue() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
float AutomatableModel::globalAutomationValueAt( const MidiTime& time )
|
||||
{
|
||||
// get patterns that connect to this model
|
||||
QVector<AutomationPattern *> patterns = AutomationPattern::patternsForModel( this );
|
||||
if( patterns.isEmpty() )
|
||||
{
|
||||
// if no such patterns exist, return current value
|
||||
return m_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// of those patterns:
|
||||
// find the patterns which overlap with the miditime position
|
||||
QVector<AutomationPattern *> patternsInRange;
|
||||
for( QVector<AutomationPattern *>::ConstIterator it = patterns.begin(); it != patterns.end(); it++ )
|
||||
{
|
||||
int s = ( *it )->startPosition();
|
||||
int e = ( *it )->endPosition();
|
||||
if( s <= time && e >= time ) { patternsInRange += ( *it ); }
|
||||
}
|
||||
|
||||
AutomationPattern * latestPattern = NULL;
|
||||
|
||||
if( ! patternsInRange.isEmpty() )
|
||||
{
|
||||
// if there are more than one overlapping patterns, just use the first one because
|
||||
// multiple pattern behaviour is undefined anyway
|
||||
latestPattern = patternsInRange[0];
|
||||
}
|
||||
else
|
||||
// if we find no patterns at the exact miditime, we need to search for the last pattern before time and use that
|
||||
{
|
||||
int latestPosition = 0;
|
||||
|
||||
for( QVector<AutomationPattern *>::ConstIterator it = patterns.begin(); it != patterns.end(); it++ )
|
||||
{
|
||||
int e = ( *it )->endPosition();
|
||||
if( e <= time && e > latestPosition )
|
||||
{
|
||||
latestPosition = e;
|
||||
latestPattern = ( *it );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( latestPattern )
|
||||
{
|
||||
// scale/fit the value appropriately and return it
|
||||
const float value = latestPattern->valueAt( time - latestPattern->startPosition() );
|
||||
const float scaled_value = scaledValue( value );
|
||||
return fittedValue( scaled_value );
|
||||
}
|
||||
// if we still find no pattern, the value at that time is undefined so
|
||||
// just return current value as the best we can do
|
||||
else return m_value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#include "moc_AutomatableModel.cxx"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -35,8 +35,13 @@
|
||||
#include "ProjectJournal.h"
|
||||
#include "bb_track_container.h"
|
||||
#include "song.h"
|
||||
#include "text_float.h"
|
||||
#include "embed.h"
|
||||
|
||||
|
||||
const float AutomationPattern::DEFAULT_MIN_VALUE = 0;
|
||||
const float AutomationPattern::DEFAULT_MAX_VALUE = 1;
|
||||
|
||||
|
||||
AutomationPattern::AutomationPattern( AutomationTrack * _auto_track ) :
|
||||
trackContentObject( _auto_track ),
|
||||
@@ -44,7 +49,9 @@ AutomationPattern::AutomationPattern( AutomationTrack * _auto_track ) :
|
||||
m_objects(),
|
||||
m_tension( 1.0 ),
|
||||
m_progressionType( DiscreteProgression ),
|
||||
m_dragging( false )
|
||||
m_dragging( false ),
|
||||
m_isRecording( false ),
|
||||
m_lastRecordedValue( 0 )
|
||||
{
|
||||
changeLength( MidiTime( 1, 0 ) );
|
||||
}
|
||||
@@ -91,8 +98,8 @@ void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup )
|
||||
{
|
||||
if( *it == _obj )
|
||||
{
|
||||
// Already exists
|
||||
// TODO: Maybe let the user know in some non-annoying way
|
||||
textFloat::displayMessage( _obj->displayName(), tr( "Model is already connected "
|
||||
"to this pattern." ), embed::getIconPixmap( "automation" ), 2000 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -102,7 +109,7 @@ void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup )
|
||||
if( m_objects.isEmpty() && hasAutomation() == false )
|
||||
{
|
||||
// then initialize first value
|
||||
putValue( MidiTime(0), _obj->value<float>(), false );
|
||||
putValue( MidiTime(0), _obj->inverseScaledValue( _obj->value<float>() ), false );
|
||||
}
|
||||
|
||||
m_objects += _obj;
|
||||
@@ -466,27 +473,44 @@ const QString AutomationPattern::name() const
|
||||
|
||||
|
||||
|
||||
void AutomationPattern::processMidiTime( const MidiTime & _time )
|
||||
void AutomationPattern::processMidiTime( const MidiTime & time )
|
||||
{
|
||||
if( _time >= 0 && hasAutomation() )
|
||||
if( ! isRecording() )
|
||||
{
|
||||
const float val = valueAt( _time );
|
||||
for( objectVector::iterator it = m_objects.begin();
|
||||
it != m_objects.end(); ++it )
|
||||
if( time >= 0 && hasAutomation() )
|
||||
{
|
||||
if( *it )
|
||||
const float val = valueAt( time );
|
||||
for( objectVector::iterator it = m_objects.begin();
|
||||
it != m_objects.end(); ++it )
|
||||
{
|
||||
( *it )->setAutomatedValue( val );
|
||||
}
|
||||
if( *it )
|
||||
{
|
||||
( *it )->setAutomatedValue( val );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( time >= 0 && ! m_objects.isEmpty() )
|
||||
{
|
||||
const float value = static_cast<float>( firstObject()->value<float>() );
|
||||
if( value != m_lastRecordedValue )
|
||||
{
|
||||
putValue( time, value, true );
|
||||
m_lastRecordedValue = value;
|
||||
}
|
||||
else if( valueAt( time ) != value )
|
||||
{
|
||||
removeValue( time, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
trackContentObjectView * AutomationPattern::createView( trackView * _tv )
|
||||
{
|
||||
return new AutomationPatternView( this, _tv );
|
||||
@@ -529,6 +553,51 @@ bool AutomationPattern::isAutomated( const AutomatableModel * _m )
|
||||
}
|
||||
|
||||
|
||||
/*! \brief returns a list of all the automation patterns everywhere that are connected to a specific model
|
||||
* \param _m the model we want to look for
|
||||
*/
|
||||
QVector<AutomationPattern *> AutomationPattern::patternsForModel( const AutomatableModel * _m )
|
||||
{
|
||||
QVector<AutomationPattern *> patterns;
|
||||
TrackContainer::TrackList l;
|
||||
l += engine::getSong()->tracks();
|
||||
l += engine::getBBTrackContainer()->tracks();
|
||||
l += engine::getSong()->globalAutomationTrack();
|
||||
|
||||
// go through all tracks...
|
||||
for( TrackContainer::TrackList::ConstIterator it = l.begin(); it != l.end(); ++it )
|
||||
{
|
||||
// we want only automation tracks...
|
||||
if( ( *it )->type() == track::AutomationTrack ||
|
||||
( *it )->type() == track::HiddenAutomationTrack )
|
||||
{
|
||||
// get patterns in those tracks....
|
||||
const track::tcoVector & v = ( *it )->getTCOs();
|
||||
// go through all the patterns...
|
||||
for( track::tcoVector::ConstIterator j = v.begin(); j != v.end(); ++j )
|
||||
{
|
||||
AutomationPattern * a = dynamic_cast<AutomationPattern *>( *j );
|
||||
// check that the pattern has automation
|
||||
if( a && a->hasAutomation() )
|
||||
{
|
||||
// now check is the pattern is connected to the model we want by going through all the connections
|
||||
// of the pattern
|
||||
bool has_object = false;
|
||||
for( objectVector::const_iterator k = a->m_objects.begin(); k != a->m_objects.end(); ++k )
|
||||
{
|
||||
if( *k == _m )
|
||||
{
|
||||
has_object = true;
|
||||
}
|
||||
}
|
||||
// if the patterns is connected to the model, add it to the list
|
||||
if( has_object ) { patterns += a; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return patterns;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
271
src/core/BandLimitedWave.cpp
Normal file
271
src/core/BandLimitedWave.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* BandLimitedWave.h - helper functions for band-limited
|
||||
* waveform generation
|
||||
*
|
||||
* Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* 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 "BandLimitedWave.h"
|
||||
|
||||
|
||||
WaveMipMap BandLimitedWave::s_waveforms[4] = { };
|
||||
bool BandLimitedWave::s_wavesGenerated = false;
|
||||
QString BandLimitedWave::s_wavetableDir = "";
|
||||
|
||||
|
||||
QDataStream& operator<< ( QDataStream &out, WaveMipMap &waveMipMap )
|
||||
{
|
||||
for( int tbl = 0; tbl <= MAXTBL; tbl++ )
|
||||
{
|
||||
for( int i = 0; i < TLENS[tbl]; i++ )
|
||||
{
|
||||
out << waveMipMap.sampleAt( tbl, i );
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream& operator>> ( QDataStream &in, WaveMipMap &waveMipMap )
|
||||
{
|
||||
sample_t sample;
|
||||
for( int tbl = 0; tbl <= MAXTBL; tbl++ )
|
||||
{
|
||||
for( int i = 0; i < TLENS[tbl]; i++ )
|
||||
{
|
||||
in >> sample;
|
||||
waveMipMap.setSampleAt( tbl, i, sample );
|
||||
}
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
void BandLimitedWave::generateWaves()
|
||||
{
|
||||
// don't generate if they already exist
|
||||
if( s_wavesGenerated ) return;
|
||||
|
||||
int i;
|
||||
|
||||
// set wavetable directory
|
||||
s_wavetableDir = configManager::inst()->dataDir() + "wavetables/";
|
||||
|
||||
// set wavetable files
|
||||
QFile saw_file( s_wavetableDir + "saw.bin" );
|
||||
QFile sqr_file( s_wavetableDir + "sqr.bin" );
|
||||
QFile tri_file( s_wavetableDir + "tri.bin" );
|
||||
QFile moog_file( s_wavetableDir + "moog.bin" );
|
||||
|
||||
// saw wave - BLSaw
|
||||
// check for file and use it if exists
|
||||
if( saw_file.exists() )
|
||||
{
|
||||
saw_file.open( QIODevice::ReadOnly );
|
||||
QDataStream in( &saw_file );
|
||||
in >> s_waveforms[ BandLimitedWave::BLSaw ];
|
||||
saw_file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i <= MAXTBL; i++ )
|
||||
{
|
||||
const int len = TLENS[i];
|
||||
//const double om = 1.0 / len;
|
||||
double max = 0.0;
|
||||
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
int harm = 1;
|
||||
double s = 0.0f;
|
||||
double hlen;
|
||||
do
|
||||
{
|
||||
hlen = static_cast<double>( len ) / static_cast<double>( harm );
|
||||
const double amp = -1.0 / static_cast<double>( harm );
|
||||
//const double a2 = cos( om * harm * F_2PI );
|
||||
s += amp * /*a2 **/sin( static_cast<double>( ph * harm ) / static_cast<double>( len ) * F_2PI );
|
||||
harm++;
|
||||
} while( hlen > 2.0 );
|
||||
s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s );
|
||||
max = qMax( max, qAbs( s ) );
|
||||
}
|
||||
// normalize
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
sample_t s = s_waveforms[ BandLimitedWave::BLSaw ].sampleAt( i, ph ) / max;
|
||||
s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// square wave - BLSquare
|
||||
// check for file and use it if exists
|
||||
if( sqr_file.exists() )
|
||||
{
|
||||
sqr_file.open( QIODevice::ReadOnly );
|
||||
QDataStream in( &sqr_file );
|
||||
in >> s_waveforms[ BandLimitedWave::BLSquare ];
|
||||
sqr_file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i <= MAXTBL; i++ )
|
||||
{
|
||||
const int len = TLENS[i];
|
||||
//const double om = 1.0 / len;
|
||||
double max = 0.0;
|
||||
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
int harm = 1;
|
||||
double s = 0.0f;
|
||||
double hlen;
|
||||
do
|
||||
{
|
||||
hlen = static_cast<double>( len ) / static_cast<double>( harm );
|
||||
const double amp = 1.0 / static_cast<double>( harm );
|
||||
//const double a2 = cos( om * harm * F_2PI );
|
||||
s += amp * /*a2 **/ sin( static_cast<double>( ph * harm ) / static_cast<double>( len ) * F_2PI );
|
||||
harm += 2;
|
||||
} while( hlen > 2.0 );
|
||||
s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s );
|
||||
max = qMax( max, qAbs( s ) );
|
||||
}
|
||||
// normalize
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
sample_t s = s_waveforms[ BandLimitedWave::BLSquare ].sampleAt( i, ph ) / max;
|
||||
s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// triangle wave - BLTriangle
|
||||
if( tri_file.exists() )
|
||||
{
|
||||
tri_file.open( QIODevice::ReadOnly );
|
||||
QDataStream in( &tri_file );
|
||||
in >> s_waveforms[ BandLimitedWave::BLTriangle ];
|
||||
tri_file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i <= MAXTBL; i++ )
|
||||
{
|
||||
const int len = TLENS[i];
|
||||
//const double om = 1.0 / len;
|
||||
double max = 0.0;
|
||||
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
int harm = 1;
|
||||
double s = 0.0f;
|
||||
double hlen;
|
||||
do
|
||||
{
|
||||
hlen = static_cast<double>( len ) / static_cast<double>( harm );
|
||||
const double amp = 1.0 / static_cast<double>( harm * harm );
|
||||
//const double a2 = cos( om * harm * F_2PI );
|
||||
s += amp * /*a2 **/ sin( ( static_cast<double>( ph * harm ) / static_cast<double>( len ) +
|
||||
( ( harm + 1 ) % 4 == 0 ? 0.5 : 0.0 ) ) * F_2PI );
|
||||
harm += 2;
|
||||
} while( hlen > 2.0 );
|
||||
s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s );
|
||||
max = qMax( max, qAbs( s ) );
|
||||
}
|
||||
// normalize
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
sample_t s = s_waveforms[ BandLimitedWave::BLTriangle ].sampleAt( i, ph ) / max;
|
||||
s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// moog saw wave - BLMoog
|
||||
// basically, just add in triangle + 270-phase saw
|
||||
if( moog_file.exists() )
|
||||
{
|
||||
moog_file.open( QIODevice::ReadOnly );
|
||||
QDataStream in( &moog_file );
|
||||
in >> s_waveforms[ BandLimitedWave::BLMoog ];
|
||||
moog_file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i <= MAXTBL; i++ )
|
||||
{
|
||||
const int len = TLENS[i];
|
||||
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
const int sawph = ( ph + static_cast<int>( len * 0.75 ) ) % len;
|
||||
const sample_t saw = s_waveforms[ BandLimitedWave::BLSaw ].sampleAt( i, sawph );
|
||||
const sample_t tri = s_waveforms[ BandLimitedWave::BLTriangle ].sampleAt( i, ph );
|
||||
s_waveforms[ BandLimitedWave::BLMoog ].setSampleAt( i, ph, ( saw + tri ) * 0.5f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set the generated flag so we don't load/generate them again needlessly
|
||||
s_wavesGenerated = true;
|
||||
|
||||
|
||||
// generate files, serialize mipmaps as QDataStreams and save them on disk
|
||||
//
|
||||
// normally these are now provided with LMMS as pre-generated so we don't have to do this,
|
||||
// but I'm leaving the code here in case it's needed in the future
|
||||
// (maybe we add more waveforms or change the generation code or mipmap format, etc.)
|
||||
|
||||
/*
|
||||
|
||||
// if you want to generate the files, you need to set the filenames and paths here -
|
||||
// can't use the usual wavetable directory here as it can require permissions on
|
||||
// some systems...
|
||||
|
||||
QFile sawfile( "path-to-wavetables/saw.bin" );
|
||||
QFile sqrfile( "path-to-wavetables/sqr.bin" );
|
||||
QFile trifile( "path-to-wavetables/tri.bin" );
|
||||
QFile moogfile( "path-to-wavetables/moog.bin" );
|
||||
|
||||
sawfile.open( QIODevice::WriteOnly );
|
||||
QDataStream sawout( &sawfile );
|
||||
sawout << s_waveforms[ BandLimitedWave::BLSaw ];
|
||||
sawfile.close();
|
||||
|
||||
sqrfile.open( QIODevice::WriteOnly );
|
||||
QDataStream sqrout( &sqrfile );
|
||||
sqrout << s_waveforms[ BandLimitedWave::BLSquare ];
|
||||
sqrfile.close();
|
||||
|
||||
trifile.open( QIODevice::WriteOnly );
|
||||
QDataStream triout( &trifile );
|
||||
triout << s_waveforms[ BandLimitedWave::BLTriangle ];
|
||||
trifile.close();
|
||||
|
||||
moogfile.open( QIODevice::WriteOnly );
|
||||
QDataStream moogout( &moogfile );
|
||||
moogout << s_waveforms[ BandLimitedWave::BLMoog ];
|
||||
moogfile.close();
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* ComboBoxModel.cpp - implementation of ComboBoxModel
|
||||
*
|
||||
* Copyright (c) 2008-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -27,9 +27,9 @@
|
||||
|
||||
|
||||
|
||||
void ComboBoxModel::addItem( const QString & _item, PixmapLoader * _pl )
|
||||
void ComboBoxModel::addItem( const QString& item, PixmapLoader* loader )
|
||||
{
|
||||
m_items.push_back( qMakePair( _item, _pl ) );
|
||||
m_items.push_back( qMakePair( item, loader ) );
|
||||
setRange( 0, m_items.size() - 1 );
|
||||
}
|
||||
|
||||
@@ -39,23 +39,24 @@ void ComboBoxModel::addItem( const QString & _item, PixmapLoader * _pl )
|
||||
void ComboBoxModel::clear()
|
||||
{
|
||||
setRange( 0, 0 );
|
||||
foreach( const Item & _i, m_items )
|
||||
foreach( const Item& i, m_items )
|
||||
{
|
||||
delete _i.second;
|
||||
delete i.second;
|
||||
}
|
||||
|
||||
m_items.clear();
|
||||
|
||||
emit propertiesChanged();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int ComboBoxModel::findText( const QString & _txt ) const
|
||||
int ComboBoxModel::findText( const QString& txt ) const
|
||||
{
|
||||
for( QVector<Item>::ConstIterator it = m_items.begin();
|
||||
it != m_items.end(); ++it )
|
||||
for( QVector<Item>::ConstIterator it = m_items.begin(); it != m_items.end(); ++it )
|
||||
{
|
||||
if( ( *it ).first == _txt )
|
||||
if( ( *it ).first == txt )
|
||||
{
|
||||
return it - m_items.begin();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
|
||||
* Copyright (c) 2014 Lukas W <lukaswhl/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
|
||||
* Copyright (c) 2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2012-2013 Paul Giblock <p/at/pgiblock.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "SongEditor.h"
|
||||
#include "Effect.h"
|
||||
#include "lmmsversion.h"
|
||||
#include "base64.h"
|
||||
|
||||
// bbTCO::defaultColor()
|
||||
#include "bb_track.h"
|
||||
@@ -61,6 +62,38 @@ DataFile::typeDescStruct
|
||||
|
||||
|
||||
|
||||
DataFile::LocaleHelper::LocaleHelper( Mode mode )
|
||||
{
|
||||
switch( mode )
|
||||
{
|
||||
case ModeLoad:
|
||||
// set a locale for which QString::fromFloat() returns valid values if
|
||||
// floating point separator is a comma - otherwise we would fail to load
|
||||
// older projects made by people from various countries due to their
|
||||
// locale settings
|
||||
QLocale::setDefault( QLocale::German );
|
||||
break;
|
||||
|
||||
case ModeSave:
|
||||
// set default locale to C so that floating point decimals are rendered to
|
||||
// strings with periods as decimal point instead of commas in some countries
|
||||
QLocale::setDefault( QLocale::C );
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
DataFile::LocaleHelper::~LocaleHelper()
|
||||
{
|
||||
// revert to original locale
|
||||
QLocale::setDefault( QLocale::system() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DataFile::DataFile( Type type ) :
|
||||
QDomDocument( "lmms-project" ),
|
||||
m_content(),
|
||||
@@ -683,28 +716,25 @@ void DataFile::upgrade()
|
||||
|
||||
}
|
||||
|
||||
// new default colour for B&B tracks
|
||||
QDomNodeList list = elementsByTagName( "bbtco" );
|
||||
for( int i = 0; !list.item( i ).isNull(); ++i )
|
||||
// update document meta data
|
||||
documentElement().setAttribute( "version", LDF_VERSION_STRING );
|
||||
documentElement().setAttribute( "type", typeName( type() ) );
|
||||
documentElement().setAttribute( "creator", "LMMS" );
|
||||
documentElement().setAttribute( "creatorversion", LMMS_VERSION );
|
||||
|
||||
if( type() == SongProject || type() == SongProjectTemplate )
|
||||
{
|
||||
QDomElement el = list.item( i ).toElement();
|
||||
unsigned int rgb = el.attribute( "color" ).toUInt();
|
||||
if( rgb == qRgb( 64, 128, 255 ) )
|
||||
// Time-signature
|
||||
if ( !m_head.hasAttribute( "timesig_numerator" ) )
|
||||
{
|
||||
el.setAttribute( "color", bbTCO::defaultColor() );
|
||||
m_head.setAttribute( "timesig_numerator", 4 );
|
||||
m_head.setAttribute( "timesig_denominator", 4 );
|
||||
}
|
||||
}
|
||||
|
||||
// Time-signature
|
||||
if ( !m_head.hasAttribute( "timesig_numerator" ) )
|
||||
{
|
||||
m_head.setAttribute( "timesig_numerator", 4 );
|
||||
m_head.setAttribute( "timesig_denominator", 4 );
|
||||
}
|
||||
|
||||
if( !m_head.hasAttribute( "mastervol" ) )
|
||||
{
|
||||
m_head.setAttribute( "mastervol", 100 );
|
||||
if( !m_head.hasAttribute( "mastervol" ) )
|
||||
{
|
||||
m_head.setAttribute( "mastervol", 100 );
|
||||
}
|
||||
}
|
||||
//printf("%s\n", toString( 2 ).toUtf8().constData());
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
* Effect.cpp - base-class for effects
|
||||
*
|
||||
* Copyright (c) 2006-2007 Danny McRae <khjklujn/at/users.sourceforge.net>
|
||||
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -23,15 +23,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <QtXml/QDomElement>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "Effect.h"
|
||||
#include "engine.h"
|
||||
#include "DummyEffect.h"
|
||||
#include "EffectChain.h"
|
||||
#include "EffectControls.h"
|
||||
#include "EffectView.h"
|
||||
|
||||
|
||||
@@ -109,11 +106,11 @@ void Effect::loadSettings( const QDomElement & _this )
|
||||
|
||||
|
||||
|
||||
Effect * Effect::instantiate( const QString & _plugin_name,
|
||||
Effect * Effect::instantiate( const QString& pluginName,
|
||||
Model * _parent,
|
||||
Descriptor::SubPluginFeatures::Key * _key )
|
||||
{
|
||||
Plugin * p = Plugin::instantiate( _plugin_name, _parent, _key );
|
||||
Plugin * p = Plugin::instantiate( pluginName, _parent, _key );
|
||||
// check whether instantiated plugin is an effect
|
||||
if( dynamic_cast<Effect *>( p ) != NULL )
|
||||
{
|
||||
@@ -123,9 +120,10 @@ Effect * Effect::instantiate( const QString & _plugin_name,
|
||||
return effect;
|
||||
}
|
||||
|
||||
// not quite... so delete plugin and return dummy effect
|
||||
// not quite... so delete plugin and leave it up to the caller to instantiate a DummyEffect
|
||||
delete p;
|
||||
return new DummyEffect( _parent );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +133,7 @@ void Effect::checkGate( double _out_sum )
|
||||
{
|
||||
// Check whether we need to continue processing input. Restart the
|
||||
// counter if the threshold has been exceeded.
|
||||
if( _out_sum <= gate()+0.000001 )
|
||||
if( _out_sum - gate() <= typeInfo<float>::minEps() )
|
||||
{
|
||||
incrementBufferCount();
|
||||
if( bufferCount() > timeout() )
|
||||
@@ -175,7 +173,7 @@ void Effect::reinitSRC()
|
||||
libsrcInterpolation(),
|
||||
DEFAULT_CHANNELS, &error ) ) == NULL )
|
||||
{
|
||||
fprintf( stderr, "Error: src_new() failed in effect.cpp!\n" );
|
||||
qFatal( "Error: src_new() failed in effect.cpp!\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,7 +199,7 @@ void Effect::resample( int _i, const sampleFrame * _src_buf,
|
||||
int error;
|
||||
if( ( error = src_process( m_srcState[_i], &m_srcData[_i] ) ) )
|
||||
{
|
||||
fprintf( stderr, "Effect::resample(): error while resampling: %s\n",
|
||||
qFatal( "Effect::resample(): error while resampling: %s\n",
|
||||
src_strerror( error ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
|
||||
* Copyright (c) 2008-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -56,12 +56,19 @@ void EffectChain::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
{
|
||||
_this.setAttribute( "enabled", m_enabledModel.value() );
|
||||
_this.setAttribute( "numofeffects", m_effects.count() );
|
||||
for( EffectList::Iterator it = m_effects.begin();
|
||||
it != m_effects.end(); it++ )
|
||||
|
||||
for( EffectList::Iterator it = m_effects.begin(); it != m_effects.end(); it++ )
|
||||
{
|
||||
QDomElement ef = ( *it )->saveState( _doc, _this );
|
||||
ef.setAttribute( "name", ( *it )->descriptor()->name );
|
||||
ef.appendChild( ( *it )->key().saveXML( _doc ) );
|
||||
if( dynamic_cast<DummyEffect *>( *it ) )
|
||||
{
|
||||
_this.appendChild( dynamic_cast<DummyEffect *>( *it )->originalPluginData() );
|
||||
}
|
||||
else
|
||||
{
|
||||
QDomElement ef = ( *it )->saveState( _doc, _this );
|
||||
ef.setAttribute( "name", ( *it )->descriptor()->name );
|
||||
ef.appendChild( ( *it )->key().saveXML( _doc ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,26 +89,23 @@ void EffectChain::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
if( node.isElement() && node.nodeName() == "effect" )
|
||||
{
|
||||
QDomElement cn = node.toElement();
|
||||
const QString name = cn.attribute( "name" );
|
||||
EffectKey key( cn.elementsByTagName( "key" ).
|
||||
item( 0 ).toElement() );
|
||||
Effect * e = Effect::instantiate( name, this, &key );
|
||||
if( e->isOkay() )
|
||||
QDomElement effectData = node.toElement();
|
||||
|
||||
const QString name = effectData.attribute( "name" );
|
||||
EffectKey key( effectData.elementsByTagName( "key" ).item( 0 ).toElement() );
|
||||
|
||||
Effect* e = Effect::instantiate( name, this, &key );
|
||||
|
||||
if( e != NULL && e->isOkay() && e->nodeName() == node.nodeName() )
|
||||
{
|
||||
if( node.isElement() )
|
||||
{
|
||||
if( e->nodeName() == node.nodeName() )
|
||||
{
|
||||
e->restoreState( node.toElement() );
|
||||
}
|
||||
}
|
||||
e->restoreState( effectData );
|
||||
}
|
||||
else
|
||||
{
|
||||
delete e;
|
||||
e = new DummyEffect( parentModel() );
|
||||
e = new DummyEffect( parentModel(), effectData );
|
||||
}
|
||||
|
||||
m_effects.push_back( e );
|
||||
++fx_loaded;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -68,6 +68,7 @@ void EnvelopeAndLfoParameters::LfoInstances::reset()
|
||||
|
||||
|
||||
|
||||
|
||||
void EnvelopeAndLfoParameters::LfoInstances::add( EnvelopeAndLfoParameters * lfo )
|
||||
{
|
||||
QMutexLocker m( &m_lfoListMutex );
|
||||
@@ -86,7 +87,6 @@ void EnvelopeAndLfoParameters::LfoInstances::remove( EnvelopeAndLfoParameters *
|
||||
|
||||
|
||||
|
||||
|
||||
EnvelopeAndLfoParameters::EnvelopeAndLfoParameters(
|
||||
float _value_for_zero_amount,
|
||||
Model * _parent ) :
|
||||
@@ -218,6 +218,13 @@ inline sample_t EnvelopeAndLfoParameters::lfoShapeSample( fpp_t _frame_offset )
|
||||
case UserDefinedWave:
|
||||
shape_sample = m_userWave.userWaveSample( phase );
|
||||
break;
|
||||
case RandomWave:
|
||||
if( frame == 0 )
|
||||
{
|
||||
m_random = Oscillator::noiseSample( 0.0f );
|
||||
}
|
||||
shape_sample = m_random;
|
||||
break;
|
||||
case SineWave:
|
||||
default:
|
||||
shape_sample = Oscillator::sinSample( phase );
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2011 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -22,17 +22,44 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <QtXml/QDomElement>
|
||||
|
||||
#include "FxMixer.h"
|
||||
#include "MixerWorkerThread.h"
|
||||
#include "MixHelpers.h"
|
||||
#include "Effect.h"
|
||||
#include "song.h"
|
||||
|
||||
#include "InstrumentTrack.h"
|
||||
#include "bb_track_container.h"
|
||||
|
||||
FxChannel::FxChannel( Model * _parent ) :
|
||||
|
||||
FxRoute::FxRoute( FxChannel * from, FxChannel * to, float amount ) :
|
||||
m_from( from ),
|
||||
m_to( to ),
|
||||
m_amount( amount, 0, 1, 0.001, NULL,
|
||||
tr( "Amount to send from channel %1 to channel %2" ).arg( m_from->m_channelIndex ).arg( m_to->m_channelIndex ) )
|
||||
{
|
||||
//qDebug( "created: %d to %d", m_from->m_channelIndex, m_to->m_channelIndex );
|
||||
// create send amount model
|
||||
}
|
||||
|
||||
|
||||
FxRoute::~FxRoute()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void FxRoute::updateName()
|
||||
{
|
||||
m_amount.setDisplayName(
|
||||
tr( "Amount to send from channel %1 to channel %2" ).arg( m_from->m_channelIndex ).arg( m_to->m_channelIndex ) );
|
||||
}
|
||||
|
||||
|
||||
FxChannel::FxChannel( int idx, Model * _parent ) :
|
||||
m_fxChain( NULL ),
|
||||
m_used( false ),
|
||||
m_hasInput( false ),
|
||||
m_stillRunning( false ),
|
||||
m_peakLeft( 0.0f ),
|
||||
m_peakRight( 0.0f ),
|
||||
@@ -40,7 +67,10 @@ FxChannel::FxChannel( Model * _parent ) :
|
||||
m_muteModel( false, _parent ),
|
||||
m_volumeModel( 1.0, 0.0, 2.0, 0.01, _parent ),
|
||||
m_name(),
|
||||
m_lock()
|
||||
m_lock(),
|
||||
m_channelIndex( idx ),
|
||||
m_queued( false ),
|
||||
m_dependenciesMet( 0 )
|
||||
{
|
||||
engine::mixer()->clearAudioBuffer( m_buffer,
|
||||
engine::mixer()->framesPerPeriod() );
|
||||
@@ -55,28 +85,110 @@ FxChannel::~FxChannel()
|
||||
}
|
||||
|
||||
|
||||
inline void FxChannel::processed()
|
||||
{
|
||||
foreach( FxRoute * receiverRoute, m_sends )
|
||||
{
|
||||
if( receiverRoute->receiver()->m_muted == false )
|
||||
{
|
||||
receiverRoute->receiver()->incrementDeps();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FxChannel::incrementDeps()
|
||||
{
|
||||
m_dependenciesMet.ref();
|
||||
if( m_dependenciesMet >= m_receives.size() )
|
||||
{
|
||||
m_queued = true;
|
||||
MixerWorkerThread::addJob( this );
|
||||
m_dependenciesMet = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FxChannel::doProcessing( sampleFrame * _buf )
|
||||
{
|
||||
const fpp_t fpp = engine::mixer()->framesPerPeriod();
|
||||
|
||||
// <tobydox> ignore the passed _buf
|
||||
// <tobydox> always use m_buffer
|
||||
// <tobydox> this is just an auxilliary buffer if doProcessing()
|
||||
// needs one for processing while running
|
||||
// <tobydox> particularly important for playHandles, so Instruments
|
||||
// can operate on this buffer the whole time
|
||||
// <tobydox> this improves cache hit rate
|
||||
_buf = m_buffer;
|
||||
|
||||
if( m_muted == false )
|
||||
{
|
||||
foreach( FxRoute * senderRoute, m_receives )
|
||||
{
|
||||
FxChannel * sender = senderRoute->sender();
|
||||
FloatModel * sendModel = senderRoute->amount();
|
||||
if( ! sendModel ) qFatal( "Error: no send model found from %d to %d", senderRoute->senderIndex(), m_channelIndex );
|
||||
|
||||
if( sender->m_hasInput || sender->m_stillRunning )
|
||||
{
|
||||
// get the send level...
|
||||
const float amt = sendModel->value();
|
||||
|
||||
// mix it's output with this one's output
|
||||
sampleFrame * ch_buf = sender->m_buffer;
|
||||
const float v = sender->m_volumeModel.value() * amt;
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
_buf[f][0] += ch_buf[f][0] * v;
|
||||
_buf[f][1] += ch_buf[f][1] * v;
|
||||
}
|
||||
m_hasInput = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const float v = m_volumeModel.value();
|
||||
|
||||
if( m_hasInput )
|
||||
{
|
||||
// only start fxchain when we have input...
|
||||
m_fxChain.startRunning();
|
||||
}
|
||||
|
||||
m_stillRunning = m_fxChain.processAudioBuffer( _buf, fpp, m_hasInput );
|
||||
|
||||
m_peakLeft = qMax( m_peakLeft, engine::mixer()->peakValueLeft( _buf, fpp ) * v );
|
||||
m_peakRight = qMax( m_peakRight, engine::mixer()->peakValueRight( _buf, fpp ) * v );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_peakLeft = m_peakRight = 0.0f;
|
||||
}
|
||||
|
||||
// increment dependency counter of all receivers
|
||||
processed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
FxMixer::FxMixer() :
|
||||
JournallingObject(),
|
||||
Model( NULL )
|
||||
Model( NULL ),
|
||||
m_fxChannels()
|
||||
{
|
||||
for( int i = 0; i < NumFxChannels+1; ++i )
|
||||
{
|
||||
m_fxChannels[i] = new FxChannel( this );
|
||||
}
|
||||
// reset name etc.
|
||||
clear();
|
||||
// create master channel
|
||||
createChannel();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
FxMixer::~FxMixer()
|
||||
{
|
||||
for( int i = 0; i < NumFxChannels+1; ++i )
|
||||
while( ! m_fxRoutes.isEmpty() )
|
||||
{
|
||||
deleteChannelSend( m_fxRoutes.first() );
|
||||
}
|
||||
for( int i = 0; i < m_fxChannels.size(); ++i )
|
||||
{
|
||||
delete m_fxChannels[i];
|
||||
}
|
||||
@@ -84,19 +196,274 @@ FxMixer::~FxMixer()
|
||||
|
||||
|
||||
|
||||
int FxMixer::createChannel()
|
||||
{
|
||||
const int index = m_fxChannels.size();
|
||||
// create new channel
|
||||
m_fxChannels.push_back( new FxChannel( index, this ) );
|
||||
|
||||
// reset channel state
|
||||
clearChannel( index );
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
void FxMixer::deleteChannel( int index )
|
||||
{
|
||||
m_fxChannels[index]->m_lock.lock();
|
||||
|
||||
FxChannel * ch = m_fxChannels[index];
|
||||
|
||||
// go through every instrument and adjust for the channel index change
|
||||
TrackContainer::TrackList tracks;
|
||||
tracks += engine::getSong()->tracks();
|
||||
tracks += engine::getBBTrackContainer()->tracks();
|
||||
|
||||
foreach( track* t, tracks )
|
||||
{
|
||||
if( t->type() == track::InstrumentTrack )
|
||||
{
|
||||
InstrumentTrack* inst = dynamic_cast<InstrumentTrack *>( t );
|
||||
int val = inst->effectChannelModel()->value(0);
|
||||
if( val == index )
|
||||
{
|
||||
// we are deleting this track's fx send
|
||||
// send to master
|
||||
inst->effectChannelModel()->setValue(0);
|
||||
}
|
||||
else if( val > index )
|
||||
{
|
||||
// subtract 1 to make up for the missing channel
|
||||
inst->effectChannelModel()->setValue(val-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete all of this channel's sends and receives
|
||||
while( ! ch->m_sends.isEmpty() )
|
||||
{
|
||||
deleteChannelSend( ch->m_sends.first() );
|
||||
}
|
||||
while( ! ch->m_receives.isEmpty() )
|
||||
{
|
||||
deleteChannelSend( ch->m_receives.first() );
|
||||
}
|
||||
|
||||
// actually delete the channel
|
||||
delete m_fxChannels[index];
|
||||
m_fxChannels.remove(index);
|
||||
|
||||
for( int i = index; i < m_fxChannels.size(); ++i )
|
||||
{
|
||||
validateChannelName( i, i + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FxMixer::moveChannelLeft( int index )
|
||||
{
|
||||
// can't move master or first channel
|
||||
if( index <= 1 || index >= m_fxChannels.size() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
// channels to swap
|
||||
int a = index - 1, b = index;
|
||||
|
||||
// go through every instrument and adjust for the channel index change
|
||||
QVector<track *> songTrackList = engine::getSong()->tracks();
|
||||
QVector<track *> bbTrackList = engine::getBBTrackContainer()->tracks();
|
||||
|
||||
QVector<track *> trackLists[] = {songTrackList, bbTrackList};
|
||||
for(int tl=0; tl<2; ++tl)
|
||||
{
|
||||
QVector<track *> trackList = trackLists[tl];
|
||||
for(int i=0; i<trackList.size(); ++i)
|
||||
{
|
||||
if( trackList[i]->type() == track::InstrumentTrack )
|
||||
{
|
||||
InstrumentTrack * inst = (InstrumentTrack *) trackList[i];
|
||||
int val = inst->effectChannelModel()->value(0);
|
||||
if( val == a )
|
||||
{
|
||||
inst->effectChannelModel()->setValue(b);
|
||||
}
|
||||
else if( val == b )
|
||||
{
|
||||
inst->effectChannelModel()->setValue(a);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// actually do the swap
|
||||
FxChannel * tmpChannel = m_fxChannels[a];
|
||||
m_fxChannels[a] = m_fxChannels[b];
|
||||
m_fxChannels[b] = tmpChannel;
|
||||
|
||||
validateChannelName( a, b );
|
||||
validateChannelName( b, a );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FxMixer::moveChannelRight( int index )
|
||||
{
|
||||
moveChannelLeft( index + 1 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
FxRoute * FxMixer::createChannelSend( fx_ch_t fromChannel, fx_ch_t toChannel,
|
||||
float amount )
|
||||
{
|
||||
// qDebug( "requested: %d to %d", fromChannel, toChannel );
|
||||
// find the existing connection
|
||||
FxChannel * from = m_fxChannels[fromChannel];
|
||||
FxChannel * to = m_fxChannels[toChannel];
|
||||
|
||||
for( int i=0; i<from->m_sends.size(); ++i )
|
||||
{
|
||||
if( from->m_sends[i]->receiver() == to )
|
||||
{
|
||||
// simply adjust the amount
|
||||
from->m_sends[i]->amount()->setValue( amount );
|
||||
return from->m_sends[i];
|
||||
}
|
||||
}
|
||||
|
||||
// connection does not exist. create a new one
|
||||
return createRoute( from, to, amount );
|
||||
}
|
||||
|
||||
|
||||
FxRoute * FxMixer::createRoute( FxChannel * from, FxChannel * to, float amount )
|
||||
{
|
||||
if( from == to )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
m_sendsMutex.lock();
|
||||
FxRoute * route = new FxRoute( from, to, amount );
|
||||
|
||||
// add us to from's sends
|
||||
from->m_sends.append( route );
|
||||
|
||||
// add us to to's receives
|
||||
to->m_receives.append( route );
|
||||
|
||||
// add us to fxmixer's list
|
||||
engine::fxMixer()->m_fxRoutes.append( route );
|
||||
m_sendsMutex.unlock();
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
|
||||
// delete the connection made by createChannelSend
|
||||
void FxMixer::deleteChannelSend( fx_ch_t fromChannel, fx_ch_t toChannel )
|
||||
{
|
||||
// delete the send
|
||||
FxChannel * from = m_fxChannels[fromChannel];
|
||||
FxChannel * to = m_fxChannels[toChannel];
|
||||
|
||||
// find and delete the send entry
|
||||
for( int i = 0; i < from->m_sends.size(); ++i )
|
||||
{
|
||||
if( from->m_sends[i]->receiver() == to )
|
||||
{
|
||||
deleteChannelSend( from->m_sends[i] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FxMixer::deleteChannelSend( FxRoute * route )
|
||||
{
|
||||
m_sendsMutex.lock();
|
||||
// remove us from from's sends
|
||||
route->sender()->m_sends.remove( route->sender()->m_sends.indexOf( route ) );
|
||||
// remove us from to's receives
|
||||
route->receiver()->m_receives.remove( route->receiver()->m_receives.indexOf( route ) );
|
||||
// remove us from fxmixer's list
|
||||
engine::fxMixer()->m_fxRoutes.remove( engine::fxMixer()->m_fxRoutes.indexOf( route ) );
|
||||
delete route;
|
||||
m_sendsMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
bool FxMixer::isInfiniteLoop( fx_ch_t sendFrom, fx_ch_t sendTo )
|
||||
{
|
||||
if( sendFrom == sendTo ) return true;
|
||||
FxChannel * from = m_fxChannels[sendFrom];
|
||||
FxChannel * to = m_fxChannels[sendTo];
|
||||
bool b = checkInfiniteLoop( from, to );
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
bool FxMixer::checkInfiniteLoop( FxChannel * from, FxChannel * to )
|
||||
{
|
||||
// can't send master to anything
|
||||
if( from == m_fxChannels[0] )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// can't send channel to itself
|
||||
if( from == to )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// follow sendTo's outputs recursively looking for something that sends
|
||||
// to sendFrom
|
||||
for( int i=0; i < to->m_sends.size(); ++i )
|
||||
{
|
||||
if( checkInfiniteLoop( from, to->m_sends[i]->receiver() ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// how much does fromChannel send its output to the input of toChannel?
|
||||
FloatModel * FxMixer::channelSendModel( fx_ch_t fromChannel, fx_ch_t toChannel )
|
||||
{
|
||||
if( fromChannel == toChannel )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
FxChannel * from = m_fxChannels[fromChannel];
|
||||
FxChannel * to = m_fxChannels[toChannel];
|
||||
|
||||
foreach( FxRoute * route, from->m_sends )
|
||||
{
|
||||
if( route->receiver() == to )
|
||||
{
|
||||
return route->amount();
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch )
|
||||
{
|
||||
if( m_fxChannels[_ch]->m_muteModel.value() == false )
|
||||
{
|
||||
m_fxChannels[_ch]->m_lock.lock();
|
||||
sampleFrame * buf = m_fxChannels[_ch]->m_buffer;
|
||||
for( f_cnt_t f = 0; f < engine::mixer()->framesPerPeriod(); ++f )
|
||||
{
|
||||
buf[f][0] += _buf[f][0];
|
||||
buf[f][1] += _buf[f][1];
|
||||
}
|
||||
m_fxChannels[_ch]->m_used = true;
|
||||
MixHelpers::add( m_fxChannels[_ch]->m_buffer, _buf, engine::mixer()->framesPerPeriod() );
|
||||
m_fxChannels[_ch]->m_hasInput = true;
|
||||
m_fxChannels[_ch]->m_lock.unlock();
|
||||
}
|
||||
}
|
||||
@@ -104,49 +471,6 @@ void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch )
|
||||
|
||||
|
||||
|
||||
void FxMixer::processChannel( fx_ch_t _ch, sampleFrame * _buf )
|
||||
{
|
||||
if( m_fxChannels[_ch]->m_muteModel.value() == false &&
|
||||
( m_fxChannels[_ch]->m_used || m_fxChannels[_ch]->m_stillRunning || _ch == 0 ) )
|
||||
{
|
||||
if( _buf == NULL )
|
||||
{
|
||||
_buf = m_fxChannels[_ch]->m_buffer;
|
||||
}
|
||||
const fpp_t f = engine::mixer()->framesPerPeriod();
|
||||
|
||||
// only start effects if sound was mixed to this FX channel before
|
||||
if( m_fxChannels[_ch]->m_used )
|
||||
{
|
||||
m_fxChannels[_ch]->m_fxChain.startRunning();
|
||||
}
|
||||
|
||||
// process FX chain
|
||||
m_fxChannels[_ch]->m_stillRunning = m_fxChannels[_ch]->m_fxChain.processAudioBuffer( _buf, f, m_fxChannels[_ch]->m_used );
|
||||
|
||||
float peakLeft = engine::mixer()->peakValueLeft( _buf, f ) * m_fxChannels[_ch]->m_volumeModel.value();
|
||||
float peakRight = engine::mixer()->peakValueRight( _buf, f ) * m_fxChannels[_ch]->m_volumeModel.value();
|
||||
|
||||
if( peakLeft > m_fxChannels[_ch]->m_peakLeft )
|
||||
{
|
||||
m_fxChannels[_ch]->m_peakLeft = peakLeft;
|
||||
}
|
||||
if( peakRight > m_fxChannels[_ch]->m_peakRight )
|
||||
{
|
||||
m_fxChannels[_ch]->m_peakRight = peakRight;
|
||||
}
|
||||
|
||||
m_fxChannels[_ch]->m_used = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fxChannels[_ch]->m_peakLeft = m_fxChannels[_ch]->m_peakRight = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void FxMixer::prepareMasterMix()
|
||||
{
|
||||
engine::mixer()->clearAudioBuffer( m_fxChannels[0]->m_buffer,
|
||||
@@ -155,47 +479,55 @@ void FxMixer::prepareMasterMix()
|
||||
|
||||
|
||||
|
||||
|
||||
void FxMixer::masterMix( sampleFrame * _buf )
|
||||
{
|
||||
const int fpp = engine::mixer()->framesPerPeriod();
|
||||
memcpy( _buf, m_fxChannels[0]->m_buffer, sizeof( sampleFrame ) * fpp );
|
||||
|
||||
for( int i = 1; i < NumFxChannels+1; ++i )
|
||||
if( m_sendsMutex.tryLock() )
|
||||
{
|
||||
if( m_fxChannels[i]->m_used )
|
||||
// add the channels that have no dependencies (no incoming senders, ie. no receives)
|
||||
// to the jobqueue. The channels that have receives get added when their senders get processed, which
|
||||
// is detected by dependency counting.
|
||||
// also instantly add all muted channels as they don't need to care about their senders, and can just increment the deps of
|
||||
// their recipients right away.
|
||||
MixerWorkerThread::resetJobQueue( MixerWorkerThread::JobQueue::Dynamic );
|
||||
foreach( FxChannel * ch, m_fxChannels )
|
||||
{
|
||||
sampleFrame * ch_buf = m_fxChannels[i]->m_buffer;
|
||||
const float v = m_fxChannels[i]->m_volumeModel.value();
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
ch->m_muted = ch->m_muteModel.value();
|
||||
if( ch->m_muted ) // instantly "process" muted channels
|
||||
{
|
||||
_buf[f][0] += ch_buf[f][0] * v;
|
||||
_buf[f][1] += ch_buf[f][1] * v;
|
||||
ch->processed();
|
||||
ch->done();
|
||||
}
|
||||
else if( ch->m_receives.size() == 0 )
|
||||
{
|
||||
ch->m_queued = true;
|
||||
MixerWorkerThread::addJob( ch );
|
||||
}
|
||||
engine::mixer()->clearAudioBuffer( ch_buf,
|
||||
engine::mixer()->framesPerPeriod() );
|
||||
m_fxChannels[i]->m_used = false;
|
||||
}
|
||||
while( m_fxChannels[0]->state() != ThreadableJob::Done )
|
||||
{
|
||||
MixerWorkerThread::startAndWaitForJobs();
|
||||
}
|
||||
m_sendsMutex.unlock();
|
||||
}
|
||||
|
||||
processChannel( 0, _buf );
|
||||
|
||||
if( m_fxChannels[0]->m_muteModel.value() )
|
||||
{
|
||||
engine::mixer()->clearAudioBuffer( _buf,
|
||||
engine::mixer()->framesPerPeriod() );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const float v = m_fxChannels[0]->m_volumeModel.value();
|
||||
for( f_cnt_t f = 0; f < engine::mixer()->framesPerPeriod(); ++f )
|
||||
{
|
||||
_buf[f][0] *= v;
|
||||
_buf[f][1] *= v;
|
||||
}
|
||||
MixHelpers::addSanitizedMultiplied( _buf, m_fxChannels[0]->m_buffer, v, fpp );
|
||||
|
||||
m_fxChannels[0]->m_peakLeft *= engine::mixer()->masterGain();
|
||||
m_fxChannels[0]->m_peakRight *= engine::mixer()->masterGain();
|
||||
|
||||
// clear all channel buffers and
|
||||
// reset channel process state
|
||||
for( int i = 0; i < numChannels(); ++i)
|
||||
{
|
||||
engine::mixer()->clearAudioBuffer( m_fxChannels[i]->m_buffer, engine::mixer()->framesPerPeriod() );
|
||||
m_fxChannels[i]->reset();
|
||||
m_fxChannels[i]->m_queued = false;
|
||||
// also reset hasInput
|
||||
m_fxChannels[i]->m_hasInput = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -203,58 +535,159 @@ void FxMixer::masterMix( sampleFrame * _buf )
|
||||
|
||||
void FxMixer::clear()
|
||||
{
|
||||
for( int i = 0; i <= NumFxChannels; ++i )
|
||||
while( m_fxChannels.size() > 1 )
|
||||
{
|
||||
m_fxChannels[i]->m_fxChain.clear();
|
||||
m_fxChannels[i]->m_volumeModel.setValue( 1.0f );
|
||||
m_fxChannels[i]->m_muteModel.setValue( false );
|
||||
m_fxChannels[i]->m_name = ( i == 0 ) ?
|
||||
tr( "Master" ) : tr( "FX %1" ).arg( i );
|
||||
m_fxChannels[i]->m_volumeModel.setDisplayName(
|
||||
m_fxChannels[i]->m_name );
|
||||
|
||||
deleteChannel(1);
|
||||
}
|
||||
|
||||
clearChannel(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FxMixer::clearChannel(fx_ch_t index)
|
||||
{
|
||||
FxChannel * ch = m_fxChannels[index];
|
||||
ch->m_fxChain.clear();
|
||||
ch->m_volumeModel.setValue( 1.0f );
|
||||
ch->m_muteModel.setValue( false );
|
||||
ch->m_name = ( index == 0 ) ? tr( "Master" ) : tr( "FX %1" ).arg( index );
|
||||
ch->m_volumeModel.setDisplayName( ch->m_name );
|
||||
|
||||
// send only to master
|
||||
if( index > 0)
|
||||
{
|
||||
// delete existing sends
|
||||
while( ! ch->m_sends.isEmpty() )
|
||||
{
|
||||
deleteChannelSend( ch->m_sends.first() );
|
||||
}
|
||||
|
||||
// add send to master
|
||||
createChannelSend( index, 0 );
|
||||
}
|
||||
|
||||
// delete receives
|
||||
while( ! ch->m_receives.isEmpty() )
|
||||
{
|
||||
deleteChannelSend( ch->m_receives.first() );
|
||||
}
|
||||
}
|
||||
|
||||
void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
{
|
||||
for( int i = 0; i <= NumFxChannels; ++i )
|
||||
// save channels
|
||||
for( int i = 0; i < m_fxChannels.size(); ++i )
|
||||
{
|
||||
FxChannel * ch = m_fxChannels[i];
|
||||
|
||||
QDomElement fxch = _doc.createElement( QString( "fxchannel" ) );
|
||||
_this.appendChild( fxch );
|
||||
m_fxChannels[i]->m_fxChain.saveState( _doc, fxch );
|
||||
m_fxChannels[i]->m_volumeModel.saveSettings( _doc, fxch,
|
||||
"volume" );
|
||||
m_fxChannels[i]->m_muteModel.saveSettings( _doc, fxch,
|
||||
"muted" );
|
||||
|
||||
ch->m_fxChain.saveState( _doc, fxch );
|
||||
ch->m_volumeModel.saveSettings( _doc, fxch, "volume" );
|
||||
ch->m_muteModel.saveSettings( _doc, fxch, "muted" );
|
||||
fxch.setAttribute( "num", i );
|
||||
fxch.setAttribute( "name", m_fxChannels[i]->m_name );
|
||||
fxch.setAttribute( "name", ch->m_name );
|
||||
|
||||
// add the channel sends
|
||||
for( int si = 0; si < ch->m_sends.size(); ++si )
|
||||
{
|
||||
QDomElement sendsDom = _doc.createElement( QString( "send" ) );
|
||||
fxch.appendChild( sendsDom );
|
||||
|
||||
sendsDom.setAttribute( "channel", ch->m_sends[si]->receiverIndex() );
|
||||
ch->m_sends[si]->amount()->saveSettings( _doc, sendsDom, "amount" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we have at least num channels
|
||||
void FxMixer::allocateChannelsTo(int num)
|
||||
{
|
||||
while( num > m_fxChannels.size() - 1 )
|
||||
{
|
||||
createChannel();
|
||||
|
||||
// delete the default send to master
|
||||
deleteChannelSend( m_fxChannels.size()-1, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void FxMixer::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
clear();
|
||||
QDomNode node = _this.firstChild();
|
||||
for( int i = 0; i <= NumFxChannels; ++i )
|
||||
bool thereIsASend = false;
|
||||
|
||||
while( ! node.isNull() )
|
||||
{
|
||||
QDomElement fxch = node.toElement();
|
||||
|
||||
// index of the channel we are about to load
|
||||
int num = fxch.attribute( "num" ).toInt();
|
||||
m_fxChannels[num]->m_fxChain.restoreState(
|
||||
fxch.firstChildElement(
|
||||
m_fxChannels[num]->m_fxChain.nodeName() ) );
|
||||
|
||||
// allocate enough channels
|
||||
allocateChannelsTo( num );
|
||||
|
||||
m_fxChannels[num]->m_volumeModel.loadSettings( fxch, "volume" );
|
||||
m_fxChannels[num]->m_muteModel.loadSettings( fxch, "muted" );
|
||||
m_fxChannels[num]->m_name = fxch.attribute( "name" );
|
||||
|
||||
m_fxChannels[num]->m_fxChain.restoreState( fxch.firstChildElement(
|
||||
m_fxChannels[num]->m_fxChain.nodeName() ) );
|
||||
|
||||
// mixer sends
|
||||
QDomNodeList chData = fxch.childNodes();
|
||||
for( unsigned int i=0; i<chData.length(); ++i )
|
||||
{
|
||||
QDomElement chDataItem = chData.at(i).toElement();
|
||||
if( chDataItem.nodeName() == QString( "send" ) )
|
||||
{
|
||||
thereIsASend = true;
|
||||
int sendTo = chDataItem.attribute( "channel" ).toInt();
|
||||
allocateChannelsTo( sendTo ) ;
|
||||
FxRoute * fxr = createChannelSend( num, sendTo, 1.0f );
|
||||
if( fxr ) fxr->amount()->loadSettings( chDataItem, "amount" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
node = node.nextSibling();
|
||||
}
|
||||
|
||||
// check for old format. 65 fx channels and no explicit sends.
|
||||
if( ! thereIsASend && m_fxChannels.size() == 65 ) {
|
||||
// create a send from every channel into master
|
||||
for( int i=1; i<m_fxChannels.size(); ++i )
|
||||
{
|
||||
createChannelSend( i, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
emit dataChanged();
|
||||
}
|
||||
|
||||
|
||||
void FxMixer::validateChannelName( int index, int oldIndex )
|
||||
{
|
||||
FxChannel * fxc = m_fxChannels[ index ];
|
||||
if( fxc->m_name == tr( "FX %1" ).arg( oldIndex ) )
|
||||
{
|
||||
fxc->m_name = tr( "FX %1" ).arg( index );
|
||||
}
|
||||
// set correct channel index
|
||||
fxc->m_channelIndex = index;
|
||||
|
||||
// now check all routes and update names of the send models
|
||||
foreach( FxRoute * r, fxc->m_sends )
|
||||
{
|
||||
r->updateName();
|
||||
}
|
||||
foreach( FxRoute * r, fxc->m_receives )
|
||||
{
|
||||
r->updateName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -133,6 +133,8 @@ InstrumentFunctionNoteStacking::ChordTable::Init InstrumentFunctionNoteStacking:
|
||||
{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Minor" ), { 0, 2, 3, 5, 7, 8, 10, -1 } },
|
||||
{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Chromatic" ), { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1 } },
|
||||
{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Half-Whole Diminished" ), { 0, 1, 3, 4, 6, 7, 9, 10, -1 } },
|
||||
|
||||
{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "5" ), { 0, 7, -1 } }
|
||||
} ;
|
||||
|
||||
|
||||
@@ -231,30 +233,22 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n )
|
||||
// at the same time we only add sub-notes if nothing of the note was
|
||||
// played yet, because otherwise we would add chord-subnotes every
|
||||
// time an audio-buffer is rendered...
|
||||
if( ( ( _n->isTopNote() && _n->instrumentTrack()->isArpeggioEnabled() == false ) || _n->isPartOfArpeggio() ) &&
|
||||
_n->totalFramesPlayed() == 0 &&
|
||||
m_chordsEnabledModel.value() == true )
|
||||
if( ( _n->origin() == NotePlayHandle::OriginArpeggio || ( _n->hasParent() == false && _n->instrumentTrack()->isArpeggioEnabled() == false ) ) &&
|
||||
_n->totalFramesPlayed() == 0 &&
|
||||
m_chordsEnabledModel.value() == true && ! _n->isReleased() )
|
||||
{
|
||||
// then insert sub-notes for chord
|
||||
const int selected_chord = m_chordsModel.value();
|
||||
|
||||
for( int octave_cnt = 0;
|
||||
octave_cnt < m_chordRangeModel.value(); ++octave_cnt )
|
||||
for( int octave_cnt = 0; octave_cnt < m_chordRangeModel.value(); ++octave_cnt )
|
||||
{
|
||||
const int sub_note_key_base = base_note_key +
|
||||
octave_cnt * KeysPerOctave;
|
||||
// if octave_cnt == 1 we're in the first octave and
|
||||
// the base-note is already done, so we don't have to
|
||||
// create it in the following loop, then we loop until
|
||||
// there's a -1 in the interval-array
|
||||
for( int i = ( octave_cnt == 0 ) ? 1 : 0;
|
||||
i < chord_table[selected_chord].size();
|
||||
++i )
|
||||
const int sub_note_key_base = base_note_key + octave_cnt * KeysPerOctave;
|
||||
|
||||
// process all notes in the chord
|
||||
for( int i = 0; i < chord_table[selected_chord].size(); ++i )
|
||||
{
|
||||
// add interval to sub-note-key
|
||||
const int sub_note_key = sub_note_key_base +
|
||||
(int) chord_table[
|
||||
selected_chord][i];
|
||||
const int sub_note_key = sub_note_key_base + (int) chord_table[selected_chord][i];
|
||||
// maybe we're out of range -> let's get outta
|
||||
// here!
|
||||
if( sub_note_key > NumKeys )
|
||||
@@ -262,16 +256,12 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n )
|
||||
break;
|
||||
}
|
||||
// create copy of base-note
|
||||
note note_copy( _n->length(), 0, sub_note_key,
|
||||
_n->getVolume(),
|
||||
_n->getPanning(),
|
||||
_n->detuning() );
|
||||
note note_copy( _n->length(), 0, sub_note_key, _n->getVolume(), _n->getPanning(), _n->detuning() );
|
||||
|
||||
// create sub-note-play-handle, only note is
|
||||
// different
|
||||
new NotePlayHandle( _n->instrumentTrack(),
|
||||
_n->offset(),
|
||||
_n->frames(), note_copy,
|
||||
_n );
|
||||
new NotePlayHandle( _n->instrumentTrack(), _n->offset(), _n->frames(), note_copy,
|
||||
_n, -1, NotePlayHandle::OriginNoteStacking );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,10 +300,8 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) :
|
||||
m_arpEnabledModel( false ),
|
||||
m_arpModel( this, tr( "Arpeggio type" ) ),
|
||||
m_arpRangeModel( 1.0f, 1.0f, 9.0f, 1.0f, this, tr( "Arpeggio range" ) ),
|
||||
m_arpTimeModel( 100.0f, 25.0f, 2000.0f, 1.0f, 2000, this,
|
||||
tr( "Arpeggio time" ) ),
|
||||
m_arpGateModel( 100.0f, 1.0f, 200.0f, 1.0f, this,
|
||||
tr( "Arpeggio gate" ) ),
|
||||
m_arpTimeModel( 100.0f, 25.0f, 2000.0f, 1.0f, 2000, this, tr( "Arpeggio time" ) ),
|
||||
m_arpGateModel( 100.0f, 1.0f, 200.0f, 1.0f, this, tr( "Arpeggio gate" ) ),
|
||||
m_arpDirectionModel( this, tr( "Arpeggio direction" ) ),
|
||||
m_arpModeModel( this, tr( "Arpeggio mode" ) )
|
||||
{
|
||||
@@ -325,10 +313,9 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) :
|
||||
|
||||
m_arpDirectionModel.addItem( tr( "Up" ), new PixmapLoader( "arp_up" ) );
|
||||
m_arpDirectionModel.addItem( tr( "Down" ), new PixmapLoader( "arp_down" ) );
|
||||
m_arpDirectionModel.addItem( tr( "Up and down" ),
|
||||
new PixmapLoader( "arp_up_and_down" ) );
|
||||
m_arpDirectionModel.addItem( tr( "Random" ),
|
||||
new PixmapLoader( "arp_random" ) );
|
||||
m_arpDirectionModel.addItem( tr( "Up and down" ), new PixmapLoader( "arp_up_and_down" ) );
|
||||
m_arpDirectionModel.addItem( tr( "Random" ), new PixmapLoader( "arp_random" ) );
|
||||
m_arpDirectionModel.addItem( tr( "Down and up" ), new PixmapLoader( "arp_up_and_down" ) );
|
||||
m_arpDirectionModel.setInitValue( ArpDirUp );
|
||||
|
||||
m_arpModeModel.addItem( tr( "Free" ), new PixmapLoader( "arp_free" ) );
|
||||
@@ -349,9 +336,10 @@ InstrumentFunctionArpeggio::~InstrumentFunctionArpeggio()
|
||||
void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
{
|
||||
const int base_note_key = _n->key();
|
||||
if( _n->isTopNote() == false ||
|
||||
!m_arpEnabledModel.value() ||
|
||||
( _n->isReleased() && _n->releaseFramesDone() >= _n->actualReleaseFramesToDo() ) )
|
||||
if( _n->origin() == NotePlayHandle::OriginArpeggio ||
|
||||
_n->origin() == NotePlayHandle::OriginNoteStacking ||
|
||||
!m_arpEnabledModel.value() ||
|
||||
( _n->isReleased() && _n->releaseFramesDone() >= _n->actualReleaseFramesToDo() ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -359,8 +347,8 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
|
||||
const int selected_arp = m_arpModel.value();
|
||||
|
||||
ConstNotePlayHandleList cnphv = NotePlayHandle::nphsOfInstrumentTrack(
|
||||
_n->instrumentTrack() );
|
||||
ConstNotePlayHandleList cnphv = NotePlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() );
|
||||
|
||||
if( m_arpModeModel.value() != FreeMode && cnphv.size() == 0 )
|
||||
{
|
||||
// maybe we're playing only a preset-preview-note?
|
||||
@@ -379,27 +367,23 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
const int total_range = range * cnphv.size();
|
||||
|
||||
// number of frames that every note should be played
|
||||
const f_cnt_t arp_frames = (f_cnt_t)( m_arpTimeModel.value() / 1000.0f *
|
||||
engine::mixer()->processingSampleRate() );
|
||||
const f_cnt_t gated_frames = (f_cnt_t)( m_arpGateModel.value() *
|
||||
arp_frames / 100.0f );
|
||||
const f_cnt_t arp_frames = (f_cnt_t)( m_arpTimeModel.value() / 1000.0f * engine::mixer()->processingSampleRate() );
|
||||
const f_cnt_t gated_frames = (f_cnt_t)( m_arpGateModel.value() * arp_frames / 100.0f );
|
||||
|
||||
// used for calculating remaining frames for arp-note, we have to add
|
||||
// arp_frames-1, otherwise the first arp-note will not be setup
|
||||
// correctly... -> arp_frames frames silence at the start of every note!
|
||||
int cur_frame = ( ( m_arpModeModel.value() != FreeMode ) ?
|
||||
cnphv.first()->totalFramesPlayed() :
|
||||
_n->totalFramesPlayed() ) + arp_frames - 1;
|
||||
cnphv.first()->totalFramesPlayed() :
|
||||
_n->totalFramesPlayed() ) + arp_frames - 1;
|
||||
// used for loop
|
||||
f_cnt_t frames_processed = 0;
|
||||
|
||||
while( frames_processed < engine::mixer()->framesPerPeriod() )
|
||||
{
|
||||
const f_cnt_t remaining_frames_for_cur_arp = arp_frames -
|
||||
( cur_frame % arp_frames );
|
||||
const f_cnt_t remaining_frames_for_cur_arp = arp_frames - ( cur_frame % arp_frames );
|
||||
// does current arp-note fill whole audio-buffer?
|
||||
if( remaining_frames_for_cur_arp >
|
||||
engine::mixer()->framesPerPeriod() )
|
||||
if( remaining_frames_for_cur_arp > engine::mixer()->framesPerPeriod() )
|
||||
{
|
||||
// then we don't have to do something!
|
||||
break;
|
||||
@@ -413,8 +397,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
// in sorted mode: is it our turn or do we have to be quiet for
|
||||
// now?
|
||||
if( m_arpModeModel.value() == SortMode &&
|
||||
( ( cur_frame / arp_frames ) % total_range ) /
|
||||
range != (f_cnt_t) _n->index() )
|
||||
( ( cur_frame / arp_frames ) % total_range ) / range != (f_cnt_t) _n->index() )
|
||||
{
|
||||
// update counters
|
||||
frames_processed += arp_frames;
|
||||
@@ -439,33 +422,41 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
// once down -> makes 2 * range possible notes...
|
||||
// because we don't play the lower and upper notes
|
||||
// twice, we have to subtract 2
|
||||
cur_arp_idx = ( cur_frame / arp_frames ) %
|
||||
( range * 2 - 2 );
|
||||
cur_arp_idx = ( cur_frame / arp_frames ) % ( range * 2 - 2 );
|
||||
// if greater than range, we have to play down...
|
||||
// looks like the code for arp_dir==DOWN... :)
|
||||
if( cur_arp_idx >= range )
|
||||
{
|
||||
cur_arp_idx = range - cur_arp_idx %
|
||||
( range - 1 ) - 1;
|
||||
cur_arp_idx = range - cur_arp_idx % ( range - 1 ) - 1;
|
||||
}
|
||||
}
|
||||
else if( dir == ArpDirDownAndUp && range > 1 )
|
||||
{
|
||||
// copied from ArpDirUpAndDown above
|
||||
cur_arp_idx = ( cur_frame / arp_frames ) % ( range * 2 - 2 );
|
||||
// if greater than range, we have to play down...
|
||||
// looks like the code for arp_dir==DOWN... :)
|
||||
if( cur_arp_idx >= range )
|
||||
{
|
||||
cur_arp_idx = range - cur_arp_idx % ( range - 1 ) - 1;
|
||||
}
|
||||
// inverts direction
|
||||
cur_arp_idx = range - cur_arp_idx - 1;
|
||||
}
|
||||
else if( dir == ArpDirRandom )
|
||||
{
|
||||
// just pick a random chord-index
|
||||
cur_arp_idx = (int)( range * ( (float) rand() /
|
||||
(float) RAND_MAX ) );
|
||||
cur_arp_idx = (int)( range * ( (float) rand() / (float) RAND_MAX ) );
|
||||
}
|
||||
|
||||
// now calculate final key for our arp-note
|
||||
const int sub_note_key = base_note_key + (cur_arp_idx /
|
||||
cur_chord_size ) *
|
||||
KeysPerOctave +
|
||||
chord_table[selected_arp][cur_arp_idx % cur_chord_size];
|
||||
const int sub_note_key = base_note_key + (cur_arp_idx / cur_chord_size ) *
|
||||
KeysPerOctave + chord_table[selected_arp][cur_arp_idx % cur_chord_size];
|
||||
|
||||
// range-checking
|
||||
if( sub_note_key >= NumKeys ||
|
||||
sub_note_key < 0 ||
|
||||
engine::mixer()->criticalXRuns() )
|
||||
engine::mixer()->criticalXRuns() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -477,34 +468,20 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
}
|
||||
|
||||
// create new arp-note
|
||||
note new_note( MidiTime( 0 ), MidiTime( 0 ),
|
||||
sub_note_key,
|
||||
(volume_t)
|
||||
qRound( _n->getVolume() * vol_level ),
|
||||
_n->getPanning(), _n->detuning() );
|
||||
|
||||
// create sub-note-play-handle, only ptr to note is different
|
||||
// and is_arp_note=true
|
||||
new NotePlayHandle( _n->instrumentTrack(),
|
||||
( ( m_arpModeModel.value() != FreeMode ) ?
|
||||
cnphv.first()->offset() :
|
||||
_n->offset() ) +
|
||||
frames_processed,
|
||||
gated_frames,
|
||||
new_note,
|
||||
_n, true );
|
||||
( ( m_arpModeModel.value() != FreeMode ) ? cnphv.first()->offset() : _n->offset() ) + frames_processed,
|
||||
gated_frames,
|
||||
note( MidiTime( 0 ), MidiTime( 0 ), sub_note_key, (volume_t) qRound( _n->getVolume() * vol_level ),
|
||||
_n->getPanning(), _n->detuning() ),
|
||||
_n, -1, NotePlayHandle::OriginArpeggio );
|
||||
|
||||
// update counters
|
||||
frames_processed += arp_frames;
|
||||
cur_frame += arp_frames;
|
||||
}
|
||||
|
||||
// make sure, note is handled as arp-base-note, even if we didn't add a
|
||||
// sub-note so far
|
||||
if( m_arpModeModel.value() != FreeMode )
|
||||
{
|
||||
_n->setPartOfArpeggio( true );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -315,23 +315,19 @@ f_cnt_t InstrumentSoundShaping::envFrames( const bool _only_vol ) const
|
||||
|
||||
f_cnt_t InstrumentSoundShaping::releaseFrames() const
|
||||
{
|
||||
f_cnt_t ret_val = m_envLfoParameters[Volume]->isUsed() ?
|
||||
m_envLfoParameters[Volume]->releaseFrames() : 0;
|
||||
if( m_instrumentTrack->instrument() &&
|
||||
m_instrumentTrack->instrument()->desiredReleaseFrames() > ret_val )
|
||||
if( m_envLfoParameters[Volume]->isUsed() )
|
||||
{
|
||||
ret_val = m_instrumentTrack->instrument()->desiredReleaseFrames();
|
||||
return m_envLfoParameters[Volume]->releaseFrames();
|
||||
}
|
||||
f_cnt_t ret_val = m_instrumentTrack->instrument()
|
||||
? m_instrumentTrack->instrument()->desiredReleaseFrames()
|
||||
: 0;
|
||||
|
||||
if( m_envLfoParameters[Volume]->isUsed() == false )
|
||||
for( int i = Volume+1; i < NumTargets; ++i )
|
||||
{
|
||||
for( int i = Volume+1; i < NumTargets; ++i )
|
||||
if( m_envLfoParameters[i]->isUsed() )
|
||||
{
|
||||
if( m_envLfoParameters[i]->isUsed() &&
|
||||
m_envLfoParameters[i]->releaseFrames() > ret_val )
|
||||
{
|
||||
ret_val = m_envLfoParameters[i]->releaseFrames();
|
||||
}
|
||||
ret_val = qMax( ret_val, m_envLfoParameters[i]->releaseFrames() );
|
||||
}
|
||||
}
|
||||
return ret_val;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* JournallingObject.cpp - implementation of journalling-object related stuff
|
||||
*
|
||||
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -37,8 +37,6 @@
|
||||
JournallingObject::JournallingObject() :
|
||||
SerializingObject(),
|
||||
m_id( engine::projectJournal()->allocID( this ) ),
|
||||
m_journalEntries(),
|
||||
m_currentJournalEntry( m_journalEntries.end() ),
|
||||
m_journalling( true ),
|
||||
m_journallingStateStack()
|
||||
{
|
||||
@@ -58,32 +56,11 @@ JournallingObject::~JournallingObject()
|
||||
|
||||
|
||||
|
||||
void JournallingObject::undo()
|
||||
void JournallingObject::addJournalCheckPoint()
|
||||
{
|
||||
if( m_journalEntries.empty() == true )
|
||||
if( isJournalling() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( m_currentJournalEntry - 1 >= m_journalEntries.begin() )
|
||||
{
|
||||
undoStep( *--m_currentJournalEntry );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void JournallingObject::redo()
|
||||
{
|
||||
if( m_journalEntries.empty() == true )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( m_currentJournalEntry < m_journalEntries.end() )
|
||||
{
|
||||
redoStep( *m_currentJournalEntry++ );
|
||||
engine::projectJournal()->addJournalCheckPoint( this );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +71,12 @@ QDomElement JournallingObject::saveState( QDomDocument & _doc,
|
||||
QDomElement & _parent )
|
||||
{
|
||||
QDomElement _this = SerializingObject::saveState( _doc, _parent );
|
||||
saveJournal( _doc, _this );
|
||||
|
||||
QDomElement journalNode = _doc.createElement( "journallingObject" );
|
||||
journalNode.setAttribute( "id", id() );
|
||||
journalNode.setAttribute( "metadata", true );
|
||||
_this.appendChild( journalNode );
|
||||
|
||||
return _this;
|
||||
}
|
||||
|
||||
@@ -113,7 +95,11 @@ void JournallingObject::restoreState( const QDomElement & _this )
|
||||
{
|
||||
if( node.isElement() && node.nodeName() == "journal" )
|
||||
{
|
||||
loadJournal( node.toElement() );
|
||||
const jo_id_t new_id = node.toElement().attribute( "id" ).toInt();
|
||||
if( new_id )
|
||||
{
|
||||
changeID( new_id );
|
||||
}
|
||||
}
|
||||
node = node.nextSibling();
|
||||
}
|
||||
@@ -124,21 +110,6 @@ void JournallingObject::restoreState( const QDomElement & _this )
|
||||
|
||||
|
||||
|
||||
void JournallingObject::addJournalEntry( const JournalEntry & _je )
|
||||
{
|
||||
if( engine::projectJournal()->isJournalling() && isJournalling() )
|
||||
{
|
||||
m_journalEntries.erase( m_currentJournalEntry,
|
||||
m_journalEntries.end() );
|
||||
m_journalEntries.push_back( _je );
|
||||
m_currentJournalEntry = m_journalEntries.end();
|
||||
engine::projectJournal()->journalEntryAdded( id() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void JournallingObject::changeID( jo_id_t _id )
|
||||
{
|
||||
if( id() != _id )
|
||||
@@ -159,78 +130,10 @@ void JournallingObject::changeID( jo_id_t _id )
|
||||
(int) _id, used_by.toUtf8().constData() );
|
||||
return;
|
||||
}
|
||||
engine::projectJournal()->forgetAboutID( id() );
|
||||
|
||||
engine::projectJournal()->reallocID( _id, this );
|
||||
m_id = _id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void JournallingObject::saveJournal( QDomDocument & _doc,
|
||||
QDomElement & _parent )
|
||||
{
|
||||
/* // avoid creating empty journal-nodes
|
||||
if( m_journalEntries.size() == 0 )
|
||||
{
|
||||
return;
|
||||
}*/
|
||||
QDomElement journal_de = _doc.createElement( "journal" );
|
||||
journal_de.setAttribute( "id", id() );
|
||||
journal_de.setAttribute( "entries", m_journalEntries.size() );
|
||||
journal_de.setAttribute( "curentry", (int)( m_currentJournalEntry -
|
||||
m_journalEntries.begin() ) );
|
||||
journal_de.setAttribute( "metadata", true );
|
||||
|
||||
for( JournalEntryVector::const_iterator it = m_journalEntries.begin();
|
||||
it != m_journalEntries.end(); ++it )
|
||||
{
|
||||
QDomElement je_de = _doc.createElement( "entry" );
|
||||
je_de.setAttribute( "pos", (int)( it -
|
||||
m_journalEntries.begin() ) );
|
||||
je_de.setAttribute( "actionid", it->actionID() );
|
||||
je_de.setAttribute( "data", base64::encode( it->data() ) );
|
||||
journal_de.appendChild( je_de );
|
||||
}
|
||||
|
||||
_parent.appendChild( journal_de );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void JournallingObject::loadJournal( const QDomElement & _this )
|
||||
{
|
||||
clear();
|
||||
|
||||
const jo_id_t new_id = _this.attribute( "id" ).toInt();
|
||||
|
||||
if( new_id == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
changeID( new_id );
|
||||
|
||||
m_journalEntries.resize( _this.attribute( "entries" ).toInt() );
|
||||
|
||||
QDomNode node = _this.firstChild();
|
||||
while( !node.isNull() )
|
||||
{
|
||||
if( node.isElement() )
|
||||
{
|
||||
const QDomElement & je = node.toElement();
|
||||
m_journalEntries[je.attribute( "pos" ).toInt()] =
|
||||
JournalEntry(
|
||||
je.attribute( "actionid" ).toInt(),
|
||||
base64::decode( je.attribute( "data" ) ) );
|
||||
}
|
||||
node = node.nextSibling();
|
||||
}
|
||||
|
||||
m_currentJournalEntry = m_journalEntries.begin() +
|
||||
_this.attribute( "curentry" ).toInt();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -54,6 +54,8 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port,
|
||||
{
|
||||
m_toggledModel.setValue( true );
|
||||
}
|
||||
// TODO: careful: we must prevent saved scales
|
||||
m_toggledModel.setScaleLogarithmic( m_port->suggests_logscale );
|
||||
break;
|
||||
|
||||
case INTEGER:
|
||||
@@ -65,6 +67,8 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port,
|
||||
static_cast<int>( m_port->def ) );
|
||||
connect( &m_knobModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( knobChanged() ) );
|
||||
// TODO: careful: we must prevent saved scales
|
||||
m_knobModel.setScaleLogarithmic( m_port->suggests_logscale );
|
||||
break;
|
||||
|
||||
case FLOATING:
|
||||
@@ -72,19 +76,23 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port,
|
||||
( m_port->max - m_port->min )
|
||||
/ ( m_port->name.toUpper() == "GAIN"
|
||||
&& m_port->max == 10.0f ? 4000.0f :
|
||||
400.0f ) );
|
||||
( m_port->suggests_logscale ? 8000.0f : 800.0f ) ) );
|
||||
m_knobModel.setInitValue( m_port->def );
|
||||
connect( &m_knobModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( knobChanged() ) );
|
||||
// TODO: careful: we must prevent saved scales
|
||||
m_knobModel.setScaleLogarithmic( m_port->suggests_logscale );
|
||||
break;
|
||||
|
||||
case TIME:
|
||||
m_tempoSyncKnobModel.setRange( m_port->min, m_port->max,
|
||||
( m_port->max -
|
||||
m_port->min ) / 400.0f );
|
||||
m_port->min ) / 800.0f );
|
||||
m_tempoSyncKnobModel.setInitValue( m_port->def );
|
||||
connect( &m_tempoSyncKnobModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( tempoKnobChanged() ) );
|
||||
// TODO: careful: we must prevent saved scales
|
||||
m_tempoSyncKnobModel.setScaleLogarithmic( m_port->suggests_logscale );
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
65
src/core/MemoryHelper.cpp
Normal file
65
src/core/MemoryHelper.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Simon Symeonidis <lethaljellybean/at/gmail/com>
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
|
||||
#include "lmms_basics.h"
|
||||
#include "MemoryHelper.h"
|
||||
|
||||
/**
|
||||
* Allocate a number of bytes and return them.
|
||||
* @param byteNum is the number of bytes
|
||||
*/
|
||||
void* MemoryHelper::alignedMalloc( int byteNum )
|
||||
{
|
||||
char *ptr, *ptr2, *aligned_ptr;
|
||||
int align_mask = ALIGN_SIZE - 1;
|
||||
|
||||
ptr = static_cast<char*>( malloc( byteNum + ALIGN_SIZE + sizeof( int ) ) );
|
||||
|
||||
if( ptr == NULL ) return NULL;
|
||||
|
||||
ptr2 = ptr + sizeof( int );
|
||||
aligned_ptr = ptr2 + ( ALIGN_SIZE - ( ( size_t ) ptr2 & align_mask ) );
|
||||
|
||||
ptr2 = aligned_ptr - sizeof( int );
|
||||
*( ( int* ) ptr2 ) = ( int )( aligned_ptr - ptr );
|
||||
|
||||
return aligned_ptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Free an aligned buffer
|
||||
* @param _buffer is the buffer to free
|
||||
*/
|
||||
void MemoryHelper::alignedFree( void* _buffer )
|
||||
{
|
||||
if( _buffer )
|
||||
{
|
||||
int *ptr2 = static_cast<int*>( _buffer ) - 1;
|
||||
_buffer = static_cast<char*>( _buffer ) - *ptr2;
|
||||
free( _buffer );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -22,8 +22,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "lmms_math.h"
|
||||
#include "MixHelpers.h"
|
||||
|
||||
|
||||
@@ -106,6 +105,26 @@ void addMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, in
|
||||
|
||||
|
||||
|
||||
struct AddSanitizedMultipliedOp
|
||||
{
|
||||
AddSanitizedMultipliedOp( float coeff ) : m_coeff( coeff ) { }
|
||||
|
||||
void operator()( sampleFrame& dst, const sampleFrame& src ) const
|
||||
{
|
||||
dst[0] += ( isinff( src[0] ) || isnanf( src[0] ) ) ? 0.0f : src[0] * m_coeff;
|
||||
dst[1] += ( isinff( src[1] ) || isnanf( src[1] ) ) ? 0.0f : src[1] * m_coeff;
|
||||
}
|
||||
|
||||
const float m_coeff;
|
||||
};
|
||||
|
||||
void addSanitizedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames )
|
||||
{
|
||||
run<>( dst, src, frames, AddSanitizedMultipliedOp(coeffSrc) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct AddMultipliedStereoOp
|
||||
{
|
||||
AddMultipliedStereoOp( float coeffLeft, float coeffRight )
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "Mixer.h"
|
||||
#include "FxMixer.h"
|
||||
#include "MixHelpers.h"
|
||||
#include "MixerWorkerThread.h"
|
||||
#include "song.h"
|
||||
#include "templates.h"
|
||||
#include "EnvelopeAndLfoParameters.h"
|
||||
@@ -37,7 +38,6 @@
|
||||
#include "config_mgr.h"
|
||||
#include "SamplePlayHandle.h"
|
||||
#include "PianoRoll.h"
|
||||
#include "MicroTimer.h"
|
||||
#include "atomic_int.h"
|
||||
|
||||
// platform-specific audio-interface-classes
|
||||
@@ -56,237 +56,7 @@
|
||||
#include "MidiWinMM.h"
|
||||
#include "MidiDummy.h"
|
||||
|
||||
|
||||
static QVector<fx_ch_t> __fx_channel_jobs( NumFxChannels );
|
||||
|
||||
|
||||
|
||||
static void aligned_free( void * _buf )
|
||||
{
|
||||
if( _buf != NULL )
|
||||
{
|
||||
int *ptr2=(int *)_buf - 1;
|
||||
_buf = (char *)_buf- *ptr2;
|
||||
free(_buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void * aligned_malloc( int _bytes )
|
||||
{
|
||||
char *ptr,*ptr2,*aligned_ptr;
|
||||
int align_mask = ALIGN_SIZE- 1;
|
||||
ptr=(char *)malloc(_bytes +ALIGN_SIZE+ sizeof(int));
|
||||
if(ptr==NULL) return(NULL);
|
||||
|
||||
ptr2 = ptr + sizeof(int);
|
||||
aligned_ptr = ptr2 + (ALIGN_SIZE- ((size_t)ptr2 & align_mask));
|
||||
|
||||
|
||||
ptr2 = aligned_ptr - sizeof(int);
|
||||
*((int *)ptr2)=(int)(aligned_ptr - ptr);
|
||||
|
||||
return(aligned_ptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
class MixerWorkerThread : public QThread
|
||||
{
|
||||
public:
|
||||
enum JobTypes
|
||||
{
|
||||
InvalidJob,
|
||||
PlayHandle,
|
||||
AudioPortEffects,
|
||||
EffectChannel,
|
||||
NumJobTypes
|
||||
} ;
|
||||
|
||||
struct JobQueueItem
|
||||
{
|
||||
JobQueueItem() :
|
||||
type( InvalidJob ),
|
||||
job( NULL ),
|
||||
param( 0 ),
|
||||
done( false )
|
||||
{
|
||||
}
|
||||
JobQueueItem( JobTypes _type, void * _job, int _param = 0 ) :
|
||||
type( _type ),
|
||||
job( _job ),
|
||||
param( _param ),
|
||||
done( false )
|
||||
{
|
||||
}
|
||||
|
||||
JobTypes type;
|
||||
void * job;
|
||||
int param;
|
||||
|
||||
AtomicInt done;
|
||||
} ;
|
||||
|
||||
|
||||
struct JobQueue
|
||||
{
|
||||
#define JOB_QUEUE_SIZE 1024
|
||||
JobQueue() :
|
||||
queueSize( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
JobQueueItem items[JOB_QUEUE_SIZE];
|
||||
int queueSize;
|
||||
AtomicInt itemsDone;
|
||||
} ;
|
||||
|
||||
static JobQueue s_jobQueue;
|
||||
|
||||
MixerWorkerThread( int _worker_num, Mixer* mixer ) :
|
||||
QThread( mixer ),
|
||||
m_workingBuf( (sampleFrame *) aligned_malloc( mixer->framesPerPeriod() * sizeof( sampleFrame ) ) ),
|
||||
m_workerNum( _worker_num ),
|
||||
m_quit( false ),
|
||||
m_mixer( mixer ),
|
||||
m_queueReadyWaitCond( &m_mixer->m_queueReadyWaitCond )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MixerWorkerThread()
|
||||
{
|
||||
aligned_free( m_workingBuf );
|
||||
}
|
||||
|
||||
virtual void quit()
|
||||
{
|
||||
m_quit = true;
|
||||
}
|
||||
|
||||
void processJobQueue();
|
||||
|
||||
|
||||
private:
|
||||
virtual void run()
|
||||
{
|
||||
#if 0
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
#ifdef LMMS_HAVE_SCHED_H
|
||||
cpu_set_t mask;
|
||||
CPU_ZERO( &mask );
|
||||
CPU_SET( m_workerNum, &mask );
|
||||
sched_setaffinity( 0, sizeof( mask ), &mask );
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
QMutex m;
|
||||
while( m_quit == false )
|
||||
{
|
||||
m.lock();
|
||||
m_queueReadyWaitCond->wait( &m );
|
||||
processJobQueue();
|
||||
m.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
sampleFrame * m_workingBuf;
|
||||
int m_workerNum;
|
||||
volatile bool m_quit;
|
||||
Mixer* m_mixer;
|
||||
QWaitCondition * m_queueReadyWaitCond;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
MixerWorkerThread::JobQueue MixerWorkerThread::s_jobQueue;
|
||||
|
||||
|
||||
|
||||
void MixerWorkerThread::processJobQueue()
|
||||
{
|
||||
for( int i = 0; i < s_jobQueue.queueSize; ++i )
|
||||
{
|
||||
JobQueueItem * it = &s_jobQueue.items[i];
|
||||
if( it->done.fetchAndStoreOrdered( 1 ) == 0 )
|
||||
{
|
||||
switch( it->type )
|
||||
{
|
||||
case PlayHandle:
|
||||
( (::PlayHandle *) it->job )->play( m_workingBuf );
|
||||
break;
|
||||
case AudioPortEffects:
|
||||
{
|
||||
AudioPort * a = (AudioPort *) it->job;
|
||||
const bool me = a->processEffects();
|
||||
if( me || a->m_bufferUsage != AudioPort::NoUsage )
|
||||
{
|
||||
engine::fxMixer()->mixToChannel( a->firstBuffer(), a->nextFxChannel() );
|
||||
a->nextPeriod();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EffectChannel:
|
||||
engine::fxMixer()->processChannel( (fx_ch_t) it->param );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
s_jobQueue.itemsDone.fetchAndAddOrdered( 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition) \
|
||||
MixerWorkerThread::s_jobQueue.queueSize = 0; \
|
||||
MixerWorkerThread::s_jobQueue.itemsDone = 0; \
|
||||
for( _vec_type::Iterator it = _vec.begin(); \
|
||||
it != _vec.end(); ++it ) \
|
||||
{ \
|
||||
if( _condition ) \
|
||||
{
|
||||
|
||||
#define FILL_JOB_QUEUE_END() \
|
||||
++MixerWorkerThread::s_jobQueue.queueSize; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FILL_JOB_QUEUE(_vec_type,_vec,_job_type,_condition) \
|
||||
FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition) \
|
||||
MixerWorkerThread::s_jobQueue.items \
|
||||
[MixerWorkerThread::s_jobQueue.queueSize] = \
|
||||
MixerWorkerThread::JobQueueItem( _job_type, \
|
||||
(void *) *it ); \
|
||||
FILL_JOB_QUEUE_END()
|
||||
|
||||
#define FILL_JOB_QUEUE_PARAM(_vec_type,_vec,_job_type,_condition) \
|
||||
FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition) \
|
||||
MixerWorkerThread::s_jobQueue.items \
|
||||
[MixerWorkerThread::s_jobQueue.queueSize] = \
|
||||
MixerWorkerThread::JobQueueItem( _job_type, \
|
||||
NULL, *it ); \
|
||||
FILL_JOB_QUEUE_END()
|
||||
|
||||
#define START_JOBS() \
|
||||
m_queueReadyWaitCond.wakeAll();
|
||||
|
||||
// define a pause instruction for spinlock-loop - merely useful on
|
||||
// HyperThreading systems with just one physical core (e.g. Intel Atom)
|
||||
#ifdef LMMS_HOST_X86
|
||||
#define SPINLOCK_PAUSE() asm( "pause" )
|
||||
#else
|
||||
#ifdef LMMS_HOST_X86_64
|
||||
#define SPINLOCK_PAUSE() asm( "pause" )
|
||||
#else
|
||||
#define SPINLOCK_PAUSE()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define WAIT_FOR_JOBS() \
|
||||
m_workers[m_numWorkers]->processJobQueue(); \
|
||||
while( MixerWorkerThread::s_jobQueue.itemsDone < \
|
||||
MixerWorkerThread::s_jobQueue.queueSize ) \
|
||||
{ \
|
||||
SPINLOCK_PAUSE(); \
|
||||
} \
|
||||
#include "MemoryHelper.h"
|
||||
|
||||
|
||||
|
||||
@@ -298,7 +68,6 @@ Mixer::Mixer() :
|
||||
m_inputBufferWrite( 1 ),
|
||||
m_readBuf( NULL ),
|
||||
m_writeBuf( NULL ),
|
||||
m_cpuLoad( 0 ),
|
||||
m_workers(),
|
||||
m_numWorkers( QThread::idealThreadCount()-1 ),
|
||||
m_queueReadyWaitCond(),
|
||||
@@ -306,7 +75,8 @@ Mixer::Mixer() :
|
||||
m_masterGain( 1.0f ),
|
||||
m_audioDev( NULL ),
|
||||
m_oldAudioDev( NULL ),
|
||||
m_globalMutex( QMutex::Recursive )
|
||||
m_globalMutex( QMutex::Recursive ),
|
||||
m_profiler()
|
||||
{
|
||||
for( int i = 0; i < 2; ++i )
|
||||
{
|
||||
@@ -316,11 +86,6 @@ Mixer::Mixer() :
|
||||
clearAudioBuffer( m_inputBuffer[i], m_inputBufferSize[i] );
|
||||
}
|
||||
|
||||
for( int i = 1; i < NumFxChannels+1; ++i )
|
||||
{
|
||||
__fx_channel_jobs[i-1] = (fx_ch_t) i;
|
||||
}
|
||||
|
||||
// just rendering?
|
||||
if( !engine::hasGUI() )
|
||||
{
|
||||
@@ -353,12 +118,12 @@ Mixer::Mixer() :
|
||||
m_fifo = new fifo( 1 );
|
||||
}
|
||||
|
||||
m_workingBuf = (sampleFrame*) aligned_malloc( m_framesPerPeriod *
|
||||
m_workingBuf = (sampleFrame*) MemoryHelper::alignedMalloc( m_framesPerPeriod *
|
||||
sizeof( sampleFrame ) );
|
||||
for( int i = 0; i < 3; i++ )
|
||||
{
|
||||
m_readBuf = (surroundSampleFrame*)
|
||||
aligned_malloc( m_framesPerPeriod *
|
||||
MemoryHelper::alignedMalloc( m_framesPerPeriod *
|
||||
sizeof( surroundSampleFrame ) );
|
||||
|
||||
clearAudioBuffer( m_readBuf, m_framesPerPeriod );
|
||||
@@ -367,7 +132,7 @@ Mixer::Mixer() :
|
||||
|
||||
for( int i = 0; i < m_numWorkers+1; ++i )
|
||||
{
|
||||
MixerWorkerThread * wt = new MixerWorkerThread( i, this );
|
||||
MixerWorkerThread * wt = new MixerWorkerThread( this );
|
||||
if( i < m_numWorkers )
|
||||
{
|
||||
wt->start( QThread::TimeCriticalPriority );
|
||||
@@ -385,14 +150,13 @@ Mixer::Mixer() :
|
||||
|
||||
Mixer::~Mixer()
|
||||
{
|
||||
// distribute an empty job-queue so that worker-threads
|
||||
// get out of their processing-loop
|
||||
MixerWorkerThread::s_jobQueue.queueSize = 0;
|
||||
for( int w = 0; w < m_numWorkers; ++w )
|
||||
{
|
||||
m_workers[w]->quit();
|
||||
}
|
||||
START_JOBS();
|
||||
|
||||
MixerWorkerThread::startAndWaitForJobs();
|
||||
|
||||
for( int w = 0; w < m_numWorkers; ++w )
|
||||
{
|
||||
m_workers[w]->wait( 500 );
|
||||
@@ -409,10 +173,10 @@ Mixer::~Mixer()
|
||||
|
||||
for( int i = 0; i < 3; i++ )
|
||||
{
|
||||
aligned_free( m_bufferPool[i] );
|
||||
MemoryHelper::alignedFree( m_bufferPool[i] );
|
||||
}
|
||||
|
||||
aligned_free( m_workingBuf );
|
||||
MemoryHelper::alignedFree( m_workingBuf );
|
||||
|
||||
for( int i = 0; i < 2; ++i )
|
||||
{
|
||||
@@ -512,7 +276,7 @@ sample_rate_t Mixer::processingSampleRate() const
|
||||
|
||||
bool Mixer::criticalXRuns() const
|
||||
{
|
||||
return m_cpuLoad >= 99 && engine::getSong()->isExporting() == false;
|
||||
return cpuLoad() >= 99 && engine::getSong()->isExporting() == false;
|
||||
}
|
||||
|
||||
|
||||
@@ -550,7 +314,8 @@ void Mixer::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames )
|
||||
|
||||
const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
{
|
||||
MicroTimer timer;
|
||||
m_profiler.startPeriod();
|
||||
|
||||
static song::playPos last_metro_pos = -1;
|
||||
|
||||
song::playPos p = engine::getSong()->getPlayPos(
|
||||
@@ -612,9 +377,8 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
|
||||
|
||||
// STAGE 1: run and render all play handles
|
||||
FILL_JOB_QUEUE(PlayHandleList,m_playHandles,MixerWorkerThread::PlayHandle, !( *it )->isFinished());
|
||||
START_JOBS();
|
||||
WAIT_FOR_JOBS();
|
||||
MixerWorkerThread::fillJobQueue<PlayHandleList>( m_playHandles );
|
||||
MixerWorkerThread::startAndWaitForJobs();
|
||||
|
||||
// removed all play handles which are done
|
||||
for( PlayHandleList::Iterator it = m_playHandles.begin();
|
||||
@@ -639,20 +403,11 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
|
||||
|
||||
// STAGE 2: process effects of all instrument- and sampletracks
|
||||
FILL_JOB_QUEUE(QVector<AudioPort*>,m_audioPorts,
|
||||
MixerWorkerThread::AudioPortEffects,1);
|
||||
START_JOBS();
|
||||
WAIT_FOR_JOBS();
|
||||
MixerWorkerThread::fillJobQueue<QVector<AudioPort *> >( m_audioPorts );
|
||||
MixerWorkerThread::startAndWaitForJobs();
|
||||
|
||||
|
||||
// STAGE 3: process effects in FX mixer
|
||||
FILL_JOB_QUEUE_PARAM(QVector<fx_ch_t>,__fx_channel_jobs,
|
||||
MixerWorkerThread::EffectChannel,1);
|
||||
START_JOBS();
|
||||
WAIT_FOR_JOBS();
|
||||
|
||||
|
||||
// STAGE 4: do master mix in FX mixer
|
||||
// STAGE 3: do master mix in FX mixer
|
||||
engine::fxMixer()->masterMix( m_writeBuf );
|
||||
|
||||
unlock();
|
||||
@@ -664,10 +419,7 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
EnvelopeAndLfoParameters::instances()->trigger();
|
||||
Controller::triggerFrameCounter();
|
||||
|
||||
const float new_cpu_load = timer.elapsed() / 10000.0f *
|
||||
processingSampleRate() / m_framesPerPeriod;
|
||||
m_cpuLoad = tLimit( (int) ( new_cpu_load * 0.1f + m_cpuLoad * 0.9f ), 0,
|
||||
100 );
|
||||
m_profiler.finishPeriod( processingSampleRate(), m_framesPerPeriod );
|
||||
|
||||
return m_readBuf;
|
||||
}
|
||||
@@ -763,14 +515,7 @@ float Mixer::peakValueLeft( sampleFrame * _ab, const f_cnt_t _frames )
|
||||
float p = 0.0f;
|
||||
for( f_cnt_t f = 0; f < _frames; ++f )
|
||||
{
|
||||
if( _ab[f][0] > p )
|
||||
{
|
||||
p = _ab[f][0];
|
||||
}
|
||||
else if( -_ab[f][0] > p )
|
||||
{
|
||||
p = -_ab[f][0];
|
||||
}
|
||||
p = qMax( p, qAbs( _ab[f][0] ) );
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -783,14 +528,7 @@ float Mixer::peakValueRight( sampleFrame * _ab, const f_cnt_t _frames )
|
||||
float p = 0.0f;
|
||||
for( f_cnt_t f = 0; f < _frames; ++f )
|
||||
{
|
||||
if( _ab[f][1] > p )
|
||||
{
|
||||
p = _ab[f][1];
|
||||
}
|
||||
else if( -_ab[f][1] > p )
|
||||
{
|
||||
p = -_ab[f][1];
|
||||
}
|
||||
p = qMax( p, qAbs( _ab[f][1] ) );
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -931,13 +669,13 @@ void Mixer::removePlayHandle( PlayHandle * _ph )
|
||||
|
||||
|
||||
|
||||
void Mixer::removePlayHandles( track * _track )
|
||||
void Mixer::removePlayHandles( track * _track, bool removeIPHs )
|
||||
{
|
||||
lock();
|
||||
PlayHandleList::Iterator it = m_playHandles.begin();
|
||||
while( it != m_playHandles.end() )
|
||||
{
|
||||
if( ( *it )->isFromTrack( _track ) )
|
||||
if( ( *it )->isFromTrack( _track ) && ( removeIPHs || ( *it )->type() != PlayHandle::TypeInstrumentPlayHandle ) )
|
||||
{
|
||||
delete *it;
|
||||
it = m_playHandles.erase( it );
|
||||
@@ -1179,6 +917,16 @@ void Mixer::fifoWriter::finish()
|
||||
|
||||
void Mixer::fifoWriter::run()
|
||||
{
|
||||
// set denormal protection for this thread
|
||||
#ifdef __SSE3__
|
||||
/* DAZ flag */
|
||||
_MM_SET_DENORMALS_ZERO_MODE( _MM_DENORMALS_ZERO_ON );
|
||||
#endif
|
||||
#ifdef __SSE__
|
||||
/* FTZ flag */
|
||||
_MM_SET_FLUSH_ZERO_MODE( _MM_FLUSH_ZERO_ON );
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
#ifdef LMMS_HAVE_SCHED_H
|
||||
|
||||
63
src/core/MixerProfiler.cpp
Normal file
63
src/core/MixerProfiler.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* MixerProfiler.cpp - class for profiling performance of Mixer
|
||||
*
|
||||
* Copyright (c) 2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* 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 "MixerProfiler.h"
|
||||
|
||||
|
||||
MixerProfiler::MixerProfiler() :
|
||||
m_periodTimer(),
|
||||
m_cpuLoad( 0 ),
|
||||
m_outputFile()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
MixerProfiler::~MixerProfiler()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void MixerProfiler::finishPeriod( sample_rate_t sampleRate, fpp_t framesPerPeriod )
|
||||
{
|
||||
int periodElapsed = m_periodTimer.elapsed();
|
||||
|
||||
const float newCpuLoad = periodElapsed / 10000.0f * sampleRate / framesPerPeriod;
|
||||
m_cpuLoad = qBound<int>( 0, ( newCpuLoad * 0.1f + m_cpuLoad * 0.9f ), 100 );
|
||||
|
||||
if( m_outputFile.isOpen() )
|
||||
{
|
||||
m_outputFile.write( QString( "%1\n" ).arg( periodElapsed ).toLatin1() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MixerProfiler::setOutputFile( const QString& outputFile )
|
||||
{
|
||||
m_outputFile.close();
|
||||
m_outputFile.setFileName( outputFile );
|
||||
m_outputFile.open( QFile::WriteOnly | QFile::Truncate );
|
||||
}
|
||||
|
||||
174
src/core/MixerWorkerThread.cpp
Normal file
174
src/core/MixerWorkerThread.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* MixerWorkerThread.cpp - implementation of MixerWorkerThread
|
||||
*
|
||||
* Copyright (c) 2009-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* 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 "MixerWorkerThread.h"
|
||||
#include "engine.h"
|
||||
|
||||
|
||||
MixerWorkerThread::JobQueue MixerWorkerThread::globalJobQueue;
|
||||
QWaitCondition * MixerWorkerThread::queueReadyWaitCond = NULL;
|
||||
QList<MixerWorkerThread *> MixerWorkerThread::workerThreads;
|
||||
|
||||
|
||||
|
||||
// implementation of internal JobQueue
|
||||
void MixerWorkerThread::JobQueue::reset( OperationMode _opMode )
|
||||
{
|
||||
m_queueSize = 0;
|
||||
m_itemsDone = 0;
|
||||
m_opMode = _opMode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void MixerWorkerThread::JobQueue::addJob( ThreadableJob * _job )
|
||||
{
|
||||
if( _job->requiresProcessing() )
|
||||
{
|
||||
// update job state
|
||||
_job->queue();
|
||||
// actually queue the job via atomic operations
|
||||
m_items[m_queueSize.fetchAndAddOrdered(1)] = _job;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MixerWorkerThread::JobQueue::run( sampleFrame * _buffer )
|
||||
{
|
||||
bool processedJob = true;
|
||||
while( processedJob && (int) m_itemsDone < (int) m_queueSize )
|
||||
{
|
||||
processedJob = false;
|
||||
for( int i = 0; i < m_queueSize; ++i )
|
||||
{
|
||||
ThreadableJob * job = m_items[i].fetchAndStoreOrdered( NULL );
|
||||
if( job )
|
||||
{
|
||||
job->process( _buffer );
|
||||
processedJob = true;
|
||||
m_itemsDone.fetchAndAddOrdered( 1 );
|
||||
}
|
||||
}
|
||||
// always exit loop if we're not in dynamic mode
|
||||
processedJob = processedJob && ( m_opMode == Dynamic );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void MixerWorkerThread::JobQueue::wait()
|
||||
{
|
||||
while( (int) m_itemsDone < (int) m_queueSize )
|
||||
{
|
||||
#if defined(LMMS_HOST_X86) || defined(LMMS_HOST_X86_64)
|
||||
asm( "pause" );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// implementation of worker threads
|
||||
|
||||
MixerWorkerThread::MixerWorkerThread( Mixer* mixer ) :
|
||||
QThread( mixer ),
|
||||
m_workingBuf( new sampleFrame[mixer->framesPerPeriod()] ),
|
||||
m_quit( false )
|
||||
{
|
||||
// initialize global static data
|
||||
if( queueReadyWaitCond == NULL )
|
||||
{
|
||||
queueReadyWaitCond = new QWaitCondition;
|
||||
}
|
||||
|
||||
// keep track of all instantiated worker threads - this is used for
|
||||
// processing the last worker thread "inline", see comments in
|
||||
// MixerWorkerThread::startAndWaitForJobs() for details
|
||||
workerThreads << this;
|
||||
|
||||
resetJobQueue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
MixerWorkerThread::~MixerWorkerThread()
|
||||
{
|
||||
delete[] m_workingBuf;
|
||||
|
||||
workerThreads.removeAll( this );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void MixerWorkerThread::quit()
|
||||
{
|
||||
m_quit = true;
|
||||
resetJobQueue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void MixerWorkerThread::startAndWaitForJobs()
|
||||
{
|
||||
queueReadyWaitCond->wakeAll();
|
||||
// The last worker-thread is never started. Instead it's processed "inline"
|
||||
// i.e. within the global Mixer thread. This way we can reduce latencies
|
||||
// that otherwise would be caused by synchronizing with another thread.
|
||||
globalJobQueue.run( workerThreads.last()->m_workingBuf );
|
||||
globalJobQueue.wait();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void MixerWorkerThread::run()
|
||||
{
|
||||
// set denormal protection for this thread
|
||||
#ifdef __SSE3__
|
||||
/* DAZ flag */
|
||||
_MM_SET_DENORMALS_ZERO_MODE( _MM_DENORMALS_ZERO_ON );
|
||||
#endif
|
||||
#ifdef __SSE__
|
||||
/* FTZ flag */
|
||||
_MM_SET_FLUSH_ZERO_MODE( _MM_FLUSH_ZERO_ON );
|
||||
#endif
|
||||
QMutex m;
|
||||
while( m_quit == false )
|
||||
{
|
||||
m.lock();
|
||||
queueReadyWaitCond->wait( &m );
|
||||
globalJobQueue.run( m_workingBuf );
|
||||
m.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2007-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -49,7 +49,6 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
const f_cnt_t _frames,
|
||||
const note& n,
|
||||
NotePlayHandle *parent,
|
||||
const bool _part_of_arp,
|
||||
int midiEventChannel,
|
||||
Origin origin ) :
|
||||
PlayHandle( TypeNotePlayHandle, _offset ),
|
||||
@@ -62,13 +61,14 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
m_framesBeforeRelease( 0 ),
|
||||
m_releaseFramesToDo( 0 ),
|
||||
m_releaseFramesDone( 0 ),
|
||||
m_scheduledNoteOff( -1 ),
|
||||
m_released( false ),
|
||||
m_topNote( parent == NULL ),
|
||||
m_partOfArpeggio( _part_of_arp ),
|
||||
m_hasParent( parent != NULL ),
|
||||
m_hadChildren( false ),
|
||||
m_muted( false ),
|
||||
m_bbTrack( NULL ),
|
||||
m_origTempo( engine::getSong()->getTempo() ),
|
||||
m_origBaseNote( instrumentTrack->baseNoteModel()->value() ),
|
||||
m_origBaseNote( instrumentTrack->baseNote() ),
|
||||
m_frequency( 0 ),
|
||||
m_unpitchedFrequency( 0 ),
|
||||
m_baseDetuning( NULL ),
|
||||
@@ -76,7 +76,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
m_midiChannel( midiEventChannel >= 0 ? midiEventChannel : instrumentTrack->midiPort()->realOutputChannel() ),
|
||||
m_origin( origin )
|
||||
{
|
||||
if( isTopNote() )
|
||||
if( hasParent() == false )
|
||||
{
|
||||
m_baseDetuning = new BaseDetuning( detuning() );
|
||||
m_instrumentTrack->m_processHandles.push_back( this );
|
||||
@@ -86,10 +86,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
m_baseDetuning = parent->m_baseDetuning;
|
||||
|
||||
parent->m_subNotes.push_back( this );
|
||||
// if there was an arp-note added and parent is a base-note
|
||||
// we set arp-note-flag for indicating that parent is an
|
||||
// arpeggio-base-note
|
||||
parent->m_partOfArpeggio = isPartOfArpeggio() && parent->isTopNote();
|
||||
parent->m_hadChildren = true;
|
||||
|
||||
m_bbTrack = parent->m_bbTrack;
|
||||
}
|
||||
@@ -104,14 +101,15 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
m_instrumentTrack->midiNoteOn( *this );
|
||||
}
|
||||
|
||||
if( !isTopNote() || !instrumentTrack->isArpeggioEnabled() )
|
||||
if( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() )
|
||||
{
|
||||
const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity();
|
||||
|
||||
// send MidiNoteOn event
|
||||
m_instrumentTrack->processOutEvent(
|
||||
MidiEvent( MidiNoteOn, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ),
|
||||
MidiTime::fromFrames( offset(), engine::framesPerTick() ) );
|
||||
MidiTime::fromFrames( offset(), engine::framesPerTick() ),
|
||||
offset() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +119,15 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
NotePlayHandle::~NotePlayHandle()
|
||||
{
|
||||
noteOff( 0 );
|
||||
|
||||
if( isTopNote() )
|
||||
if( m_scheduledNoteOff >= 0 ) // ensure that scheduled noteoffs get triggered if somehow the nph got destructed prematurely
|
||||
{
|
||||
m_instrumentTrack->processOutEvent(
|
||||
MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),
|
||||
MidiTime::fromFrames( m_scheduledNoteOff, engine::framesPerTick() ),
|
||||
m_scheduledNoteOff );
|
||||
}
|
||||
|
||||
if( hasParent() == false )
|
||||
{
|
||||
delete m_baseDetuning;
|
||||
m_instrumentTrack->m_processHandles.removeAll( this );
|
||||
@@ -177,7 +182,7 @@ void NotePlayHandle::setPanning( panning_t panning )
|
||||
|
||||
int NotePlayHandle::midiKey() const
|
||||
{
|
||||
return key() - m_origBaseNote + instrumentTrack()->baseNoteModel()->value();
|
||||
return key() - m_origBaseNote + instrumentTrack()->baseNote();
|
||||
}
|
||||
|
||||
|
||||
@@ -185,6 +190,22 @@ int NotePlayHandle::midiKey() const
|
||||
|
||||
void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
{
|
||||
if( m_scheduledNoteOff >= 0 ) // always trigger scheduled noteoffs, because they're only scheduled if the note is released
|
||||
{
|
||||
if( m_scheduledNoteOff < engine::mixer()->framesPerPeriod() )
|
||||
{
|
||||
m_instrumentTrack->processOutEvent(
|
||||
MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),
|
||||
MidiTime::fromFrames( m_scheduledNoteOff, engine::framesPerTick() ),
|
||||
m_scheduledNoteOff );
|
||||
m_scheduledNoteOff = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_scheduledNoteOff -= engine::mixer()->framesPerPeriod();
|
||||
}
|
||||
}
|
||||
|
||||
if( m_muted )
|
||||
{
|
||||
return;
|
||||
@@ -192,7 +213,7 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
|
||||
if( m_released == false &&
|
||||
instrumentTrack()->isSustainPedalPressed() == false &&
|
||||
m_totalFramesPlayed + engine::mixer()->framesPerPeriod() >= m_frames )
|
||||
m_totalFramesPlayed + engine::mixer()->framesPerPeriod() > m_frames )
|
||||
{
|
||||
noteOff( m_frames - m_totalFramesPlayed );
|
||||
}
|
||||
@@ -209,12 +230,13 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
if( m_released )
|
||||
{
|
||||
f_cnt_t todo = engine::mixer()->framesPerPeriod();
|
||||
|
||||
// if this note is base-note for arpeggio, always set
|
||||
// m_releaseFramesToDo to bigger value than m_releaseFramesDone
|
||||
// because we do not allow NotePlayHandle::isFinished() to be true
|
||||
// until all sub-notes are completely played and no new ones
|
||||
// are inserted by arpAndChordsTabWidget::processNote()
|
||||
if( isArpeggioBaseNote() )
|
||||
if( ! m_subNotes.isEmpty() )
|
||||
{
|
||||
m_releaseFramesToDo = m_releaseFramesDone + 2 * engine::mixer()->framesPerPeriod();
|
||||
}
|
||||
@@ -274,16 +296,6 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
}
|
||||
}
|
||||
|
||||
// if this note is a base-note and there're no more sub-notes left we
|
||||
// can set m_releaseFramesDone to m_releaseFramesToDo so that
|
||||
// NotePlayHandle::isFinished() returns true and also this base-note is
|
||||
// removed from mixer's active note vector
|
||||
if( m_released && isArpeggioBaseNote() && m_subNotes.size() == 0 )
|
||||
{
|
||||
m_releaseFramesDone = m_releaseFramesToDo;
|
||||
m_frames = 0;
|
||||
}
|
||||
|
||||
// update internal data
|
||||
m_totalFramesPlayed += engine::mixer()->framesPerPeriod();
|
||||
}
|
||||
@@ -301,9 +313,9 @@ f_cnt_t NotePlayHandle::framesLeft() const
|
||||
{
|
||||
return m_framesBeforeRelease;
|
||||
}
|
||||
else if( m_released && actualReleaseFramesToDo() >= m_releaseFramesDone )
|
||||
else if( m_released )
|
||||
{
|
||||
return m_framesBeforeRelease + actualReleaseFramesToDo() - m_releaseFramesDone;
|
||||
return m_framesBeforeRelease + m_releaseFramesToDo - m_releaseFramesDone;
|
||||
}
|
||||
return m_frames+actualReleaseFramesToDo()-m_totalFramesPlayed;
|
||||
}
|
||||
@@ -342,14 +354,23 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )
|
||||
|
||||
// then set some variables indicating release-state
|
||||
m_framesBeforeRelease = _s;
|
||||
m_releaseFramesToDo = qMax<f_cnt_t>( 0, m_instrumentTrack->m_soundShaping.releaseFrames() );
|
||||
m_releaseFramesToDo = qMax<f_cnt_t>( 0, actualReleaseFramesToDo() );
|
||||
|
||||
if( !isTopNote() || !instrumentTrack()->isArpeggioEnabled() )
|
||||
if( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() )
|
||||
{
|
||||
// send MidiNoteOff event
|
||||
m_instrumentTrack->processOutEvent(
|
||||
MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),
|
||||
MidiTime::fromFrames( m_framesBeforeRelease, engine::framesPerTick() ) );
|
||||
f_cnt_t realOffset = offset() + _s; // get actual frameoffset of release, in global time
|
||||
if( realOffset < engine::mixer()->framesPerPeriod() ) // if release happens during this period, trigger midievent
|
||||
{
|
||||
m_instrumentTrack->processOutEvent(
|
||||
MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),
|
||||
MidiTime::fromFrames( realOffset, engine::framesPerTick() ),
|
||||
realOffset );
|
||||
}
|
||||
else // if release flows over to next period, use m_scheduledNoteOff to trigger it later
|
||||
{
|
||||
m_scheduledNoteOff = realOffset - engine::mixer()->framesPerPeriod();
|
||||
}
|
||||
}
|
||||
|
||||
// inform attached components about MIDI finished (used for recording in Piano Roll)
|
||||
@@ -367,8 +388,7 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )
|
||||
|
||||
f_cnt_t NotePlayHandle::actualReleaseFramesToDo() const
|
||||
{
|
||||
return m_instrumentTrack->m_soundShaping.releaseFrames(/*
|
||||
isArpeggioBaseNote()*/ );
|
||||
return m_instrumentTrack->m_soundShaping.releaseFrames();
|
||||
}
|
||||
|
||||
|
||||
@@ -395,19 +415,10 @@ float NotePlayHandle::volumeLevel( const f_cnt_t _frame )
|
||||
|
||||
|
||||
|
||||
bool NotePlayHandle::isArpeggioBaseNote() const
|
||||
{
|
||||
return isTopNote() && ( m_partOfArpeggio || m_instrumentTrack->isArpeggioEnabled() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void NotePlayHandle::mute()
|
||||
{
|
||||
// mute all sub-notes
|
||||
for( NotePlayHandleList::Iterator it = m_subNotes.begin();
|
||||
it != m_subNotes.end(); ++it )
|
||||
for( NotePlayHandleList::Iterator it = m_subNotes.begin(); it != m_subNotes.end(); ++it )
|
||||
{
|
||||
( *it )->mute();
|
||||
}
|
||||
@@ -471,10 +482,11 @@ bool NotePlayHandle::operator==( const NotePlayHandle & _nph ) const
|
||||
offset() == _nph.offset() &&
|
||||
m_totalFramesPlayed == _nph.m_totalFramesPlayed &&
|
||||
m_released == _nph.m_released &&
|
||||
m_topNote == _nph.m_topNote &&
|
||||
m_partOfArpeggio == _nph.m_partOfArpeggio &&
|
||||
m_hasParent == _nph.m_hasParent &&
|
||||
m_origBaseNote == _nph.m_origBaseNote &&
|
||||
m_muted == _nph.m_muted;
|
||||
m_muted == _nph.m_muted &&
|
||||
m_midiChannel == _nph.m_midiChannel &&
|
||||
m_origin == _nph.m_origin;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* for testing + according model class
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -31,7 +31,7 @@
|
||||
* \mainpage Instrument plugin keyboard display classes
|
||||
*
|
||||
* \section introduction Introduction
|
||||
*
|
||||
*
|
||||
* \todo fill this out
|
||||
* \todo write isWhite inline function and replace throughout
|
||||
*/
|
||||
@@ -95,9 +95,13 @@ void Piano::setKeyState( int key, bool state )
|
||||
*/
|
||||
void Piano::handleKeyPress( int key, int midiVelocity )
|
||||
{
|
||||
if( midiVelocity == -1 )
|
||||
{
|
||||
midiVelocity = m_instrumentTrack->midiPort()->baseVelocity();
|
||||
}
|
||||
if( isValidKey( key ) )
|
||||
{
|
||||
m_midiEvProc->processInEvent( MidiEvent( MidiNoteOn, 0, key, midiVelocity ) );
|
||||
m_midiEvProc->processInEvent( MidiEvent( MidiNoteOn, -1, key, midiVelocity ) );
|
||||
m_pressedKeys[key] = true;
|
||||
}
|
||||
}
|
||||
@@ -114,7 +118,7 @@ void Piano::handleKeyRelease( int key )
|
||||
{
|
||||
if( isValidKey( key ) )
|
||||
{
|
||||
m_midiEvProc->processInEvent( MidiEvent( MidiNoteOff, 0, key, 0 ) );
|
||||
m_midiEvProc->processInEvent( MidiEvent( MidiNoteOff, -1, key, 0 ) );
|
||||
m_pressedKeys[key] = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#ifndef SINGLE_SOURCE_COMPILE
|
||||
|
||||
/*
|
||||
* Plugin.cpp - implementation of plugin-class including plugin-loader
|
||||
*
|
||||
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -24,6 +22,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QLibrary>
|
||||
#include <QtGui/QMessageBox>
|
||||
@@ -54,9 +53,9 @@ static Plugin::Descriptor dummy_plugin_descriptor =
|
||||
|
||||
|
||||
|
||||
Plugin::Plugin( const Descriptor * _descriptor, Model * _parent ) :
|
||||
Plugin::Plugin( const Descriptor * _descriptor, Model * parent ) :
|
||||
JournallingObject(),
|
||||
Model( _parent ),
|
||||
Model( parent ),
|
||||
m_descriptor( _descriptor )
|
||||
{
|
||||
if( m_descriptor == NULL )
|
||||
@@ -91,89 +90,80 @@ AutomatableModel * Plugin::childModel( const QString & )
|
||||
|
||||
|
||||
|
||||
Plugin * Plugin::instantiate( const QString & _plugin_name, Model * _parent,
|
||||
void * _data )
|
||||
Plugin * Plugin::instantiate( const QString & pluginName, Model * parent,
|
||||
void * data )
|
||||
{
|
||||
QLibrary plugin_lib( configManager::inst()->pluginDir() +
|
||||
_plugin_name );
|
||||
if( plugin_lib.load() == false )
|
||||
QLibrary pluginLibrary( configManager::inst()->pluginDir() + pluginName );
|
||||
if( pluginLibrary.load() == false )
|
||||
{
|
||||
if( engine::hasGUI() )
|
||||
{
|
||||
QMessageBox::information( NULL,
|
||||
tr( "Plugin not found" ),
|
||||
tr( "The plugin \"%1\" wasn't found "
|
||||
"or could not be loaded!\n"
|
||||
"Reason: \"%2\"" ).arg( _plugin_name ).
|
||||
arg( plugin_lib.errorString() ),
|
||||
QMessageBox::Ok |
|
||||
QMessageBox::Default );
|
||||
tr( "The plugin \"%1\" wasn't found or could not be loaded!\nReason: \"%2\"" ).
|
||||
arg( pluginName ).arg( pluginLibrary.errorString() ),
|
||||
QMessageBox::Ok | QMessageBox::Default );
|
||||
}
|
||||
return new DummyPlugin();
|
||||
}
|
||||
instantiationHook inst_hook = ( instantiationHook ) plugin_lib.resolve(
|
||||
"lmms_plugin_main" );
|
||||
if( inst_hook == NULL )
|
||||
|
||||
InstantiationHook instantiationHook = ( InstantiationHook ) pluginLibrary.resolve( "lmms_plugin_main" );
|
||||
if( instantiationHook == NULL )
|
||||
{
|
||||
if( engine::hasGUI() )
|
||||
{
|
||||
QMessageBox::information( NULL,
|
||||
tr( "Error while loading plugin" ),
|
||||
tr( "Failed to load plugin \"%1\"!"
|
||||
).arg( _plugin_name ),
|
||||
QMessageBox::Ok |
|
||||
QMessageBox::Default );
|
||||
tr( "Failed to load plugin \"%1\"!").arg( pluginName ),
|
||||
QMessageBox::Ok | QMessageBox::Default );
|
||||
}
|
||||
return new DummyPlugin();
|
||||
}
|
||||
Plugin * inst = inst_hook( _parent, _data );
|
||||
|
||||
Plugin * inst = instantiationHook( parent, data );
|
||||
return inst;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Plugin::getDescriptorsOfAvailPlugins( DescriptorList & _plugin_descs )
|
||||
void Plugin::getDescriptorsOfAvailPlugins( DescriptorList& pluginDescriptors )
|
||||
{
|
||||
QDir directory( configManager::inst()->pluginDir() );
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
QFileInfoList list = directory.entryInfoList(
|
||||
QStringList( "*.dll" ) );
|
||||
QFileInfoList list = directory.entryInfoList( QStringList( "*.dll" ) );
|
||||
#else
|
||||
QFileInfoList list = directory.entryInfoList(
|
||||
QStringList( "lib*.so" ) );
|
||||
QFileInfoList list = directory.entryInfoList( QStringList( "lib*.so" ) );
|
||||
#endif
|
||||
foreach( const QFileInfo & f, list )
|
||||
foreach( const QFileInfo& f, list )
|
||||
{
|
||||
QLibrary( f.absoluteFilePath() ).load();
|
||||
}
|
||||
|
||||
foreach( const QFileInfo & f, list )
|
||||
foreach( const QFileInfo& f, list )
|
||||
{
|
||||
QLibrary plugin_lib( f.absoluteFilePath() );
|
||||
if( plugin_lib.load() == false ||
|
||||
plugin_lib.resolve( "lmms_plugin_main" ) == NULL )
|
||||
QLibrary pluginLibrary( f.absoluteFilePath() );
|
||||
if( pluginLibrary.load() == false ||
|
||||
pluginLibrary.resolve( "lmms_plugin_main" ) == NULL )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
QString desc_name = f.fileName().section( '.', 0, 0 ) +
|
||||
"_plugin_descriptor";
|
||||
if( desc_name.left( 3 ) == "lib" )
|
||||
|
||||
QString descriptorName = f.baseName() + "_plugin_descriptor";
|
||||
if( descriptorName.left( 3 ) == "lib" )
|
||||
{
|
||||
desc_name = desc_name.mid( 3 );
|
||||
descriptorName = descriptorName.mid( 3 );
|
||||
}
|
||||
Descriptor * plugin_desc =
|
||||
(Descriptor *) plugin_lib.resolve(
|
||||
desc_name.toUtf8().constData() );
|
||||
if( plugin_desc == NULL )
|
||||
|
||||
Descriptor* pluginDescriptor = (Descriptor *) pluginLibrary.resolve( descriptorName.toUtf8().constData() );
|
||||
if( pluginDescriptor == NULL )
|
||||
{
|
||||
printf( "LMMS plugin %s does not have a "
|
||||
"plugin descriptor named %s!\n",
|
||||
f.absoluteFilePath().toUtf8().constData(),
|
||||
desc_name.toUtf8().constData() );
|
||||
qWarning() << tr( "LMMS plugin %1 does not have a plugin descriptor named %2!" ).
|
||||
arg( f.absoluteFilePath() ).arg( descriptorName );
|
||||
continue;
|
||||
}
|
||||
_plugin_descs.push_back( *plugin_desc );
|
||||
|
||||
pluginDescriptors += *pluginDescriptor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -181,9 +171,9 @@ void Plugin::getDescriptorsOfAvailPlugins( DescriptorList & _plugin_descs )
|
||||
|
||||
|
||||
|
||||
PluginView * Plugin::createView( QWidget * _parent )
|
||||
PluginView * Plugin::createView( QWidget * parent )
|
||||
{
|
||||
PluginView * pv = instantiateView( _parent );
|
||||
PluginView * pv = instantiateView( parent );
|
||||
if( pv != NULL )
|
||||
{
|
||||
pv->setModel( this );
|
||||
@@ -227,4 +217,3 @@ QDomElement Plugin::Descriptor::SubPluginFeatures::Key::saveXML(
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* ProjectJournal.cpp - implementation of ProjectJournal
|
||||
*
|
||||
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -29,11 +29,12 @@
|
||||
#include "JournallingObject.h"
|
||||
#include "song.h"
|
||||
|
||||
const int ProjectJournal::MAX_UNDO_STATES = 100; // TODO: make this configurable in settings
|
||||
|
||||
ProjectJournal::ProjectJournal() :
|
||||
m_joIDs(),
|
||||
m_journalEntries(),
|
||||
m_currentJournalEntry( m_journalEntries.end() ),
|
||||
m_undoCheckPoints(),
|
||||
m_redoCheckPoints(),
|
||||
m_journalling( false )
|
||||
{
|
||||
}
|
||||
@@ -50,51 +51,70 @@ ProjectJournal::~ProjectJournal()
|
||||
|
||||
void ProjectJournal::undo()
|
||||
{
|
||||
if( m_journalEntries.empty() == true )
|
||||
while( !m_undoCheckPoints.isEmpty() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
CheckPoint c = m_undoCheckPoints.pop();
|
||||
JournallingObject *jo = m_joIDs[c.joID];
|
||||
|
||||
JournallingObject * jo;
|
||||
if( jo )
|
||||
{
|
||||
DataFile curState( DataFile::JournalData );
|
||||
jo->saveState( curState, curState.content() );
|
||||
m_redoCheckPoints.push( CheckPoint( c.joID, curState ) );
|
||||
|
||||
if( m_currentJournalEntry - 1 >= m_journalEntries.begin() &&
|
||||
( jo = m_joIDs[*--m_currentJournalEntry] ) != NULL )
|
||||
{
|
||||
jo->undo();
|
||||
engine::getSong()->setModified();
|
||||
bool prev = isJournalling();
|
||||
setJournalling( false );
|
||||
jo->restoreState( c.data.content().firstChildElement() );
|
||||
setJournalling( prev );
|
||||
engine::getSong()->setModified();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void ProjectJournal::redo()
|
||||
{
|
||||
if( m_journalEntries.empty() == true )
|
||||
while( !m_redoCheckPoints.isEmpty() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
CheckPoint c = m_redoCheckPoints.pop();
|
||||
JournallingObject *jo = m_joIDs[c.joID];
|
||||
|
||||
JournallingObject * jo;
|
||||
if( jo )
|
||||
{
|
||||
DataFile curState( DataFile::JournalData );
|
||||
jo->saveState( curState, curState.content() );
|
||||
m_undoCheckPoints.push( CheckPoint( c.joID, curState ) );
|
||||
|
||||
//printf("%d\n", m_joIDs[*(m_currentJournalEntry+1)] );
|
||||
if( m_currentJournalEntry < m_journalEntries.end() &&
|
||||
( jo = m_joIDs[*m_currentJournalEntry++] ) != NULL )
|
||||
{
|
||||
jo->redo();
|
||||
engine::getSong()->setModified();
|
||||
bool prev = isJournalling();
|
||||
setJournalling( false );
|
||||
jo->restoreState( c.data.content().firstChildElement() );
|
||||
setJournalling( prev );
|
||||
engine::getSong()->setModified();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void ProjectJournal::journalEntryAdded( const jo_id_t _id )
|
||||
void ProjectJournal::addJournalCheckPoint( JournallingObject *jo )
|
||||
{
|
||||
m_journalEntries.erase( m_currentJournalEntry, m_journalEntries.end() );
|
||||
m_journalEntries.push_back( _id );
|
||||
m_currentJournalEntry = m_journalEntries.end();
|
||||
engine::getSong()->setModified();
|
||||
if( isJournalling() )
|
||||
{
|
||||
m_redoCheckPoints.clear();
|
||||
|
||||
DataFile dataFile( DataFile::JournalData );
|
||||
jo->saveState( dataFile, dataFile.content() );
|
||||
|
||||
m_undoCheckPoints.push( CheckPoint( jo->id(), dataFile ) );
|
||||
if( m_undoCheckPoints.size() > MAX_UNDO_STATES )
|
||||
{
|
||||
m_undoCheckPoints.remove( 0, m_undoCheckPoints.size() - MAX_UNDO_STATES );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +125,7 @@ jo_id_t ProjectJournal::allocID( JournallingObject * _obj )
|
||||
const jo_id_t EO_ID_MAX = (1 << 23)-1;
|
||||
jo_id_t id;
|
||||
while( m_joIDs.contains( id =
|
||||
static_cast<jo_id_t>( (jo_id_t)rand()*(jo_id_t)rand() %
|
||||
static_cast<jo_id_t>( (jo_id_t)rand()*(jo_id_t)rand() %
|
||||
EO_ID_MAX ) ) )
|
||||
{
|
||||
}
|
||||
@@ -130,29 +150,11 @@ void ProjectJournal::reallocID( const jo_id_t _id, JournallingObject * _obj )
|
||||
|
||||
|
||||
|
||||
void ProjectJournal::forgetAboutID( const jo_id_t _id )
|
||||
{
|
||||
//printf("forget about %d\n", _id );
|
||||
JournalEntryVector::Iterator it;
|
||||
while( ( it = qFind( m_journalEntries.begin(), m_journalEntries.end(),
|
||||
_id ) ) != m_journalEntries.end() )
|
||||
{
|
||||
if( m_currentJournalEntry >= it )
|
||||
{
|
||||
--m_currentJournalEntry;
|
||||
}
|
||||
m_journalEntries.erase( it );
|
||||
}
|
||||
m_joIDs.remove( _id );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void ProjectJournal::clearJournal()
|
||||
{
|
||||
m_journalEntries.clear();
|
||||
m_currentJournalEntry = m_journalEntries.end();
|
||||
m_undoCheckPoints.clear();
|
||||
m_redoCheckPoints.clear();
|
||||
|
||||
for( JoIdMap::Iterator it = m_joIDs.begin(); it != m_joIDs.end(); )
|
||||
{
|
||||
if( it.value() == NULL )
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
|
||||
#include "SampleBuffer.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
@@ -186,6 +185,7 @@ void SampleBuffer::update( bool _keep_settings )
|
||||
char * f = qstrdup( file.toUtf8().constData() );
|
||||
#endif
|
||||
int_sample_t * buf = NULL;
|
||||
sample_t * fbuf = NULL;
|
||||
ch_cnt_t channels = DEFAULT_CHANNELS;
|
||||
sample_rate_t samplerate = engine::mixer()->baseSampleRate();
|
||||
m_frames = 0;
|
||||
@@ -210,7 +210,7 @@ void SampleBuffer::update( bool _keep_settings )
|
||||
#endif
|
||||
if( m_frames == 0 )
|
||||
{
|
||||
m_frames = decodeSampleSF( f, buf, channels,
|
||||
m_frames = decodeSampleSF( f, fbuf, channels,
|
||||
samplerate );
|
||||
}
|
||||
#ifdef LMMS_HAVE_OGGVORBIS
|
||||
@@ -272,8 +272,7 @@ void SampleBuffer::convertIntToFloat ( int_sample_t * & _ibuf, f_cnt_t _frames,
|
||||
{
|
||||
// following code transforms int-samples into
|
||||
// float-samples and does amplifying & reversing
|
||||
const float fac = m_amplification /
|
||||
OUTPUT_SAMPLE_MULTIPLIER;
|
||||
const float fac = 1 / OUTPUT_SAMPLE_MULTIPLIER;
|
||||
m_data = new sampleFrame[_frames];
|
||||
const int ch = ( _channels > 1 ) ? 1 : 0;
|
||||
|
||||
@@ -377,7 +376,7 @@ void SampleBuffer::normalizeSampleRate( const sample_rate_t _src_sr,
|
||||
|
||||
|
||||
f_cnt_t SampleBuffer::decodeSampleSF( const char * _f,
|
||||
int_sample_t * & _buf,
|
||||
sample_t * & _buf,
|
||||
ch_cnt_t & _channels,
|
||||
sample_rate_t & _samplerate )
|
||||
{
|
||||
@@ -385,29 +384,19 @@ f_cnt_t SampleBuffer::decodeSampleSF( const char * _f,
|
||||
SF_INFO sf_info;
|
||||
f_cnt_t frames = 0;
|
||||
bool sf_rr = false;
|
||||
sample_t * fbuf = 0;
|
||||
|
||||
if( ( snd_file = sf_open( _f, SFM_READ, &sf_info ) ) != NULL )
|
||||
{
|
||||
frames = sf_info.frames;
|
||||
|
||||
// check if float
|
||||
if ( (sf_info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ) // if yes, use float format for buffer
|
||||
{
|
||||
fbuf = new sample_t[sf_info.channels * frames];
|
||||
sf_rr = sf_read_float( snd_file, fbuf, sf_info.channels * frames );
|
||||
}
|
||||
else // otherwise, use int
|
||||
{
|
||||
_buf = new int_sample_t[sf_info.channels * frames];
|
||||
sf_rr = sf_read_short( snd_file, _buf, sf_info.channels * frames );
|
||||
}
|
||||
_buf = new sample_t[sf_info.channels * frames];
|
||||
sf_rr = sf_read_float( snd_file, _buf, sf_info.channels * frames );
|
||||
|
||||
if( sf_rr < sf_info.channels * frames )
|
||||
{
|
||||
#ifdef DEBUG_LMMS
|
||||
printf( "SampleBuffer::decodeSampleSF(): could not read"
|
||||
" sample %s: %s\n", _f, sf_strerror( NULL ) );
|
||||
qDebug( "SampleBuffer::decodeSampleSF(): could not read"
|
||||
" sample %s: %s", _f, sf_strerror( NULL ) );
|
||||
#endif
|
||||
}
|
||||
_channels = sf_info.channels;
|
||||
@@ -418,19 +407,15 @@ f_cnt_t SampleBuffer::decodeSampleSF( const char * _f,
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG_LMMS
|
||||
printf( "SampleBuffer::decodeSampleSF(): could not load "
|
||||
"sample %s: %s\n", _f, sf_strerror( NULL ) );
|
||||
qDebug( "SampleBuffer::decodeSampleSF(): could not load "
|
||||
"sample %s: %s", _f, sf_strerror( NULL ) );
|
||||
#endif
|
||||
}
|
||||
//write down either directly or convert i->f depending on file type
|
||||
|
||||
if ( frames > 0 && fbuf != NULL )
|
||||
if ( frames > 0 && _buf != NULL )
|
||||
{
|
||||
directFloatWrite ( fbuf, frames, _channels);
|
||||
}
|
||||
else if ( frames > 0 && _buf != NULL )
|
||||
{
|
||||
convertIntToFloat ( _buf, frames, _channels);
|
||||
directFloatWrite ( _buf, frames, _channels);
|
||||
}
|
||||
|
||||
return frames;
|
||||
@@ -610,60 +595,66 @@ f_cnt_t SampleBuffer::decodeSampleDS( const char * _f,
|
||||
bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
const fpp_t _frames,
|
||||
const float _freq,
|
||||
const bool _looped )
|
||||
const LoopMode _loopmode )
|
||||
{
|
||||
QMutexLocker ml( &m_varLock );
|
||||
|
||||
engine::mixer()->clearAudioBuffer( _ab, _frames );
|
||||
f_cnt_t startFrame = m_startFrame;
|
||||
f_cnt_t endFrame = m_endFrame;
|
||||
f_cnt_t loopStartFrame = m_loopStartFrame;
|
||||
f_cnt_t loopEndFrame = m_loopEndFrame;
|
||||
|
||||
if( m_endFrame == 0 || _frames == 0 )
|
||||
if( endFrame == 0 || _frames == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// variable for determining if we should currently be playing backwards in a ping-pong loop
|
||||
bool is_backwards = _state->isBackwards();
|
||||
|
||||
const double freq_factor = (double) _freq / (double) m_frequency *
|
||||
m_sampleRate / engine::mixer()->processingSampleRate();
|
||||
|
||||
// calculate how many frames we have in requested pitch
|
||||
const f_cnt_t total_frames_for_current_pitch = static_cast<f_cnt_t>( (
|
||||
m_endFrame - m_startFrame ) /
|
||||
endFrame - startFrame ) /
|
||||
freq_factor );
|
||||
|
||||
if( total_frames_for_current_pitch == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// this holds the number of the first frame to play
|
||||
f_cnt_t play_frame = _state->m_frameIndex;
|
||||
if( play_frame < m_startFrame )
|
||||
|
||||
if( play_frame < startFrame )
|
||||
{
|
||||
play_frame = m_startFrame;
|
||||
play_frame = startFrame;
|
||||
}
|
||||
|
||||
// this holds the number of remaining frames in current loop
|
||||
f_cnt_t frames_for_loop;
|
||||
if( _looped )
|
||||
if( _loopmode == LoopOff )
|
||||
{
|
||||
play_frame = getLoopedIndex( play_frame );
|
||||
frames_for_loop = static_cast<f_cnt_t>(
|
||||
( m_loopEndFrame - play_frame ) /
|
||||
freq_factor );
|
||||
if( play_frame >= endFrame )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( ( endFrame - play_frame ) / freq_factor == 0 ) return false;
|
||||
}
|
||||
|
||||
else if( _loopmode == LoopOn )
|
||||
{
|
||||
play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame );
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if( play_frame >= m_endFrame )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
frames_for_loop = static_cast<f_cnt_t>(
|
||||
( m_endFrame - play_frame ) /
|
||||
freq_factor );
|
||||
if( frames_for_loop == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame );
|
||||
}
|
||||
|
||||
|
||||
sampleFrame * tmp = NULL;
|
||||
|
||||
// check whether we have to change pitch...
|
||||
@@ -671,11 +662,10 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
{
|
||||
SRC_DATA src_data;
|
||||
// Generate output
|
||||
const f_cnt_t margin = 64;
|
||||
f_cnt_t fragment_size = (f_cnt_t)( _frames * freq_factor )
|
||||
+ margin;
|
||||
src_data.data_in = getSampleFragment( play_frame,
|
||||
fragment_size, _looped, &tmp )[0];
|
||||
f_cnt_t fragment_size = (f_cnt_t)( _frames * freq_factor ) + MARGIN[ _state->interpolationMode() ];
|
||||
src_data.data_in =
|
||||
getSampleFragment( play_frame, fragment_size, _loopmode, &tmp, &is_backwards,
|
||||
loopStartFrame, loopEndFrame, endFrame )[0];
|
||||
src_data.data_out = _ab[0];
|
||||
src_data.input_frames = fragment_size;
|
||||
src_data.output_frames = _frames;
|
||||
@@ -694,10 +684,32 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
src_data.output_frames_gen, _frames );
|
||||
}
|
||||
// Advance
|
||||
play_frame += src_data.input_frames_used;
|
||||
if( _looped )
|
||||
switch( _loopmode )
|
||||
{
|
||||
play_frame = getLoopedIndex( play_frame );
|
||||
case LoopOff:
|
||||
play_frame += src_data.input_frames_used;
|
||||
break;
|
||||
case LoopOn:
|
||||
play_frame += src_data.input_frames_used;
|
||||
play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame );
|
||||
break;
|
||||
case LoopPingPong:
|
||||
{
|
||||
f_cnt_t left = src_data.input_frames_used;
|
||||
if( _state->isBackwards() )
|
||||
{
|
||||
play_frame -= src_data.input_frames_used;
|
||||
if( play_frame < loopStartFrame )
|
||||
{
|
||||
left -= ( loopStartFrame - play_frame );
|
||||
play_frame = loopStartFrame;
|
||||
}
|
||||
else left = 0;
|
||||
}
|
||||
play_frame += left;
|
||||
play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -707,19 +719,49 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
|
||||
// Generate output
|
||||
memcpy( _ab,
|
||||
getSampleFragment( play_frame, _frames, _looped, &tmp ),
|
||||
getSampleFragment( play_frame, _frames, _loopmode, &tmp, &is_backwards,
|
||||
loopStartFrame, loopEndFrame, endFrame ),
|
||||
_frames * BYTES_PER_FRAME );
|
||||
// Advance
|
||||
play_frame += _frames;
|
||||
if( _looped )
|
||||
switch( _loopmode )
|
||||
{
|
||||
play_frame = getLoopedIndex( play_frame );
|
||||
case LoopOff:
|
||||
play_frame += _frames;
|
||||
break;
|
||||
case LoopOn:
|
||||
play_frame += _frames;
|
||||
play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame );
|
||||
break;
|
||||
case LoopPingPong:
|
||||
{
|
||||
f_cnt_t left = _frames;
|
||||
if( _state->isBackwards() )
|
||||
{
|
||||
play_frame -= _frames;
|
||||
if( play_frame < loopStartFrame )
|
||||
{
|
||||
left -= ( loopStartFrame - play_frame );
|
||||
play_frame = loopStartFrame;
|
||||
}
|
||||
else left = 0;
|
||||
}
|
||||
play_frame += left;
|
||||
play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] tmp;
|
||||
if( tmp != NULL ) delete[] tmp;
|
||||
|
||||
_state->m_frameIndex = play_frame;
|
||||
_state->setBackwards( is_backwards );
|
||||
_state->setFrameIndex( play_frame );
|
||||
|
||||
for( fpp_t i = 0; i < _frames; ++i )
|
||||
{
|
||||
_ab[i][0] *= m_amplification;
|
||||
_ab[i][1] *= m_amplification;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -728,45 +770,103 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
|
||||
|
||||
|
||||
sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _start,
|
||||
f_cnt_t _frames, bool _looped, sampleFrame * * _tmp ) const
|
||||
sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _index,
|
||||
f_cnt_t _frames, LoopMode _loopmode, sampleFrame * * _tmp, bool * _backwards,
|
||||
f_cnt_t _loopstart, f_cnt_t _loopend, f_cnt_t _end ) const
|
||||
{
|
||||
if( _looped )
|
||||
if( _loopmode == LoopOff )
|
||||
{
|
||||
if( _start + _frames <= m_loopEndFrame )
|
||||
if( _index + _frames <= _end )
|
||||
{
|
||||
return m_data + _start;
|
||||
return m_data + _index;
|
||||
}
|
||||
}
|
||||
else if( _loopmode == LoopOn )
|
||||
{
|
||||
if( _index + _frames <= _loopend )
|
||||
{
|
||||
return m_data + _index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( _start + _frames <= m_endFrame )
|
||||
{
|
||||
return m_data + _start;
|
||||
}
|
||||
if( ! *_backwards && _index + _frames < _loopend )
|
||||
return m_data + _index;
|
||||
}
|
||||
|
||||
*_tmp = new sampleFrame[_frames];
|
||||
|
||||
if( _looped )
|
||||
if( _loopmode == LoopOff )
|
||||
{
|
||||
f_cnt_t copied = m_loopEndFrame - _start;
|
||||
memcpy( *_tmp, m_data + _start, copied * BYTES_PER_FRAME );
|
||||
f_cnt_t loop_frames = m_loopEndFrame - m_loopStartFrame;
|
||||
while( _frames - copied > 0 )
|
||||
f_cnt_t available = _end - _index;
|
||||
memcpy( *_tmp, m_data + _index, available * BYTES_PER_FRAME );
|
||||
memset( *_tmp + available, 0, ( _frames - available ) *
|
||||
BYTES_PER_FRAME );
|
||||
}
|
||||
else if( _loopmode == LoopOn )
|
||||
{
|
||||
f_cnt_t copied = qMin( _frames, _loopend - _index );
|
||||
memcpy( *_tmp, m_data + _index, copied * BYTES_PER_FRAME );
|
||||
f_cnt_t loop_frames = _loopend - _loopstart;
|
||||
while( copied < _frames )
|
||||
{
|
||||
f_cnt_t todo = qMin( _frames - copied, loop_frames );
|
||||
memcpy( *_tmp + copied, m_data + m_loopStartFrame,
|
||||
todo * BYTES_PER_FRAME );
|
||||
memcpy( *_tmp + copied, m_data + _loopstart, todo * BYTES_PER_FRAME );
|
||||
copied += todo;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
f_cnt_t available = m_endFrame - _start;
|
||||
memcpy( *_tmp, m_data + _start, available * BYTES_PER_FRAME );
|
||||
memset( *_tmp + available, 0, ( _frames - available ) *
|
||||
BYTES_PER_FRAME );
|
||||
f_cnt_t pos = _index;
|
||||
bool backwards = pos < _loopstart
|
||||
? false
|
||||
: *_backwards;
|
||||
f_cnt_t copied = 0;
|
||||
|
||||
|
||||
if( backwards )
|
||||
{
|
||||
copied = qMin( _frames, pos - _loopstart );
|
||||
for( int i=0; i < copied; i++ )
|
||||
{
|
||||
(*_tmp)[i][0] = m_data[ pos - i ][0];
|
||||
(*_tmp)[i][1] = m_data[ pos - i ][1];
|
||||
}
|
||||
pos -= copied;
|
||||
if( pos == _loopstart ) backwards = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
copied = qMin( _frames, _loopend - pos );
|
||||
memcpy( *_tmp, m_data + pos, copied * BYTES_PER_FRAME );
|
||||
pos += copied;
|
||||
if( pos == _loopend ) backwards = true;
|
||||
}
|
||||
|
||||
while( copied < _frames )
|
||||
{
|
||||
if( backwards )
|
||||
{
|
||||
f_cnt_t todo = qMin( _frames - copied, pos - _loopstart );
|
||||
for ( int i=0; i < todo; i++ )
|
||||
{
|
||||
(*_tmp)[ copied + i ][0] = m_data[ pos - i ][0];
|
||||
(*_tmp)[ copied + i ][1] = m_data[ pos - i ][1];
|
||||
}
|
||||
pos -= todo;
|
||||
copied += todo;
|
||||
if( pos <= _loopstart ) backwards = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
f_cnt_t todo = qMin( _frames - copied, _loopend - pos );
|
||||
memcpy( *_tmp + copied, m_data + pos, todo * BYTES_PER_FRAME );
|
||||
pos += todo;
|
||||
copied += todo;
|
||||
if( pos >= _loopend ) backwards = true;
|
||||
}
|
||||
}
|
||||
*_backwards = backwards;
|
||||
}
|
||||
|
||||
return *_tmp;
|
||||
@@ -775,22 +875,37 @@ sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _start,
|
||||
|
||||
|
||||
|
||||
f_cnt_t SampleBuffer::getLoopedIndex( f_cnt_t _index ) const
|
||||
f_cnt_t SampleBuffer::getLoopedIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const
|
||||
{
|
||||
if( _index < m_loopEndFrame )
|
||||
if( _index < _endf )
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
return m_loopStartFrame + ( _index - m_loopStartFrame )
|
||||
% ( m_loopEndFrame - m_loopStartFrame );
|
||||
return _startf + ( _index - _startf )
|
||||
% ( _endf - _startf );
|
||||
}
|
||||
|
||||
|
||||
f_cnt_t SampleBuffer::getPingPongIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const
|
||||
{
|
||||
if( _index < _endf )
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
const f_cnt_t looplen = _endf - _startf;
|
||||
const f_cnt_t looppos = ( _index - _endf ) % ( looplen*2 );
|
||||
|
||||
return ( looppos < looplen )
|
||||
? _endf - looppos
|
||||
: _startf + ( looppos - looplen );
|
||||
}
|
||||
|
||||
|
||||
void SampleBuffer::visualize( QPainter & _p, const QRect & _dr,
|
||||
const QRect & _clip, f_cnt_t _from_frame, f_cnt_t _to_frame )
|
||||
{
|
||||
if( m_frames == 0 ) return;
|
||||
|
||||
const bool focus_on_range = _to_frame <= m_frames
|
||||
&& 0 <= _from_frame && _from_frame < _to_frame;
|
||||
// _p.setClipRect( _clip );
|
||||
@@ -800,7 +915,7 @@ void SampleBuffer::visualize( QPainter & _p, const QRect & _dr,
|
||||
const int h = _dr.height();
|
||||
|
||||
const int yb = h / 2 + _dr.y();
|
||||
const float y_space = h*0.25f;
|
||||
const float y_space = h*0.5f;
|
||||
const int nb_frames = focus_on_range ? _to_frame - _from_frame : m_frames;
|
||||
|
||||
if( nb_frames < 60000 )
|
||||
@@ -811,6 +926,7 @@ void SampleBuffer::visualize( QPainter & _p, const QRect & _dr,
|
||||
}
|
||||
const int fpp = tLimit<int>( nb_frames / w, 1, 20 );
|
||||
QPoint * l = new QPoint[nb_frames / fpp + 1];
|
||||
QPoint * r = new QPoint[nb_frames / fpp + 1];
|
||||
int n = 0;
|
||||
const int xb = _dr.x();
|
||||
const int first = focus_on_range ? _from_frame : 0;
|
||||
@@ -818,11 +934,13 @@ void SampleBuffer::visualize( QPainter & _p, const QRect & _dr,
|
||||
for( int frame = first; frame < last; frame += fpp )
|
||||
{
|
||||
l[n] = QPoint( xb + ( (frame - first) * double( w ) / nb_frames ),
|
||||
(int)( yb - ( ( m_data[frame][0]+m_data[frame][1] ) *
|
||||
y_space ) ) );
|
||||
(int)( yb - ( m_data[frame][0] * y_space * m_amplification ) ) );
|
||||
r[n] = QPoint( xb + ( (frame - first) * double( w ) / nb_frames ),
|
||||
(int)( yb - ( m_data[frame][1] * y_space * m_amplification ) ) );
|
||||
++n;
|
||||
}
|
||||
_p.drawPolyline( l, nb_frames / fpp );
|
||||
_p.drawPolyline( r, nb_frames / fpp );
|
||||
delete[] l;
|
||||
}
|
||||
|
||||
@@ -914,19 +1032,19 @@ QString SampleBuffer::openAndSetWaveformFile()
|
||||
{
|
||||
m_audioFile = configManager::inst()->factorySamplesDir() + "waveforms/10saw.flac";
|
||||
}
|
||||
|
||||
|
||||
QString fileName = this->openAudioFile();
|
||||
|
||||
if(!fileName.isEmpty())
|
||||
{
|
||||
this->setAudioFile( fileName );
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
m_audioFile = "";
|
||||
}
|
||||
|
||||
return fileName;
|
||||
return fileName;
|
||||
}
|
||||
|
||||
|
||||
@@ -1243,7 +1361,7 @@ void SampleBuffer::loadFromBase64( const QString & _data )
|
||||
void SampleBuffer::setStartFrame( const f_cnt_t _s )
|
||||
{
|
||||
m_varLock.lock();
|
||||
m_loopStartFrame = m_startFrame = _s;
|
||||
m_startFrame = _s;
|
||||
m_varLock.unlock();
|
||||
}
|
||||
|
||||
@@ -1253,7 +1371,7 @@ void SampleBuffer::setStartFrame( const f_cnt_t _s )
|
||||
void SampleBuffer::setEndFrame( const f_cnt_t _e )
|
||||
{
|
||||
m_varLock.lock();
|
||||
m_loopEndFrame = m_endFrame = _e;
|
||||
m_endFrame = _e;
|
||||
m_varLock.unlock();
|
||||
}
|
||||
|
||||
@@ -1263,7 +1381,7 @@ void SampleBuffer::setEndFrame( const f_cnt_t _e )
|
||||
void SampleBuffer::setAmplification( float _a )
|
||||
{
|
||||
m_amplification = _a;
|
||||
update( true );
|
||||
emit sampleUpdated();
|
||||
}
|
||||
|
||||
|
||||
@@ -1325,18 +1443,17 @@ QString SampleBuffer::tryToMakeAbsolute( const QString & _file )
|
||||
|
||||
|
||||
|
||||
SampleBuffer::handleState::handleState( bool _varying_pitch ) :
|
||||
SampleBuffer::handleState::handleState( bool _varying_pitch, int interpolation_mode ) :
|
||||
m_frameIndex( 0 ),
|
||||
m_varyingPitch( _varying_pitch )
|
||||
m_varyingPitch( _varying_pitch ),
|
||||
m_isBackwards( false )
|
||||
{
|
||||
int error;
|
||||
if( ( m_resamplingData = src_new(/*
|
||||
( engine::mixer()->highQuality() == true ) ?
|
||||
SRC_SINC_FASTEST :*/
|
||||
SRC_LINEAR,
|
||||
DEFAULT_CHANNELS, &error ) ) == NULL )
|
||||
m_interpolationMode = interpolation_mode;
|
||||
|
||||
if( ( m_resamplingData = src_new( interpolation_mode, DEFAULT_CHANNELS, &error ) ) == NULL )
|
||||
{
|
||||
printf( "Error: src_new() failed in sample_buffer.cpp!\n" );
|
||||
qDebug( "Error: src_new() failed in sample_buffer.cpp!\n" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "bb_track.h"
|
||||
#include "engine.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "pattern.h"
|
||||
#include "Pattern.h"
|
||||
#include "SampleBuffer.h"
|
||||
#include "SampleTrack.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008 Csaba Hruska <csaba.hruska/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "bb_track.h"
|
||||
#include "engine.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "pattern.h"
|
||||
#include "Pattern.h"
|
||||
#include "SampleBuffer.h"
|
||||
#include "SampleTrack.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -47,41 +47,46 @@ SerializingObject::~SerializingObject()
|
||||
|
||||
|
||||
|
||||
QDomElement SerializingObject::saveState( QDomDocument & _doc,
|
||||
QDomElement & _parent )
|
||||
QDomElement SerializingObject::saveState( QDomDocument& doc, QDomElement& parent )
|
||||
{
|
||||
QDomElement _this = _doc.createElement( nodeName() );
|
||||
_parent.appendChild( _this );
|
||||
saveSettings( _doc, _this );
|
||||
if( getHook() )
|
||||
QDomElement element = doc.createElement( nodeName() );
|
||||
parent.appendChild( element );
|
||||
|
||||
saveSettings( doc, element );
|
||||
|
||||
if( hook() )
|
||||
{
|
||||
getHook()->saveSettings( _doc, _this );
|
||||
hook()->saveSettings( doc, element );
|
||||
}
|
||||
return _this;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void SerializingObject::restoreState( const QDomElement & _this )
|
||||
void SerializingObject::restoreState( const QDomElement& element )
|
||||
{
|
||||
loadSettings( _this );
|
||||
if( getHook() )
|
||||
loadSettings( element );
|
||||
|
||||
if( hook() )
|
||||
{
|
||||
getHook()->loadSettings( _this );
|
||||
hook()->loadSettings( element );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void SerializingObject::setHook( SerializingObjectHook * _hook )
|
||||
void SerializingObject::setHook( SerializingObjectHook* hook )
|
||||
{
|
||||
if( m_hook )
|
||||
{
|
||||
m_hook->m_hookedIn = NULL;
|
||||
}
|
||||
m_hook = _hook;
|
||||
|
||||
m_hook = hook;
|
||||
|
||||
if( m_hook )
|
||||
{
|
||||
m_hook->m_hookedIn = this;
|
||||
@@ -91,16 +96,18 @@ void SerializingObject::setHook( SerializingObjectHook * _hook )
|
||||
|
||||
|
||||
|
||||
void SerializingObject::saveSettings( QDomDocument &/* _doc*/,
|
||||
QDomElement &/* _this*/ )
|
||||
void SerializingObject::saveSettings( QDomDocument& doc, QDomElement& element )
|
||||
{
|
||||
Q_UNUSED(doc)
|
||||
Q_UNUSED(element)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void SerializingObject::loadSettings( const QDomElement & /* _this*/ )
|
||||
void SerializingObject::loadSettings( const QDomElement& element )
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2005-2007 Danny McRae <khjklujn/at/yahoo.com>
|
||||
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
|
||||
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -73,10 +73,16 @@ void TrackContainer::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
|
||||
void TrackContainer::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
bool journalRestore = _this.parentNode().nodeName() == "journaldata";
|
||||
if( journalRestore )
|
||||
{
|
||||
clearAllTracks();
|
||||
}
|
||||
|
||||
static QProgressDialog * pd = NULL;
|
||||
bool was_null = ( pd == NULL );
|
||||
int start_val = 0;
|
||||
if( engine::hasGUI() )
|
||||
if( !journalRestore && engine::hasGUI() )
|
||||
{
|
||||
if( pd == NULL )
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2013 Mike Choi <rdavidian71/at/gmail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* audio_alsa.cpp - device-class which implements ALSA-PCM-output
|
||||
*
|
||||
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -93,6 +93,7 @@ AudioAlsa::AudioAlsa( bool & _success_ful, Mixer* _mixer ) :
|
||||
oldflags |= FD_CLOEXEC;
|
||||
fcntl( fd, F_SETFD, oldflags );
|
||||
}
|
||||
delete[] ufds;
|
||||
_success_ful = true;
|
||||
}
|
||||
|
||||
@@ -145,10 +146,11 @@ int AudioAlsa::handleError( int _err )
|
||||
// under-run
|
||||
_err = snd_pcm_prepare( m_handle );
|
||||
if( _err < 0 )
|
||||
printf( "Can't recovery from underrun, prepare "
|
||||
printf( "Can't recover from underrun, prepare "
|
||||
"failed: %s\n", snd_strerror( _err ) );
|
||||
return ( 0 );
|
||||
}
|
||||
#ifdef ESTRPIPE
|
||||
else if( _err == -ESTRPIPE )
|
||||
{
|
||||
while( ( _err = snd_pcm_resume( m_handle ) ) == -EAGAIN )
|
||||
@@ -161,11 +163,12 @@ int AudioAlsa::handleError( int _err )
|
||||
{
|
||||
_err = snd_pcm_prepare( m_handle );
|
||||
if( _err < 0 )
|
||||
printf( "Can't recovery from suspend, prepare "
|
||||
printf( "Can't recover from suspend, prepare "
|
||||
"failed: %s\n", snd_strerror( _err ) );
|
||||
}
|
||||
return ( 0 );
|
||||
}
|
||||
#endif
|
||||
return _err;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2013 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* AudioPort.cpp - base-class for objects providing sound at a port
|
||||
*
|
||||
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "AudioPort.h"
|
||||
#include "AudioDevice.h"
|
||||
#include "EffectChain.h"
|
||||
#include "FxMixer.h"
|
||||
#include "engine.h"
|
||||
|
||||
|
||||
@@ -118,3 +119,13 @@ bool AudioPort::processEffects()
|
||||
}
|
||||
|
||||
|
||||
void AudioPort::doProcessing( sampleFrame * )
|
||||
{
|
||||
const bool me = processEffects();
|
||||
if( me || m_bufferUsage != NoUsage )
|
||||
{
|
||||
engine::fxMixer()->mixToChannel( firstBuffer(), nextFxChannel() );
|
||||
nextPeriod();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2008 Csaba Hruska <csaba.hruska/at/gmail.com>
|
||||
* Copyright (c) 2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2006-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* config_mgr.cpp - implementation of class configManager
|
||||
*
|
||||
* Copyright (c) 2005-2011 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -159,6 +159,7 @@ void configManager::addRecentlyOpenedProject( const QString & _file )
|
||||
m_recentlyOpenedProjects.removeLast();
|
||||
}
|
||||
m_recentlyOpenedProjects.push_front( _file );
|
||||
configManager::inst()->saveConfigFile();
|
||||
}
|
||||
|
||||
|
||||
@@ -313,7 +314,7 @@ void configManager::loadConfigFile()
|
||||
#endif
|
||||
setBackgroundArtwork( value( "paths", "backgroundartwork" ) );
|
||||
}
|
||||
else
|
||||
else if( QApplication::type() == QApplication::GuiClient )
|
||||
{
|
||||
QMessageBox::warning( NULL, MainWindow::tr( "Configuration file" ),
|
||||
MainWindow::tr( "Error while parsing configuration file at line %1:%2: %3" ).
|
||||
@@ -344,8 +345,10 @@ void configManager::loadConfigFile()
|
||||
if( m_ladDir.isEmpty() || m_ladDir == QDir::separator() ||
|
||||
( !m_ladDir.contains( ':' ) && !QDir( m_ladDir ).exists() ) )
|
||||
{
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
#if defined(LMMS_BUILD_WIN32)
|
||||
m_ladDir = m_pluginDir + "ladspa" + QDir::separator();
|
||||
#elif defined(LMMS_BUILD_APPLE)
|
||||
m_ladDir = qApp->applicationDirPath() + "/../lib/lmms/ladspa/";
|
||||
#else
|
||||
m_ladDir = qApp->applicationDirPath() + '/' + LIB_DIR + "/ladspa/";
|
||||
#endif
|
||||
@@ -355,8 +358,10 @@ void configManager::loadConfigFile()
|
||||
if( m_stkDir.isEmpty() || m_stkDir == QDir::separator() ||
|
||||
!QDir( m_stkDir ).exists() )
|
||||
{
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
#if defined(LMMS_BUILD_WIN32)
|
||||
m_stkDir = m_dataDir + "stk/rawwaves/";
|
||||
#elif defined(LMMS_BUILD_APPLE)
|
||||
m_stkDir = qApp->applicationDirPath() + "/../share/stk/rawwaves/";
|
||||
#else
|
||||
m_stkDir = "/usr/share/stk/rawwaves/";
|
||||
#endif
|
||||
@@ -367,19 +372,18 @@ void configManager::loadConfigFile()
|
||||
QDir::setSearchPaths( "resources", QStringList() << artworkDir()
|
||||
<< defaultArtworkDir() );
|
||||
|
||||
if( !QDir( m_workingDir ).exists() )
|
||||
{
|
||||
if( QMessageBox::question( 0,
|
||||
if( !QDir( m_workingDir ).exists() &&
|
||||
QApplication::type() == QApplication::GuiClient &&
|
||||
QMessageBox::question( 0,
|
||||
MainWindow::tr( "Working directory" ),
|
||||
MainWindow::tr( "The LMMS working directory %1 does not "
|
||||
"exist. Create it now? You can change the directory "
|
||||
"later via Edit -> Settings." ).arg( m_workingDir ),
|
||||
QMessageBox::Yes, QMessageBox::No ) ==
|
||||
QMessageBox::Yes )
|
||||
{
|
||||
QDir().mkpath( m_workingDir );
|
||||
}
|
||||
QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes )
|
||||
{
|
||||
QDir().mkpath( m_workingDir );
|
||||
}
|
||||
|
||||
if( QDir( m_workingDir ).exists() )
|
||||
{
|
||||
QDir().mkpath( userProjectsDir() );
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 1998-2000 Paul Kellett (mda-vst.com)
|
||||
* Copyright (c) 2007 Paul Giblock <drfaygo/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -35,7 +35,7 @@
|
||||
#include "ladspa_2_lmms.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Mixer.h"
|
||||
#include "pattern.h"
|
||||
#include "Pattern.h"
|
||||
#include "PianoRoll.h"
|
||||
#include "PresetPreviewPlayHandle.h"
|
||||
#include "ProjectJournal.h"
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "Plugin.h"
|
||||
#include "SongEditor.h"
|
||||
#include "song.h"
|
||||
#include "BandLimitedWave.h"
|
||||
|
||||
|
||||
bool engine::s_hasGUI = true;
|
||||
@@ -72,6 +73,9 @@ void engine::init( const bool _has_gui )
|
||||
{
|
||||
s_hasGUI = _has_gui;
|
||||
|
||||
// generate (load from file) bandlimited wavetables
|
||||
BandLimitedWave::generateWaves();
|
||||
|
||||
initPluginFileHandling();
|
||||
|
||||
s_projectJournal = new ProjectJournal;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2012 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "fft_helpers.h"
|
||||
|
||||
#include <math.h>
|
||||
#include "lmms_math.h"
|
||||
|
||||
|
||||
/* returns biggest value from abs_spectrum[spec_size] array
|
||||
@@ -73,7 +73,7 @@ int hanming(float *timebuffer, int length, WINDOWS type)
|
||||
|
||||
for ( i=0; i<length; i++ )
|
||||
{
|
||||
timebuffer[i]=timebuffer[i]*(alpha+(1-alpha)*cos(2*M_PI*i/((float)length-1.0)));
|
||||
timebuffer[i]=timebuffer[i]*(alpha+(1-alpha)*cos(2*F_PI*i/((float)length-1.0)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2005-2008 Danny McRae <khjklujn@netscape.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Copyright (c) 2005-2008 Danny McRae <khjklujn@netscape.net>
|
||||
* Copyright (c) 2011-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -46,10 +46,11 @@ ladspaManager::ladspaManager()
|
||||
ladspaDirectories.push_back( configManager::inst()->pluginDir() + "ladspa" );
|
||||
#ifndef LMMS_BUILD_WIN32
|
||||
ladspaDirectories.push_back( qApp->applicationDirPath() + '/' + LIB_DIR + "ladspa" );
|
||||
ladspaDirectories.push_back( "/usr/lib/lmms/ladspa" );
|
||||
ladspaDirectories.push_back( "/usr/local/lib/lmms/ladspa" );
|
||||
ladspaDirectories.push_back( "/usr/lib/ladspa" );
|
||||
ladspaDirectories.push_back( "/usr/lib64/ladspa" );
|
||||
ladspaDirectories.push_back( "/usr/local/lib/ladspa" );
|
||||
ladspaDirectories.push_back( "/usr/local/lib64/ladspa" );
|
||||
ladspaDirectories.push_back( "/Library/Audio/Plug-Ins/LADSPA" );
|
||||
#endif
|
||||
|
||||
for( QStringList::iterator it = ladspaDirectories.begin();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2012-2013 Paul Giblock <p/at/pgiblock.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -27,6 +27,14 @@
|
||||
#include "lmmsversion.h"
|
||||
#include "versioninfo.h"
|
||||
|
||||
// denormals stripping
|
||||
#ifdef __SSE__
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#ifdef __SSE3__
|
||||
#include <pmmintrin.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QLocale>
|
||||
@@ -65,6 +73,7 @@
|
||||
#include "ProjectRenderer.h"
|
||||
#include "DataFile.h"
|
||||
#include "song.h"
|
||||
#include "LmmsPalette.h"
|
||||
|
||||
static inline QString baseName( const QString & _file )
|
||||
{
|
||||
@@ -91,10 +100,20 @@ int main( int argc, char * * argv )
|
||||
// intialize RNG
|
||||
srand( getpid() + time( 0 ) );
|
||||
|
||||
// set denormal protection for this thread
|
||||
#ifdef __SSE3__
|
||||
/* DAZ flag */
|
||||
_MM_SET_DENORMALS_ZERO_MODE( _MM_DENORMALS_ZERO_ON );
|
||||
#endif
|
||||
#ifdef __SSE__
|
||||
/* FTZ flag */
|
||||
_MM_SET_FLUSH_ZERO_MODE( _MM_FLUSH_ZERO_ON );
|
||||
#endif
|
||||
|
||||
bool core_only = false;
|
||||
bool fullscreen = true;
|
||||
bool exit_after_import = false;
|
||||
QString file_to_load, file_to_save, file_to_import, render_out;
|
||||
QString file_to_load, file_to_save, file_to_import, render_out, profilerOutputFile;
|
||||
|
||||
for( int i = 1; i < argc; ++i )
|
||||
{
|
||||
@@ -119,7 +138,6 @@ int main( int argc, char * * argv )
|
||||
new QCoreApplication( argc, argv ) :
|
||||
new QApplication( argc, argv ) ;
|
||||
|
||||
|
||||
Mixer::qualitySettings qs( Mixer::qualitySettings::Mode_HighQuality );
|
||||
ProjectRenderer::OutputSettings os( 44100, false, 160,
|
||||
ProjectRenderer::Depth_16Bit );
|
||||
@@ -131,13 +149,13 @@ int main( int argc, char * * argv )
|
||||
if( QString( argv[i] ) == "--version" ||
|
||||
QString( argv[i] ) == "-v" )
|
||||
{
|
||||
printf( "\nLinux MultiMedia Studio %s\n(%s %s, Qt %s, %s)\n\n"
|
||||
printf( "LMMS %s\n(%s %s, Qt %s, %s)\n\n"
|
||||
"Copyright (c) 2004-2014 LMMS developers.\n\n"
|
||||
"This program is free software; you can redistribute it and/or\n"
|
||||
"modify it under the terms of the GNU General Public\n"
|
||||
"License as published by the Free Software Foundation; either\n"
|
||||
"version 2 of the License, or (at your option) any later version.\n\n"
|
||||
"Try \"%s --help\" for more information.\n\n", LMMS_VERSION,
|
||||
"Try \"%s --help\" for more information.\n\n", LMMS_VERSION,
|
||||
PLATFORM, MACHINE, QT_VERSION_STR, GCC_VERSION,
|
||||
argv[0] );
|
||||
|
||||
@@ -146,7 +164,7 @@ int main( int argc, char * * argv )
|
||||
else if( argc > i && ( QString( argv[i] ) == "--help" ||
|
||||
QString( argv[i] ) == "-h" ) )
|
||||
{
|
||||
printf( "\nLinux MultiMedia Studio %s\n"
|
||||
printf( "LMMS %s\n"
|
||||
"Copyright (c) 2004-2014 LMMS developers.\n\n"
|
||||
"usage: lmms [ -r <project file> ] [ options ]\n"
|
||||
" [ -u <in> <out> ]\n"
|
||||
@@ -181,10 +199,12 @@ int main( int argc, char * * argv )
|
||||
else if( argc > i+1 && ( QString( argv[i] ) == "--upgrade" ||
|
||||
QString( argv[i] ) == "-u" ) )
|
||||
{
|
||||
DataFile dataFile( QString( argv[i + 1] ) );
|
||||
QString inFile( argv[i + 1] );
|
||||
DataFile dataFile( inFile );
|
||||
if (argc > i+2)
|
||||
{
|
||||
dataFile.writeFile( argv[i + 2] );
|
||||
const QString outFile = argv[i + 2];
|
||||
dataFile.writeFile( outFile );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -339,6 +359,11 @@ int main( int argc, char * * argv )
|
||||
exit_after_import = true;
|
||||
}
|
||||
}
|
||||
else if( argc > i && ( QString( argv[i] ) == "--profile" || QString( argv[i] ) == "-p" ) )
|
||||
{
|
||||
profilerOutputFile = argv[i+1];
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( argv[i][0] == '-' )
|
||||
@@ -388,7 +413,15 @@ int main( int argc, char * * argv )
|
||||
if( render_out.isEmpty() )
|
||||
{
|
||||
// init style and palette
|
||||
QApplication::setStyle( new LmmsStyle() );
|
||||
LmmsStyle * lmmsstyle = new LmmsStyle();
|
||||
QApplication::setStyle( lmmsstyle );
|
||||
|
||||
LmmsPalette * lmmspal = new LmmsPalette( NULL, lmmsstyle );
|
||||
QPalette lpal = lmmspal->palette();
|
||||
|
||||
QApplication::setPalette( lpal );
|
||||
LmmsStyle::s_palette = &lpal;
|
||||
|
||||
|
||||
// show splash screen
|
||||
QSplashScreen splashScreen( embed::getIconPixmap( "splash" ) );
|
||||
@@ -407,7 +440,7 @@ int main( int argc, char * * argv )
|
||||
srand( getpid() + time( 0 ) );
|
||||
|
||||
// recover a file?
|
||||
QString recoveryFile = QDir(configManager::inst()->workingDir()).absoluteFilePath("recover.dataFile");
|
||||
QString recoveryFile = QDir(configManager::inst()->workingDir()).absoluteFilePath("recover.mmp");
|
||||
if( QFileInfo(recoveryFile).exists() &&
|
||||
QMessageBox::question( engine::mainWindow(), MainWindow::tr( "Project recovery" ),
|
||||
MainWindow::tr( "It looks like the last session did not end properly. "
|
||||
@@ -484,6 +517,11 @@ int main( int argc, char * * argv )
|
||||
SLOT( updateConsoleProgress() ) );
|
||||
t->start( 200 );
|
||||
|
||||
if( profilerOutputFile.isEmpty() == false )
|
||||
{
|
||||
engine::mixer()->profiler().setOutputFile( profilerOutputFile );
|
||||
}
|
||||
|
||||
// start now!
|
||||
r->startProcessing();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* This file partly contains code from Fluidsynth, Peter Hanappe
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -73,7 +73,7 @@ void MidiController::updateName()
|
||||
|
||||
|
||||
|
||||
void MidiController::processInEvent( const MidiEvent& event, const MidiTime& time )
|
||||
void MidiController::processInEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset )
|
||||
{
|
||||
unsigned char controllerNum;
|
||||
switch( event.type() )
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -48,7 +48,7 @@ MidiPort::MidiPort( const QString& name,
|
||||
m_outputControllerModel( 0, 0, MidiControllerCount, this, tr( "Output controller" ) ),
|
||||
m_fixedInputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed input velocity" ) ),
|
||||
m_fixedOutputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed output velocity" ) ),
|
||||
m_fixedOutputNoteModel( -1, -1, MidiMaxNote, this, tr( "Fixed output note" ) ),
|
||||
m_fixedOutputNoteModel( -1, -1, MidiMaxKey, this, tr( "Fixed output note" ) ),
|
||||
m_outputProgramModel( 1, 1, MidiProgramCount, this, tr( "Output MIDI program" ) ),
|
||||
m_baseVelocityModel( MidiMaxVelocity/2, 1, MidiMaxVelocity, this, tr( "Base velocity" ) ),
|
||||
m_readableModel( false, this, tr( "Receive MIDI-events" ) ),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* note.cpp - implementation of class note
|
||||
*
|
||||
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -50,13 +50,14 @@ note::note( const MidiTime & _length, const MidiTime & _pos,
|
||||
m_pos( _pos ),
|
||||
m_detuning( NULL )
|
||||
{
|
||||
//saveJournallingState( false );
|
||||
// setJournalling( false );
|
||||
if( _detuning )
|
||||
{
|
||||
m_detuning = sharedObject::ref( _detuning );
|
||||
}
|
||||
//restoreJournallingState();
|
||||
else
|
||||
{
|
||||
createDetuning();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +99,6 @@ note::~note()
|
||||
|
||||
void note::setLength( const MidiTime & _length )
|
||||
{
|
||||
// addJournalEntry( journalEntry( ChangeLength, m_length - _length ) );
|
||||
m_length = _length;
|
||||
}
|
||||
|
||||
@@ -107,7 +107,6 @@ void note::setLength( const MidiTime & _length )
|
||||
|
||||
void note::setPos( const MidiTime & _pos )
|
||||
{
|
||||
// addJournalEntry( journalEntry( ChangePosition, m_pos - _pos ) );
|
||||
m_pos = _pos;
|
||||
}
|
||||
|
||||
@@ -117,7 +116,6 @@ void note::setPos( const MidiTime & _pos )
|
||||
void note::setKey( const int _key )
|
||||
{
|
||||
const int k = qBound( 0, _key, NumKeys );
|
||||
// addJournalEntry( journalEntry( ChangeKey, m_key - k ) );
|
||||
m_key = k;
|
||||
}
|
||||
|
||||
@@ -127,7 +125,6 @@ void note::setKey( const int _key )
|
||||
void note::setVolume( volume_t _volume )
|
||||
{
|
||||
const volume_t v = qBound( MinVolume, _volume, MaxVolume );
|
||||
// addJournalEntry( journalEntry( ChangeVolume, (int) m_volume - v ) );
|
||||
m_volume = v;
|
||||
}
|
||||
|
||||
@@ -137,7 +134,6 @@ void note::setVolume( volume_t _volume )
|
||||
void note::setPanning( panning_t _panning )
|
||||
{
|
||||
const panning_t p = qBound( PanningLeft, _panning, PanningRight );
|
||||
// addJournalEntry( journalEntry( ChangePanning, (int) m_panning - p ) );
|
||||
m_panning = p;
|
||||
}
|
||||
|
||||
@@ -213,46 +209,6 @@ void note::loadSettings( const QDomElement & _this )
|
||||
|
||||
|
||||
|
||||
/*void note::undoStep( journalEntry & _je )
|
||||
{
|
||||
saveJournallingState( false );
|
||||
switch( static_cast<Actions>( _je.actionID() ) )
|
||||
{
|
||||
case ChangeKey:
|
||||
setKey( key() - _je.data().toInt() );
|
||||
break;
|
||||
|
||||
case ChangeVolume:
|
||||
setVolume( getVolume() - _je.data().toInt() );
|
||||
break;
|
||||
|
||||
case ChangePanning:
|
||||
setPanning( getPanning() - _je.data().toInt() );
|
||||
break;
|
||||
|
||||
case ChangeLength:
|
||||
setLength( length() - _je.data().toInt() );
|
||||
break;
|
||||
|
||||
case ChangePosition:
|
||||
setPos( pos() - _je.data().toInt() );
|
||||
break;
|
||||
}
|
||||
restoreJournallingState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void note::redoStep( journalEntry & _je )
|
||||
{
|
||||
journalEntry je( _je.actionID(), -_je.data().toInt() );
|
||||
undoStep( je );
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
void note::editDetuningPattern()
|
||||
{
|
||||
createDetuning();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2007 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
|
||||
* Copyright (c) 2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -51,7 +51,7 @@
|
||||
#include "MidiClient.h"
|
||||
#include "DataFile.h"
|
||||
#include "NotePlayHandle.h"
|
||||
#include "pattern.h"
|
||||
#include "Pattern.h"
|
||||
#include "PianoRoll.h"
|
||||
#include "ProjectJournal.h"
|
||||
#include "project_notes.h"
|
||||
@@ -471,14 +471,14 @@ void song::playBB()
|
||||
|
||||
|
||||
|
||||
void song::playPattern( pattern * _patternToPlay, bool _loop )
|
||||
void song::playPattern( Pattern* patternToPlay, bool _loop )
|
||||
{
|
||||
if( isStopped() == false )
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
m_patternToPlay = _patternToPlay;
|
||||
m_patternToPlay = patternToPlay;
|
||||
m_loopPattern = _loop;
|
||||
|
||||
if( m_patternToPlay != NULL )
|
||||
@@ -666,8 +666,7 @@ void song::addBBTrack()
|
||||
{
|
||||
engine::mixer()->lock();
|
||||
track * t = track::create( track::BBTrack, this );
|
||||
engine::getBBTrackContainer()->setCurrentBB(
|
||||
bbTrack::numOfBBTrack( t ) );
|
||||
engine::getBBTrackContainer()->setCurrentBB( dynamic_cast<bbTrack *>( t )->index() );
|
||||
engine::mixer()->unlock();
|
||||
}
|
||||
|
||||
@@ -749,6 +748,11 @@ void song::clearProject()
|
||||
engine::automationEditor()->setCurrentPattern( NULL );
|
||||
}
|
||||
|
||||
if( engine::pianoRoll() )
|
||||
{
|
||||
engine::pianoRoll()->reset();
|
||||
}
|
||||
|
||||
m_tempoModel.reset();
|
||||
m_masterVolumeModel.reset();
|
||||
m_masterPitchModel.reset();
|
||||
@@ -872,6 +876,8 @@ void song::createNewProjectFromTemplate( const QString & _template )
|
||||
// load given song
|
||||
void song::loadProject( const QString & _file_name )
|
||||
{
|
||||
QDomNode node;
|
||||
|
||||
m_loadingProject = true;
|
||||
|
||||
clearProject();
|
||||
@@ -890,6 +896,8 @@ void song::loadProject( const QString & _file_name )
|
||||
return;
|
||||
}
|
||||
|
||||
DataFile::LocaleHelper localeHelper( DataFile::LocaleHelper::ModeLoad );
|
||||
|
||||
engine::mixer()->lock();
|
||||
|
||||
// get the header information from the DOM
|
||||
@@ -913,57 +921,52 @@ void song::loadProject( const QString & _file_name )
|
||||
//Backward compatibility for LMMS <= 0.4.15
|
||||
PeakController::initGetControllerBySetting();
|
||||
|
||||
QDomNode node = dataFile.content().firstChild();
|
||||
// Load mixer first to be able to set the correct range for FX channels
|
||||
node = dataFile.content().firstChildElement( engine::fxMixer()->nodeName() );
|
||||
if( !node.isNull() )
|
||||
{
|
||||
engine::fxMixer()->restoreState( node.toElement() );
|
||||
if( engine::hasGUI() )
|
||||
{
|
||||
// refresh FxMixerView
|
||||
engine::fxMixerView()->refreshDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
node = dataFile.content().firstChild();
|
||||
while( !node.isNull() )
|
||||
{
|
||||
if( node.isElement() )
|
||||
{
|
||||
if( node.nodeName() == "trackcontainer" )
|
||||
{
|
||||
( (JournallingObject *)( this ) )->
|
||||
restoreState( node.toElement() );
|
||||
( (JournallingObject *)( this ) )->restoreState( node.toElement() );
|
||||
}
|
||||
else if( node.nodeName() == "controllers" )
|
||||
{
|
||||
restoreControllerStates( node.toElement() );
|
||||
}
|
||||
else if( node.nodeName() == engine::fxMixer()->nodeName() )
|
||||
{
|
||||
engine::fxMixer()->restoreState( node.toElement() );
|
||||
}
|
||||
else if( engine::hasGUI() )
|
||||
{
|
||||
if( node.nodeName() ==
|
||||
engine::getControllerRackView()->nodeName() )
|
||||
if( node.nodeName() == engine::getControllerRackView()->nodeName() )
|
||||
{
|
||||
engine::getControllerRackView()->
|
||||
restoreState( node.toElement() );
|
||||
engine::getControllerRackView()->restoreState( node.toElement() );
|
||||
}
|
||||
else if( node.nodeName() == engine::pianoRoll()->nodeName() )
|
||||
{
|
||||
engine::pianoRoll()->restoreState( node.toElement() );
|
||||
}
|
||||
else if( node.nodeName() ==
|
||||
engine::automationEditor()->
|
||||
nodeName() )
|
||||
else if( node.nodeName() == engine::automationEditor()->nodeName() )
|
||||
{
|
||||
engine::automationEditor()->
|
||||
restoreState( node.toElement() );
|
||||
engine::automationEditor()->restoreState( node.toElement() );
|
||||
}
|
||||
else if( node.nodeName() ==
|
||||
engine::getProjectNotes()->
|
||||
nodeName() )
|
||||
else if( node.nodeName() == engine::getProjectNotes()->nodeName() )
|
||||
{
|
||||
engine::getProjectNotes()->
|
||||
SerializingObject::restoreState( node.toElement() );
|
||||
engine::getProjectNotes()->SerializingObject::restoreState( node.toElement() );
|
||||
}
|
||||
else if( node.nodeName() ==
|
||||
m_playPos[Mode_PlaySong].
|
||||
m_timeLine->nodeName() )
|
||||
else if( node.nodeName() == m_playPos[Mode_PlaySong].m_timeLine->nodeName() )
|
||||
{
|
||||
m_playPos[Mode_PlaySong].
|
||||
m_timeLine->restoreState(
|
||||
node.toElement() );
|
||||
m_playPos[Mode_PlaySong].m_timeLine->restoreState( node.toElement() );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1003,6 +1006,8 @@ void song::loadProject( const QString & _file_name )
|
||||
// only save current song as _filename and do nothing else
|
||||
bool song::saveProjectFile( const QString & _filename )
|
||||
{
|
||||
DataFile::LocaleHelper localeHelper( DataFile::LocaleHelper::ModeSave );
|
||||
|
||||
DataFile dataFile( DataFile::SongProject );
|
||||
|
||||
m_tempoModel.saveSettings( dataFile, dataFile.head(), "bpm" );
|
||||
@@ -1019,15 +1024,13 @@ bool song::saveProjectFile( const QString & _filename )
|
||||
engine::getControllerRackView()->saveState( dataFile, dataFile.content() );
|
||||
engine::pianoRoll()->saveState( dataFile, dataFile.content() );
|
||||
engine::automationEditor()->saveState( dataFile, dataFile.content() );
|
||||
engine::getProjectNotes()->
|
||||
SerializingObject::saveState( dataFile, dataFile.content() );
|
||||
m_playPos[Mode_PlaySong].m_timeLine->saveState(
|
||||
dataFile, dataFile.content() );
|
||||
engine::getProjectNotes()->SerializingObject::saveState( dataFile, dataFile.content() );
|
||||
m_playPos[Mode_PlaySong].m_timeLine->saveState( dataFile, dataFile.content() );
|
||||
}
|
||||
|
||||
saveControllerStates( dataFile, dataFile.content() );
|
||||
|
||||
return dataFile.writeFile( _filename );
|
||||
return dataFile.writeFile( _filename );
|
||||
}
|
||||
|
||||
|
||||
@@ -1172,8 +1175,7 @@ void song::exportProject(bool multiExport)
|
||||
efd.setFileMode( FileDialog::AnyFile );
|
||||
int idx = 0;
|
||||
QStringList types;
|
||||
while( __fileEncodeDevices[idx].m_fileFormat !=
|
||||
ProjectRenderer::NumFileFormats )
|
||||
while( __fileEncodeDevices[idx].m_fileFormat != ProjectRenderer::NumFileFormats )
|
||||
{
|
||||
types << tr( __fileEncodeDevices[idx].m_description );
|
||||
++idx;
|
||||
@@ -1197,12 +1199,28 @@ void song::exportProject(bool multiExport)
|
||||
efd.setAcceptMode( FileDialog::AcceptSave );
|
||||
|
||||
|
||||
if( efd.exec() == QDialog::Accepted &&
|
||||
!efd.selectedFiles().isEmpty() && !efd.selectedFiles()[0].isEmpty() )
|
||||
if( efd.exec() == QDialog::Accepted && !efd.selectedFiles().isEmpty() && !efd.selectedFiles()[0].isEmpty() )
|
||||
{
|
||||
const QString export_file_name = efd.selectedFiles()[0];
|
||||
exportProjectDialog epd( export_file_name,
|
||||
engine::mainWindow(), multiExport );
|
||||
QString suffix = "";
|
||||
if ( !multiExport )
|
||||
{
|
||||
int stx = efd.selectedNameFilter().indexOf( "(*." );
|
||||
int etx = efd.selectedNameFilter().indexOf( ")" );
|
||||
|
||||
if ( stx > 0 && etx > stx )
|
||||
{
|
||||
// Get first extension from selected dropdown.
|
||||
// i.e. ".wav" from "WAV-File (*.wav), Dummy-File (*.dum)"
|
||||
suffix = efd.selectedNameFilter().mid( stx + 2, etx - stx - 2 ).split( " " )[0].trimmed();
|
||||
if ( efd.selectedFiles()[0].endsWith( suffix ) )
|
||||
{
|
||||
suffix = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QString export_file_name = efd.selectedFiles()[0] + suffix;
|
||||
exportProjectDialog epd( export_file_name, engine::mainWindow(), multiExport );
|
||||
epd.exec();
|
||||
}
|
||||
}
|
||||
@@ -1224,8 +1242,7 @@ void song::setModified()
|
||||
{
|
||||
m_modified = true;
|
||||
if( engine::mainWindow() &&
|
||||
QThread::currentThread() ==
|
||||
engine::mainWindow()->thread() )
|
||||
QThread::currentThread() == engine::mainWindow()->thread() )
|
||||
{
|
||||
engine::mainWindow()->resetWindowTitle();
|
||||
}
|
||||
@@ -1263,14 +1280,6 @@ void song::removeController( Controller * _controller )
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool song::isLoadingProject()
|
||||
{
|
||||
return m_loadingProject;
|
||||
}
|
||||
|
||||
|
||||
#include "moc_song.cxx"
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -40,11 +40,15 @@
|
||||
#include "text_float.h"
|
||||
|
||||
|
||||
#if QT_VERSION < 0x040800
|
||||
#define MiddleButton MidButton
|
||||
#endif
|
||||
|
||||
|
||||
QPixmap * timeLine::s_timeLinePixmap = NULL;
|
||||
QPixmap * timeLine::s_posMarkerPixmap = NULL;
|
||||
QPixmap * timeLine::s_loopPointPixmap = NULL;
|
||||
|
||||
QPixmap * timeLine::s_loopPointBeginPixmap = NULL;
|
||||
QPixmap * timeLine::s_loopPointEndPixmap = NULL;
|
||||
|
||||
timeLine::timeLine( const int _xoff, const int _yoff, const float _ppt,
|
||||
song::playPos & _pos, const MidiTime & _begin,
|
||||
@@ -77,10 +81,15 @@ timeLine::timeLine( const int _xoff, const int _yoff, const float _ppt,
|
||||
s_posMarkerPixmap = new QPixmap( embed::getIconPixmap(
|
||||
"playpos_marker" ) );
|
||||
}
|
||||
if( s_loopPointPixmap == NULL )
|
||||
if( s_loopPointBeginPixmap == NULL )
|
||||
{
|
||||
s_loopPointPixmap = new QPixmap( embed::getIconPixmap(
|
||||
"loop_point" ) );
|
||||
s_loopPointBeginPixmap = new QPixmap( embed::getIconPixmap(
|
||||
"loop_point_b" ) );
|
||||
}
|
||||
if( s_loopPointEndPixmap == NULL )
|
||||
{
|
||||
s_loopPointEndPixmap = new QPixmap( embed::getIconPixmap(
|
||||
"loop_point_e" ) );
|
||||
}
|
||||
|
||||
setAttribute( Qt::WA_OpaquePaintEvent, true );
|
||||
@@ -231,8 +240,8 @@ void timeLine::paintEvent( QPaintEvent * )
|
||||
p.setPen( QColor( 0, 0, 0 ) );
|
||||
|
||||
p.setOpacity( loopPointsEnabled() ? 0.9 : 0.2 );
|
||||
p.drawPixmap( markerX( loopBegin() )+2, 2, *s_loopPointPixmap );
|
||||
p.drawPixmap( markerX( loopEnd() )+2, 2, *s_loopPointPixmap );
|
||||
p.drawPixmap( markerX( loopBegin() )+2, 2, *s_loopPointBeginPixmap );
|
||||
p.drawPixmap( markerX( loopEnd() )+2, 2, *s_loopPointEndPixmap );
|
||||
p.setOpacity( 1.0 );
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -149,7 +149,6 @@ void trackContentObject::movePosition( const MidiTime & _pos )
|
||||
{
|
||||
if( m_startPosition != _pos )
|
||||
{
|
||||
addJournalEntry( JournalEntry( Move, m_startPosition - _pos ) );
|
||||
m_startPosition = _pos;
|
||||
engine::getSong()->updateLength();
|
||||
}
|
||||
@@ -170,7 +169,6 @@ void trackContentObject::changeLength( const MidiTime & _length )
|
||||
{
|
||||
if( m_length != _length )
|
||||
{
|
||||
addJournalEntry( JournalEntry( Resize, m_length - _length ) );
|
||||
m_length = _length;
|
||||
engine::getSong()->updateLength();
|
||||
}
|
||||
@@ -180,47 +178,6 @@ void trackContentObject::changeLength( const MidiTime & _length )
|
||||
|
||||
|
||||
|
||||
/*! \brief Undo one journal entry of this trackContentObject
|
||||
*
|
||||
* Restore the previous state of this track content object. This will
|
||||
* restore the position or the length of the track content object
|
||||
* depending on what was changed.
|
||||
*
|
||||
* \param _je The journal entry to undo
|
||||
*/
|
||||
void trackContentObject::undoStep( JournalEntry & _je )
|
||||
{
|
||||
saveJournallingState( false );
|
||||
switch( _je.actionID() )
|
||||
{
|
||||
case Move:
|
||||
movePosition( startPosition() + _je.data().toInt() );
|
||||
break;
|
||||
case Resize:
|
||||
changeLength( length() + _je.data().toInt() );
|
||||
break;
|
||||
}
|
||||
restoreJournallingState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*! \brief Redo one journal entry of this trackContentObject
|
||||
*
|
||||
* Undoes one 'undo' of this track content object.
|
||||
*
|
||||
* \param _je The journal entry to redo
|
||||
*/
|
||||
void trackContentObject::redoStep( JournalEntry & _je )
|
||||
{
|
||||
JournalEntry je( _je.actionID(), -_je.data().toInt() );
|
||||
undoStep( je );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*! \brief Copy this trackContentObject to the clipboard.
|
||||
*
|
||||
* Copies this track content object to the clipboard.
|
||||
@@ -292,7 +249,9 @@ trackContentObjectView::trackContentObjectView( trackContentObject * _tco,
|
||||
m_action( NoAction ),
|
||||
m_autoResize( false ),
|
||||
m_initialMouseX( 0 ),
|
||||
m_hint( NULL )
|
||||
m_hint( NULL ),
|
||||
m_fgColor( 0, 0, 0 ),
|
||||
m_textColor( 0, 0, 0 )
|
||||
{
|
||||
if( s_textFloat == NULL )
|
||||
{
|
||||
@@ -303,7 +262,7 @@ trackContentObjectView::trackContentObjectView( trackContentObject * _tco,
|
||||
setAttribute( Qt::WA_OpaquePaintEvent, true );
|
||||
setAttribute( Qt::WA_DeleteOnClose, true );
|
||||
setFocusPolicy( Qt::StrongFocus );
|
||||
setCursor( QCursor( embed::getIconPixmap( "hand" ), 0, 0 ) );
|
||||
setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) );
|
||||
move( 0, 1 );
|
||||
show();
|
||||
|
||||
@@ -357,6 +316,23 @@ bool trackContentObjectView::fixedTCOs()
|
||||
|
||||
|
||||
|
||||
// qproperty access functions, to be inherited & used by TCOviews
|
||||
//! \brief CSS theming qproperty access method
|
||||
QColor trackContentObjectView::fgColor() const
|
||||
{ return m_fgColor; }
|
||||
|
||||
//! \brief CSS theming qproperty access method
|
||||
QColor trackContentObjectView::textColor() const
|
||||
{ return m_textColor; }
|
||||
|
||||
//! \brief CSS theming qproperty access method
|
||||
void trackContentObjectView::setFgColor( const QColor & _c )
|
||||
{ m_fgColor = QColor( _c ); }
|
||||
|
||||
//! \brief CSS theming qproperty access method
|
||||
void trackContentObjectView::setTextColor( const QColor & _c )
|
||||
{ m_textColor = QColor( _c ); }
|
||||
|
||||
|
||||
/*! \brief Close a trackContentObjectView
|
||||
*
|
||||
@@ -383,6 +359,8 @@ bool trackContentObjectView::close()
|
||||
*/
|
||||
void trackContentObjectView::remove()
|
||||
{
|
||||
m_trackView->getTrack()->addJournalCheckPoint();
|
||||
|
||||
// delete ourself
|
||||
close();
|
||||
m_tco->deleteLater();
|
||||
@@ -554,11 +532,6 @@ void trackContentObjectView::mousePressEvent( QMouseEvent * _me )
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if( _me->modifiers() & Qt::ShiftModifier )
|
||||
{
|
||||
// add/remove object to/from selection
|
||||
selectableObject::mousePressEvent( _me );
|
||||
}
|
||||
else if( _me->button() == Qt::LeftButton &&
|
||||
_me->modifiers() & Qt::ControlModifier )
|
||||
{
|
||||
@@ -577,6 +550,8 @@ void trackContentObjectView::mousePressEvent( QMouseEvent * _me )
|
||||
/* engine::mainWindow()->isShiftPressed() == false &&*/
|
||||
fixedTCOs() == false )
|
||||
{
|
||||
m_tco->addJournalCheckPoint();
|
||||
|
||||
// move or resize
|
||||
m_tco->setJournalling( false );
|
||||
|
||||
@@ -613,6 +588,17 @@ void trackContentObjectView::mousePressEvent( QMouseEvent * _me )
|
||||
mouseMoveEvent( _me );
|
||||
s_textFloat->show();
|
||||
}
|
||||
else if( _me->button() == Qt::RightButton )
|
||||
{
|
||||
if( _me->modifiers() & Qt::ControlModifier )
|
||||
{
|
||||
m_tco->toggleMute();
|
||||
}
|
||||
else if( _me->modifiers() & Qt::ShiftModifier && fixedTCOs() == false )
|
||||
{
|
||||
remove();
|
||||
}
|
||||
}
|
||||
else if( _me->button() == Qt::MidButton )
|
||||
{
|
||||
if( _me->modifiers() & Qt::ControlModifier )
|
||||
@@ -770,9 +756,6 @@ void trackContentObjectView::mouseReleaseEvent( QMouseEvent * _me )
|
||||
if( m_action == Move || m_action == Resize )
|
||||
{
|
||||
m_tco->setJournalling( true );
|
||||
m_tco->addJournalEntry( JournalEntry( m_action, m_oldTime -
|
||||
( ( m_action == Move ) ?
|
||||
m_tco->startPosition() : m_tco->length() ) ) );
|
||||
}
|
||||
m_action = NoAction;
|
||||
delete m_hint;
|
||||
@@ -794,6 +777,11 @@ void trackContentObjectView::mouseReleaseEvent( QMouseEvent * _me )
|
||||
*/
|
||||
void trackContentObjectView::contextMenuEvent( QContextMenuEvent * _cme )
|
||||
{
|
||||
if( _cme->modifiers() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu contextMenu( this );
|
||||
if( fixedTCOs() == false )
|
||||
{
|
||||
@@ -867,6 +855,8 @@ trackContentWidget::trackContentWidget( trackView * _parent ) :
|
||||
SIGNAL( positionChanged( const MidiTime & ) ),
|
||||
this, SLOT( changePosition( const MidiTime & ) ) );
|
||||
|
||||
setStyle( QApplication::style() );
|
||||
|
||||
updateBackground();
|
||||
}
|
||||
|
||||
@@ -897,22 +887,13 @@ void trackContentWidget::updateBackground()
|
||||
m_background = QPixmap( w * 2, height() );
|
||||
QPainter pmp( &m_background );
|
||||
|
||||
QLinearGradient grad( 0,0, 0, h );
|
||||
grad.setColorAt( 0.0, QColor( 50, 50, 50 ) );
|
||||
grad.setColorAt( 0.33, QColor( 20, 20, 20 ) );
|
||||
grad.setColorAt( 1.0, QColor( 15, 15, 15 ) );
|
||||
pmp.fillRect( 0, 0, w, h, grad );
|
||||
|
||||
QLinearGradient grad2( 0,0, 0, h );
|
||||
grad2.setColorAt( 0.0, QColor( 50, 50, 50 ) );
|
||||
grad2.setColorAt( 0.33, QColor( 40, 40, 40 ) );
|
||||
grad2.setColorAt( 1.0, QColor( 30, 30, 30 ) );
|
||||
pmp.fillRect( w, 0, w , h, grad2 );
|
||||
pmp.fillRect( 0, 0, w, h, darkerColor() );
|
||||
pmp.fillRect( w, 0, w , h, lighterColor() );
|
||||
|
||||
// draw lines
|
||||
pmp.setPen( QPen( QColor( 0, 0, 0, 160 ), 1 ) );
|
||||
// horizontal line
|
||||
pmp.drawLine( 0, 0, w*2, 0 );
|
||||
pmp.drawLine( 0, h-1, w*2, h-1 );
|
||||
|
||||
// vertical lines
|
||||
for( float x = 0; x < w * 2; x += ppt )
|
||||
@@ -945,9 +926,6 @@ void trackContentWidget::updateBackground()
|
||||
void trackContentWidget::addTCOView( trackContentObjectView * _tcov )
|
||||
{
|
||||
trackContentObject * tco = _tcov->getTrackContentObject();
|
||||
/* QMap<QString, QVariant> map;
|
||||
map["id"] = tco->id();
|
||||
addJournalEntry( JournalEntry( AddTrackContentObject, map ) );*/
|
||||
|
||||
m_tcoViews.push_back( _tcov );
|
||||
|
||||
@@ -972,14 +950,6 @@ void trackContentWidget::removeTCOView( trackContentObjectView * _tcov )
|
||||
_tcov );
|
||||
if( it != m_tcoViews.end() )
|
||||
{
|
||||
/* QMap<QString, QVariant> map;
|
||||
DataFile dataFile( DataFile::JournalData );
|
||||
_tcov->getTrackContentObject()->saveState( dataFile, dataFile.content() );
|
||||
map["id"] = _tcov->getTrackContentObject()->id();
|
||||
map["state"] = dataFile.toString();
|
||||
addJournalEntry( JournalEntry( RemoveTrackContentObject,
|
||||
map ) );*/
|
||||
|
||||
m_tcoViews.erase( it );
|
||||
engine::getSong()->setModified();
|
||||
}
|
||||
@@ -1120,7 +1090,8 @@ void trackContentWidget::dropEvent( QDropEvent * _de )
|
||||
m_trackView->trackContainerView()->fixedTCOs() == false )
|
||||
{
|
||||
const MidiTime pos = getPosition( _de->pos().x()
|
||||
).toNearestTact();
|
||||
).getTact() * MidiTime::ticksPerTact();
|
||||
getTrack()->addJournalCheckPoint();
|
||||
trackContentObject * tco = getTrack()->createTCO( pos );
|
||||
|
||||
// value contains our XML-data so simply create a
|
||||
@@ -1209,71 +1180,6 @@ void trackContentWidget::resizeEvent( QResizeEvent * resizeEvent )
|
||||
|
||||
|
||||
|
||||
/*! \brief Undo an action on the trackContentWidget
|
||||
*
|
||||
* \param _je the details of the edit journal
|
||||
*/
|
||||
void trackContentWidget::undoStep( JournalEntry & _je )
|
||||
{
|
||||
saveJournallingState( false );
|
||||
switch( _je.actionID() )
|
||||
{
|
||||
case AddTrackContentObject:
|
||||
{
|
||||
QMap<QString, QVariant> map = _je.data().toMap();
|
||||
trackContentObject * tco =
|
||||
dynamic_cast<trackContentObject *>(
|
||||
engine::projectJournal()->journallingObject( map["id"].toInt() ) );
|
||||
DataFile dataFile( DataFile::JournalData );
|
||||
tco->saveState( dataFile, dataFile.content() );
|
||||
map["state"] = dataFile.toString();
|
||||
_je.data() = map;
|
||||
tco->deleteLater();
|
||||
break;
|
||||
}
|
||||
|
||||
case RemoveTrackContentObject:
|
||||
{
|
||||
trackContentObject * tco = getTrack()->createTCO( MidiTime( 0 ) );
|
||||
DataFile dataFile(
|
||||
_je.data().toMap()["state"].
|
||||
toString().toUtf8() );
|
||||
tco->restoreState( dataFile.content().firstChild().toElement() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
restoreJournallingState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*! \brief Redo an action of the trackContentWidget
|
||||
*
|
||||
* \param _je the entry in the edit journal to redo.
|
||||
*/
|
||||
void trackContentWidget::redoStep( JournalEntry & _je )
|
||||
{
|
||||
switch( _je.actionID() )
|
||||
{
|
||||
case AddTrackContentObject:
|
||||
case RemoveTrackContentObject:
|
||||
_je.actionID() = ( _je.actionID() ==
|
||||
AddTrackContentObject ) ?
|
||||
RemoveTrackContentObject :
|
||||
AddTrackContentObject;
|
||||
undoStep( _je );
|
||||
_je.actionID() = ( _je.actionID() ==
|
||||
AddTrackContentObject ) ?
|
||||
RemoveTrackContentObject :
|
||||
AddTrackContentObject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*! \brief Return the track shown by the trackContentWidget
|
||||
*
|
||||
*/
|
||||
@@ -1312,7 +1218,22 @@ MidiTime trackContentWidget::endPosition( const MidiTime & _pos_start )
|
||||
}
|
||||
|
||||
|
||||
// qproperty access methods
|
||||
//! \brief CSS theming qproperty access method
|
||||
QBrush trackContentWidget::darkerColor() const
|
||||
{ return m_darkerColor; }
|
||||
|
||||
//! \brief CSS theming qproperty access method
|
||||
QBrush trackContentWidget::lighterColor() const
|
||||
{ return m_lighterColor; }
|
||||
|
||||
//! \brief CSS theming qproperty access method
|
||||
void trackContentWidget::setDarkerColor( const QBrush & c )
|
||||
{ m_darkerColor = c; }
|
||||
|
||||
//! \brief CSS theming qproperty access method
|
||||
void trackContentWidget::setLighterColor( const QBrush & c )
|
||||
{ m_lighterColor = c; }
|
||||
|
||||
|
||||
|
||||
@@ -1484,6 +1405,14 @@ void trackOperationsWidget::cloneTrack()
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Clear this track - clears all TCOs from the track */
|
||||
void trackOperationsWidget::clearTrack()
|
||||
{
|
||||
engine::mixer()->lock();
|
||||
m_trackView->getTrack()->deleteTCOs();
|
||||
engine::mixer()->unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*! \brief Remove this track from the track list
|
||||
@@ -1501,6 +1430,9 @@ void trackOperationsWidget::removeTrack()
|
||||
*
|
||||
* For all track types, we have the Clone and Remove options.
|
||||
* For instrument-tracks we also offer the MIDI-control-menu
|
||||
* For automation tracks, extra options: turn on/off recording
|
||||
* on all TCOs (same should be added for sample tracks when
|
||||
* sampletrack recording is implemented)
|
||||
*/
|
||||
void trackOperationsWidget::updateMenu()
|
||||
{
|
||||
@@ -1512,6 +1444,11 @@ void trackOperationsWidget::updateMenu()
|
||||
to_menu->addAction( embed::getIconPixmap( "cancel", 16, 16 ),
|
||||
tr( "Remove this track" ),
|
||||
this, SLOT( removeTrack() ) );
|
||||
|
||||
if( ! m_trackView->trackContainerView()->fixedTCOs() )
|
||||
{
|
||||
to_menu->addAction( tr( "Clear this track" ), this, SLOT( clearTrack() ) );
|
||||
}
|
||||
|
||||
if( dynamic_cast<InstrumentTrackView *>( m_trackView ) )
|
||||
{
|
||||
@@ -1519,11 +1456,45 @@ void trackOperationsWidget::updateMenu()
|
||||
to_menu->addMenu( dynamic_cast<InstrumentTrackView *>(
|
||||
m_trackView )->midiMenu() );
|
||||
}
|
||||
if( dynamic_cast<AutomationTrackView *>( m_trackView ) )
|
||||
{
|
||||
to_menu->addAction( tr( "Turn all recording on" ), this, SLOT( recordingOn() ) );
|
||||
to_menu->addAction( tr( "Turn all recording off" ), this, SLOT( recordingOff() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void trackOperationsWidget::recordingOn()
|
||||
{
|
||||
AutomationTrackView * atv = dynamic_cast<AutomationTrackView *>( m_trackView );
|
||||
if( atv )
|
||||
{
|
||||
const track::tcoVector & tcov = atv->getTrack()->getTCOs();
|
||||
for( track::tcoVector::const_iterator it = tcov.begin(); it != tcov.end(); it++ )
|
||||
{
|
||||
AutomationPattern * ap = dynamic_cast<AutomationPattern *>( *it );
|
||||
if( ap ) { ap->setRecording( true ); }
|
||||
}
|
||||
atv->update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void trackOperationsWidget::recordingOff()
|
||||
{
|
||||
AutomationTrackView * atv = dynamic_cast<AutomationTrackView *>( m_trackView );
|
||||
if( atv )
|
||||
{
|
||||
const track::tcoVector & tcov = atv->getTrack()->getTCOs();
|
||||
for( track::tcoVector::const_iterator it = tcov.begin(); it != tcov.end(); it++ )
|
||||
{
|
||||
AutomationPattern * ap = dynamic_cast<AutomationPattern *>( *it );
|
||||
if( ap ) { ap->setRecording( false ); }
|
||||
}
|
||||
atv->update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
// track
|
||||
@@ -1815,6 +1786,14 @@ void track::removeTCO( trackContentObject * _tco )
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Remove all TCOs from this track */
|
||||
void track::deleteTCOs()
|
||||
{
|
||||
while( ! m_trackContentObjects.isEmpty() )
|
||||
{
|
||||
delete m_trackContentObjects.first();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Return the number of trackContentObjects we contain
|
||||
@@ -2227,55 +2206,6 @@ void trackView::modelChanged()
|
||||
|
||||
|
||||
|
||||
/*! \brief Undo a change to this track View.
|
||||
*
|
||||
* \param _je the Journal Entry to undo.
|
||||
*/
|
||||
void trackView::undoStep( JournalEntry & _je )
|
||||
{
|
||||
saveJournallingState( false );
|
||||
switch( _je.actionID() )
|
||||
{
|
||||
case MoveTrack:
|
||||
if( _je.data().toInt() > 0 )
|
||||
{
|
||||
m_trackContainerView->moveTrackViewUp( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_trackContainerView->moveTrackViewDown( this );
|
||||
}
|
||||
break;
|
||||
case ResizeTrack:
|
||||
setFixedHeight( qMax<int>( height() +
|
||||
_je.data().toInt(),
|
||||
MINIMAL_TRACK_HEIGHT ) );
|
||||
m_trackContainerView->realignTracks();
|
||||
break;
|
||||
/*case RestoreTrack:
|
||||
setFixedHeight( DEFAULT_TRACK_HEIGHT );
|
||||
m_trackContainerView->realignTracks();
|
||||
break; */
|
||||
}
|
||||
restoreJournallingState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*! \brief Redo a change to this track View.
|
||||
*
|
||||
* \param _je the Journal Event to redo.
|
||||
*/
|
||||
void trackView::redoStep( JournalEntry & _je )
|
||||
{
|
||||
JournalEntry je( _je.actionID(), -_je.data().toInt() );
|
||||
undoStep( je );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*! \brief Start a drag event on this track View.
|
||||
*
|
||||
* \param _dee the DragEnterEvent to start.
|
||||
@@ -2408,7 +2338,7 @@ void trackView::mouseMoveEvent( QMouseEvent * _me )
|
||||
|
||||
// debug code
|
||||
// qDebug( "y position %d", y_pos );
|
||||
|
||||
|
||||
// a track-widget not equal to ourself?
|
||||
if( track_at_y != NULL && track_at_y != this )
|
||||
{
|
||||
@@ -2421,7 +2351,6 @@ void trackView::mouseMoveEvent( QMouseEvent * _me )
|
||||
{
|
||||
m_trackContainerView->moveTrackViewDown( this );
|
||||
}
|
||||
addJournalEntry( JournalEntry( MoveTrack, _me->y() ) );
|
||||
}
|
||||
}
|
||||
else if( m_action == ResizeTrack )
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2011-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -41,7 +41,7 @@ AutomatableModelView::AutomatableModelView( ::Model* model, QWidget* _this ) :
|
||||
m_unit( QString::null )
|
||||
{
|
||||
widget()->setAcceptDrops( true );
|
||||
widget()->setCursor( QCursor( embed::getIconPixmap( "hand" ), 0, 0 ) );
|
||||
widget()->setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* AutomationEditor.cpp - implementation of AutomationEditor which is used for
|
||||
* actual setting of dynamic values
|
||||
* actual setting of dynamic values
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2008-2013 Paul Giblock <pgib/at/users.sourceforge.net>
|
||||
* Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -40,7 +40,6 @@
|
||||
#include <QToolTip>
|
||||
|
||||
|
||||
|
||||
#ifndef __USE_XOPEN
|
||||
#define __USE_XOPEN
|
||||
#endif
|
||||
@@ -63,7 +62,7 @@
|
||||
#include "bb_track_container.h"
|
||||
#include "PianoRoll.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "MeterModel.h"
|
||||
|
||||
|
||||
QPixmap * AutomationEditor::s_toolDraw = NULL;
|
||||
@@ -72,8 +71,6 @@ 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() :
|
||||
@@ -99,12 +96,17 @@ AutomationEditor::AutomationEditor() :
|
||||
m_y_delta( DEFAULT_Y_DELTA ),
|
||||
m_y_auto( TRUE ),
|
||||
m_editMode( DRAW ),
|
||||
m_scrollBack( FALSE )
|
||||
m_scrollBack( FALSE ),
|
||||
m_gridColor( 0,0,0 ),
|
||||
m_graphColor(),
|
||||
m_vertexColor( 0,0,0 ),
|
||||
m_scaleColor()
|
||||
{
|
||||
connect( this, SIGNAL( currentPatternChanged() ),
|
||||
this, SLOT( updateAfterPatternChange() ),
|
||||
Qt::QueuedConnection );
|
||||
|
||||
connect( engine::getSong(), SIGNAL( timeSignatureChanged( int, int ) ),
|
||||
this, SLOT( update() ) );
|
||||
// init pixmaps
|
||||
if( s_toolDraw == NULL )
|
||||
{
|
||||
@@ -358,7 +360,7 @@ AutomationEditor::AutomationEditor() :
|
||||
m_zoomingYComboBox->setFixedSize( 80, 22 );
|
||||
|
||||
m_zoomingYModel.addItem( "Auto" );
|
||||
for( int i = 0; i < 6; ++i )
|
||||
for( int i = 0; i < 7; ++i )
|
||||
{
|
||||
m_zoomingYModel.addItem( QString::number( 25 << i ) + "%" );
|
||||
}
|
||||
@@ -377,15 +379,13 @@ AutomationEditor::AutomationEditor() :
|
||||
m_quantizeComboBox = new comboBox( m_toolBar );
|
||||
m_quantizeComboBox->setFixedSize( 60, 22 );
|
||||
|
||||
// TODO: leak
|
||||
ComboBoxModel * quantize_model = new ComboBoxModel( /* this */ );
|
||||
for( int i = 0; i < 7; ++i )
|
||||
{
|
||||
quantize_model->addItem( "1/" + QString::number( 1 << i ) );
|
||||
m_quantizeModel.addItem( "1/" + QString::number( 1 << i ) );
|
||||
}
|
||||
quantize_model->setValue( quantize_model->findText( "1/16" ) );
|
||||
m_quantizeModel.setValue( m_quantizeModel.findText( "1/16" ) );
|
||||
|
||||
m_quantizeComboBox->setModel( quantize_model );
|
||||
m_quantizeComboBox->setModel( &m_quantizeModel );
|
||||
|
||||
|
||||
tb_layout->addSpacing( 5 );
|
||||
@@ -456,7 +456,10 @@ AutomationEditor::~AutomationEditor()
|
||||
{
|
||||
m_zoomingXModel.disconnect();
|
||||
m_zoomingYModel.disconnect();
|
||||
m_quantizeModel.disconnect();
|
||||
m_tensionModel->disconnect();
|
||||
|
||||
delete m_tensionModel;
|
||||
}
|
||||
|
||||
|
||||
@@ -502,6 +505,24 @@ void AutomationEditor::setPauseIcon( bool pause )
|
||||
}
|
||||
}
|
||||
|
||||
// qproperty access methods
|
||||
|
||||
QColor AutomationEditor::gridColor() const
|
||||
{ return m_gridColor; }
|
||||
QBrush AutomationEditor::graphColor() const
|
||||
{ return m_graphColor; }
|
||||
QColor AutomationEditor::vertexColor() const
|
||||
{ return m_vertexColor; }
|
||||
QBrush AutomationEditor::scaleColor() const
|
||||
{ return m_scaleColor; }
|
||||
void AutomationEditor::setGridColor( const QColor & c )
|
||||
{ m_gridColor = c; }
|
||||
void AutomationEditor::setGraphColor( const QBrush & c )
|
||||
{ m_graphColor = c; }
|
||||
void AutomationEditor::setVertexColor( const QColor & c )
|
||||
{ m_vertexColor = c; }
|
||||
void AutomationEditor::setScaleColor( const QBrush & c )
|
||||
{ m_scaleColor = c; }
|
||||
|
||||
|
||||
|
||||
@@ -820,7 +841,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent * _me )
|
||||
x -= VALUES_WIDTH;
|
||||
|
||||
// get tick in which the user clicked
|
||||
int pos_ticks = x * DefaultTicksPerTact / m_ppt +
|
||||
int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt +
|
||||
m_currentPosition;
|
||||
|
||||
// get time map of current pattern
|
||||
@@ -840,7 +861,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent * _me )
|
||||
len > 0 &&
|
||||
( it+1==time_map.end() ||
|
||||
pos_ticks <= (it+1).key() ) &&
|
||||
( pos_ticks<= it.key() + DefaultTicksPerTact *4 / m_ppt ) &&
|
||||
( pos_ticks<= it.key() + MidiTime::ticksPerTact() *4 / m_ppt ) &&
|
||||
level <= it.value() )
|
||||
{
|
||||
break;
|
||||
@@ -884,7 +905,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent * _me )
|
||||
int aligned_x = (int)( (float)( (
|
||||
it.key() -
|
||||
m_currentPosition ) *
|
||||
m_ppt ) / DefaultTicksPerTact );
|
||||
m_ppt ) / MidiTime::ticksPerTact() );
|
||||
m_moveXOffset = x - aligned_x - 1;
|
||||
// set move-cursor
|
||||
QCursor c( Qt::SizeAllCursor );
|
||||
@@ -993,7 +1014,7 @@ void AutomationEditor::mouseMoveEvent( QMouseEvent * _me )
|
||||
x -= m_moveXOffset;
|
||||
}
|
||||
|
||||
int pos_ticks = x * DefaultTicksPerTact / m_ppt +
|
||||
int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt +
|
||||
m_currentPosition;
|
||||
if( _me->buttons() & Qt::LeftButton && m_editMode == DRAW )
|
||||
{
|
||||
@@ -1117,7 +1138,7 @@ void AutomationEditor::mouseMoveEvent( QMouseEvent * _me )
|
||||
}
|
||||
|
||||
// get tick in which the cursor is posated
|
||||
int pos_ticks = x * DefaultTicksPerTact / m_ppt +
|
||||
int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt +
|
||||
m_currentPosition;
|
||||
|
||||
m_selectedTick = pos_ticks - m_selectStartTick;
|
||||
@@ -1138,7 +1159,7 @@ void AutomationEditor::mouseMoveEvent( QMouseEvent * _me )
|
||||
// move selection + selected values
|
||||
|
||||
// do horizontal move-stuff
|
||||
int pos_ticks = x * DefaultTicksPerTact / m_ppt +
|
||||
int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt +
|
||||
m_currentPosition;
|
||||
int ticks_diff = pos_ticks -
|
||||
m_moveStartTick;
|
||||
@@ -1163,8 +1184,8 @@ void AutomationEditor::mouseMoveEvent( QMouseEvent * _me )
|
||||
}
|
||||
m_selectStartTick += ticks_diff;
|
||||
|
||||
int tact_diff = ticks_diff / DefaultTicksPerTact;
|
||||
ticks_diff = ticks_diff % DefaultTicksPerTact;
|
||||
int tact_diff = ticks_diff / MidiTime::ticksPerTact();
|
||||
ticks_diff = ticks_diff % MidiTime::ticksPerTact();
|
||||
|
||||
|
||||
// do vertical move-stuff
|
||||
@@ -1214,19 +1235,19 @@ void AutomationEditor::mouseMoveEvent( QMouseEvent * _me )
|
||||
{
|
||||
int value_tact =
|
||||
( it.key() /
|
||||
DefaultTicksPerTact )
|
||||
MidiTime::ticksPerTact() )
|
||||
+ tact_diff;
|
||||
int value_ticks =
|
||||
( it.key() %
|
||||
DefaultTicksPerTact )
|
||||
MidiTime::ticksPerTact() )
|
||||
+ ticks_diff;
|
||||
// ensure value_ticks range
|
||||
if( value_ticks / DefaultTicksPerTact )
|
||||
if( value_ticks / MidiTime::ticksPerTact() )
|
||||
{
|
||||
value_tact += value_ticks
|
||||
/ DefaultTicksPerTact;
|
||||
/ MidiTime::ticksPerTact();
|
||||
value_ticks %=
|
||||
DefaultTicksPerTact;
|
||||
MidiTime::ticksPerTact();
|
||||
}
|
||||
m_pattern->removeValue( it.key() );
|
||||
new_value_pos = MidiTime( value_tact,
|
||||
@@ -1277,7 +1298,7 @@ void AutomationEditor::mouseMoveEvent( QMouseEvent * _me )
|
||||
}
|
||||
|
||||
// get tick in which the cursor is posated
|
||||
int pos_ticks = x * DefaultTicksPerTact / m_ppt +
|
||||
int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt +
|
||||
m_currentPosition;
|
||||
|
||||
m_selectedTick = pos_ticks -
|
||||
@@ -1340,7 +1361,8 @@ inline void AutomationEditor::drawCross( QPainter & _p )
|
||||
QPoint tt_pos = QCursor::pos();
|
||||
tt_pos.ry() -= 64;
|
||||
tt_pos.rx() += 32;
|
||||
QToolTip::showText( tt_pos,QString::number( level ),this);
|
||||
float scaledLevel = m_pattern->firstObject()->scaledValue( level );
|
||||
QToolTip::showText( tt_pos, QString::number( scaledLevel ), this );
|
||||
}
|
||||
|
||||
|
||||
@@ -1350,13 +1372,10 @@ inline void AutomationEditor::drawAutomationPoint( QPainter & p, timeMap::iterat
|
||||
{
|
||||
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() );
|
||||
const int outerRadius = qBound( 2, ( m_ppt * quantization() ) / 576, 5 ); // man, getting this calculation right took forever
|
||||
p.setPen( QPen( vertexColor().lighter( 200 ) ) );
|
||||
p.setBrush( QBrush( vertexColor() ) );
|
||||
p.drawEllipse( x - outerRadius, y - outerRadius, outerRadius * 2, outerRadius * 2 );
|
||||
}
|
||||
|
||||
|
||||
@@ -1371,6 +1390,12 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
QPainter p( this );
|
||||
style()->drawPrimitive( QStyle::PE_Widget, &opt, &p, this );
|
||||
|
||||
// get foreground color
|
||||
QColor fgColor = p.pen().brush().color();
|
||||
// get background color and fill background
|
||||
QBrush bgColor = p.background();
|
||||
p.fillRect( 0, 0, width(), height(), bgColor );
|
||||
|
||||
// set font-size to 8
|
||||
p.setFont( pointSize<8>( p.font() ) );
|
||||
|
||||
@@ -1380,7 +1405,7 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
int grid_bottom = height() - SCROLLBAR_SIZE - 1;
|
||||
|
||||
p.fillRect( 0, TOP_MARGIN, VALUES_WIDTH, height() - TOP_MARGIN,
|
||||
QColor( 0x33, 0x33, 0x33 ) );
|
||||
scaleColor() );
|
||||
|
||||
// print value numbers
|
||||
int font_height = p.fontMetrics().height();
|
||||
@@ -1397,11 +1422,12 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
{
|
||||
const QString & label = m_pattern->firstObject()
|
||||
->displayValue( level[i] );
|
||||
p.setPen( QColor( 240, 240, 240 ) );
|
||||
p.setPen( QApplication::palette().color( QPalette::Active,
|
||||
QPalette::Shadow ) );
|
||||
p.drawText( 1, y[i] - font_height + 1,
|
||||
VALUES_WIDTH - 10, 2 * font_height,
|
||||
text_flags, label );
|
||||
p.setPen( QColor( 0, 0, 0 ) );
|
||||
p.setPen( fgColor );
|
||||
p.drawText( 0, y[i] - font_height,
|
||||
VALUES_WIDTH - 10, 2 * font_height,
|
||||
text_flags, label );
|
||||
@@ -1425,11 +1451,12 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
const QString & label = m_pattern->firstObject()
|
||||
->displayValue( level );
|
||||
y = yCoordOfLevel( level );
|
||||
p.setPen( QColor( 240, 240, 240 ) );
|
||||
p.setPen( QApplication::palette().color( QPalette::Active,
|
||||
QPalette::Shadow ) );
|
||||
p.drawText( 1, y - font_height + 1,
|
||||
VALUES_WIDTH - 10, 2 * font_height,
|
||||
text_flags, label );
|
||||
p.setPen( QColor( 0, 0, 0 ) );
|
||||
p.setPen( fgColor );
|
||||
p.drawText( 0, y - font_height,
|
||||
VALUES_WIDTH - 10, 2 * font_height,
|
||||
text_flags, label );
|
||||
@@ -1443,46 +1470,54 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
grid_height );
|
||||
|
||||
// draw vertical raster
|
||||
int tact_16th = m_currentPosition / ( DefaultTicksPerTact / 16 );
|
||||
const int offset = ( m_currentPosition % (DefaultTicksPerTact/16) ) *
|
||||
m_ppt / DEFAULT_STEPS_PER_TACT / 8;
|
||||
|
||||
QColor lineColor = QColor( gridColor() );
|
||||
if( m_pattern )
|
||||
{
|
||||
int tick, x;
|
||||
int x_line_end = (int)( m_y_auto || m_topLevel < m_maxLevel ?
|
||||
TOP_MARGIN :
|
||||
grid_bottom - ( m_topLevel - m_bottomLevel )
|
||||
* m_y_delta );
|
||||
|
||||
for( int x = VALUES_WIDTH - offset; x < width();
|
||||
x += m_ppt / DEFAULT_STEPS_PER_TACT, ++tact_16th )
|
||||
// 3 independent loops, because quantization might not divide evenly into
|
||||
// exotic denominators (e.g. 7/11 time), which are allowed ATM.
|
||||
// First quantization grid...
|
||||
for( tick = m_currentPosition - m_currentPosition % quantization(),
|
||||
x = xCoordOfTick( tick );
|
||||
x<=width();
|
||||
tick += quantization(), x = xCoordOfTick( tick ) )
|
||||
{
|
||||
if( x >= VALUES_WIDTH )
|
||||
{
|
||||
// every tact-start needs to be a bright line
|
||||
if( tact_16th % 16 == 0 )
|
||||
{
|
||||
p.setPen( QColor( 0x7F, 0x7F, 0x7F ) );
|
||||
}
|
||||
// normal line
|
||||
else if( tact_16th % 4 == 0 )
|
||||
{
|
||||
p.setPen( QColor( 0x5F, 0x5F, 0x5F ) );
|
||||
}
|
||||
// weak line
|
||||
else
|
||||
{
|
||||
p.setPen( QColor( 0x3F, 0x3F, 0x3F ) );
|
||||
}
|
||||
p.drawLine( x, grid_bottom, x, x_line_end );
|
||||
}
|
||||
lineColor.setAlpha( 80 );
|
||||
p.setPen( lineColor );
|
||||
p.drawLine( x, grid_bottom, x, x_line_end );
|
||||
}
|
||||
// Then beat grid
|
||||
int ticksPerBeat = DefaultTicksPerTact /
|
||||
engine::getSong()->getTimeSigModel().getDenominator();
|
||||
for( tick = m_currentPosition - m_currentPosition % ticksPerBeat,
|
||||
x = xCoordOfTick( tick );
|
||||
x<=width();
|
||||
tick += ticksPerBeat, x = xCoordOfTick( tick ) )
|
||||
{
|
||||
lineColor.setAlpha( 160 );
|
||||
p.setPen( lineColor );
|
||||
p.drawLine( x, grid_bottom, x, x_line_end );
|
||||
}
|
||||
// and finally bars
|
||||
for( tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerTact(),
|
||||
x = xCoordOfTick( tick );
|
||||
x<=width();
|
||||
tick += MidiTime::ticksPerTact(), x = xCoordOfTick( tick ) )
|
||||
{
|
||||
lineColor.setAlpha( 255 );
|
||||
p.setPen( lineColor );
|
||||
p.drawLine( x, grid_bottom, x, x_line_end );
|
||||
}
|
||||
|
||||
// TODO: move this horizontal line drawing code into the same loop as
|
||||
// the value ticks?
|
||||
/// \todo move this horizontal line drawing code into the same loop as the value ticks?
|
||||
if( m_y_auto )
|
||||
{
|
||||
QPen pen( QColor( 0x4F, 0x4F, 0x4F ) );
|
||||
lineColor.setAlpha( 160 );
|
||||
QPen pen( lineColor );
|
||||
p.setPen( pen );
|
||||
p.drawLine( VALUES_WIDTH, grid_bottom, width(),
|
||||
grid_bottom );
|
||||
@@ -1503,11 +1538,13 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
y = yCoordOfLevel( (float)level );
|
||||
if( level % 5 == 0 )
|
||||
{
|
||||
p.setPen( QColor( 0x4F, 0x4F, 0x4F ) );
|
||||
lineColor.setAlpha( 160 );
|
||||
p.setPen( lineColor );
|
||||
}
|
||||
else
|
||||
{
|
||||
p.setPen( QColor( 0x3F, 0x3F, 0x3F ) );
|
||||
lineColor.setAlpha( 80 );
|
||||
p.setPen( lineColor );
|
||||
}
|
||||
|
||||
// draw level line
|
||||
@@ -1544,8 +1581,7 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
//Don't bother doing/rendering anything if there is no automation points
|
||||
if( time_map.size() > 0 )
|
||||
{
|
||||
timeMap::iterator it = time_map.begin();
|
||||
p.setPen( QColor( 0xCF, 0xD9, 0xFF ) );
|
||||
timeMap::iterator it = time_map.begin();
|
||||
while( it+1 != time_map.end() )
|
||||
{
|
||||
// skip this section if it occurs completely before the
|
||||
@@ -1582,10 +1618,11 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
@@ -1597,8 +1634,6 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
++it;
|
||||
}
|
||||
|
||||
Q_ASSERT( it == time_map.end()-1 );
|
||||
|
||||
for( int i = it.key(), x = xCoordOfTick( i ); x <= width();
|
||||
i++, x = xCoordOfTick( i ) )
|
||||
{
|
||||
@@ -1616,7 +1651,8 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
QFont f = p.font();
|
||||
f.setBold( TRUE );
|
||||
p.setFont( pointSize<14>( f ) );
|
||||
p.setPen( QColor( 74, 253, 133 ) );
|
||||
p.setPen( QApplication::palette().color( QPalette::Active,
|
||||
QPalette::BrightText ) );
|
||||
p.drawText( VALUES_WIDTH + 20, TOP_MARGIN + 40,
|
||||
width() - VALUES_WIDTH - 20 - SCROLLBAR_SIZE,
|
||||
grid_height - 40, Qt::TextWordWrap,
|
||||
@@ -1626,8 +1662,8 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
|
||||
// now draw selection-frame
|
||||
int x = ( sel_pos_start - m_currentPosition ) * m_ppt /
|
||||
DefaultTicksPerTact;
|
||||
int w = ( sel_pos_end - sel_pos_start ) * m_ppt / DefaultTicksPerTact;
|
||||
MidiTime::ticksPerTact();
|
||||
int w = ( sel_pos_end - sel_pos_start ) * m_ppt / MidiTime::ticksPerTact();
|
||||
int y, h;
|
||||
if( m_y_auto )
|
||||
{
|
||||
@@ -1681,7 +1717,7 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe )
|
||||
int AutomationEditor::xCoordOfTick( int _tick )
|
||||
{
|
||||
return VALUES_WIDTH + ( ( _tick - m_currentPosition )
|
||||
* m_ppt / DefaultTicksPerTact );
|
||||
* m_ppt / MidiTime::ticksPerTact() );
|
||||
}
|
||||
|
||||
|
||||
@@ -1735,17 +1771,18 @@ void AutomationEditor::drawLevelTick( QPainter & _p, int _tick, float _level,
|
||||
rect_height = (int)( _level * m_y_delta );
|
||||
}
|
||||
|
||||
QColor current_color( 0x9F, 0xAF, 0xFF );
|
||||
if( _is_selected == TRUE )
|
||||
{
|
||||
current_color.setRgb( 0x00, 0x40, 0xC0 );
|
||||
}
|
||||
_p.fillRect( x, y_start, rect_width, rect_height, current_color );
|
||||
QBrush currentColor = _is_selected
|
||||
? QBrush( QColor( 0x00, 0x40, 0xC0 ) )
|
||||
: graphColor();
|
||||
|
||||
_p.fillRect( x, y_start, rect_width, rect_height, currentColor );
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
printf("not in range\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1797,26 +1834,49 @@ void AutomationEditor::resizeEvent( QResizeEvent * )
|
||||
void AutomationEditor::wheelEvent( QWheelEvent * _we )
|
||||
{
|
||||
_we->accept();
|
||||
if( _we->modifiers() & Qt::ControlModifier )
|
||||
if( _we->modifiers() & Qt::ControlModifier && _we->modifiers() & Qt::ShiftModifier )
|
||||
{
|
||||
int y = m_zoomingYModel.value();
|
||||
if( _we->delta() > 0 )
|
||||
{
|
||||
m_ppt = qMin( m_ppt * 2, m_y_delta *
|
||||
DEFAULT_STEPS_PER_TACT * 8 );
|
||||
y++;
|
||||
}
|
||||
else if( m_ppt >= 72 )
|
||||
if( _we->delta() < 0 )
|
||||
{
|
||||
m_ppt /= 2;
|
||||
y--;
|
||||
}
|
||||
// update combobox with zooming-factor
|
||||
m_zoomingXComboBox->model()->setValue(
|
||||
m_zoomingXComboBox->model()->findText( QString::number(
|
||||
qRound( m_ppt * 100 /
|
||||
DEFAULT_PPT ) ) +"%" ) );
|
||||
// update timeline
|
||||
m_timeLine->setPixelsPerTact( m_ppt );
|
||||
y = qBound( 0, y, m_zoomingYModel.size() - 1 );
|
||||
m_zoomingYModel.setValue( y );
|
||||
}
|
||||
else if( _we->modifiers() & Qt::ControlModifier && _we->modifiers() & Qt::AltModifier )
|
||||
{
|
||||
int q = m_quantizeModel.value();
|
||||
if( _we->delta() > 0 )
|
||||
{
|
||||
q--;
|
||||
}
|
||||
if( _we->delta() < 0 )
|
||||
{
|
||||
q++;
|
||||
}
|
||||
q = qBound( 0, q, m_quantizeModel.size() - 1 );
|
||||
m_quantizeModel.setValue( q );
|
||||
update();
|
||||
}
|
||||
else if( _we->modifiers() & Qt::ControlModifier )
|
||||
{
|
||||
int x = m_zoomingXModel.value();
|
||||
if( _we->delta() > 0 )
|
||||
{
|
||||
x++;
|
||||
}
|
||||
if( _we->delta() < 0 )
|
||||
{
|
||||
x--;
|
||||
}
|
||||
x = qBound( 0, x, m_zoomingXModel.size() - 1 );
|
||||
m_zoomingXModel.setValue( x );
|
||||
}
|
||||
else if( _we->modifiers() & Qt::ShiftModifier
|
||||
|| _we->orientation() == Qt::Horizontal )
|
||||
{
|
||||
@@ -1839,17 +1899,10 @@ float AutomationEditor::getLevel( int _y )
|
||||
// pressed level
|
||||
float level = roundf( ( m_bottomLevel + ( m_y_auto ?
|
||||
( m_maxLevel - m_minLevel ) * ( level_line_y - _y )
|
||||
/ (float)( level_line_y - TOP_MARGIN ) :
|
||||
/ (float)( level_line_y - ( TOP_MARGIN + 2 ) ) :
|
||||
( level_line_y - _y ) / (float)m_y_delta ) ) / m_step ) * m_step;
|
||||
// some range-checking-stuff
|
||||
if( level < m_bottomLevel )
|
||||
{
|
||||
level = m_bottomLevel;
|
||||
}
|
||||
else if( level > m_topLevel )
|
||||
{
|
||||
level = m_topLevel;
|
||||
}
|
||||
level = qBound( m_bottomLevel, level, m_topLevel );
|
||||
|
||||
return( level );
|
||||
}
|
||||
@@ -1881,7 +1934,7 @@ void AutomationEditor::play()
|
||||
if( engine::getSong()->playMode() != song::Mode_PlayPattern )
|
||||
{
|
||||
engine::getSong()->stop();
|
||||
engine::getSong()->playPattern( (pattern *) engine::pianoRoll()->currentPattern() );
|
||||
engine::getSong()->playPattern( (Pattern *) engine::pianoRoll()->currentPattern() );
|
||||
}
|
||||
else if( engine::getSong()->isStopped() == false )
|
||||
{
|
||||
@@ -1889,7 +1942,7 @@ void AutomationEditor::play()
|
||||
}
|
||||
else
|
||||
{
|
||||
engine::getSong()->playPattern( (pattern *) engine::pianoRoll()->currentPattern() );
|
||||
engine::getSong()->playPattern( (Pattern *) engine::pianoRoll()->currentPattern() );
|
||||
}
|
||||
}
|
||||
else if( inBBEditor() )
|
||||
@@ -2126,7 +2179,7 @@ void AutomationEditor::getSelectedValues( timeMap & _selected_values )
|
||||
++it )
|
||||
{
|
||||
//TODO: Add constant
|
||||
tick_t len_ticks = DefaultTicksPerTact / 16;
|
||||
tick_t len_ticks = MidiTime::ticksPerTact() / 16;
|
||||
|
||||
float level = it.value();
|
||||
tick_t pos_ticks = it.key();
|
||||
@@ -2260,17 +2313,17 @@ void AutomationEditor::updatePosition( const MidiTime & _t )
|
||||
m_scrollBack == TRUE )
|
||||
{
|
||||
const int w = width() - VALUES_WIDTH;
|
||||
if( _t > m_currentPosition + w * DefaultTicksPerTact / m_ppt )
|
||||
if( _t > m_currentPosition + w * MidiTime::ticksPerTact() / m_ppt )
|
||||
{
|
||||
m_leftRightScroll->setValue( _t.getTact() *
|
||||
DefaultTicksPerTact );
|
||||
MidiTime::ticksPerTact() );
|
||||
}
|
||||
else if( _t < m_currentPosition )
|
||||
{
|
||||
MidiTime t = qMax( _t - w * DefaultTicksPerTact *
|
||||
DefaultTicksPerTact / m_ppt, 0 );
|
||||
MidiTime t = qMax( _t - w * MidiTime::ticksPerTact() *
|
||||
MidiTime::ticksPerTact() / m_ppt, 0 );
|
||||
m_leftRightScroll->setValue( t.getTact() *
|
||||
DefaultTicksPerTact );
|
||||
MidiTime::ticksPerTact() );
|
||||
}
|
||||
m_scrollBack = FALSE;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "tooltip.h"
|
||||
|
||||
|
||||
QPixmap * AutomationPatternView::s_pat_rec = NULL;
|
||||
|
||||
AutomationPatternView::AutomationPatternView( AutomationPattern * _pattern,
|
||||
trackView * _parent ) :
|
||||
@@ -58,6 +59,9 @@ AutomationPatternView::AutomationPatternView( AutomationPattern * _pattern,
|
||||
toolTip::add( this, tr( "double-click to open this pattern in "
|
||||
"automation editor" ) );
|
||||
setStyle( QApplication::style() );
|
||||
|
||||
if( s_pat_rec == NULL ) { s_pat_rec = new QPixmap( embed::getIconPixmap(
|
||||
"pat_rec" ) ); }
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +138,11 @@ void AutomationPatternView::disconnectObject( QAction * _a )
|
||||
}
|
||||
|
||||
|
||||
void AutomationPatternView::toggleRecording()
|
||||
{
|
||||
m_pat->setRecording( ! m_pat->isRecording() );
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
void AutomationPatternView::constructContextMenu( QMenu * _cm )
|
||||
@@ -156,6 +165,9 @@ void AutomationPatternView::constructContextMenu( QMenu * _cm )
|
||||
_cm->addAction( embed::getIconPixmap( "edit_rename" ),
|
||||
tr( "Change name" ),
|
||||
this, SLOT( changeName() ) );
|
||||
_cm->addAction( embed::getIconPixmap( "record" ),
|
||||
tr( "Set/clear record" ),
|
||||
this, SLOT( toggleRecording() ) );
|
||||
if( !m_pat->m_objects.isEmpty() )
|
||||
{
|
||||
_cm->addSeparator();
|
||||
@@ -236,12 +248,6 @@ void AutomationPatternView::paintEvent( QPaintEvent * )
|
||||
p.setPen( c.lighter( 130 ) );
|
||||
p.drawRect( 1, 1, width()-3, height()-3 );
|
||||
|
||||
p.setBrush( QBrush() );
|
||||
if( engine::automationEditor()->currentPattern() == m_pat )
|
||||
p.setPen( c.lighter( 130 ) );
|
||||
else
|
||||
p.setPen( c.darker( 300 ) );
|
||||
p.drawRect( 0, 0, width()-1, height()-1 );
|
||||
|
||||
const float ppt = fixedTCOs() ?
|
||||
( parentWidget()->width() - 2 * TCO_BORDER_WIDTH )
|
||||
@@ -273,8 +279,9 @@ void AutomationPatternView::paintEvent( QPaintEvent * )
|
||||
|
||||
QLinearGradient lin2grad( 0, min, 0, max );
|
||||
|
||||
lin2grad.setColorAt( 1, c.lighter( 200 ) );
|
||||
lin2grad.setColorAt( 0, c );
|
||||
lin2grad.setColorAt( 1, fgColor().lighter( 150 ) );
|
||||
lin2grad.setColorAt( 0.5, fgColor() );
|
||||
lin2grad.setColorAt( 0, fgColor().darker( 150 ) );
|
||||
|
||||
for( AutomationPattern::timeMap::const_iterator it =
|
||||
m_pat->getTimeMap().begin();
|
||||
@@ -309,11 +316,27 @@ void AutomationPatternView::paintEvent( QPaintEvent * )
|
||||
}
|
||||
|
||||
p.resetMatrix();
|
||||
|
||||
// recording icon for when recording automation
|
||||
if( m_pat->isRecording() )
|
||||
{
|
||||
p.drawPixmap( 4, 14, *s_pat_rec );
|
||||
}
|
||||
|
||||
// outer edge
|
||||
p.setBrush( QBrush() );
|
||||
if( engine::automationEditor()->currentPattern() == m_pat )
|
||||
p.setPen( c.lighter( 130 ) );
|
||||
else
|
||||
p.setPen( c.darker( 300 ) );
|
||||
p.drawRect( 0, 0, width()-1, height()-1 );
|
||||
|
||||
// pattern name
|
||||
p.setFont( pointSize<8>( p.font() ) );
|
||||
|
||||
QColor text_color = ( m_pat->isMuted() || m_pat->getTrack()->isMuted() )
|
||||
? QColor( 30, 30, 30 )
|
||||
: QColor( 255, 255, 255 );
|
||||
: textColor();
|
||||
|
||||
p.setPen( QColor( 0, 0, 0 ) );
|
||||
p.drawText( 4, p.fontMetrics().height()+1, m_pat->name() );
|
||||
@@ -326,6 +349,7 @@ void AutomationPatternView::paintEvent( QPaintEvent * )
|
||||
embed::getIconPixmap( "muted", 16, 16 ) );
|
||||
}
|
||||
|
||||
|
||||
p.end();
|
||||
|
||||
_p.drawPixmap( 0, 0, m_paintPixmap );
|
||||
@@ -367,14 +391,6 @@ 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
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual void processInEvent( const MidiEvent& event, const MidiTime& time )
|
||||
virtual void processInEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset = 0 )
|
||||
{
|
||||
if( event.type() == MidiControlChange &&
|
||||
( m_midiPort.inputChannel() == 0 || m_midiPort.inputChannel() == event.channel() + 1 ) )
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -128,6 +128,7 @@ EffectSelectDialog::EffectSelectDialog( QWidget * _parent ) :
|
||||
|
||||
EffectSelectDialog::~EffectSelectDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
* file_browser.cpp - implementation of the project-, preset- and
|
||||
* FileBrowser.cpp - implementation of the project-, preset- and
|
||||
* sample-file-browser
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <QtGui/QMdiArea>
|
||||
#include <QtGui/QMdiSubWindow>
|
||||
|
||||
#include "file_browser.h"
|
||||
#include "FileBrowser.h"
|
||||
#include "bb_track_container.h"
|
||||
#include "config_mgr.h"
|
||||
#include "debug.h"
|
||||
@@ -60,16 +60,16 @@ enum TreeWidgetItemTypes
|
||||
|
||||
|
||||
|
||||
fileBrowser::fileBrowser( const QString & _directories, const QString & _filter,
|
||||
const QString & _title, const QPixmap & _pm,
|
||||
QWidget * _parent, bool _dirs_as_items ) :
|
||||
SideBarWidget( _title, _pm, _parent ),
|
||||
m_directories( _directories ),
|
||||
m_filter( _filter ),
|
||||
m_dirsAsItems( _dirs_as_items )
|
||||
FileBrowser::FileBrowser(const QString & directories, const QString & filter,
|
||||
const QString & title, const QPixmap & pm,
|
||||
QWidget * parent, bool dirs_as_items ) :
|
||||
SideBarWidget( title, pm, parent ),
|
||||
m_directories( directories ),
|
||||
m_filter( filter ),
|
||||
m_dirsAsItems( dirs_as_items )
|
||||
{
|
||||
setWindowTitle( tr( "Browser" ) );
|
||||
m_l = new fileBrowserTreeWidget( contentParent() );
|
||||
m_l = new FileBrowserTreeWidget( contentParent() );
|
||||
addContentWidget( m_l );
|
||||
|
||||
QWidget * ops = new QWidget( contentParent() );
|
||||
@@ -101,16 +101,16 @@ fileBrowser::fileBrowser( const QString & _directories, const QString & _filter,
|
||||
|
||||
|
||||
|
||||
fileBrowser::~fileBrowser()
|
||||
FileBrowser::~FileBrowser()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void fileBrowser::filterItems( const QString & _filter )
|
||||
void FileBrowser::filterItems( const QString & filter )
|
||||
{
|
||||
const bool show_all = _filter.isEmpty();
|
||||
const bool show_all = filter.isEmpty();
|
||||
|
||||
for( int i = 0; i < m_l->topLevelItemCount(); ++i )
|
||||
{
|
||||
@@ -121,7 +121,7 @@ void fileBrowser::filterItems( const QString & _filter )
|
||||
it->setHidden( false );
|
||||
if( it->childCount() )
|
||||
{
|
||||
filterItems( it, _filter );
|
||||
filterItems( it, filter );
|
||||
}
|
||||
}
|
||||
// is directory?
|
||||
@@ -129,7 +129,7 @@ void fileBrowser::filterItems( const QString & _filter )
|
||||
{
|
||||
// matches filter?
|
||||
if( it->text( 0 ).
|
||||
contains( _filter, Qt::CaseInsensitive ) )
|
||||
contains( filter, Qt::CaseInsensitive ) )
|
||||
{
|
||||
// yes, then show everything below
|
||||
it->setHidden( false );
|
||||
@@ -138,7 +138,7 @@ void fileBrowser::filterItems( const QString & _filter )
|
||||
else
|
||||
{
|
||||
// only show if item below matches filter
|
||||
it->setHidden( !filterItems( it, _filter ) );
|
||||
it->setHidden( !filterItems( it, filter ) );
|
||||
}
|
||||
}
|
||||
// a standard item (i.e. no file or directory item?)
|
||||
@@ -151,7 +151,7 @@ void fileBrowser::filterItems( const QString & _filter )
|
||||
{
|
||||
// file matches filter?
|
||||
it->setHidden( !it->text( 0 ).
|
||||
contains( _filter, Qt::CaseInsensitive ) );
|
||||
contains( filter, Qt::CaseInsensitive ) );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -160,14 +160,14 @@ void fileBrowser::filterItems( const QString & _filter )
|
||||
|
||||
|
||||
|
||||
bool fileBrowser::filterItems( QTreeWidgetItem * _item, const QString & _filter )
|
||||
bool FileBrowser::filterItems(QTreeWidgetItem * item, const QString & filter )
|
||||
{
|
||||
const bool show_all = _filter.isEmpty();
|
||||
const bool show_all = filter.isEmpty();
|
||||
bool matched = false;
|
||||
|
||||
for( int i = 0; i < _item->childCount(); ++i )
|
||||
for( int i = 0; i < item->childCount(); ++i )
|
||||
{
|
||||
QTreeWidgetItem * it = _item->child( i );
|
||||
QTreeWidgetItem * it = item->child( i );
|
||||
bool cm = false; // whether current item matched
|
||||
// show all items if filter is empty
|
||||
if( show_all )
|
||||
@@ -175,7 +175,7 @@ bool fileBrowser::filterItems( QTreeWidgetItem * _item, const QString & _filter
|
||||
it->setHidden( false );
|
||||
if( it->childCount() )
|
||||
{
|
||||
filterItems( it, _filter );
|
||||
filterItems( it, filter );
|
||||
}
|
||||
}
|
||||
// is directory?
|
||||
@@ -183,7 +183,7 @@ bool fileBrowser::filterItems( QTreeWidgetItem * _item, const QString & _filter
|
||||
{
|
||||
// matches filter?
|
||||
if( it->text( 0 ).
|
||||
contains( _filter, Qt::CaseInsensitive ) )
|
||||
contains( filter, Qt::CaseInsensitive ) )
|
||||
{
|
||||
// yes, then show everything below
|
||||
it->setHidden( false );
|
||||
@@ -193,7 +193,7 @@ bool fileBrowser::filterItems( QTreeWidgetItem * _item, const QString & _filter
|
||||
else
|
||||
{
|
||||
// only show if item below matches filter
|
||||
cm = filterItems( it, _filter );
|
||||
cm = filterItems( it, filter );
|
||||
it->setHidden( !cm );
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,7 @@ bool fileBrowser::filterItems( QTreeWidgetItem * _item, const QString & _filter
|
||||
{
|
||||
// file matches filter?
|
||||
cm = it->text( 0 ).
|
||||
contains( _filter, Qt::CaseInsensitive );
|
||||
contains( filter, Qt::CaseInsensitive );
|
||||
it->setHidden( !cm );
|
||||
}
|
||||
|
||||
@@ -223,8 +223,9 @@ bool fileBrowser::filterItems( QTreeWidgetItem * _item, const QString & _filter
|
||||
|
||||
|
||||
|
||||
void fileBrowser::reloadTree( void )
|
||||
void FileBrowser::reloadTree( void )
|
||||
{
|
||||
const QString text = m_filterEdit->text();
|
||||
m_filterEdit->clear();
|
||||
m_l->clear();
|
||||
QStringList paths = m_directories.split( '*' );
|
||||
@@ -232,21 +233,23 @@ void fileBrowser::reloadTree( void )
|
||||
{
|
||||
addItems( *it );
|
||||
}
|
||||
m_filterEdit->setText( text );
|
||||
filterItems( text );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void fileBrowser::addItems( const QString & _path )
|
||||
void FileBrowser::addItems(const QString & path )
|
||||
{
|
||||
if( m_dirsAsItems )
|
||||
{
|
||||
m_l->addTopLevelItem( new directory( _path,
|
||||
m_l->addTopLevelItem( new Directory( path,
|
||||
QString::null, m_filter ) );
|
||||
return;
|
||||
}
|
||||
|
||||
QDir cdir( _path );
|
||||
QDir cdir( path );
|
||||
QStringList files = cdir.entryList( QDir::Dirs, QDir::Name );
|
||||
for( QStringList::const_iterator it = files.constBegin();
|
||||
it != files.constEnd(); ++it )
|
||||
@@ -257,27 +260,27 @@ void fileBrowser::addItems( const QString & _path )
|
||||
bool orphan = true;
|
||||
for( int i = 0; i < m_l->topLevelItemCount(); ++i )
|
||||
{
|
||||
directory * d = dynamic_cast<directory *>(
|
||||
Directory * d = dynamic_cast<Directory *>(
|
||||
m_l->topLevelItem( i ) );
|
||||
if( d == NULL || cur_file < d->text( 0 ) )
|
||||
{
|
||||
m_l->insertTopLevelItem( i,
|
||||
new directory( cur_file, _path,
|
||||
new Directory( cur_file, path,
|
||||
m_filter ) );
|
||||
orphan = false;
|
||||
break;
|
||||
}
|
||||
else if( cur_file == d->text( 0 ) )
|
||||
{
|
||||
d->addDirectory( _path );
|
||||
d->addDirectory( path );
|
||||
orphan = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( orphan )
|
||||
{
|
||||
m_l->addTopLevelItem( new directory( cur_file,
|
||||
_path, m_filter ) );
|
||||
m_l->addTopLevelItem( new Directory( cur_file,
|
||||
path, m_filter ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,7 +300,7 @@ void fileBrowser::addItems( const QString & _path )
|
||||
{
|
||||
delete existing.front();
|
||||
}
|
||||
(void) new fileItem( m_l, cur_file, _path );
|
||||
(void) new FileItem( m_l, cur_file, path );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,15 +308,15 @@ void fileBrowser::addItems( const QString & _path )
|
||||
|
||||
|
||||
|
||||
void fileBrowser::keyPressEvent( QKeyEvent * _ke )
|
||||
void FileBrowser::keyPressEvent(QKeyEvent * ke )
|
||||
{
|
||||
if( _ke->key() == Qt::Key_F5 )
|
||||
if( ke->key() == Qt::Key_F5 )
|
||||
{
|
||||
reloadTree();
|
||||
}
|
||||
else
|
||||
{
|
||||
_ke->ignore();
|
||||
ke->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,8 +327,8 @@ void fileBrowser::keyPressEvent( QKeyEvent * _ke )
|
||||
|
||||
|
||||
|
||||
fileBrowserTreeWidget::fileBrowserTreeWidget( QWidget * _parent ) :
|
||||
QTreeWidget( _parent ),
|
||||
FileBrowserTreeWidget::FileBrowserTreeWidget(QWidget * parent ) :
|
||||
QTreeWidget( parent ),
|
||||
m_mousePressed( false ),
|
||||
m_pressPos(),
|
||||
m_previewPlayHandle( NULL ),
|
||||
@@ -349,18 +352,18 @@ fileBrowserTreeWidget::fileBrowserTreeWidget( QWidget * _parent ) :
|
||||
|
||||
|
||||
|
||||
fileBrowserTreeWidget::~fileBrowserTreeWidget()
|
||||
FileBrowserTreeWidget::~FileBrowserTreeWidget()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void fileBrowserTreeWidget::contextMenuEvent( QContextMenuEvent * _e )
|
||||
void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e )
|
||||
{
|
||||
fileItem * f = dynamic_cast<fileItem *>( itemAt( _e->pos() ) );
|
||||
if( f != NULL && ( f->handling() == fileItem::LoadAsPreset ||
|
||||
f->handling() == fileItem::LoadByPlugin ) )
|
||||
FileItem * f = dynamic_cast<FileItem *>( itemAt( e->pos() ) );
|
||||
if( f != NULL && ( f->handling() == FileItem::LoadAsPreset ||
|
||||
f->handling() == FileItem::LoadByPlugin ) )
|
||||
{
|
||||
m_contextMenuItem = f;
|
||||
QMenu contextMenu( this );
|
||||
@@ -375,7 +378,7 @@ void fileBrowserTreeWidget::contextMenuEvent( QContextMenuEvent * _e )
|
||||
"B+B Editor" ),
|
||||
this,
|
||||
SLOT( openInNewInstrumentTrackBBE() ) );
|
||||
contextMenu.exec( _e->globalPos() );
|
||||
contextMenu.exec( e->globalPos() );
|
||||
m_contextMenuItem = NULL;
|
||||
}
|
||||
}
|
||||
@@ -383,15 +386,15 @@ void fileBrowserTreeWidget::contextMenuEvent( QContextMenuEvent * _e )
|
||||
|
||||
|
||||
|
||||
void fileBrowserTreeWidget::mousePressEvent( QMouseEvent * _me )
|
||||
void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me )
|
||||
{
|
||||
QTreeWidget::mousePressEvent( _me );
|
||||
if( _me->button() != Qt::LeftButton )
|
||||
QTreeWidget::mousePressEvent( me );
|
||||
if( me->button() != Qt::LeftButton )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QTreeWidgetItem * i = itemAt( _me->pos() );
|
||||
QTreeWidgetItem * i = itemAt( me->pos() );
|
||||
if ( i )
|
||||
{
|
||||
// TODO: Restrict to visible selection
|
||||
@@ -401,12 +404,12 @@ void fileBrowserTreeWidget::mousePressEvent( QMouseEvent * _me )
|
||||
// _me->x() < header()->cellPos(
|
||||
// header()->mapToActual( 0 ) ) )
|
||||
// {
|
||||
m_pressPos = _me->pos();
|
||||
m_pressPos = me->pos();
|
||||
m_mousePressed = true;
|
||||
// }
|
||||
}
|
||||
|
||||
fileItem * f = dynamic_cast<fileItem *>( i );
|
||||
FileItem * f = dynamic_cast<FileItem *>( i );
|
||||
if( f != NULL )
|
||||
{
|
||||
m_pphMutex.lock();
|
||||
@@ -419,7 +422,7 @@ void fileBrowserTreeWidget::mousePressEvent( QMouseEvent * _me )
|
||||
|
||||
// in special case of sample-files we do not care about
|
||||
// handling() rather than directly creating a SamplePlayHandle
|
||||
if( f->type() == fileItem::SampleFile )
|
||||
if( f->type() == FileItem::SampleFile )
|
||||
{
|
||||
textFloat * tf = textFloat::displayMessage(
|
||||
tr( "Loading sample" ),
|
||||
@@ -435,11 +438,11 @@ void fileBrowserTreeWidget::mousePressEvent( QMouseEvent * _me )
|
||||
m_previewPlayHandle = s;
|
||||
delete tf;
|
||||
}
|
||||
else if( f->type() != fileItem::VstPluginFile &&
|
||||
( f->handling() == fileItem::LoadAsPreset ||
|
||||
f->handling() == fileItem::LoadByPlugin ) )
|
||||
else if( f->type() != FileItem::VstPluginFile &&
|
||||
( f->handling() == FileItem::LoadAsPreset ||
|
||||
f->handling() == FileItem::LoadByPlugin ) )
|
||||
{
|
||||
m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == fileItem::LoadByPlugin );
|
||||
m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin );
|
||||
}
|
||||
if( m_previewPlayHandle != NULL )
|
||||
{
|
||||
@@ -456,33 +459,40 @@ void fileBrowserTreeWidget::mousePressEvent( QMouseEvent * _me )
|
||||
|
||||
|
||||
|
||||
void fileBrowserTreeWidget::mouseMoveEvent( QMouseEvent * _me )
|
||||
void FileBrowserTreeWidget::mouseMoveEvent( QMouseEvent * me )
|
||||
{
|
||||
if( m_mousePressed == true &&
|
||||
( m_pressPos - _me->pos() ).manhattanLength() >
|
||||
( m_pressPos - me->pos() ).manhattanLength() >
|
||||
QApplication::startDragDistance() )
|
||||
{
|
||||
// make sure any playback is stopped
|
||||
mouseReleaseEvent( NULL );
|
||||
|
||||
fileItem * f = dynamic_cast<fileItem *>( itemAt( m_pressPos ) );
|
||||
FileItem * f = dynamic_cast<FileItem *>( itemAt( m_pressPos ) );
|
||||
if( f != NULL )
|
||||
{
|
||||
switch( f->type() )
|
||||
{
|
||||
case fileItem::PresetFile:
|
||||
new stringPairDrag( f->handling() == fileItem::LoadAsPreset ?
|
||||
case FileItem::PresetFile:
|
||||
new stringPairDrag( f->handling() == FileItem::LoadAsPreset ?
|
||||
"presetfile" : "pluginpresetfile",
|
||||
f->fullName(),
|
||||
embed::getIconPixmap( "preset_file" ), this );
|
||||
break;
|
||||
|
||||
case fileItem::SampleFile:
|
||||
case FileItem::SampleFile:
|
||||
new stringPairDrag( "samplefile", f->fullName(),
|
||||
embed::getIconPixmap( "sample_file" ), this );
|
||||
break;
|
||||
|
||||
case fileItem::MidiFile:
|
||||
case FileItem::SoundFontFile:
|
||||
new stringPairDrag( "soundfontfile", f->fullName(),
|
||||
embed::getIconPixmap( "soundfont_file" ), this );
|
||||
break;
|
||||
case FileItem::VstPluginFile:
|
||||
new stringPairDrag( "vstpluginfile", f->fullName(),
|
||||
embed::getIconPixmap( "vst_plugin_file" ), this );
|
||||
break;
|
||||
case FileItem::MidiFile:
|
||||
// don't allow dragging FLP-files as FLP import filter clears project
|
||||
// without asking
|
||||
// case fileItem::FlpFile:
|
||||
@@ -490,11 +500,6 @@ void fileBrowserTreeWidget::mouseMoveEvent( QMouseEvent * _me )
|
||||
embed::getIconPixmap( "midi_file" ), this );
|
||||
break;
|
||||
|
||||
case fileItem::VstPluginFile:
|
||||
new stringPairDrag( "vstplugin", f->fullName(),
|
||||
embed::getIconPixmap( "sample_file" ), this );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -505,7 +510,7 @@ void fileBrowserTreeWidget::mouseMoveEvent( QMouseEvent * _me )
|
||||
|
||||
|
||||
|
||||
void fileBrowserTreeWidget::mouseReleaseEvent( QMouseEvent * _me )
|
||||
void FileBrowserTreeWidget::mouseReleaseEvent(QMouseEvent * me )
|
||||
{
|
||||
m_mousePressed = false;
|
||||
|
||||
@@ -538,43 +543,43 @@ void fileBrowserTreeWidget::mouseReleaseEvent( QMouseEvent * _me )
|
||||
|
||||
|
||||
|
||||
void fileBrowserTreeWidget::handleFile( fileItem * f, InstrumentTrack * _it )
|
||||
void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it )
|
||||
{
|
||||
engine::mixer()->lock();
|
||||
switch( f->handling() )
|
||||
{
|
||||
case fileItem::LoadAsProject:
|
||||
case FileItem::LoadAsProject:
|
||||
if( engine::mainWindow()->mayChangeProject() )
|
||||
{
|
||||
engine::getSong()->loadProject( f->fullName() );
|
||||
}
|
||||
break;
|
||||
|
||||
case fileItem::LoadByPlugin:
|
||||
case FileItem::LoadByPlugin:
|
||||
{
|
||||
const QString e = f->extension();
|
||||
Instrument * i = _it->instrument();
|
||||
Instrument * i = it->instrument();
|
||||
if( i == NULL ||
|
||||
!i->descriptor()->supportsFileType( e ) )
|
||||
{
|
||||
i = _it->loadInstrument(
|
||||
i = it->loadInstrument(
|
||||
engine::pluginFileHandling()[e] );
|
||||
}
|
||||
i->loadFile( f->fullName() );
|
||||
break;
|
||||
}
|
||||
|
||||
case fileItem::LoadAsPreset:
|
||||
case FileItem::LoadAsPreset:
|
||||
{
|
||||
DataFile dataFile( f->fullName() );
|
||||
InstrumentTrack::removeMidiPortNode( dataFile );
|
||||
_it->setSimpleSerializing();
|
||||
_it->loadSettings( dataFile.content().toElement() );
|
||||
it->setSimpleSerializing();
|
||||
it->loadSettings( dataFile.content().toElement() );
|
||||
break;
|
||||
}
|
||||
|
||||
case fileItem::ImportAsProject:
|
||||
if( f->type() == fileItem::FlpFile &&
|
||||
case FileItem::ImportAsProject:
|
||||
if( f->type() == FileItem::FlpFile &&
|
||||
!engine::mainWindow()->mayChangeProject() )
|
||||
{
|
||||
break;
|
||||
@@ -583,7 +588,7 @@ void fileBrowserTreeWidget::handleFile( fileItem * f, InstrumentTrack * _it )
|
||||
engine::getSong() );
|
||||
break;
|
||||
|
||||
case fileItem::NotSupported:
|
||||
case FileItem::NotSupported:
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -594,21 +599,21 @@ void fileBrowserTreeWidget::handleFile( fileItem * f, InstrumentTrack * _it )
|
||||
|
||||
|
||||
|
||||
void fileBrowserTreeWidget::activateListItem( QTreeWidgetItem * _item,
|
||||
int _column )
|
||||
void FileBrowserTreeWidget::activateListItem(QTreeWidgetItem * item,
|
||||
int column )
|
||||
{
|
||||
fileItem * f = dynamic_cast<fileItem *>( _item );
|
||||
FileItem * f = dynamic_cast<FileItem *>( item );
|
||||
if( f == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( f->handling() == fileItem::LoadAsProject ||
|
||||
f->handling() == fileItem::ImportAsProject )
|
||||
if( f->handling() == FileItem::LoadAsProject ||
|
||||
f->handling() == FileItem::ImportAsProject )
|
||||
{
|
||||
handleFile( f, NULL );
|
||||
}
|
||||
else if( f->handling() != fileItem::NotSupported )
|
||||
else if( f->handling() != FileItem::NotSupported )
|
||||
{
|
||||
engine::mixer()->lock();
|
||||
InstrumentTrack * it = dynamic_cast<InstrumentTrack *>(
|
||||
@@ -622,10 +627,10 @@ void fileBrowserTreeWidget::activateListItem( QTreeWidgetItem * _item,
|
||||
|
||||
|
||||
|
||||
void fileBrowserTreeWidget::openInNewInstrumentTrack( TrackContainer* tc )
|
||||
void FileBrowserTreeWidget::openInNewInstrumentTrack( TrackContainer* tc )
|
||||
{
|
||||
if( m_contextMenuItem->handling() == fileItem::LoadAsPreset ||
|
||||
m_contextMenuItem->handling() == fileItem::LoadByPlugin )
|
||||
if( m_contextMenuItem->handling() == FileItem::LoadAsPreset ||
|
||||
m_contextMenuItem->handling() == FileItem::LoadByPlugin )
|
||||
{
|
||||
engine::mixer()->lock();
|
||||
InstrumentTrack * it = dynamic_cast<InstrumentTrack *>(
|
||||
@@ -638,7 +643,7 @@ void fileBrowserTreeWidget::openInNewInstrumentTrack( TrackContainer* tc )
|
||||
|
||||
|
||||
|
||||
void fileBrowserTreeWidget::openInNewInstrumentTrackBBE( void )
|
||||
void FileBrowserTreeWidget::openInNewInstrumentTrackBBE( void )
|
||||
{
|
||||
openInNewInstrumentTrack( engine::getBBTrackContainer() );
|
||||
}
|
||||
@@ -646,7 +651,7 @@ void fileBrowserTreeWidget::openInNewInstrumentTrackBBE( void )
|
||||
|
||||
|
||||
|
||||
void fileBrowserTreeWidget::openInNewInstrumentTrackSE( void )
|
||||
void FileBrowserTreeWidget::openInNewInstrumentTrackSE( void )
|
||||
{
|
||||
openInNewInstrumentTrack( engine::getSong() );
|
||||
}
|
||||
@@ -654,7 +659,7 @@ void fileBrowserTreeWidget::openInNewInstrumentTrackSE( void )
|
||||
|
||||
|
||||
|
||||
void fileBrowserTreeWidget::sendToActiveInstrumentTrack( void )
|
||||
void FileBrowserTreeWidget::sendToActiveInstrumentTrack( void )
|
||||
{
|
||||
// get all windows opened in the workspace
|
||||
QList<QMdiSubWindow*> pl =
|
||||
@@ -680,9 +685,9 @@ void fileBrowserTreeWidget::sendToActiveInstrumentTrack( void )
|
||||
|
||||
|
||||
|
||||
void fileBrowserTreeWidget::updateDirectory( QTreeWidgetItem * _item )
|
||||
void FileBrowserTreeWidget::updateDirectory(QTreeWidgetItem * item )
|
||||
{
|
||||
directory * dir = dynamic_cast<directory *>( _item );
|
||||
Directory * dir = dynamic_cast<Directory *>( item );
|
||||
if( dir != NULL )
|
||||
{
|
||||
dir->update();
|
||||
@@ -694,16 +699,16 @@ void fileBrowserTreeWidget::updateDirectory( QTreeWidgetItem * _item )
|
||||
|
||||
|
||||
|
||||
QPixmap * directory::s_folderPixmap = NULL;
|
||||
QPixmap * directory::s_folderOpenedPixmap = NULL;
|
||||
QPixmap * directory::s_folderLockedPixmap = NULL;
|
||||
QPixmap * Directory::s_folderPixmap = NULL;
|
||||
QPixmap * Directory::s_folderOpenedPixmap = NULL;
|
||||
QPixmap * Directory::s_folderLockedPixmap = NULL;
|
||||
|
||||
|
||||
directory::directory( const QString & _name, const QString & _path,
|
||||
const QString & _filter ) :
|
||||
QTreeWidgetItem( QStringList( _name ), TypeDirectoryItem ),
|
||||
m_directories( _path ),
|
||||
m_filter( _filter )
|
||||
Directory::Directory(const QString & filename, const QString & path,
|
||||
const QString & filter ) :
|
||||
QTreeWidgetItem( QStringList( filename ), TypeDirectoryItem ),
|
||||
m_directories( path ),
|
||||
m_filter( filter )
|
||||
{
|
||||
initPixmaps();
|
||||
|
||||
@@ -722,7 +727,7 @@ directory::directory( const QString & _name, const QString & _path,
|
||||
|
||||
|
||||
|
||||
void directory::initPixmaps( void )
|
||||
void Directory::initPixmaps( void )
|
||||
{
|
||||
if( s_folderPixmap == NULL )
|
||||
{
|
||||
@@ -746,7 +751,7 @@ void directory::initPixmaps( void )
|
||||
|
||||
|
||||
|
||||
void directory::update( void )
|
||||
void Directory::update( void )
|
||||
{
|
||||
if( !isExpanded() )
|
||||
{
|
||||
@@ -767,7 +772,7 @@ void directory::update( void )
|
||||
{
|
||||
QTreeWidgetItem * sep = new QTreeWidgetItem;
|
||||
sep->setText( 0,
|
||||
fileBrowserTreeWidget::tr(
|
||||
FileBrowserTreeWidget::tr(
|
||||
"--- Factory files ---" ) );
|
||||
sep->setIcon( 0, embed::getIconPixmap(
|
||||
"factory_files" ) );
|
||||
@@ -780,9 +785,9 @@ void directory::update( void )
|
||||
|
||||
|
||||
|
||||
bool directory::addItems( const QString & _path )
|
||||
bool Directory::addItems(const QString & path )
|
||||
{
|
||||
QDir thisDir( _path );
|
||||
QDir thisDir( path );
|
||||
if( !thisDir.isReadable() )
|
||||
{
|
||||
return false;
|
||||
@@ -802,25 +807,25 @@ bool directory::addItems( const QString & _path )
|
||||
bool orphan = true;
|
||||
for( int i = 0; i < childCount(); ++i )
|
||||
{
|
||||
directory * d = dynamic_cast<directory *>(
|
||||
Directory * d = dynamic_cast<Directory *>(
|
||||
child( i ) );
|
||||
if( d == NULL || cur_file < d->text( 0 ) )
|
||||
{
|
||||
insertChild( i, new directory( cur_file,
|
||||
_path, m_filter ) );
|
||||
insertChild( i, new Directory( cur_file,
|
||||
path, m_filter ) );
|
||||
orphan = false;
|
||||
break;
|
||||
}
|
||||
else if( cur_file == d->text( 0 ) )
|
||||
{
|
||||
d->addDirectory( _path );
|
||||
d->addDirectory( path );
|
||||
orphan = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( orphan )
|
||||
{
|
||||
addChild( new directory( cur_file, _path,
|
||||
addChild( new Directory( cur_file, path,
|
||||
m_filter ) );
|
||||
}
|
||||
|
||||
@@ -837,7 +842,7 @@ bool directory::addItems( const QString & _path )
|
||||
if( cur_file[0] != '.' &&
|
||||
thisDir.match( m_filter, cur_file.toLower() ) )
|
||||
{
|
||||
items << new fileItem( cur_file, _path );
|
||||
items << new FileItem( cur_file, path );
|
||||
added_something = true;
|
||||
}
|
||||
}
|
||||
@@ -851,18 +856,20 @@ bool directory::addItems( const QString & _path )
|
||||
|
||||
|
||||
|
||||
QPixmap * fileItem::s_projectFilePixmap = NULL;
|
||||
QPixmap * fileItem::s_presetFilePixmap = NULL;
|
||||
QPixmap * fileItem::s_sampleFilePixmap = NULL;
|
||||
QPixmap * fileItem::s_midiFilePixmap = NULL;
|
||||
QPixmap * fileItem::s_flpFilePixmap = NULL;
|
||||
QPixmap * fileItem::s_unknownFilePixmap = NULL;
|
||||
QPixmap * FileItem::s_projectFilePixmap = NULL;
|
||||
QPixmap * FileItem::s_presetFilePixmap = NULL;
|
||||
QPixmap * FileItem::s_sampleFilePixmap = NULL;
|
||||
QPixmap * FileItem::s_soundfontFilePixmap = NULL;
|
||||
QPixmap * FileItem::s_vstPluginFilePixmap = NULL;
|
||||
QPixmap * FileItem::s_midiFilePixmap = NULL;
|
||||
QPixmap * FileItem::s_flpFilePixmap = NULL;
|
||||
QPixmap * FileItem::s_unknownFilePixmap = NULL;
|
||||
|
||||
|
||||
fileItem::fileItem( QTreeWidget * _parent, const QString & _name,
|
||||
const QString & _path ) :
|
||||
QTreeWidgetItem( _parent, QStringList( _name) , TypeFileItem ),
|
||||
m_path( _path )
|
||||
FileItem::FileItem(QTreeWidget * parent, const QString & name,
|
||||
const QString & path ) :
|
||||
QTreeWidgetItem( parent, QStringList( name) , TypeFileItem ),
|
||||
m_path( path )
|
||||
{
|
||||
determineFileType();
|
||||
initPixmaps();
|
||||
@@ -871,9 +878,9 @@ fileItem::fileItem( QTreeWidget * _parent, const QString & _name,
|
||||
|
||||
|
||||
|
||||
fileItem::fileItem( const QString & _name, const QString & _path ) :
|
||||
QTreeWidgetItem( QStringList( _name ), TypeFileItem ),
|
||||
m_path( _path )
|
||||
FileItem::FileItem(const QString & name, const QString & path ) :
|
||||
QTreeWidgetItem( QStringList( name ), TypeFileItem ),
|
||||
m_path( path )
|
||||
{
|
||||
determineFileType();
|
||||
initPixmaps();
|
||||
@@ -882,7 +889,7 @@ fileItem::fileItem( const QString & _name, const QString & _path ) :
|
||||
|
||||
|
||||
|
||||
void fileItem::initPixmaps( void )
|
||||
void FileItem::initPixmaps( void )
|
||||
{
|
||||
if( s_projectFilePixmap == NULL )
|
||||
{
|
||||
@@ -902,6 +909,18 @@ void fileItem::initPixmaps( void )
|
||||
"sample_file", 16, 16 ) );
|
||||
}
|
||||
|
||||
if ( s_soundfontFilePixmap == NULL )
|
||||
{
|
||||
s_soundfontFilePixmap = new QPixmap( embed::getIconPixmap(
|
||||
"soundfont_file", 16, 16 ) );
|
||||
}
|
||||
|
||||
if ( s_vstPluginFilePixmap == NULL )
|
||||
{
|
||||
s_vstPluginFilePixmap = new QPixmap( embed::getIconPixmap(
|
||||
"vst_plugin_file", 16, 16 ) );
|
||||
}
|
||||
|
||||
if( s_midiFilePixmap == NULL )
|
||||
{
|
||||
s_midiFilePixmap = new QPixmap( embed::getIconPixmap(
|
||||
@@ -928,8 +947,13 @@ void fileItem::initPixmaps( void )
|
||||
case PresetFile:
|
||||
setIcon( 0, *s_presetFilePixmap );
|
||||
break;
|
||||
case SoundFontFile:
|
||||
setIcon( 0, *s_soundfontFilePixmap );
|
||||
break;
|
||||
case VstPluginFile:
|
||||
setIcon( 0, *s_vstPluginFilePixmap );
|
||||
break;
|
||||
case SampleFile:
|
||||
case SoundFontFile: // TODO
|
||||
case PatchFile: // TODO
|
||||
setIcon( 0, *s_sampleFilePixmap );
|
||||
break;
|
||||
@@ -949,7 +973,7 @@ void fileItem::initPixmaps( void )
|
||||
|
||||
|
||||
|
||||
void fileItem::determineFileType( void )
|
||||
void FileItem::determineFileType( void )
|
||||
{
|
||||
m_handling = NotSupported;
|
||||
|
||||
@@ -1013,7 +1037,7 @@ void fileItem::determineFileType( void )
|
||||
|
||||
|
||||
|
||||
QString fileItem::extension( void )
|
||||
QString FileItem::extension( void )
|
||||
{
|
||||
return extension( fullName() );
|
||||
}
|
||||
@@ -1021,13 +1045,13 @@ QString fileItem::extension( void )
|
||||
|
||||
|
||||
|
||||
QString fileItem::extension( const QString & _file )
|
||||
QString FileItem::extension(const QString & file )
|
||||
{
|
||||
return QFileInfo( _file ).suffix().toLower();
|
||||
return QFileInfo( file ).suffix().toLower();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#include "moc_file_browser.cxx"
|
||||
#include "moc_FileBrowser.cxx"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -22,6 +22,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QDebug>
|
||||
|
||||
#include <QtGui/QButtonGroup>
|
||||
#include <QtGui/QInputDialog>
|
||||
#include <QtGui/QLayout>
|
||||
@@ -30,98 +33,19 @@
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtGui/QPushButton>
|
||||
#include <QtGui/QToolButton>
|
||||
#include <QtGui/QStackedLayout>
|
||||
#include <QtGui/QScrollArea>
|
||||
#include <QtGui/QStyle>
|
||||
#include <QtGui/QKeyEvent>
|
||||
|
||||
#include "FxMixerView.h"
|
||||
#include "fader.h"
|
||||
#include "EffectRackView.h"
|
||||
#include "knob.h"
|
||||
#include "engine.h"
|
||||
#include "embed.h"
|
||||
#include "MainWindow.h"
|
||||
#include "LcdWidget.h"
|
||||
#include "gui_templates.h"
|
||||
#include "tooltip.h"
|
||||
#include "pixmap_button.h"
|
||||
|
||||
|
||||
|
||||
class FxLine : public QWidget
|
||||
{
|
||||
public:
|
||||
FxLine( QWidget * _parent, FxMixerView * _mv, QString & _name ) :
|
||||
QWidget( _parent ),
|
||||
m_mv( _mv ),
|
||||
m_name( _name )
|
||||
{
|
||||
setFixedSize( 32, 232 );
|
||||
setAttribute( Qt::WA_OpaquePaintEvent, true );
|
||||
setCursor( QCursor( embed::getIconPixmap( "hand" ), 0, 0 ) );
|
||||
}
|
||||
|
||||
virtual void paintEvent( QPaintEvent * )
|
||||
{
|
||||
QPainter p( this );
|
||||
QColor bg_color = QApplication::palette().color( QPalette::Active,
|
||||
QPalette::Background );
|
||||
QColor sh_color = QApplication::palette().color( QPalette::Active,
|
||||
QPalette::Shadow );
|
||||
QColor te_color = QApplication::palette().color( QPalette::Active,
|
||||
QPalette::Text );
|
||||
QColor bt_color = QApplication::palette().color( QPalette::Active,
|
||||
QPalette::BrightText );
|
||||
|
||||
p.fillRect( rect(),
|
||||
m_mv->currentFxLine() == this ? bg_color.lighter(130) : bg_color );
|
||||
|
||||
p.setPen( bg_color.darker(130) );
|
||||
p.drawRect( 0, 0, width()-2, height()-2 );
|
||||
|
||||
p.setPen( bg_color.lighter(150) );
|
||||
p.drawRect( 1, 1, width()-2, height()-2 );
|
||||
|
||||
p.setPen( m_mv->currentFxLine() == this ? sh_color : bg_color.darker(130) );
|
||||
p.drawRect( 0, 0, width()-1, height()-1 );
|
||||
|
||||
p.rotate( -90 );
|
||||
p.setFont( pointSizeF( font(), 7.5f ) );
|
||||
|
||||
p.setPen( sh_color );
|
||||
p.drawText( -91, 21, m_name );
|
||||
|
||||
p.setPen( m_mv->currentFxLine() == this ? bt_color : te_color );
|
||||
p.drawText( -90, 20, m_name );
|
||||
|
||||
}
|
||||
|
||||
virtual void mousePressEvent( QMouseEvent * )
|
||||
{
|
||||
m_mv->setCurrentFxLine( this );
|
||||
}
|
||||
|
||||
virtual void mouseDoubleClickEvent( QMouseEvent * )
|
||||
{
|
||||
bool ok;
|
||||
QString new_name = QInputDialog::getText( this,
|
||||
FxMixerView::tr( "Rename FX channel" ),
|
||||
FxMixerView::tr( "Enter the new name for this "
|
||||
"FX channel" ),
|
||||
QLineEdit::Normal, m_name, &ok );
|
||||
if( ok && !new_name.isEmpty() )
|
||||
{
|
||||
m_name = new_name;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
FxMixerView * m_mv;
|
||||
QString & m_name;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
#include "InstrumentTrack.h"
|
||||
#include "song.h"
|
||||
#include "bb_track_container.h"
|
||||
|
||||
FxMixerView::FxMixerView() :
|
||||
QWidget(),
|
||||
@@ -131,121 +55,91 @@ FxMixerView::FxMixerView() :
|
||||
FxMixer * m = engine::fxMixer();
|
||||
m->setHook( this );
|
||||
|
||||
/* QPalette pal = palette();
|
||||
pal.setColor( QPalette::Background, QColor( 72, 76, 88 ) );
|
||||
setPalette( pal );*/
|
||||
|
||||
//QPalette pal = palette();
|
||||
//pal.setColor( QPalette::Background, QColor( 72, 76, 88 ) );
|
||||
//setPalette( pal );
|
||||
|
||||
setAutoFillBackground( true );
|
||||
setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
|
||||
setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
|
||||
|
||||
setWindowTitle( tr( "FX-Mixer" ) );
|
||||
setWindowIcon( embed::getIconPixmap( "fx_mixer" ) );
|
||||
|
||||
m_fxLineBanks = new QStackedLayout;
|
||||
m_fxLineBanks->setSpacing( 0 );
|
||||
m_fxLineBanks->setMargin( 1 );
|
||||
|
||||
m_fxRacksLayout = new QStackedLayout;
|
||||
m_fxRacksLayout->setSpacing( 0 );
|
||||
m_fxRacksLayout->setMargin( 0 );
|
||||
|
||||
// main-layout
|
||||
QHBoxLayout * ml = new QHBoxLayout;
|
||||
ml->setMargin( 0 );
|
||||
ml->setSpacing( 0 );
|
||||
ml->addSpacing( 6 );
|
||||
|
||||
// Channel area
|
||||
m_channelAreaWidget = new QWidget;
|
||||
chLayout = new QHBoxLayout( m_channelAreaWidget );
|
||||
chLayout->setSizeConstraint( QLayout::SetMinimumSize );
|
||||
chLayout->setSpacing( 0 );
|
||||
chLayout->setMargin( 0 );
|
||||
m_channelAreaWidget->setLayout(chLayout);
|
||||
|
||||
QHBoxLayout * banks[NumFxChannels/16];
|
||||
for( int i = 0; i < NumFxChannels/16; ++i )
|
||||
// create rack layout before creating the first channel
|
||||
m_racksWidget = new QWidget;
|
||||
m_racksLayout = new QStackedLayout( m_racksWidget );
|
||||
m_racksLayout->setContentsMargins( 0, 0, 0, 0 );
|
||||
m_racksWidget->setLayout( m_racksLayout );
|
||||
|
||||
// add master channel
|
||||
m_fxChannelViews.resize( m->numChannels() );
|
||||
m_fxChannelViews[0] = new FxChannelView( this, this, 0 );
|
||||
|
||||
m_racksLayout->addWidget( m_fxChannelViews[0]->m_rackView );
|
||||
|
||||
FxChannelView * masterView = m_fxChannelViews[0];
|
||||
ml->addWidget( masterView->m_fxLine, 0, Qt::AlignTop );
|
||||
|
||||
QSize fxLineSize = masterView->m_fxLine->size();
|
||||
|
||||
// add mixer channels
|
||||
for( int i = 1; i < m_fxChannelViews.size(); ++i )
|
||||
{
|
||||
QWidget * w = new QWidget( this );
|
||||
banks[i] = new QHBoxLayout( w );
|
||||
banks[i]->setMargin( 5 );
|
||||
banks[i]->setSpacing( 1 );
|
||||
m_fxLineBanks->addWidget( w );
|
||||
m_fxChannelViews[i] = new FxChannelView(m_channelAreaWidget, this, i);
|
||||
chLayout->addWidget( m_fxChannelViews[i]->m_fxLine );
|
||||
}
|
||||
|
||||
for( int i = 0; i < NumFxChannels+1; ++i )
|
||||
// add the scrolling section to the main layout
|
||||
// class solely for scroll area to pass key presses down
|
||||
class ChannelArea : public QScrollArea
|
||||
{
|
||||
FxChannelView * cv = &m_fxChannelViews[i];
|
||||
if( i == 0 )
|
||||
{
|
||||
cv->m_fxLine = new FxLine( NULL, this,
|
||||
m->m_fxChannels[i]->m_name );
|
||||
ml->addWidget( cv->m_fxLine );
|
||||
ml->addSpacing( 10 );
|
||||
}
|
||||
else
|
||||
{
|
||||
const int bank = (i-1) / 16;
|
||||
cv->m_fxLine = new FxLine( NULL, this,
|
||||
m->m_fxChannels[i]->m_name );
|
||||
banks[bank]->addWidget( cv->m_fxLine );
|
||||
}
|
||||
LcdWidget* l = new LcdWidget( 2, cv->m_fxLine );
|
||||
l->setValue( i );
|
||||
l->move( 3, 4 );
|
||||
l->setMarginWidth( 1 );
|
||||
|
||||
|
||||
cv->m_fader = new fader( &m->m_fxChannels[i]->m_volumeModel,
|
||||
tr( "FX Fader %1" ).arg( i ),
|
||||
cv->m_fxLine );
|
||||
cv->m_fader->move( 15-cv->m_fader->width()/2,
|
||||
cv->m_fxLine->height()-
|
||||
cv->m_fader->height()-5 );
|
||||
|
||||
cv->m_muteBtn = new pixmapButton( cv->m_fxLine, tr( "Mute" ) );
|
||||
cv->m_muteBtn->setModel( &m->m_fxChannels[i]->m_muteModel );
|
||||
cv->m_muteBtn->setActiveGraphic(
|
||||
embed::getIconPixmap( "led_off" ) );
|
||||
cv->m_muteBtn->setInactiveGraphic(
|
||||
embed::getIconPixmap( "led_green" ) );
|
||||
cv->m_muteBtn->setCheckable( true );
|
||||
cv->m_muteBtn->move( 9, cv->m_fader->y()-16);
|
||||
toolTip::add( cv->m_muteBtn, tr( "Mute this FX channel" ) );
|
||||
|
||||
cv->m_rackView = new EffectRackView(
|
||||
&m->m_fxChannels[i]->m_fxChain, this );
|
||||
cv->m_rackView->setMinimumWidth( 244 );
|
||||
|
||||
m_fxRacksLayout->addWidget( cv->m_rackView );
|
||||
if( i == 0 )
|
||||
{
|
||||
QVBoxLayout * l = new QVBoxLayout;
|
||||
l->addSpacing( 10 );
|
||||
QButtonGroup * g = new QButtonGroup( this );
|
||||
m_bankButtons = g;
|
||||
g->setExclusive( true );
|
||||
for( int j = 0; j < 4; ++j )
|
||||
public:
|
||||
ChannelArea( QWidget * parent, FxMixerView * mv ) :
|
||||
QScrollArea( parent ), m_mv( mv ) {}
|
||||
~ChannelArea() {}
|
||||
virtual void keyPressEvent( QKeyEvent * e )
|
||||
{
|
||||
QToolButton * btn = new QToolButton;
|
||||
btn->setText( QString( 'A'+j ) );
|
||||
btn->setCheckable( true );
|
||||
btn->setSizePolicy( QSizePolicy::Preferred,
|
||||
QSizePolicy::Expanding );
|
||||
l->addWidget( btn );
|
||||
g->addButton( btn, j );
|
||||
btn->setChecked( j == 0);
|
||||
m_mv->keyPressEvent( e );
|
||||
}
|
||||
l->addSpacing( 10 );
|
||||
ml->addLayout( l );
|
||||
connect( g, SIGNAL( buttonClicked( int ) ),
|
||||
m_fxLineBanks, SLOT( setCurrentIndex( int ) ) );
|
||||
}
|
||||
}
|
||||
private:
|
||||
FxMixerView * m_mv;
|
||||
};
|
||||
channelArea = new ChannelArea( this, this );
|
||||
channelArea->setWidget( m_channelAreaWidget );
|
||||
channelArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
|
||||
channelArea->setFrameStyle( QFrame::NoFrame );
|
||||
channelArea->setMinimumWidth( fxLineSize.width() * 6 );
|
||||
channelArea->setFixedHeight( fxLineSize.height() +
|
||||
style()->pixelMetric( QStyle::PM_ScrollBarExtent ) );
|
||||
ml->addWidget( channelArea, 1, Qt::AlignTop );
|
||||
|
||||
ml->addLayout( m_fxLineBanks );
|
||||
ml->addLayout( m_fxRacksLayout );
|
||||
// show the add new effect channel button
|
||||
QPushButton * newChannelBtn = new QPushButton( embed::getIconPixmap( "new_channel" ), QString::null, this );
|
||||
newChannelBtn->setObjectName( "newChannelBtn" );
|
||||
newChannelBtn->setFixedSize( fxLineSize );
|
||||
connect( newChannelBtn, SIGNAL( clicked() ), this, SLOT( addNewChannel() ) );
|
||||
ml->addWidget( newChannelBtn, 0, Qt::AlignTop );
|
||||
|
||||
|
||||
// add the stacked layout for the effect racks of fx channels
|
||||
ml->addWidget( m_racksWidget, 0, Qt::AlignTop | Qt::AlignRight );
|
||||
|
||||
setCurrentFxLine( m_fxChannelViews[0]->m_fxLine );
|
||||
|
||||
setLayout( ml );
|
||||
updateGeometry();
|
||||
|
||||
m_fxLineBanks->setCurrentIndex( 0 );
|
||||
setCurrentFxLine( m_fxChannelViews[0].m_fxLine );
|
||||
|
||||
// timer for updating faders
|
||||
connect( engine::mainWindow(), SIGNAL( periodicUpdate() ),
|
||||
this, SLOT( updateFaders() ) );
|
||||
@@ -255,10 +149,10 @@ FxMixerView::FxMixerView() :
|
||||
QMdiSubWindow * subWin =
|
||||
engine::mainWindow()->workspace()->addSubWindow( this );
|
||||
Qt::WindowFlags flags = subWin->windowFlags();
|
||||
flags |= Qt::MSWindowsFixedSizeDialogHint;
|
||||
flags &= ~Qt::WindowMaximizeButtonHint;
|
||||
subWin->setWindowFlags( flags );
|
||||
//subWin->layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
layout()->setSizeConstraint( QLayout::SetMinimumSize );
|
||||
subWin->layout()->setSizeConstraint( QLayout::SetMinAndMaxSize );
|
||||
|
||||
parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false );
|
||||
parentWidget()->move( 5, 310 );
|
||||
@@ -267,15 +161,91 @@ FxMixerView::FxMixerView() :
|
||||
setModel( m );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
FxMixerView::~FxMixerView()
|
||||
{
|
||||
for (int i=0; i<m_fxChannelViews.size(); i++)
|
||||
{
|
||||
delete m_fxChannelViews.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FxMixerView::addNewChannel()
|
||||
{
|
||||
// add new fx mixer channel and redraw the form.
|
||||
FxMixer * mix = engine::fxMixer();
|
||||
|
||||
int newChannelIndex = mix->createChannel();
|
||||
m_fxChannelViews.push_back(new FxChannelView(m_channelAreaWidget, this,
|
||||
newChannelIndex));
|
||||
chLayout->addWidget( m_fxChannelViews[newChannelIndex]->m_fxLine );
|
||||
m_racksLayout->addWidget( m_fxChannelViews[newChannelIndex]->m_rackView );
|
||||
|
||||
updateFxLine(newChannelIndex);
|
||||
|
||||
updateMaxChannelSelector();
|
||||
}
|
||||
|
||||
|
||||
void FxMixerView::refreshDisplay()
|
||||
{
|
||||
// delete all views and re-add them
|
||||
for( int i = 1; i<m_fxChannelViews.size(); ++i )
|
||||
{
|
||||
chLayout->removeWidget(m_fxChannelViews[i]->m_fxLine);
|
||||
delete m_fxChannelViews[i]->m_fader;
|
||||
delete m_fxChannelViews[i]->m_muteBtn;
|
||||
delete m_fxChannelViews[i]->m_fxLine;
|
||||
delete m_fxChannelViews[i];
|
||||
m_racksLayout->removeWidget( m_fxChannelViews[i]->m_rackView );
|
||||
}
|
||||
m_channelAreaWidget->adjustSize();
|
||||
|
||||
// re-add the views
|
||||
m_fxChannelViews.resize(engine::fxMixer()->numChannels());
|
||||
for( int i = 1; i < m_fxChannelViews.size(); ++i )
|
||||
{
|
||||
m_fxChannelViews[i] = new FxChannelView(m_channelAreaWidget, this, i);
|
||||
chLayout->addWidget(m_fxChannelViews[i]->m_fxLine);
|
||||
m_racksLayout->addWidget( m_fxChannelViews[i]->m_rackView );
|
||||
}
|
||||
|
||||
// set selected fx line to 0
|
||||
setCurrentFxLine( 0 );
|
||||
|
||||
// update all fx lines
|
||||
for( int i = 0; i < m_fxChannelViews.size(); ++i )
|
||||
{
|
||||
updateFxLine( i );
|
||||
}
|
||||
|
||||
updateMaxChannelSelector();
|
||||
}
|
||||
|
||||
|
||||
// update the and max. channel number for every instrument
|
||||
void FxMixerView::updateMaxChannelSelector()
|
||||
{
|
||||
QVector<track *> songTrackList = engine::getSong()->tracks();
|
||||
QVector<track *> bbTrackList = engine::getBBTrackContainer()->tracks();
|
||||
|
||||
QVector<track *> trackLists[] = {songTrackList, bbTrackList};
|
||||
for(int tl=0; tl<2; ++tl)
|
||||
{
|
||||
QVector<track *> trackList = trackLists[tl];
|
||||
for(int i=0; i<trackList.size(); ++i)
|
||||
{
|
||||
if( trackList[i]->type() == track::InstrumentTrack )
|
||||
{
|
||||
InstrumentTrack * inst = (InstrumentTrack *) trackList[i];
|
||||
inst->effectChannelModel()->setRange(0,
|
||||
m_fxChannelViews.size()-1,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FxMixerView::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
{
|
||||
@@ -291,18 +261,197 @@ void FxMixerView::loadSettings( const QDomElement & _this )
|
||||
}
|
||||
|
||||
|
||||
FxMixerView::FxChannelView::FxChannelView(QWidget * _parent, FxMixerView * _mv,
|
||||
int _chIndex )
|
||||
{
|
||||
m_fxLine = new FxLine(_parent, _mv, _chIndex);
|
||||
|
||||
FxMixer * m = engine::fxMixer();
|
||||
m_fader = new fader( &m->effectChannel(_chIndex)->m_volumeModel,
|
||||
tr( "FX Fader %1" ).arg( _chIndex ), m_fxLine );
|
||||
m_fader->move( 16-m_fader->width()/2,
|
||||
m_fxLine->height()-
|
||||
m_fader->height()-5 );
|
||||
|
||||
m_muteBtn = new pixmapButton( m_fxLine, tr( "Mute" ) );
|
||||
m_muteBtn->setModel( &m->effectChannel(_chIndex)->m_muteModel );
|
||||
m_muteBtn->setActiveGraphic(
|
||||
embed::getIconPixmap( "led_off" ) );
|
||||
m_muteBtn->setInactiveGraphic(
|
||||
embed::getIconPixmap( "led_green" ) );
|
||||
m_muteBtn->setCheckable( true );
|
||||
m_muteBtn->move( 9, m_fader->y()-16);
|
||||
toolTip::add( m_muteBtn, tr( "Mute this FX channel" ) );
|
||||
|
||||
// Create EffectRack for the channel
|
||||
m_rackView = new EffectRackView( &m->effectChannel(_chIndex)->m_fxChain, _mv->m_racksWidget );
|
||||
m_rackView->setFixedSize( 245, FxLine::FxLineHeight );
|
||||
}
|
||||
|
||||
|
||||
void FxMixerView::setCurrentFxLine( FxLine * _line )
|
||||
{
|
||||
// select
|
||||
m_currentFxLine = _line;
|
||||
for( int i = 0; i < NumFxChannels+1; ++i )
|
||||
m_racksLayout->setCurrentWidget( m_fxChannelViews[ _line->channelIndex() ]->m_rackView );
|
||||
|
||||
// set up send knob
|
||||
for(int i = 0; i < m_fxChannelViews.size(); ++i)
|
||||
{
|
||||
if( m_fxChannelViews[i].m_fxLine == _line )
|
||||
updateFxLine(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FxMixerView::updateFxLine(int index)
|
||||
{
|
||||
FxMixer * mix = engine::fxMixer();
|
||||
|
||||
// does current channel send to this channel?
|
||||
int selIndex = m_currentFxLine->channelIndex();
|
||||
FxLine * thisLine = m_fxChannelViews[index]->m_fxLine;
|
||||
FloatModel * sendModel = mix->channelSendModel(selIndex, index);
|
||||
if( sendModel == NULL )
|
||||
{
|
||||
// does not send, hide send knob
|
||||
thisLine->m_sendKnob->setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// it does send, show knob and connect
|
||||
thisLine->m_sendKnob->setVisible(true);
|
||||
thisLine->m_sendKnob->setModel(sendModel);
|
||||
}
|
||||
|
||||
// disable the send button if it would cause an infinite loop
|
||||
thisLine->m_sendBtn->setVisible(! mix->isInfiniteLoop(selIndex, index));
|
||||
thisLine->m_sendBtn->updateLightStatus();
|
||||
thisLine->update();
|
||||
}
|
||||
|
||||
|
||||
void FxMixerView::deleteChannel(int index)
|
||||
{
|
||||
// can't delete master
|
||||
if( index == 0 ) return;
|
||||
|
||||
// remember selected line
|
||||
int selLine = m_currentFxLine->channelIndex();
|
||||
|
||||
// delete the real channel
|
||||
engine::fxMixer()->deleteChannel(index);
|
||||
|
||||
// delete the view
|
||||
chLayout->removeWidget(m_fxChannelViews[index]->m_fxLine);
|
||||
delete m_fxChannelViews[index]->m_fader;
|
||||
delete m_fxChannelViews[index]->m_muteBtn;
|
||||
delete m_fxChannelViews[index]->m_fxLine;
|
||||
delete m_fxChannelViews[index];
|
||||
m_channelAreaWidget->adjustSize();
|
||||
|
||||
// delete the fx rack
|
||||
m_racksLayout->removeWidget( m_fxChannelViews[index]->m_rackView );
|
||||
|
||||
// make sure every channel knows what index it is
|
||||
for(int i=0; i<m_fxChannelViews.size(); ++i)
|
||||
{
|
||||
if( i > index )
|
||||
{
|
||||
m_fxRacksLayout->setCurrentIndex( i );
|
||||
m_fxChannelViews[i]->m_fxLine->setChannelIndex(i-1);
|
||||
}
|
||||
m_fxChannelViews[i].m_fxLine->update();
|
||||
}
|
||||
m_fxChannelViews.remove(index);
|
||||
|
||||
// select the next channel
|
||||
if( selLine >= m_fxChannelViews.size() )
|
||||
{
|
||||
selLine = m_fxChannelViews.size()-1;
|
||||
}
|
||||
setCurrentFxLine(selLine);
|
||||
|
||||
updateMaxChannelSelector();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FxMixerView::moveChannelLeft(int index)
|
||||
{
|
||||
// can't move master or first channel left or last channel right
|
||||
if( index <= 1 || index >= m_fxChannelViews.size() ) return;
|
||||
|
||||
int selIndex = m_currentFxLine->channelIndex();
|
||||
|
||||
FxMixer * mix = engine::fxMixer();
|
||||
mix->moveChannelLeft(index);
|
||||
|
||||
// refresh the two mixer views
|
||||
for( int i = index-1; i <= index; ++i )
|
||||
{
|
||||
// delete the mixer view
|
||||
int replaceIndex = chLayout->indexOf(m_fxChannelViews[i]->m_fxLine);
|
||||
|
||||
chLayout->removeWidget(m_fxChannelViews[i]->m_fxLine);
|
||||
m_racksLayout->removeWidget( m_fxChannelViews[i]->m_rackView );
|
||||
delete m_fxChannelViews[i]->m_fader;
|
||||
delete m_fxChannelViews[i]->m_muteBtn;
|
||||
delete m_fxChannelViews[i];
|
||||
|
||||
// add it again
|
||||
m_fxChannelViews[i] = new FxChannelView( m_channelAreaWidget, this, i );
|
||||
chLayout->insertWidget( replaceIndex, m_fxChannelViews[i]->m_fxLine );
|
||||
m_racksLayout->insertWidget( replaceIndex, m_fxChannelViews[i]->m_rackView );
|
||||
}
|
||||
|
||||
// keep selected channel
|
||||
if( selIndex == index )
|
||||
{
|
||||
selIndex = index-1;
|
||||
}
|
||||
else if( selIndex == index - 1 )
|
||||
{
|
||||
selIndex = index;
|
||||
}
|
||||
setCurrentFxLine(selIndex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FxMixerView::moveChannelRight(int index)
|
||||
{
|
||||
moveChannelLeft(index+1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FxMixerView::keyPressEvent(QKeyEvent * e)
|
||||
{
|
||||
switch(e->key())
|
||||
{
|
||||
case Qt::Key_Delete:
|
||||
deleteChannel(m_currentFxLine->channelIndex());
|
||||
break;
|
||||
case Qt::Key_Left:
|
||||
if( e->modifiers() & Qt::AltModifier )
|
||||
{
|
||||
moveChannelLeft( m_currentFxLine->channelIndex() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// select channel to the left
|
||||
setCurrentFxLine( m_currentFxLine->channelIndex()-1 );
|
||||
}
|
||||
break;
|
||||
case Qt::Key_Right:
|
||||
if( e->modifiers() & Qt::AltModifier )
|
||||
{
|
||||
moveChannelRight( m_currentFxLine->channelIndex() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// select channel to the right
|
||||
setCurrentFxLine( m_currentFxLine->channelIndex()+1 );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,23 +459,19 @@ void FxMixerView::setCurrentFxLine( FxLine * _line )
|
||||
|
||||
void FxMixerView::setCurrentFxLine( int _line )
|
||||
{
|
||||
if ( _line >= 0 && _line < NumFxChannels+1 )
|
||||
if( _line >= 0 && _line < m_fxChannelViews.size() )
|
||||
{
|
||||
setCurrentFxLine( m_fxChannelViews[_line].m_fxLine );
|
||||
|
||||
m_bankButtons->button( (_line-1) / 16 )->click();
|
||||
setCurrentFxLine( m_fxChannelViews[_line]->m_fxLine );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void FxMixerView::clear()
|
||||
{
|
||||
for( int i = 0; i <= NumFxChannels; ++i )
|
||||
{
|
||||
m_fxChannelViews[i].m_rackView->clearViews();
|
||||
}
|
||||
engine::fxMixer()->clear();
|
||||
|
||||
refreshDisplay();
|
||||
}
|
||||
|
||||
|
||||
@@ -335,29 +480,29 @@ void FxMixerView::clear()
|
||||
void FxMixerView::updateFaders()
|
||||
{
|
||||
FxMixer * m = engine::fxMixer();
|
||||
for( int i = 0; i < NumFxChannels+1; ++i )
|
||||
for( int i = 0; i < m_fxChannelViews.size(); ++i )
|
||||
{
|
||||
const float opl = m_fxChannelViews[i].m_fader->getPeak_L();
|
||||
const float opr = m_fxChannelViews[i].m_fader->getPeak_R();
|
||||
const float opl = m_fxChannelViews[i]->m_fader->getPeak_L();
|
||||
const float opr = m_fxChannelViews[i]->m_fader->getPeak_R();
|
||||
const float fall_off = 1.2;
|
||||
if( m->m_fxChannels[i]->m_peakLeft > opl )
|
||||
{
|
||||
m_fxChannelViews[i].m_fader->setPeak_L( m->m_fxChannels[i]->m_peakLeft );
|
||||
m_fxChannelViews[i]->m_fader->setPeak_L( m->m_fxChannels[i]->m_peakLeft );
|
||||
m->m_fxChannels[i]->m_peakLeft = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fxChannelViews[i].m_fader->setPeak_L( opl/fall_off );
|
||||
m_fxChannelViews[i]->m_fader->setPeak_L( opl/fall_off );
|
||||
}
|
||||
|
||||
if( m->m_fxChannels[i]->m_peakRight > opr )
|
||||
{
|
||||
m_fxChannelViews[i].m_fader->setPeak_R( m->m_fxChannels[i]->m_peakRight );
|
||||
m_fxChannelViews[i]->m_fader->setPeak_R( m->m_fxChannels[i]->m_peakRight );
|
||||
m->m_fxChannels[i]->m_peakRight = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fxChannelViews[i].m_fader->setPeak_R( opr/fall_off );
|
||||
m_fxChannelViews[i]->m_fader->setPeak_R( opr/fall_off );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2009 Paul Giblock <drfaygo/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
100
src/gui/LmmsPalette.cpp
Normal file
100
src/gui/LmmsPalette.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* LmmsPalette.cpp - dummy class for fetching palette qproperties from CSS
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2007-2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* 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 <QApplication>
|
||||
#include <QStyle>
|
||||
#include "LmmsPalette.h"
|
||||
#include "LmmsStyle.h"
|
||||
|
||||
|
||||
LmmsPalette::LmmsPalette( QWidget * parent, QStyle * stylearg ) :
|
||||
QWidget( parent ),
|
||||
|
||||
/* sane defaults in case fetching from stylesheet fails*/
|
||||
|
||||
m_background( 91, 101, 113 ),
|
||||
m_windowText( 240, 240, 240 ),
|
||||
m_base( 128, 128, 128 ),
|
||||
m_text( 224, 224, 224 ),
|
||||
m_button( 201, 201, 201 ),
|
||||
m_shadow( 0,0,0 ),
|
||||
m_buttonText( 0,0,0 ),
|
||||
m_brightText( 74, 253, 133 ),
|
||||
m_highlight( 100, 100, 100 ),
|
||||
m_highlightedText( 255, 255, 255 ),
|
||||
m_toolTipText( 0, 0, 0 ),
|
||||
m_toolTipBase( 128, 128, 128 )
|
||||
{
|
||||
setStyle( stylearg );
|
||||
stylearg->polish( this );
|
||||
ensurePolished();
|
||||
}
|
||||
|
||||
LmmsPalette::~LmmsPalette()
|
||||
{
|
||||
}
|
||||
|
||||
#define ACCESSMET( read, write ) \
|
||||
QColor LmmsPalette:: read () const \
|
||||
{ return m_##read ; } \
|
||||
void LmmsPalette:: write ( const QColor & c ) \
|
||||
{ m_##read = QColor( c ); }
|
||||
|
||||
|
||||
ACCESSMET( background, setBackground )
|
||||
ACCESSMET( windowText, setWindowText )
|
||||
ACCESSMET( base, setBase )
|
||||
ACCESSMET( text, setText )
|
||||
ACCESSMET( button, setButton )
|
||||
ACCESSMET( shadow, setShadow )
|
||||
ACCESSMET( buttonText, setButtonText )
|
||||
ACCESSMET( brightText, setBrightText )
|
||||
ACCESSMET( highlight, setHighlight )
|
||||
ACCESSMET( highlightedText, setHighlightedText )
|
||||
ACCESSMET( toolTipText, setToolTipText )
|
||||
ACCESSMET( toolTipBase, setToolTipBase )
|
||||
|
||||
|
||||
QPalette LmmsPalette::palette() const
|
||||
{
|
||||
QPalette pal = QApplication::style()->standardPalette();
|
||||
|
||||
pal.setColor( QPalette::Background, background() );
|
||||
pal.setColor( QPalette::WindowText, windowText() );
|
||||
pal.setColor( QPalette::Base, base() );
|
||||
pal.setColor( QPalette::ButtonText, buttonText() );
|
||||
pal.setColor( QPalette::BrightText, brightText() );
|
||||
pal.setColor( QPalette::Text, text() );
|
||||
pal.setColor( QPalette::Button, button() );
|
||||
pal.setColor( QPalette::Shadow, shadow() );
|
||||
pal.setColor( QPalette::Highlight, highlight() );
|
||||
pal.setColor( QPalette::HighlightedText, highlightedText() );
|
||||
pal.setBrush( QPalette::ToolTipText, QBrush( toolTipText() ) );
|
||||
pal.setBrush( QPalette::ToolTipBase, QBrush( toolTipBase() ) );
|
||||
return pal;
|
||||
}
|
||||
|
||||
|
||||
#include "moc_LmmsPalette.cxx"
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2007-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <QtGui/QStyleOption>
|
||||
|
||||
#include "LmmsStyle.h"
|
||||
|
||||
#include "LmmsPalette.h"
|
||||
|
||||
const int BUTTON_LENGTH = 24;
|
||||
|
||||
@@ -106,7 +106,7 @@ static const char * const s_scrollbarArrowLeftXpm[] = {
|
||||
"..#$$%",
|
||||
"...+@@"};
|
||||
|
||||
|
||||
QPalette * LmmsStyle::s_palette = NULL;
|
||||
|
||||
QLinearGradient getGradient( const QColor & _col, const QRectF & _rect )
|
||||
{
|
||||
@@ -195,6 +195,7 @@ void drawPath( QPainter *p, const QPainterPath &path,
|
||||
}
|
||||
|
||||
|
||||
|
||||
LmmsStyle::LmmsStyle() :
|
||||
QPlastiqueStyle()
|
||||
{
|
||||
@@ -202,7 +203,7 @@ LmmsStyle::LmmsStyle() :
|
||||
file.open( QIODevice::ReadOnly );
|
||||
qApp->setStyleSheet( file.readAll() );
|
||||
|
||||
qApp->setPalette( standardPalette() );
|
||||
if( s_palette != NULL ) { qApp->setPalette( *s_palette ); }
|
||||
}
|
||||
|
||||
|
||||
@@ -210,39 +211,10 @@ LmmsStyle::LmmsStyle() :
|
||||
|
||||
QPalette LmmsStyle::standardPalette( void ) const
|
||||
{
|
||||
if( s_palette != NULL) { return * s_palette; }
|
||||
|
||||
QPalette pal = QPlastiqueStyle::standardPalette();
|
||||
|
||||
/* sane defaults in case fetching from stylesheet fails*/
|
||||
|
||||
pal.setColor( QPalette::Background, QColor( 91, 101, 113 ) );
|
||||
pal.setColor( QPalette::WindowText, QColor( 240, 240, 240 ) );
|
||||
pal.setColor( QPalette::Base, QColor( 128, 128, 128 ) );
|
||||
pal.setColor( QPalette::Text, QColor( 224, 224, 224 ) );
|
||||
pal.setColor( QPalette::Button, QColor( 201, 201, 201 ) );
|
||||
pal.setColor( QPalette::Shadow, QColor( 0, 0, 0 ) );
|
||||
pal.setColor( QPalette::ButtonText, QColor( 0, 0, 0 ) );
|
||||
pal.setColor( QPalette::BrightText, QColor( 74, 253, 133 ) );
|
||||
pal.setColor( QPalette::Highlight, QColor( 100, 100, 100 ) );
|
||||
pal.setColor( QPalette::HighlightedText, QColor( 255, 255, 255 ) );
|
||||
|
||||
/* fetch from stylesheet using regexp */
|
||||
|
||||
QStringList paletteData = qApp->styleSheet().split( '\n' ).filter( QRegExp( "^palette:*" ) );
|
||||
foreach( QString s, paletteData )
|
||||
{
|
||||
if (s.contains(":background")) { pal.setColor( QPalette::Background, QColor( s.mid( s.indexOf("#"), 7 ) ) ); }
|
||||
else if (s.contains(":windowtext")) { pal.setColor( QPalette::WindowText, QColor( s.mid( s.indexOf("#"), 7 ) ) ); }
|
||||
else if (s.contains(":base")) { pal.setColor( QPalette::Base, QColor( s.mid( s.indexOf("#"), 7 ) ) ); }
|
||||
else if (s.contains(":buttontext")) { pal.setColor( QPalette::ButtonText, QColor( s.mid( s.indexOf("#"), 7 ) ) ); }
|
||||
else if (s.contains(":brighttext")) { pal.setColor( QPalette::BrightText, QColor( s.mid( s.indexOf("#"), 7 ) ) ); }
|
||||
else if (s.contains(":text")) { pal.setColor( QPalette::Text, QColor( s.mid( s.indexOf("#"), 7 ) ) ); }
|
||||
else if (s.contains(":button")) { pal.setColor( QPalette::Button, QColor( s.mid( s.indexOf("#"), 7 ) ) ); }
|
||||
else if (s.contains(":shadow")) { pal.setColor( QPalette::Shadow, QColor( s.mid( s.indexOf("#"), 7 ) ) ); }
|
||||
else if (s.contains(":highlightedtext")) { pal.setColor( QPalette::HighlightedText, QColor( s.mid( s.indexOf("#"), 7 ) ) ); }
|
||||
else if (s.contains(":highlight")) { pal.setColor( QPalette::Highlight, QColor( s.mid( s.indexOf("#"), 7 ) ) ); };
|
||||
}
|
||||
|
||||
return( pal );
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -48,7 +48,7 @@
|
||||
#include "PianoView.h"
|
||||
#include "about_dialog.h"
|
||||
#include "ControllerRackView.h"
|
||||
#include "file_browser.h"
|
||||
#include "FileBrowser.h"
|
||||
#include "plugin_browser.h"
|
||||
#include "SideBar.h"
|
||||
#include "config_mgr.h"
|
||||
@@ -95,42 +95,50 @@ MainWindow::MainWindow() :
|
||||
|
||||
QString wdir = configManager::inst()->workingDir();
|
||||
sideBar->appendTab( new pluginBrowser( splitter ) );
|
||||
sideBar->appendTab( new fileBrowser(
|
||||
sideBar->appendTab( new FileBrowser(
|
||||
configManager::inst()->userProjectsDir() + "*" +
|
||||
configManager::inst()->factoryProjectsDir(),
|
||||
"*.mmp *.mmpz *.xml *.mid *.flp",
|
||||
tr( "My projects" ),
|
||||
embed::getIconPixmap( "project_file" ).transformed( QTransform().rotate( 90 ) ),
|
||||
splitter ) );
|
||||
sideBar->appendTab( new fileBrowser(
|
||||
sideBar->appendTab( new FileBrowser(
|
||||
configManager::inst()->userSamplesDir() + "*" +
|
||||
configManager::inst()->factorySamplesDir(),
|
||||
"*", tr( "My samples" ),
|
||||
embed::getIconPixmap( "sample_file" ).transformed( QTransform().rotate( 90 ) ),
|
||||
splitter ) );
|
||||
sideBar->appendTab( new fileBrowser(
|
||||
sideBar->appendTab( new FileBrowser(
|
||||
configManager::inst()->userPresetsDir() + "*" +
|
||||
configManager::inst()->factoryPresetsDir(),
|
||||
"*.xpf *.cs.xml *.xiz",
|
||||
tr( "My presets" ),
|
||||
embed::getIconPixmap( "preset_file" ).transformed( QTransform().rotate( 90 ) ),
|
||||
splitter ) );
|
||||
sideBar->appendTab( new fileBrowser( QDir::homePath(), "*",
|
||||
sideBar->appendTab( new FileBrowser( QDir::homePath(), "*",
|
||||
tr( "My home" ),
|
||||
embed::getIconPixmap( "home" ).transformed( QTransform().rotate( 90 ) ),
|
||||
splitter ) );
|
||||
QFileInfoList drives = QDir::drives();
|
||||
|
||||
QStringList root_paths;
|
||||
#ifdef LMMS_BUILD_APPLE
|
||||
root_paths += "/Volumes";
|
||||
#else
|
||||
QFileInfoList drives = QDir::drives();
|
||||
foreach( const QFileInfo & drive, drives )
|
||||
{
|
||||
root_paths += drive.absolutePath();
|
||||
}
|
||||
sideBar->appendTab( new fileBrowser( root_paths.join( "*" ), "*",
|
||||
#endif
|
||||
sideBar->appendTab( new FileBrowser( root_paths.join( "*" ), "*",
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
tr( "My computer" ),
|
||||
#elif defined(LMMS_BUILD_APPLE)
|
||||
tr( "Volumes" ),
|
||||
#else
|
||||
tr( "Root directory" ),
|
||||
#endif
|
||||
|
||||
embed::getIconPixmap( "computer" ).transformed( QTransform().rotate( 90 ) ),
|
||||
splitter,
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
@@ -279,15 +287,15 @@ void MainWindow::finalize()
|
||||
|
||||
QMenu * edit_menu = new QMenu( this );
|
||||
menuBar()->addMenu( edit_menu )->setText( tr( "&Edit" ) );
|
||||
/* edit_menu->addAction( embed::getIconPixmap( "edit_undo" ),
|
||||
edit_menu->addAction( embed::getIconPixmap( "edit_undo" ),
|
||||
tr( "Undo" ),
|
||||
this, SLOT( undo() ),
|
||||
Qt::CTRL + Qt::Key_Z );
|
||||
edit_menu->addAction( embed::getIconPixmap( "edit_redo" ),
|
||||
tr( "Redo" ),
|
||||
this, SLOT( redo() ),
|
||||
Qt::CTRL + Qt::Key_R );
|
||||
edit_menu->addSeparator();*/
|
||||
Qt::CTRL + Qt::Key_Y );
|
||||
edit_menu->addSeparator();
|
||||
edit_menu->addAction( embed::getIconPixmap( "setup_general" ),
|
||||
tr( "Settings" ),
|
||||
this, SLOT( showSettingsDialog() ) );
|
||||
@@ -388,6 +396,12 @@ void MainWindow::finalize()
|
||||
SLOT( exportProject() ),
|
||||
m_toolBar );
|
||||
|
||||
toolButton * whatsthis = new toolButton(
|
||||
embed::getIconPixmap( "whatsthis" ),
|
||||
tr( "What's this?" ),
|
||||
this, SLOT( enterWhatsThisMode() ),
|
||||
m_toolBar );
|
||||
|
||||
|
||||
m_toolBarLayout->setColumnMinimumWidth( 0, 5 );
|
||||
m_toolBarLayout->addWidget( project_new, 0, 1 );
|
||||
@@ -396,7 +410,7 @@ void MainWindow::finalize()
|
||||
m_toolBarLayout->addWidget( project_open_recent, 0, 4 );
|
||||
m_toolBarLayout->addWidget( project_save, 0, 5 );
|
||||
m_toolBarLayout->addWidget( project_export, 0, 6 );
|
||||
|
||||
m_toolBarLayout->addWidget( whatsthis, 0, 7 );
|
||||
|
||||
|
||||
// window-toolbar
|
||||
@@ -740,11 +754,14 @@ void MainWindow::updateRecentlyOpenedProjectsMenu()
|
||||
|
||||
void MainWindow::openRecentlyOpenedProject( QAction * _action )
|
||||
{
|
||||
const QString & f = _action->text();
|
||||
setCursor( Qt::WaitCursor );
|
||||
engine::getSong()->loadProject( f );
|
||||
configManager::inst()->addRecentlyOpenedProject( f );
|
||||
setCursor( Qt::ArrowCursor );
|
||||
if ( mayChangeProject() )
|
||||
{
|
||||
const QString & f = _action->text();
|
||||
setCursor( Qt::WaitCursor );
|
||||
engine::getSong()->loadProject( f );
|
||||
configManager::inst()->addRecentlyOpenedProject( f );
|
||||
setCursor( Qt::ArrowCursor );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* ModelView.cpp - implementation of ModelView.cpp
|
||||
* ModelView.cpp - implementation of ModelView
|
||||
*
|
||||
* Copyright (c) 2007-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2007-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -28,9 +28,9 @@
|
||||
|
||||
|
||||
|
||||
ModelView::ModelView( Model * _model, QWidget * _this ) :
|
||||
m_widget( _this ),
|
||||
m_model( _model )
|
||||
ModelView::ModelView( Model* model, QWidget* widget ) :
|
||||
m_widget( widget ),
|
||||
m_model( model )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ ModelView::ModelView( Model * _model, QWidget * _this ) :
|
||||
|
||||
ModelView::~ModelView()
|
||||
{
|
||||
if( m_model != NULL && m_model->defaultConstructed() )
|
||||
if( m_model != NULL && m_model->isDefaultConstructed() )
|
||||
{
|
||||
delete m_model;
|
||||
}
|
||||
@@ -48,11 +48,11 @@ ModelView::~ModelView()
|
||||
|
||||
|
||||
|
||||
void ModelView::setModel( Model * _model, bool _old_model_valid )
|
||||
void ModelView::setModel( Model* model, bool isOldModelValid )
|
||||
{
|
||||
if( _old_model_valid && m_model != NULL )
|
||||
if( isOldModelValid && m_model != NULL )
|
||||
{
|
||||
if( m_model->defaultConstructed() )
|
||||
if( m_model->isDefaultConstructed() )
|
||||
{
|
||||
delete m_model;
|
||||
}
|
||||
@@ -61,7 +61,8 @@ void ModelView::setModel( Model * _model, bool _old_model_valid )
|
||||
m_model->disconnect( widget() );
|
||||
}
|
||||
}
|
||||
m_model = _model;
|
||||
|
||||
m_model = model;
|
||||
|
||||
doConnections();
|
||||
|
||||
@@ -77,11 +78,8 @@ void ModelView::doConnections()
|
||||
{
|
||||
if( m_model != NULL )
|
||||
{
|
||||
QObject::connect( m_model, SIGNAL( dataChanged() ),
|
||||
widget(), SLOT( update() ) );
|
||||
|
||||
QObject::connect( m_model, SIGNAL( propertiesChanged() ),
|
||||
widget(), SLOT( update() ) );
|
||||
QObject::connect( m_model, SIGNAL( dataChanged() ), widget(), SLOT( update() ) );
|
||||
QObject::connect( m_model, SIGNAL( propertiesChanged() ), widget(), SLOT( update() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright (c) 2008-2009 Paul Giblock <drfaygo/at/gmail.com>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,8 @@
|
||||
* for testing + according model class
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -31,7 +31,7 @@
|
||||
* \mainpage Instrument plugin keyboard display classes
|
||||
*
|
||||
* \section introduction Introduction
|
||||
*
|
||||
*
|
||||
* \todo fill this out
|
||||
* \todo write isWhite inline function and replace throughout
|
||||
*/
|
||||
@@ -160,7 +160,7 @@ PianoView::~PianoView()
|
||||
|
||||
|
||||
|
||||
/*! \brief Map a keyboard key being pressed to a note in our keyboard view
|
||||
/*! \brief Map a keyboard key being pressed to a note in our keyboard view
|
||||
*
|
||||
* \param _k The keyboard scan code of the key being pressed.
|
||||
* \todo check the scan codes for ',' = c, 'L' = c#, '.' = d, ':' = d#,
|
||||
@@ -255,7 +255,7 @@ int PianoView::getKeyFromKeyEvent( QKeyEvent * _ke )
|
||||
case 19: return 27; // 0 = d'#
|
||||
case 33: return 28; // P = e'
|
||||
case 34: return 29; // [
|
||||
case 21: return 30; // =
|
||||
case 21: return 30; // =
|
||||
case 35: return 31; // ]
|
||||
}
|
||||
#endif
|
||||
@@ -459,7 +459,7 @@ void PianoView::mousePressEvent( QMouseEvent * _me )
|
||||
( ( KEY_ORDER[key_num % KeysPerOctave] ==
|
||||
Piano::WhiteKey ) ?
|
||||
PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) *
|
||||
(float) MidiDefaultVelocity );
|
||||
(float) m_piano->instrumentTrack()->midiPort()->baseVelocity() );
|
||||
if( y_diff < 0 )
|
||||
{
|
||||
velocity = 0;
|
||||
@@ -469,10 +469,10 @@ void PianoView::mousePressEvent( QMouseEvent * _me )
|
||||
Piano::WhiteKey ) ?
|
||||
PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) )
|
||||
{
|
||||
velocity = MidiDefaultVelocity;
|
||||
velocity = m_piano->instrumentTrack()->midiPort()->baseVelocity();
|
||||
}
|
||||
// set note on
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOn, 0, key_num, velocity ) );
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOn, -1, key_num, velocity ) );
|
||||
m_piano->setKeyState( key_num, true );
|
||||
m_lastKey = key_num;
|
||||
|
||||
@@ -517,7 +517,7 @@ void PianoView::mouseReleaseEvent( QMouseEvent * )
|
||||
{
|
||||
if( m_piano != NULL )
|
||||
{
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, 0, m_lastKey, 0 ) );
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, -1, m_lastKey, 0 ) );
|
||||
m_piano->setKeyState( m_lastKey, false );
|
||||
}
|
||||
|
||||
@@ -557,7 +557,7 @@ void PianoView::mouseMoveEvent( QMouseEvent * _me )
|
||||
int velocity = (int)( (float) y_diff /
|
||||
( ( KEY_ORDER[key_num % KeysPerOctave] == Piano::WhiteKey ) ?
|
||||
PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) *
|
||||
(float) MidiDefaultVelocity );
|
||||
(float) m_piano->instrumentTrack()->midiPort()->baseVelocity() );
|
||||
// maybe the user moved the mouse-cursor above or under the
|
||||
// piano-widget while holding left button so check that and
|
||||
// correct volume if necessary
|
||||
@@ -569,7 +569,7 @@ void PianoView::mouseMoveEvent( QMouseEvent * _me )
|
||||
( ( KEY_ORDER[key_num % KeysPerOctave] == Piano::WhiteKey ) ?
|
||||
PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) )
|
||||
{
|
||||
velocity = MidiDefaultVelocity;
|
||||
velocity = m_piano->instrumentTrack()->midiPort()->baseVelocity();
|
||||
}
|
||||
|
||||
// is the calculated key different from current key? (could be the
|
||||
@@ -578,7 +578,7 @@ void PianoView::mouseMoveEvent( QMouseEvent * _me )
|
||||
{
|
||||
if( m_lastKey != -1 )
|
||||
{
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, 0, m_lastKey, 0 ) );
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, -1, m_lastKey, 0 ) );
|
||||
m_piano->setKeyState( m_lastKey, false );
|
||||
m_lastKey = -1;
|
||||
}
|
||||
@@ -586,7 +586,7 @@ void PianoView::mouseMoveEvent( QMouseEvent * _me )
|
||||
{
|
||||
if( _me->pos().y() > PIANO_BASE )
|
||||
{
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOn, 0, key_num, velocity ) );
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOn, -1, key_num, velocity ) );
|
||||
m_piano->setKeyState( key_num, true );
|
||||
m_lastKey = key_num;
|
||||
}
|
||||
@@ -600,7 +600,7 @@ void PianoView::mouseMoveEvent( QMouseEvent * _me )
|
||||
}
|
||||
else if( m_piano->isKeyPressed( key_num ) )
|
||||
{
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiKeyPressure, 0, key_num, velocity ) );
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiKeyPressure, -1, key_num, velocity ) );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -697,7 +697,7 @@ void PianoView::focusOutEvent( QFocusEvent * )
|
||||
// hang otherwise
|
||||
for( int i = 0; i < NumKeys; ++i )
|
||||
{
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, 0, i, 0 ) );
|
||||
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, -1, i, 0 ) );
|
||||
m_piano->setKeyState( i, false );
|
||||
}
|
||||
update();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
|
||||
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -36,7 +36,7 @@
|
||||
#include "bb_track.h"
|
||||
#include "MainWindow.h"
|
||||
#include "debug.h"
|
||||
#include "file_browser.h"
|
||||
#include "FileBrowser.h"
|
||||
#include "ImportFilter.h"
|
||||
#include "Instrument.h"
|
||||
#include "InstrumentTrack.h"
|
||||
@@ -121,10 +121,6 @@ void TrackContainerView::loadSettings( const QDomElement & _this )
|
||||
|
||||
trackView * TrackContainerView::addTrackView( trackView * _tv )
|
||||
{
|
||||
/* QMap<QString, QVariant> map;
|
||||
map["id"] = _tv->getTrack()->id();
|
||||
addJournalEntry( JournalEntry( AddTrack, map ) );*/
|
||||
|
||||
m_trackViews.push_back( _tv );
|
||||
m_scrollLayout->addWidget( _tv );
|
||||
connect( this, SIGNAL( positionChanged( const MidiTime & ) ),
|
||||
@@ -142,13 +138,6 @@ void TrackContainerView::removeTrackView( trackView * _tv )
|
||||
int index = m_trackViews.indexOf( _tv );
|
||||
if( index != -1 )
|
||||
{
|
||||
/* QMap<QString, QVariant> map;
|
||||
DataFile dataFile( DataFile::JournalData );
|
||||
_tv->getTrack()->saveState( dataFile, dataFile.content() );
|
||||
map["id"] = _tv->getTrack()->id();
|
||||
map["state"] = dataFile.toString();
|
||||
addJournalEntry( JournalEntry( RemoveTrack, map ) );*/
|
||||
|
||||
m_trackViews.removeAt( index );
|
||||
|
||||
disconnect( _tv );
|
||||
@@ -230,6 +219,8 @@ void TrackContainerView::realignTracks()
|
||||
|
||||
void TrackContainerView::createTrackView( track * _t )
|
||||
{
|
||||
//m_tc->addJournalCheckPoint();
|
||||
|
||||
_t->createView( this );
|
||||
}
|
||||
|
||||
@@ -238,6 +229,8 @@ void TrackContainerView::createTrackView( track * _t )
|
||||
|
||||
void TrackContainerView::deleteTrackView( trackView * _tv )
|
||||
{
|
||||
//m_tc->addJournalCheckPoint();
|
||||
|
||||
track * t = _tv->getTrack();
|
||||
removeTrackView( _tv );
|
||||
delete _tv;
|
||||
@@ -311,69 +304,12 @@ void TrackContainerView::clearAllTracks()
|
||||
|
||||
|
||||
|
||||
void TrackContainerView::undoStep( JournalEntry & _je )
|
||||
{
|
||||
#if 0
|
||||
saveJournallingState( false );
|
||||
switch( _je.actionID() )
|
||||
{
|
||||
case AddTrack:
|
||||
{
|
||||
QMap<QString, QVariant> map = _je.data().toMap();
|
||||
track * t =
|
||||
dynamic_cast<track *>(
|
||||
engine::projectJournal()->getJournallingObject(
|
||||
map["id"].toInt() ) );
|
||||
assert( t != NULL );
|
||||
DataFile dataFile( DataFile::JournalData );
|
||||
t->saveState( dataFile, dataFile.content() );
|
||||
map["state"] = dataFile.toString();
|
||||
_je.data() = map;
|
||||
t->deleteLater();
|
||||
break;
|
||||
}
|
||||
|
||||
case RemoveTrack:
|
||||
{
|
||||
DataFile dataFile(
|
||||
_je.data().toMap()["state"].toString().utf8() );
|
||||
track::create( dataFile.content().firstChild().toElement(),
|
||||
m_tc );
|
||||
break;
|
||||
}
|
||||
}
|
||||
restoreJournallingState();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TrackContainerView::redoStep( JournalEntry & _je )
|
||||
{
|
||||
#if 0
|
||||
switch( _je.actionID() )
|
||||
{
|
||||
case AddTrack:
|
||||
case RemoveTrack:
|
||||
_je.actionID() = ( _je.actionID() == AddTrack ) ?
|
||||
RemoveTrack : AddTrack;
|
||||
undoStep( _je );
|
||||
_je.actionID() = ( _je.actionID() == AddTrack ) ?
|
||||
RemoveTrack : AddTrack;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TrackContainerView::dragEnterEvent( QDragEnterEvent * _dee )
|
||||
{
|
||||
stringPairDrag::processDragEnterEvent( _dee,
|
||||
QString( "presetfile,pluginpresetfile,samplefile,instrument,"
|
||||
"importedproject,track_%1,track_%2" ).
|
||||
"importedproject,soundfontfile,vstpluginfile,"
|
||||
"track_%1,track_%2" ).
|
||||
arg( track::InstrumentTrack ).
|
||||
arg( track::SampleTrack ) );
|
||||
}
|
||||
@@ -395,13 +331,14 @@ void TrackContainerView::dropEvent( QDropEvent * _de )
|
||||
//it->toggledInstrumentTrackButton( true );
|
||||
_de->accept();
|
||||
}
|
||||
else if( type == "samplefile" || type == "pluginpresetfile" )
|
||||
else if( type == "samplefile" || type == "pluginpresetfile"
|
||||
|| type == "soundfontfile" || type == "vstpluginfile")
|
||||
{
|
||||
InstrumentTrack * it = dynamic_cast<InstrumentTrack *>(
|
||||
track::create( track::InstrumentTrack,
|
||||
m_tc ) );
|
||||
Instrument * i = it->loadInstrument(
|
||||
engine::pluginFileHandling()[fileItem::extension(
|
||||
engine::pluginFileHandling()[FileItem::extension(
|
||||
value )]);
|
||||
i->loadFile( value );
|
||||
//it->toggledInstrumentTrackButton( true );
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
@@ -35,9 +35,11 @@
|
||||
#include "song.h"
|
||||
#include "tool_button.h"
|
||||
#include "config_mgr.h"
|
||||
#include "DataFile.h"
|
||||
#include "string_pair_drag.h"
|
||||
|
||||
#include "TrackContainer.h"
|
||||
#include "pattern.h"
|
||||
#include "Pattern.h"
|
||||
|
||||
|
||||
|
||||
@@ -157,14 +159,33 @@ bbEditor::~bbEditor()
|
||||
}
|
||||
|
||||
|
||||
void bbEditor::dropEvent( QDropEvent * de )
|
||||
{
|
||||
QString type = stringPairDrag::decodeKey( de );
|
||||
QString value = stringPairDrag::decodeValue( de );
|
||||
|
||||
if( type.left( 6 ) == "track_" )
|
||||
{
|
||||
DataFile dataFile( value.toUtf8() );
|
||||
track * t = track::create( dataFile.content().firstChild().toElement(), model() );
|
||||
|
||||
t->deleteTCOs();
|
||||
m_bbtc->updateAfterTrackAdd();
|
||||
|
||||
de->accept();
|
||||
}
|
||||
else
|
||||
{
|
||||
TrackContainerView::dropEvent( de );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bbEditor::removeBBView( int _bb )
|
||||
{
|
||||
QList<trackView *> tl = trackViews();
|
||||
for( int i = 0; i < tl.size(); ++i )
|
||||
foreach( trackView* view, trackViews() )
|
||||
{
|
||||
tl[i]->getTrackContentWidget()->removeTCOView( _bb );
|
||||
view->getTrackContentWidget()->removeTCOView( _bb );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,8 +256,7 @@ void bbEditor::addSteps()
|
||||
{
|
||||
if( ( *it )->type() == track::InstrumentTrack )
|
||||
{
|
||||
pattern * p = static_cast<pattern *>(
|
||||
( *it )->getTCO( m_bbtc->currentBB() ) );
|
||||
Pattern* p = static_cast<Pattern *>( ( *it )->getTCO( m_bbtc->currentBB() ) );
|
||||
p->addSteps();
|
||||
}
|
||||
}
|
||||
@@ -254,8 +274,7 @@ void bbEditor::removeSteps()
|
||||
{
|
||||
if( ( *it )->type() == track::InstrumentTrack )
|
||||
{
|
||||
pattern * p = static_cast<pattern *>(
|
||||
( *it )->getTCO( m_bbtc->currentBB() ) );
|
||||
Pattern* p = static_cast<Pattern *>( ( *it )->getTCO( m_bbtc->currentBB() ) );
|
||||
p->removeSteps();
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user