Merge branch 'stable-1.1' of https://github.com/midi-pascal/lmms into stable-1.1

This commit is contained in:
pascal
2014-11-23 15:44:38 -05:00
2131 changed files with 251560 additions and 89596 deletions

View File

@@ -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"

View File

@@ -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;
}

View 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();
*/
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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());
}

View File

@@ -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 ) );
}
}

View File

@@ -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;
}

View File

@@ -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 );

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 );
}
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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:

View File

@@ -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
View 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 );
}
}

View File

@@ -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

View File

@@ -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 )

View File

@@ -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

View 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 );
}

View 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();
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 )

View File

@@ -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

View File

@@ -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

View File

@@ -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" );
}
}

View File

@@ -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"

View File

@@ -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"

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 )
{

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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() );

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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() )

View File

@@ -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

View File

@@ -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" ) ),

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -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"

View File

@@ -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 );

View File

@@ -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 )

View File

@@ -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 ) );
}

View File

@@ -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;
}

View File

@@ -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
{

View File

@@ -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 ) )

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -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 );
}
}
}

View File

@@ -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

View File

@@ -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
View 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"

View File

@@ -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 );
}

View File

@@ -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 );
}
}

View File

@@ -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() ) );
}
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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 );

View File

@@ -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

View File

@@ -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