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 FxLine::contextMenuEvent( QContextMenuEvent * ) { FxMixer * mix = engine::fxMixer(); QPointer<captionMenu> contextMenu = new captionMenu( mix->effectChannel( m_channelIndex )->m_name ); if( m_channelIndex != 0 ) // no move-options in master { contextMenu->addAction( tr( "Move &left" ), this, SLOT( moveChannelLeft() ) ); contextMenu->addAction( tr( "Move &right" ), this, SLOT( moveChannelRight() ) ); } contextMenu->addAction( tr( "Rename &channel" ), this, SLOT( renameChannel() ) ); contextMenu->addSeparator(); if( m_channelIndex != 0 ) // no remove-option in master { contextMenu->addAction( embed::getIconPixmap( "cancel" ), tr( "R&emove channel" ), this, SLOT( removeChannel() ) ); contextMenu->addSeparator(); } contextMenu->addAction( embed::getIconPixmap( "help" ), tr( "&Help" ), this, SLOT( displayHelp() ) ); contextMenu->exec( QCursor::pos() ); delete contextMenu; }
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 FxLine::paintEvent( QPaintEvent * ) { FxMixer * mix = engine::fxMixer(); bool sendToThis = mix->channelSendModel( m_mv->currentFxLine()->m_channelIndex, m_channelIndex) != NULL; QPainter painter; painter.begin( this ); drawFxLine( &painter, this, mix->effectChannel(m_channelIndex)->m_name, m_mv->currentFxLine() == this, sendToThis ); painter.end(); }
void FxMixerView::updateFaders() { FxMixer * m = Engine::fxMixer(); // apply master gain m->effectChannel(0)->m_peakLeft *= Engine::mixer()->masterGain(); m->effectChannel(0)->m_peakRight *= Engine::mixer()->masterGain(); 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 fall_off = 1.2; if( m->effectChannel(i)->m_peakLeft > opl ) { m_fxChannelViews[i]->m_fader->setPeak_L( m->effectChannel(i)->m_peakLeft ); m->effectChannel(i)->m_peakLeft = 0; } else { m_fxChannelViews[i]->m_fader->setPeak_L( opl/fall_off ); } if( m->effectChannel(i)->m_peakRight > opr ) { m_fxChannelViews[i]->m_fader->setPeak_R( m->effectChannel(i)->m_peakRight ); m->effectChannel(i)->m_peakRight = 0; } else { m_fxChannelViews[i]->m_fader->setPeak_R( opr/fall_off ); } } }
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); updateFxLine(newChannelIndex); updateMaxChannelSelector(); }
void FxLine::renameChannel() { bool ok; FxMixer * mix = engine::fxMixer(); QString new_name = QInputDialog::getText( this, FxMixerView::tr( "Rename FX channel" ), FxMixerView::tr( "Enter the new name for this " "FX channel" ), QLineEdit::Normal, mix->effectChannel(m_channelIndex)->m_name, &ok ); if( ok && !new_name.isEmpty() ) { mix->effectChannel( m_channelIndex )->m_name = new_name; update(); } }
void FxMixerView::moveChannelLeft(int index, int focusIndex) { // can't move master or first channel left or last channel right if( index <= 1 || index >= m_fxChannelViews.size() ) return; FxMixer *m = Engine::fxMixer(); // Move instruments channels m->moveChannelLeft( index ); // Update widgets models m_fxChannelViews[index]->setChannelIndex( index ); m_fxChannelViews[index - 1]->setChannelIndex( index - 1 ); // Focus on new position setCurrentFxLine( focusIndex ); }
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]->m_soloBtn; delete m_fxChannelViews[i]->m_fxLine; 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); }
const surroundSampleFrame * Mixer::renderNextBuffer() { m_profiler.startPeriod(); static Song::PlayPos last_metro_pos = -1; Song *song = Engine::getSong(); Song::PlayModes currentPlayMode = song->playMode(); Song::PlayPos p = song->getPlayPos( currentPlayMode ); bool playModeSupportsMetronome = currentPlayMode == Song::Mode_PlayPattern || currentPlayMode == Song::Mode_PlaySong || currentPlayMode == Song::Mode_PlayBB; if( playModeSupportsMetronome && m_metronomeActive && !song->isExporting() && p != last_metro_pos ) { tick_t ticksPerTact = MidiTime::ticksPerTact(); if ( p.getTicks() % (ticksPerTact / 1 ) == 0 ) { addPlayHandle( new SamplePlayHandle( "misc/metronome02.ogg" ) ); } else if ( p.getTicks() % (ticksPerTact / song->getTimeSigModel().getNumerator() ) == 0 ) { addPlayHandle( new SamplePlayHandle( "misc/metronome01.ogg" ) ); } last_metro_pos = p; } lockInputFrames(); // swap buffer m_inputBufferWrite = ( m_inputBufferWrite + 1 ) % 2; m_inputBufferRead = ( m_inputBufferRead + 1 ) % 2; // clear new write buffer m_inputBufferFrames[ m_inputBufferWrite ] = 0; unlockInputFrames(); // remove all play-handles that have to be deleted and delete // them if they still exist... // maybe this algorithm could be optimized... lockPlayHandleRemoval(); ConstPlayHandleList::Iterator it_rem = m_playHandlesToRemove.begin(); while( it_rem != m_playHandlesToRemove.end() ) { PlayHandleList::Iterator it = qFind( m_playHandles.begin(), m_playHandles.end(), *it_rem ); if( it != m_playHandles.end() ) { ( *it )->audioPort()->removePlayHandle( ( *it ) ); if( ( *it )->type() == PlayHandle::TypeNotePlayHandle ) { NotePlayHandleManager::release( (NotePlayHandle*) *it ); } else delete *it; m_playHandles.erase( it ); } it_rem = m_playHandlesToRemove.erase( it_rem ); } unlockPlayHandleRemoval(); // now we have to make sure no other thread does anything bad // while we're acting... lock(); // rotate buffers m_writeBuffer = ( m_writeBuffer + 1 ) % m_poolDepth; m_readBuffer = ( m_readBuffer + 1 ) % m_poolDepth; m_writeBuf = m_bufferPool[m_writeBuffer]; m_readBuf = m_bufferPool[m_readBuffer]; // clear last audio-buffer clearAudioBuffer( m_writeBuf, m_framesPerPeriod ); // prepare master mix (clear internal buffers etc.) FxMixer * fxMixer = Engine::fxMixer(); fxMixer->prepareMasterMix(); // create play-handles for new notes, samples etc. song->processNextBuffer(); // add all play-handles that have to be added m_playHandleMutex.lock(); m_playHandles += m_newPlayHandles; m_newPlayHandles.clear(); m_playHandleMutex.unlock(); // STAGE 1: run and render all play handles lockPlayHandleRemoval(); MixerWorkerThread::fillJobQueue<PlayHandleList>( m_playHandles ); MixerWorkerThread::startAndWaitForJobs(); // removed all play handles which are done for( PlayHandleList::Iterator it = m_playHandles.begin(); it != m_playHandles.end(); ) { if( ( *it )->affinityMatters() && ( *it )->affinity() != QThread::currentThread() ) { ++it; continue; } if( ( *it )->isFinished() ) { ( *it )->audioPort()->removePlayHandle( ( *it ) ); if( ( *it )->type() == PlayHandle::TypeNotePlayHandle ) { NotePlayHandleManager::release( (NotePlayHandle*) *it ); } else delete *it; it = m_playHandles.erase( it ); } else { ++it; } } unlockPlayHandleRemoval(); // STAGE 2: process effects of all instrument- and sampletracks MixerWorkerThread::fillJobQueue<QVector<AudioPort *> >( m_audioPorts ); MixerWorkerThread::startAndWaitForJobs(); // STAGE 3: do master mix in FX mixer fxMixer->masterMix( m_writeBuf ); unlock(); emit nextAudioBuffer(); // and trigger LFOs EnvelopeAndLfoParameters::instances()->trigger(); Controller::triggerFrameCounter(); AutomatableModel::incrementPeriodCounter(); // refresh buffer pool BufferManager::refresh(); m_profiler.finishPeriod( processingSampleRate(), m_framesPerPeriod ); return m_readBuf; }
FxMixerView::FxMixerView() : QWidget(), ModelView( NULL, this ), SerializingObjectHook() { FxMixer * m = Engine::fxMixer(); m->setHook( this ); //QPalette pal = palette(); //pal.setColor( QPalette::Background, QColor( 72, 76, 88 ) ); //setPalette( pal ); setAutoFillBackground( true ); setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ); setWindowTitle( tr( "FX-Mixer" ) ); setWindowIcon( embed::getIconPixmap( "fx_mixer" ) ); // main-layout QHBoxLayout * ml = new QHBoxLayout; // Set margins ml->setContentsMargins( 0, 4, 0, 0 ); // 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); // 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 ) { m_fxChannelViews[i] = new FxChannelView(m_channelAreaWidget, this, i); chLayout->addWidget( m_fxChannelViews[i]->m_fxLine ); } // add the scrolling section to the main layout // class solely for scroll area to pass key presses down class ChannelArea : public QScrollArea { public: ChannelArea( QWidget * parent, FxMixerView * mv ) : QScrollArea( parent ), m_mv( mv ) {} ~ChannelArea() {} virtual void keyPressEvent( QKeyEvent * e ) { m_mv->keyPressEvent( e ); } 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 ); // 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(); // timer for updating faders connect( gui->mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( updateFaders() ) ); // add ourself to workspace QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget( this ); Qt::WindowFlags flags = subWin->windowFlags(); flags &= ~Qt::WindowMaximizeButtonHint; subWin->setWindowFlags( flags ); layout()->setSizeConstraint( QLayout::SetMinimumSize ); subWin->layout()->setSizeConstraint( QLayout::SetMinAndMaxSize ); parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false ); parentWidget()->move( 5, 310 ); // we want to receive dataChanged-signals in order to update setModel( m ); }
sampleFrameA * Mixer::renderNextBuffer() { MicroTimer timer; static song::playPos last_metro_pos = -1; FxMixer * fxm = engine::fxMixer(); song::playPos p = engine::getSong()->getPlayPos( song::Mode_PlayPattern ); if( engine::getSong()->playMode() == song::Mode_PlayPattern && engine::getPianoRoll()->isRecording() == true && p != last_metro_pos && p.getTicks() % (DefaultTicksPerTact / 4 ) == 0 ) { addPlayHandle( new samplePlayHandle( "misc/metronome01.ogg" ) ); last_metro_pos = p; } lockInputFrames(); // swap buffer m_inputBufferWrite = ( m_inputBufferWrite + 1 ) % 2; m_inputBufferRead = ( m_inputBufferRead + 1 ) % 2; // clear new write buffer m_inputBufferFrames[ m_inputBufferWrite ] = 0; unlockInputFrames(); // now we have to make sure no other thread does anything bad // while we're acting... lock(); // remove all play-handles that have to be deleted and delete // them if they still exist... // maybe this algorithm could be optimized... ConstPlayHandleList::Iterator it_rem = m_playHandlesToRemove.begin(); while( it_rem != m_playHandlesToRemove.end() ) { PlayHandleList::Iterator it = qFind( m_playHandles.begin(), m_playHandles.end(), *it_rem ); if( it != m_playHandles.end() ) { delete *it; m_playHandles.erase( it ); } it_rem = m_playHandlesToRemove.erase( it_rem ); } // rotate buffers m_writeBuffer = ( m_writeBuffer + 1 ) % m_poolDepth; m_readBuffer = ( m_readBuffer + 1 ) % m_poolDepth; m_writeBuf = m_bufferPool[m_writeBuffer]; m_readBuf = m_bufferPool[m_readBuffer]; // clear last audio-buffer clearAudioBuffer( m_writeBuf, m_framesPerPeriod ); // prepare master mix (clear internal buffers etc.) fxm->prepareMasterMix(); // create play-handles for new notes, samples etc. engine::getSong()->processNextBuffer(); // STAGE 1: run and render all play handles MixerWorkerThread::fillJobQueue<PlayHandleList>( m_playHandles ); MixerWorkerThread::startAndWaitForJobs(); // removed all play handles which are done for( PlayHandleList::Iterator it = m_playHandles.begin(); it != m_playHandles.end(); ) { if( ( *it )->affinityMatters() && ( *it )->affinity() != QThread::currentThread() ) { ++it; continue; } if( ( *it )->done() ) { delete *it; it = m_playHandles.erase( it ); } else { ++it; } } // STAGE 2: process effects of all instrument- and sampletracks MixerWorkerThread::fillJobQueue<QVector<AudioPort *> >( m_audioPorts ); MixerWorkerThread::startAndWaitForJobs(); // STAGE 3: do master mix in FX mixer fxm->masterMix( m_writeBuf ); unlock(); emit nextAudioBuffer(); // and trigger LFOs 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 ); return m_readBuf; }