Used FloatModels as the backend for mixer sends

Can add new channels in the mixer, and sends are implemented.
Instruments are hardcoded at 10. FL Import is hardcoded at 64.
This commit is contained in:
Andrew Kelley
2009-09-16 09:41:57 -07:00
parent 45c4b7b824
commit 89d5be7855
13 changed files with 263 additions and 185 deletions

View File

@@ -61,49 +61,64 @@ FxChannel::~FxChannel()
FxMixer::FxMixer() :
JournallingObject(),
Model( NULL )
Model( NULL ),
m_fxChannels()
{
// create master channel
m_fxChannels[0] = new FxChannel(this);
// create the rest of the channels
for( int i = 1; i < NumFxChannels+1; ++i )
{
// create new channel
m_fxChannels[i] = new FxChannel( this );
// send the channel into master
createChannelSend(i, 0);
}
// reset name etc.
clear();
createChannel();
}
FxMixer::~FxMixer()
{
for( int i = 0; i < NumFxChannels+1; ++i )
for( int i = 0; i < m_fxChannels.size(); ++i )
{
delete m_fxChannels[i]->m_sendAmount[i];
delete m_fxChannels[i];
}
}
void FxMixer::createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel)
int FxMixer::createChannel()
{
// first make sure the send doesn't already exist
if( ! channelSendsTo(fromChannel, toChannel) )
{
// add to from's sends
m_fxChannels[fromChannel]->m_sends.push_back(toChannel);
// create new channel
m_fxChannels.push_back(new FxChannel( this ));
// add to to's receives
m_fxChannels[toChannel]->m_receives.push_back(fromChannel);
// reset channel state
int index = m_fxChannels.size() - 1;
clearChannel(index);
return index;
}
void FxMixer::createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel,
float amount)
{
// 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 )
{
// simply adjust the amount
from->m_sendAmount[i]->setValue(amount);
return;
}
}
// connection does not exist. create a new one
// 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")));
// add to to's receives
m_fxChannels[toChannel]->m_receives.push_back(fromChannel);
}
@@ -120,6 +135,8 @@ void FxMixer::deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel)
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;
}
@@ -139,15 +156,15 @@ void FxMixer::deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel)
// does fromChannel send its output to the input of toChannel?
bool FxMixer::channelSendsTo(fx_ch_t fromChannel, fx_ch_t toChannel)
// how much does fromChannel send its output to the input of 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 true;
return from->m_sendAmount[i];
}
return false;
return NULL;
}
@@ -182,12 +199,13 @@ void FxMixer::processChannel( fx_ch_t _ch, sampleFrame * _buf )
processChannel( senderIndex );
// mix it with this one
float amt = channelSendModel(senderIndex, _ch)->value();
sampleFrame * ch_buf = sender->m_buffer;
const float v = sender->m_volumeModel.value();
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;
_buf[f][0] += ch_buf[f][0] * v * amt;
_buf[f][1] += ch_buf[f][1] * v * amt;
}
engine::getMixer()->clearAudioBuffer( ch_buf,
engine::getMixer()->framesPerPeriod() );
@@ -233,13 +251,6 @@ void FxMixer::masterMix( sampleFrame * _buf )
processChannel( 0, _buf );
/*if( m_fxChannels[0]->m_muteModel.value() )
{
engine::getMixer()->clearAudioBuffer( _buf,
engine::getMixer()->framesPerPeriod() );
return;
}*/
const float v = m_fxChannels[0]->m_volumeModel.value();
for( f_cnt_t f = 0; f < engine::getMixer()->framesPerPeriod(); ++f )
{
@@ -256,25 +267,46 @@ void FxMixer::masterMix( sampleFrame * _buf )
void FxMixer::clear()
{
for( int i = 0; i <= NumFxChannels; ++i )
for( int i = 0; i < m_fxChannels.size(); ++i )
{
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 );
clearChannel(i);
}
}
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
for( int i=0; i<ch->m_sends.size(); ++i)
{
deleteChannelSend(index, ch->m_sends[i]);
}
// add send to master
createChannelSend(index, 0);
}
// delete receives
for( int i=0; i<ch->m_receives.size(); ++i)
{
deleteChannelSend(ch->m_receives[i], index);
}
}
void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
for( int i = 0; i <= NumFxChannels; ++i )
for( int i = 0; i < m_fxChannels.size(); ++i )
{
QDomElement fxch = _doc.createElement( QString( "fxchannel" ) );
_this.appendChild( fxch );
@@ -295,7 +327,7 @@ void FxMixer::loadSettings( const QDomElement & _this )
{
clear();
QDomNode node = _this.firstChild();
for( int i = 0; i <= NumFxChannels; ++i )
for( int i = 0; i <= 64; ++i ) // TODO make this work
{
QDomElement fxch = node.toElement();
int num = fxch.attribute( "num" ).toInt();

View File

@@ -61,10 +61,6 @@
#endif
static QVector<fx_ch_t> __fx_channel_jobs( NumFxChannels );
class MixerWorkerThread : public QThread
{
public:
@@ -284,11 +280,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() )
{

View File

@@ -22,6 +22,8 @@
*
*/
#include <QtGlobal>
#include <QtGui/QButtonGroup>
#include <QtGui/QInputDialog>
#include <QtGui/QLayout>
@@ -31,9 +33,12 @@
#include <QtGui/QPushButton>
#include <QtGui/QToolButton>
#include <QtGui/QStackedLayout>
#include <QtGui/QScrollArea>
#include "FxMixerView.h"
#include "fader.h"
#include "knob.h"
#include "EffectRackView.h"
#include "engine.h"
#include "embed.h"
@@ -44,26 +49,40 @@
#include "pixmap_button.h"
class SendIndicator : public QWidget
{
public:
SendIndicator( QWidget * _parent ) :
QWidget( _parent )
{
setFixedSize(23, 16);
}
};
class FxLine : public QWidget
{
public:
FxLine( QWidget * _parent, FxMixerView * _mv, QString & _name ) :
FxLine( QWidget * _parent, FxMixerView * _mv, QString & _name,
int _channelIndex) :
QWidget( _parent ),
m_channelIndex( _channelIndex ),
m_mv( _mv ),
m_name( _name )
{
setFixedSize( 32, 232 );
setFixedSize( 32, 287 );
setAttribute( Qt::WA_OpaquePaintEvent, true );
setCursor( QCursor( embed::getIconPixmap( "hand" ), 0, 0 ) );
}
virtual void paintEvent( QPaintEvent * )
{
bool sendToThis = engine::fxMixer()->channelSendModel(
m_mv->currentFxLine()->m_channelIndex, m_channelIndex) != NULL;
QPainter painter;
painter.begin( this );
engine::getLmmsStyle()->drawFxLine( &painter,
this, m_name, m_mv->currentFxLine() == this );
engine::getLmmsStyle()->drawFxLine( &painter, this, m_name,
m_mv->currentFxLine() == this, sendToThis );
painter.end();
}
@@ -87,7 +106,8 @@ public:
}
}
knob * m_sendKnob;
int m_channelIndex;
private:
FxMixerView * m_mv;
QString & m_name;
@@ -114,10 +134,6 @@ FxMixerView::FxMixerView() :
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 );
@@ -128,93 +144,40 @@ FxMixerView::FxMixerView() :
ml->setSpacing( 0 );
ml->addSpacing( 6 );
m_fxChannelViews.resize(m->numChannels());
channelArea = new QScrollArea(this);
chLayout = new QHBoxLayout(channelArea);
QHBoxLayout * banks[NumFxChannels/16];
for( int i = 0; i < NumFxChannels/16; ++i )
// add master channel
FxChannelView * masterView = &m_fxChannelViews[0];
addFxLine(0, this, ml);
ml->addSpacing(5);
QSize fxLineSize = masterView->m_fxLine->size();
chLayout->setSizeConstraint(QLayout::SetMinimumSize);
channelArea->setWidgetResizable(true);
// 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 );
addFxLine(i, channelArea, chLayout);
}
// add the scrolling section to the main layout
ml->addLayout(chLayout);
for( int i = 0; i < NumFxChannels+1; ++i )
{
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 );
}
lcdSpinBox * l = new lcdSpinBox( 2, cv->m_fxLine );
l->model()->setRange( i, i );
l->model()->setValue( i );
l->move( 2, 4 );
l->setMarginWidth( 1 );
// show the add new effect channel button
QPushButton * newChannelBtn = new QPushButton("new", this );
newChannelBtn->setFont(QFont("sans-serif", 10, 1, false));
newChannelBtn->setFixedSize(fxLineSize);
connect( newChannelBtn, SIGNAL(clicked()), this, SLOT(addNewChannel()));
ml->addWidget( newChannelBtn );
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 );
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 )
{
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);
}
l->addSpacing( 10 );
ml->addLayout( l );
connect( g, SIGNAL( buttonClicked( int ) ),
m_fxLineBanks, SLOT( setCurrentIndex( int ) ) );
}
}
ml->addLayout( m_fxLineBanks );
ml->addLayout( m_fxRacksLayout );
setLayout( ml );
updateGeometry();
m_fxLineBanks->setCurrentIndex( 0 );
setCurrentFxLine( m_fxChannelViews[0].m_fxLine );
// timer for updating faders
@@ -226,10 +189,9 @@ 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);
subWin->layout()->setSizeConstraint(QLayout::SetMinimumSize);
parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false );
parentWidget()->move( 5, 310 );
@@ -239,6 +201,54 @@ FxMixerView::FxMixerView() :
}
void FxMixerView::addFxLine(int i, QWidget * parent, QLayout * layout)
{
FxMixer * m = engine::fxMixer();
FxChannelView * cv = &m_fxChannelViews[i];
cv->m_fxLine = new FxLine( parent, this,
m->m_fxChannels[i]->m_name, i );
layout->addWidget(cv->m_fxLine);
// mixer sends knob
cv->m_fxLine->m_sendKnob = new knob(0, cv->m_fxLine,
tr("Channel send amount"));
cv->m_fxLine->m_sendKnob->move(0, 22);
cv->m_fxLine->m_sendKnob->setVisible(false);
// send light indicator
// channel number
lcdSpinBox * l = new lcdSpinBox( 2, cv->m_fxLine );
l->model()->setRange( i, i );
l->model()->setValue( i );
l->move( 2, 58 );
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 );
m_fxRacksLayout->addWidget( cv->m_rackView );
}
FxMixerView::~FxMixerView()
@@ -247,6 +257,18 @@ FxMixerView::~FxMixerView()
void FxMixerView::addNewChannel()
{
// add new fx mixer channel and redraw the form.
FxMixer * mix = engine::fxMixer();
int newChannelIndex = mix->createChannel();
m_fxChannelViews.push_back(FxChannelView());
addFxLine(newChannelIndex, channelArea, chLayout);
}
void FxMixerView::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
@@ -266,13 +288,29 @@ void FxMixerView::loadSettings( const QDomElement & _this )
void FxMixerView::setCurrentFxLine( FxLine * _line )
{
FxMixer * mix = engine::fxMixer();
// select
m_currentFxLine = _line;
for( int i = 0; i < NumFxChannels+1; ++i )
m_fxRacksLayout->setCurrentIndex( _line->m_channelIndex );
// set up send knob
for(int i = 0; i < m_fxChannelViews.size(); ++i)
{
if( m_fxChannelViews[i].m_fxLine == _line )
// does current channel send to this channel?
FloatModel * sendModel = mix->channelSendModel(_line->m_channelIndex, i);
if( sendModel == NULL )
{
m_fxRacksLayout->setCurrentIndex( i );
// does not send, hide send knob
m_fxChannelViews[i].m_fxLine->m_sendKnob->setVisible(false);
}
else
{
// it does send, show knob and connect
m_fxChannelViews[i].m_fxLine->m_sendKnob->setVisible(true);
m_fxChannelViews[i].m_fxLine->m_sendKnob->setModel(sendModel);
}
m_fxChannelViews[i].m_fxLine->update();
}
}
@@ -281,12 +319,7 @@ void FxMixerView::setCurrentFxLine( FxLine * _line )
void FxMixerView::setCurrentFxLine( int _line )
{
if ( _line >= 0 && _line < NumFxChannels+1 )
{
setCurrentFxLine( m_fxChannelViews[_line].m_fxLine );
m_bankButtons->button( (_line-1) / 16 )->click();
}
setCurrentFxLine( m_fxChannelViews[_line].m_fxLine );
}
@@ -294,7 +327,7 @@ void FxMixerView::setCurrentFxLine( int _line )
void FxMixerView::clear()
{
for( int i = 0; i <= NumFxChannels; ++i )
for( int i = 0; i < m_fxChannelViews.size(); ++i )
{
m_fxChannelViews[i].m_rackView->clearViews();
}
@@ -306,7 +339,7 @@ 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();

View File

@@ -279,7 +279,7 @@ int ClassicStyle::pixelMetric( PixelMetric _metric,
void ClassicStyle::drawFxLine( QPainter * _painter, const QWidget *_fxLine,
const QString & _name, bool _active )
const QString & _name, bool _active, bool _sendToThis )
{
int width = _fxLine->rect().width();
int height = _fxLine->rect().height();
@@ -293,10 +293,18 @@ void ClassicStyle::drawFxLine( QPainter * _painter, const QWidget *_fxLine,
p->setPen( QColor( 20, 24, 32 ) );
p->drawRect( 0, 0, width-1, height-1 );
// draw the mixer send background
if( _sendToThis )
{
p->drawPixmap(2, 0, 28, 56,
embed::getIconPixmap("send_bg_arrow", 28, 56));
}
// draw the channel name
p->rotate( -90 );
p->setPen( _active ? QColor( 0, 255, 0 ) : Qt::white );
p->setFont( pointSizeF( _fxLine->font(), 7.5f ) );
p->drawText( -90, 20, _name );
p->drawText( -145, 20, _name );
}
void ClassicStyle::drawTrackContentBackground(QPainter * _painter,

View File

@@ -881,7 +881,7 @@ int CusisStyle::pixelMetric( PixelMetric _metric, const QStyleOption * _option,
void CusisStyle::drawFxLine( QPainter * _painter, const QWidget *_fxLine,
const QString & _name, bool _active )
const QString & _name, bool _active, bool _sendToThis )
{
int width = _fxLine->rect().width();
int height = _fxLine->rect().height();

View File

@@ -103,7 +103,7 @@ InstrumentTrack::InstrumentTrack( trackContainer * _tc ) :
this, tr( "Panning" ) ),
m_pitchModel( 0, -100, 100, 1, this, tr( "Pitch" ) ),
m_pitchRangeModel( 1, 1, 24, this, tr( "Pitch range" ) ),
m_effectChannelModel( 0, 0, NumFxChannels, this, tr( "FX channel" ) ),
m_effectChannelModel( 0, 0, 10, this, tr( "FX channel" ) ), // change this so it's a combo box, all the channels and then new.
m_instrument( NULL ),
m_soundShaping( this ),
m_arpeggiator( this ),