Merge branch 'stable-1.1'

Conflicts:
	include/basic_filters.h
	src/core/FxMixer.cpp
This commit is contained in:
Vesa
2014-06-28 17:24:23 +03:00
22 changed files with 584 additions and 355 deletions

View File

@@ -34,6 +34,34 @@
#include "bb_track_container.h"
#include "ValueBuffer.h"
FxRoute::FxRoute( FxChannel * from, FxChannel * to, float amount ) :
m_from( from ),
m_to( to )
{
//qDebug( "created: %d to %d", m_from->m_channelIndex, m_to->m_channelIndex );
// create send amount model
m_amount = new FloatModel( 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 ) );
}
FxRoute::~FxRoute()
{
// remove send model
delete m_amount;
}
void FxRoute::updateName()
{
if( m_amount)
{
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_hasInput( false ),
@@ -65,7 +93,6 @@ FxChannel::~FxChannel()
void FxChannel::doProcessing( sampleFrame * _buf )
{
FxMixer * fxm = engine::fxMixer();
const fpp_t fpp = engine::mixer()->framesPerPeriod();
// <tobydox> ignore the passed _buf
@@ -77,17 +104,15 @@ void FxChannel::doProcessing( sampleFrame * _buf )
// <tobydox> this improves cache hit rate
_buf = m_buffer;
// SMF: OK, due to the fact, that the data from the audio-tracks has been
// written into our buffer already, all which needs to be done at this
// stage is to process inter-channel sends.
if( m_muteModel.value() == false )
{
// OK, we are not muted, so we go recursively through all the channels
// which send to us (our children)...
foreach( fx_ch_t senderIndex, m_receives )
foreach( FxRoute * senderRoute, m_receives )
{
FxChannel * sender = fxm->effectChannel( senderIndex );
FxChannel * sender = senderRoute->sender();
FloatModel * sendModel = senderRoute->amount();
if( ! sendModel ) qFatal( "Error: no send model found from %d to %d", senderRoute->senderIndex(), m_channelIndex );
// wait for the sender job - either it's just been queued yet,
// then ThreadableJob::process() will process it now within this
@@ -101,8 +126,8 @@ void FxChannel::doProcessing( sampleFrame * _buf )
if( sender->m_hasInput || sender->m_stillRunning )
{
// figure out if we're getting sample-exact input
ValueBuffer * sendBuf = fxm->channelSendModel( senderIndex, m_channelIndex )->hasSampleExactData()
? fxm->channelSendModel( senderIndex, m_channelIndex )->valueBuffer()
ValueBuffer * sendBuf = sendModel->hasSampleExactData()
? sendModel->valueBuffer()
: NULL;
ValueBuffer * volBuf = sender->m_volumeModel.hasSampleExactData()
? sender->m_volumeModel.valueBuffer()
@@ -114,7 +139,7 @@ void FxChannel::doProcessing( sampleFrame * _buf )
// use sample-exact mixing if sample-exact values are available
if( ! volBuf && ! sendBuf ) // neither volume nor send has sample-exact data...
{
const float v = sender->m_volumeModel.value() * fxm->channelSendModel( senderIndex, m_channelIndex )->value();
const float v = sender->m_volumeModel.value() * sendModel->value();
MixHelpers::addMultiplied( _buf, ch_buf, v, fpp );
}
else if( volBuf && sendBuf ) // both volume and send have sample-exact data
@@ -123,7 +148,7 @@ void FxChannel::doProcessing( sampleFrame * _buf )
}
else if( volBuf ) // volume has sample-exact data but send does not
{
const float v = fxm->channelSendModel( senderIndex, m_channelIndex )->value();
const float v = sendModel->value();
MixHelpers::addMultipliedByBuffer( _buf, ch_buf, v, volBuf, fpp );
}
else // vice versa
@@ -168,12 +193,12 @@ FxMixer::FxMixer() :
FxMixer::~FxMixer()
{
while( ! m_fxRoutes.isEmpty() )
{
deleteChannelSend( m_fxRoutes.first() );
}
for( int i = 0; i < m_fxChannels.size(); ++i )
{
for( int j = 0; j < m_fxChannels[i]->m_sendAmount.size(); ++j)
{
delete m_fxChannels[i]->m_sendAmount[j];
}
delete m_fxChannels[i];
}
}
@@ -193,10 +218,12 @@ int FxMixer::createChannel()
}
void FxMixer::deleteChannel(int 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();
@@ -223,52 +250,34 @@ void FxMixer::deleteChannel(int index)
}
// delete all of this channel's sends and receives
while( ! m_fxChannels[index]->m_sends.isEmpty() )
while( ! ch->m_sends.isEmpty() )
{
deleteChannelSend( index, m_fxChannels[index]->m_sends.first() );
deleteChannelSend( ch->m_sends.first() );
}
while( ! m_fxChannels[index]->m_receives.isEmpty() )
while( ! ch->m_receives.isEmpty() )
{
deleteChannelSend( m_fxChannels[index]->m_receives.first(), index );
}
for(int i=0; i<m_fxChannels.size(); ++i)
{
// for every send/receive, adjust for the channel index change
for(int j=0; j<m_fxChannels[i]->m_sends.size(); ++j)
{
if( m_fxChannels[i]->m_sends[j] > index )
{
// subtract 1 to make up for the missing channel
--m_fxChannels[i]->m_sends[j];
}
}
for(int j=0; j<m_fxChannels[i]->m_receives.size(); ++j)
{
if( m_fxChannels[i]->m_receives[j] > index )
{
// subtract 1 to make up for the missing channel
--m_fxChannels[i]->m_receives[j];
}
}
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)
void FxMixer::moveChannelLeft( int index )
{
// can't move master or first channel
if( index <= 1 || index >= m_fxChannels.size() )
{
return;
}
m_sendsMutex.lock();
// channels to swap
int a = index - 1, b = index;
@@ -299,122 +308,132 @@ void FxMixer::moveChannelLeft(int index)
}
}
for(int i=0; i<m_fxChannels.size(); ++i)
{
// for every send/receive, adjust for the channel index change
for(int j=0; j<m_fxChannels[i]->m_sends.size(); ++j)
{
if( m_fxChannels[i]->m_sends[j] == a )
{
m_fxChannels[i]->m_sends[j] = b;
}
else if( m_fxChannels[i]->m_sends[j] == b )
{
m_fxChannels[i]->m_sends[j] = a;
}
}
for(int j=0; j<m_fxChannels[i]->m_receives.size(); ++j)
{
if( m_fxChannels[i]->m_receives[j] == a )
{
m_fxChannels[i]->m_receives[j] = b;
}
else if( m_fxChannels[i]->m_receives[j] == b )
{
m_fxChannels[i]->m_receives[j] = a;
}
}
}
// actually do the swap
FxChannel * tmpChannel = m_fxChannels[a];
m_fxChannels[a] = m_fxChannels[b];
m_fxChannels[b] = tmpChannel;
m_sendsMutex.unlock();
validateChannelName( a, b );
validateChannelName( b, a );
}
void FxMixer::moveChannelRight(int index)
void FxMixer::moveChannelRight( int index )
{
moveChannelLeft(index+1);
moveChannelLeft( index + 1 );
}
void FxMixer::createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel,
float amount)
void 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];
for(int i=0; i<from->m_sends.size(); ++i){
if( from->m_sends[i] == toChannel )
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_sendAmount[i]->setValue(amount);
from->m_sends[i]->amount()->setValue( amount );
return;
}
}
// connection does not exist. create a new one
m_sendsMutex.lock();
// add to from's sends
from->m_sends.push_back(toChannel);
from->m_sendAmount.push_back(new FloatModel(amount, 0, 1, 0.001, NULL,
tr("Amount to send")));
createRoute( from, to, amount );
}
// add to to's receives
m_fxChannels[toChannel]->m_receives.push_back(fromChannel);
void FxMixer::createRoute( FxChannel * from, FxChannel * to, float amount )
{
if( from == to )
{
return;
}
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();
}
// delete the connection made by createChannelSend
void FxMixer::deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel)
void FxMixer::deleteChannelSend( fx_ch_t fromChannel, fx_ch_t toChannel )
{
// delete the send
FxChannel * from = m_fxChannels[fromChannel];
FxChannel * to = m_fxChannels[toChannel];
m_sendsMutex.lock();
// find and delete the send entry
for(int i=0; i<from->m_sends.size(); ++i) {
if( from->m_sends[i] == toChannel )
{
// delete this index
delete from->m_sendAmount[i];
from->m_sendAmount.remove(i);
from->m_sends.remove(i);
break;
}
}
// find and delete the receive entry
for(int i=0; i<to->m_receives.size(); ++i)
// find and delete the send entry
for( int i = 0; i < from->m_sends.size(); ++i )
{
if( to->m_receives[i] == fromChannel )
if( from->m_sends[i]->receiver() == to )
{
// delete this index
to->m_receives.remove(i);
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) {
bool FxMixer::isInfiniteLoop( fx_ch_t sendFrom, fx_ch_t sendTo )
{
if( sendFrom == sendTo ) return true;
//m_sendsMutex.lock();
FxChannel * from = m_fxChannels[sendFrom];
FxChannel * to = m_fxChannels[sendTo];
bool b = checkInfiniteLoop( from, to );
//m_sendsMutex.unlock();
return b;
}
bool FxMixer::checkInfiniteLoop( FxChannel * from, FxChannel * to )
{
// can't send master to anything
if( sendFrom == 0 ) return true;
if( from == m_fxChannels[0] )
{
return true;
}
// can't send channel to itself
if( sendFrom == sendTo ) return true;
if( from == to )
{
return true;
}
// follow sendTo's outputs recursively looking for something that sends
// to sendFrom
for(int i=0; i<m_fxChannels[sendTo]->m_sends.size(); ++i)
for( int i=0; i < to->m_sends.size(); ++i )
{
if( isInfiniteLoop( sendFrom, m_fxChannels[sendTo]->m_sends[i] ) )
if( checkInfiniteLoop( from, to->m_sends[i]->receiver() ) )
{
return true;
}
@@ -425,13 +444,23 @@ bool FxMixer::isInfiniteLoop(fx_ch_t sendFrom, fx_ch_t sendTo) {
// how much does fromChannel send its output to the input of toChannel?
FloatModel * FxMixer::channelSendModel(fx_ch_t fromChannel, fx_ch_t toChannel)
FloatModel * FxMixer::channelSendModel( fx_ch_t fromChannel, fx_ch_t toChannel )
{
FxChannel * from = m_fxChannels[fromChannel];
for(int i=0; i<from->m_sends.size(); ++i){
if( from->m_sends[i] == toChannel )
return from->m_sendAmount[i];
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;
}
@@ -439,15 +468,6 @@ FloatModel * FxMixer::channelSendModel(fx_ch_t fromChannel, fx_ch_t toChannel)
void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch )
{
// SMF: it seems like here the track-channels are mixed in... but from where
// is this called and when and why...?!?
//
// OK, found it (git grep is your friend...): This is the next part,
// where there is a mix between push and pull model inside the core, as
// the audio-tracks *push* their data into the fx-channels hopefully just
// before the Mixer-Channels are processed... Sorry to say this: but this
// took me senseless hours to find out and is silly, too...
if( m_fxChannels[_ch]->m_muteModel.value() == false )
{
m_fxChannels[_ch]->m_lock.lock();
@@ -468,24 +488,22 @@ void FxMixer::prepareMasterMix()
void FxMixer::addChannelLeaf( int _ch, sampleFrame * _buf )
void FxMixer::addChannelLeaf( FxChannel * ch, sampleFrame * buf )
{
FxChannel * thisCh = m_fxChannels[_ch];
// if we're muted or this channel is seen already, discount it
if( thisCh->m_queued )
if( ch->m_queued )
{
return;
}
foreach( const int senderIndex, thisCh->m_receives )
foreach( FxRoute * senderRoute, ch->m_receives )
{
addChannelLeaf( senderIndex, _buf );
addChannelLeaf( senderRoute->sender(), buf );
}
// add this channel to job list
thisCh->m_queued = true;
MixerWorkerThread::addJob( thisCh );
ch->m_queued = true;
MixerWorkerThread::addJob( ch );
}
@@ -498,15 +516,14 @@ void FxMixer::masterMix( sampleFrame * _buf )
// and add all channels to job list that have no dependencies
// when the channel completes it will check its parent to see if it needs
// to be processed.
m_sendsMutex.lock();
//m_sendsMutex.lock();
MixerWorkerThread::resetJobQueue( MixerWorkerThread::JobQueue::Dynamic );
addChannelLeaf( 0, _buf );
addChannelLeaf( m_fxChannels[0], _buf );
while( m_fxChannels[0]->state() != ThreadableJob::Done )
{
MixerWorkerThread::startAndWaitForJobs();
}
//m_fxChannels[0]->doProcessing( NULL );
m_sendsMutex.unlock();
//m_sendsMutex.unlock();
// handle sample-exact data in master volume fader
ValueBuffer * volBuf = m_fxChannels[0]->m_volumeModel.hasSampleExactData()
@@ -564,7 +581,7 @@ void FxMixer::clearChannel(fx_ch_t index)
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 );
ch->m_volumeModel.setDisplayName( ch->m_name );
// send only to master
if( index > 0)
@@ -572,17 +589,17 @@ void FxMixer::clearChannel(fx_ch_t index)
// delete existing sends
while( ! ch->m_sends.isEmpty() )
{
deleteChannelSend( index, ch->m_sends.first() );
deleteChannelSend( ch->m_sends.first() );
}
// add send to master
createChannelSend(index, 0);
createChannelSend( index, 0 );
}
// delete receives
while( ! ch->m_receives.isEmpty() )
{
deleteChannelSend( ch->m_receives.first(), index );
deleteChannelSend( ch->m_receives.first() );
}
}
@@ -607,8 +624,8 @@ void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this )
QDomElement sendsDom = _doc.createElement( QString( "send" ) );
fxch.appendChild( sendsDom );
sendsDom.setAttribute( "channel", ch->m_sends[si] );
ch->m_sendAmount[si]->saveSettings( _doc, sendsDom, "amount");
sendsDom.setAttribute( "channel", ch->m_sends[si]->receiverIndex() );
ch->m_sends[si]->amount()->saveSettings( _doc, sendsDom, "amount" );
}
}
}
@@ -621,7 +638,7 @@ void FxMixer::allocateChannelsTo(int num)
createChannel();
// delete the default send to master
deleteChannelSend(m_fxChannels.size()-1, 0);
deleteChannelSend( m_fxChannels.size()-1, 0 );
}
}
@@ -658,7 +675,7 @@ void FxMixer::loadSettings( const QDomElement & _this )
{
thereIsASend = true;
int sendTo = chDataItem.attribute( "channel" ).toInt();
allocateChannelsTo( sendTo) ;
allocateChannelsTo( sendTo ) ;
float amount = chDataItem.attribute( "amount" ).toFloat();
createChannelSend( num, sendTo, amount );
}
@@ -674,10 +691,31 @@ void FxMixer::loadSettings( const QDomElement & _this )
// create a send from every channel into master
for( int i=1; i<m_fxChannels.size(); ++i )
{
createChannelSend(i, 0);
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

@@ -121,7 +121,7 @@ FxMixerView::FxMixerView() :
channelArea->setMinimumWidth( fxLineSize.width() * 6 );
channelArea->setFixedHeight( fxLineSize.height() +
style()->pixelMetric( QStyle::PM_ScrollBarExtent ) );
ml->addWidget(channelArea);
ml->addWidget( channelArea, 1, Qt::AlignTop );
// show the add new effect channel button
QPushButton * newChannelBtn = new QPushButton( embed::getIconPixmap( "new_channel" ), QString::null, this );
@@ -132,7 +132,7 @@ FxMixerView::FxMixerView() :
// add the stacked layout for the effect racks of fx channels
ml->addWidget( m_racksWidget, 0, Qt::AlignTop );
ml->addWidget( m_racksWidget, 0, Qt::AlignTop | Qt::AlignRight );
setCurrentFxLine( m_fxChannelViews[0]->m_fxLine );

View File

@@ -40,12 +40,23 @@
#include "caption_menu.h"
const int FxLine::FxLineHeight = 287;
QPixmap * FxLine::s_sendBgArrow = NULL;
QPixmap * FxLine::s_receiveBgArrow = NULL;
FxLine::FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex) :
QWidget( _parent ),
m_mv( _mv ),
m_channelIndex( _channelIndex )
{
if( ! s_sendBgArrow )
{
s_sendBgArrow = new QPixmap( embed::getIconPixmap( "send_bg_arrow", 29, 56 ) );
}
if( ! s_receiveBgArrow )
{
s_receiveBgArrow = new QPixmap( embed::getIconPixmap( "receive_bg_arrow", 29, 56 ) );
}
setFixedSize( 33, FxLineHeight );
setAttribute( Qt::WA_OpaquePaintEvent, true );
setCursor( QCursor( embed::getIconPixmap( "hand" ), 0, 0 ) );
@@ -97,7 +108,7 @@ void FxLine::setChannelIndex(int index) {
}
static void drawFxLine( QPainter* p, const FxLine *fxLine, const QString& name, bool isActive, bool sendToThis )
void FxLine::drawFxLine( QPainter* p, const FxLine *fxLine, const QString& name, bool isActive, bool sendToThis, bool receiveFromThis )
{
int width = fxLine->rect().width();
int height = fxLine->rect().height();
@@ -111,10 +122,7 @@ static void drawFxLine( QPainter* p, const FxLine *fxLine, const QString& name,
p->fillRect( fxLine->rect(), isActive ? fxLine->backgroundActive() : p->background() );
p->setPen( QColor( 0, 0, 0, 75 ) );
p->drawRect( 0, 0, width-2, height-2 );
p->setPen( QColor( 255, 255, 255, 75 ) );
p->setPen( QColor( 255, 255, 255, isActive ? 100 : 50 ) );
p->drawRect( 1, 1, width-3, height-3 );
p->setPen( isActive ? sh_color : QColor( 0, 0, 0, 50 ) );
@@ -123,8 +131,11 @@ static void drawFxLine( QPainter* p, const FxLine *fxLine, const QString& name,
// draw the mixer send background
if( sendToThis )
{
p->drawPixmap( 3, 0, 28, 56,
embed::getIconPixmap("send_bg_arrow", 28, 56 ) );
p->drawPixmap( 2, 0, 29, 56, *s_sendBgArrow );
}
else if( receiveFromThis )
{
p->drawPixmap( 2, 0, 29, 56, *s_receiveBgArrow );
}
// draw the channel name
@@ -145,12 +156,14 @@ void FxLine::paintEvent( QPaintEvent * )
{
FxMixer * mix = engine::fxMixer();
bool sendToThis = mix->channelSendModel(
m_mv->currentFxLine()->m_channelIndex, m_channelIndex) != NULL;
m_mv->currentFxLine()->m_channelIndex, m_channelIndex ) != NULL;
bool receiveFromThis = mix->channelSendModel(
m_channelIndex, m_mv->currentFxLine()->m_channelIndex ) != NULL;
QPainter painter;
painter.begin( this );
drawFxLine( &painter, this,
mix->effectChannel(m_channelIndex)->m_name,
m_mv->currentFxLine() == this, sendToThis );
mix->effectChannel( m_channelIndex )->m_name,
m_mv->currentFxLine() == this, sendToThis, receiveFromThis );
painter.end();
}