diff --git a/ChangeLog b/ChangeLog index 7ecf020a4..3a0333eb8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,24 @@ 2008-03-14 Tobias Doerffel + * include/engine.h: + * include/mixer.h: + * include/fx_mixer.h: + * include/fader.h: + * include/effect_chain.h: + * src/audio/audio_port.cpp: + * src/widgets/fader.cpp: + * src/tracks/instrument_track.cpp: + * src/core/effect_chain.cpp: + * src/core/mixer.cpp: + * src/core/engine.cpp: + * src/core/fx_mixer.cpp: + * data/themes/default/fader_background.png: + * data/themes/default/fader_knob.png: + * data/themes/default/fader_leds.png: + * Makefile.am: + added initial FX-mixer implementation - not perfect and very usable + yet but the basics work so far :) + * src/widgets/instrument_midi_io_view.cpp: fixed segfault when using raw MIDI-client diff --git a/Makefile.am b/Makefile.am index 59d3b1335..1e66eb1b0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -66,7 +66,9 @@ lmms_MOC = \ ./envelope_and_lfo_view.moc \ ./export_project_dialog.moc \ ./fade_button.moc \ + ./fader.moc \ ./file_browser.moc \ + ./fx_mixer.moc \ ./graph.moc \ ./group_box.moc \ ./instrument_functions.moc \ @@ -171,6 +173,7 @@ lmms_SOURCES = \ $(srcdir)/src/core/instrument_sound_shaping.cpp \ $(srcdir)/src/core/export_project_dialog.cpp \ $(srcdir)/src/core/file_browser.cpp \ + $(srcdir)/src/core/fx_mixer.cpp \ $(srcdir)/src/core/import_filter.cpp \ $(srcdir)/src/core/instrument.cpp \ $(srcdir)/src/core/instrument_functions.cpp \ @@ -229,6 +232,7 @@ lmms_SOURCES = \ $(srcdir)/src/widgets/effect_rack_view.cpp \ $(srcdir)/src/widgets/effect_view.cpp \ $(srcdir)/src/widgets/fade_button.cpp \ + $(srcdir)/src/widgets/fader.cpp \ $(srcdir)/src/widgets/graph.cpp \ $(srcdir)/src/widgets/group_box.cpp \ $(srcdir)/src/widgets/envelope_and_lfo_view.cpp \ @@ -308,6 +312,7 @@ lmms_SOURCES = \ $(srcdir)/include/instrument_function_views.h \ $(srcdir)/include/instrument_sound_shaping_view.h \ $(srcdir)/include/export.h \ + $(srcdir)/include/fader.h \ $(srcdir)/include/mv_base.h \ $(srcdir)/include/automatable_model.h \ $(srcdir)/include/automatable_model_templates.h \ diff --git a/data/themes/default/fader_background.png b/data/themes/default/fader_background.png new file mode 100644 index 000000000..d1002e4ee Binary files /dev/null and b/data/themes/default/fader_background.png differ diff --git a/data/themes/default/fader_knob.png b/data/themes/default/fader_knob.png new file mode 100644 index 000000000..5c85ddcae Binary files /dev/null and b/data/themes/default/fader_knob.png differ diff --git a/data/themes/default/fader_leds.png b/data/themes/default/fader_leds.png new file mode 100644 index 000000000..7b8a265c7 Binary files /dev/null and b/data/themes/default/fader_leds.png differ diff --git a/include/effect_chain.h b/include/effect_chain.h index 0c88d8104..eab2de62a 100644 --- a/include/effect_chain.h +++ b/include/effect_chain.h @@ -32,7 +32,7 @@ class effectChain : public journallingObject, public model { public: - effectChain( audioPort * _port, track * _track ); + effectChain( track * _track ); virtual ~effectChain(); virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); @@ -52,11 +52,6 @@ public: void startRunning( void ); bool isRunning( void ); -/* inline const effect_list_t & getEffects( void ) - { - return( m_effects ); - }*/ - void deleteAllPlugins( void ); @@ -64,7 +59,6 @@ private: typedef QVector effectList; effectList m_effects; - audioPort * m_port; track * m_track; boolModel m_enabledModel; diff --git a/include/engine.h b/include/engine.h index 3e87bf49d..ec6e1a2a4 100644 --- a/include/engine.h +++ b/include/engine.h @@ -35,6 +35,8 @@ class automationEditor; class bbEditor; class bbTrackContainer; +class fxMixer; +class fxMixerView; class projectJournal; class mainWindow; class mixer; @@ -62,6 +64,11 @@ public: return( s_mixer ); } + static fxMixer * getFxMixer( void ) + { + return( s_fxMixer ); + } + static song * getSong( void ) { return( s_song ); @@ -83,6 +90,11 @@ public: return( s_mainWindow ); } + static fxMixerView * getFxMixerView( void ) + { + return( s_fxMixerView ); + } + static songEditor * getSongEditor( void ) { return( s_songEditor ); @@ -131,12 +143,14 @@ private: // core static mixer * s_mixer; + static fxMixer * s_fxMixer; static song * s_song; static bbTrackContainer * s_bbTrackContainer; static projectJournal * s_projectJournal; // GUI static mainWindow * s_mainWindow; + static fxMixerView * s_fxMixerView; static songEditor * s_songEditor; static automationEditor * s_automationEditor; static bbEditor * s_bbEditor; diff --git a/include/fader.h b/include/fader.h new file mode 100644 index 000000000..1bf2726a9 --- /dev/null +++ b/include/fader.h @@ -0,0 +1,93 @@ +/* + * fader.h - fader-widget used in FX-mixer - partly taken from Hydrogen + * + * Copyright (c) 2008 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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. + * + */ + +/* + * Hydrogen + * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net] + * + * http://www.hydrogen-music.org + * + * 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; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef _FADER_H +#define _FADER_H + +#include +#include + +#include "automatable_model.h" + + +class fader : public QWidget, public floatModelView +{ + Q_OBJECT +public: + fader( floatModel * _model, QWidget * _parent ); + virtual ~fader(); + + void setMaxPeak( float _max ); + void setMinPeak( float _min ); + + void setPeak_L( float peak ); + float getPeak_L() { return m_fPeakValue_L; } + + void setPeak_R( float peak ); + float getPeak_R() { return m_fPeakValue_R; } + + +private: + virtual void mousePressEvent( QMouseEvent *ev ); + virtual void mouseMoveEvent( QMouseEvent *ev ); + virtual void wheelEvent( QWheelEvent *ev ); + virtual void paintEvent( QPaintEvent *ev ); + + floatModel * m_model; + + float m_fPeakValue_L; + float m_fPeakValue_R; + float m_fMinPeak; + float m_fMaxPeak; + + QPixmap m_back; + QPixmap m_leds; + QPixmap m_knob; +} ; + + +#endif diff --git a/include/fx_mixer.h b/include/fx_mixer.h index 9ecc9148c..a536b34ef 100644 --- a/include/fx_mixer.h +++ b/include/fx_mixer.h @@ -30,15 +30,22 @@ #include #endif +#include #include "types.h" -#include "effect_chain.h" #include "mv_base.h" +#include "mixer.h" #include "journalling_object.h" const int NUM_FX_CHANNELS = 64; +class fader; +class fxLine; +class effectRackView; +class pixmapButton; +struct fxChannel; + class fxMixer : public journallingObject, public model { @@ -49,7 +56,8 @@ public: void mixToChannel( const surroundSampleFrame * _buf, fx_ch_t _ch ); void processChannel( fx_ch_t _ch ); - void masterMix( surroundSampleFrame * _dest ); + void prepareMasterMix( void ); + const surroundSampleFrame * masterMix( void ); virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); @@ -62,17 +70,7 @@ public: private: - struct fxChannel - { - effectChain m_fxChain; - surroundSampleFrame * m_buffer; - boolModel m_muteModel; - boolModel m_soloModel; - QMutex m_lock; - } ; - - fxChannel m_fxChannels[NUM_FX_CHANNELS]; - fxChannel m_masterChannel; + fxChannel * m_fxChannels[NUM_FX_CHANNELS+1]; // +1 = master friend class mixerWorkerThread; @@ -83,20 +81,36 @@ private: class fxMixerView : public QWidget, public modelView { + Q_OBJECT public: fxMixerView(); virtual ~fxMixerView(); + fxLine * currentFxLine( void ) + { + return( m_currentFxLine ); + } + void setCurrentFxLine( fxLine * _line ); + + +private slots: + void updateFaders( void ); + + private: struct fxChannelView { + fxLine * m_fxLine; effectRackView * m_rackView; pixmapButton * m_muteButton; pixmapButton * m_soloButton; + fader * m_fader; } ; - fxChannelView m_fxChannelViews[NUM_FX_CHANNELS]; - fxChannelView m_masterChannelView; + fxChannelView m_fxChannelViews[NUM_FX_CHANNELS+1]; + + QWidget * m_fxRackArea; + fxLine * m_currentFxLine; } ; diff --git a/include/mixer.h b/include/mixer.h index b7ccda455..5661a97ae 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -1,7 +1,7 @@ /* * mixer.h - audio-device-independent mixer for LMMS * - * Copyright (c) 2004-2007 Tobias Doerffel + * Copyright (c) 2004-2008 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -286,6 +286,10 @@ public: const f_cnt_t _offset = 0 ); #endif + float peakValueLeft( surroundSampleFrame * _ab, const f_cnt_t _frames ); + float peakValueRight( surroundSampleFrame * _ab, const f_cnt_t _frames ); + + bool criticalXRuns( void ) const; const surroundSampleFrame * nextBuffer( void ) diff --git a/src/audio/audio_port.cpp b/src/audio/audio_port.cpp index 97a13e4dd..6567401f0 100644 --- a/src/audio/audio_port.cpp +++ b/src/audio/audio_port.cpp @@ -40,9 +40,9 @@ audioPort::audioPort( const QString & _name, track * _track ) : m_secondBuffer( new surroundSampleFrame[ engine::getMixer()->framesPerPeriod()] ), m_extOutputEnabled( FALSE ), - m_nextFxChannel( -1 ), + m_nextFxChannel( 0 ), m_name( "unnamed port" ), - m_effects( this, _track ), + m_effects( _track ), m_frames( engine::getMixer()->framesPerPeriod() ) { engine::getMixer()->clearAudioBuffer( m_firstBuffer, diff --git a/src/core/effect_chain.cpp b/src/core/effect_chain.cpp index 5928e05b4..a3fabf06c 100644 --- a/src/core/effect_chain.cpp +++ b/src/core/effect_chain.cpp @@ -33,9 +33,8 @@ -effectChain::effectChain( audioPort * _port, track * _track ) : +effectChain::effectChain( track * _track ) : model( /*_track*/ NULL ), - m_port( _port ), m_track( _track ), m_enabledModel( FALSE, FALSE, TRUE ) { diff --git a/src/core/engine.cpp b/src/core/engine.cpp index 62879a351..6dddae1a4 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -29,6 +29,7 @@ #include "automation_editor.h" #include "bb_editor.h" #include "config_mgr.h" +#include "fx_mixer.h" #include "ladspa_2_lmms.h" #include "main_window.h" #include "mixer.h" @@ -44,6 +45,8 @@ bool engine::s_hasGUI = TRUE; float engine::s_framesPerTact64th; mixer * engine::s_mixer = NULL; +fxMixer * engine::s_fxMixer = NULL; +fxMixerView * engine::s_fxMixerView = NULL; mainWindow * engine::s_mainWindow = NULL; bbTrackContainer * engine::s_bbTrackContainer = NULL; song * engine::s_song = NULL; @@ -67,12 +70,14 @@ void engine::init( const bool _has_gui ) s_projectJournal = new projectJournal; s_mixer = new mixer; + s_fxMixer = new fxMixer; s_song = new song; s_bbTrackContainer = new bbTrackContainer; if( s_hasGUI ) { s_mainWindow = new mainWindow; + s_fxMixerView = new fxMixerView; s_songEditor = new songEditor( s_song ); s_projectNotes = new projectNotes; s_bbEditor = new bbEditor( s_bbTrackContainer ); diff --git a/src/core/fx_mixer.cpp b/src/core/fx_mixer.cpp new file mode 100644 index 000000000..061f81338 --- /dev/null +++ b/src/core/fx_mixer.cpp @@ -0,0 +1,402 @@ +#ifndef SINGLE_SOURCE_COMPILE + +/* + * fx_mixer.cpp - effect-mixer for LMMS + * + * Copyright (c) 2008 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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 +#include +#include +#include +#include +#include + +#include "fx_mixer.h" +#include "fader.h" +#include "effect_chain.h" +#include "effect_rack_view.h" +#include "embed.h" +#include "main_window.h" +#include "lcd_spinbox.h" +#include "gui_templates.h" + + +struct fxChannel +{ + fxChannel( model * _parent ) : + m_fxChain( NULL ), + m_used( FALSE ), + m_peakLeft( 0.0f ), + m_peakRight( 0.0f ), + m_buffer( new surroundSampleFrame[ + engine::getMixer()->framesPerPeriod()] ), + m_muteModel( FALSE, _parent ), + m_soloModel( FALSE, _parent ), + m_volumeModel( 1.0, 0.0, 2.0, 0.01, _parent ), + m_lock() + { + engine::getMixer()->clearAudioBuffer( m_buffer, + engine::getMixer()->framesPerPeriod() ); + } + + effectChain m_fxChain; + bool m_used; + float m_peakLeft; + float m_peakRight; + surroundSampleFrame * m_buffer; + boolModel m_muteModel; + boolModel m_soloModel; + floatModel m_volumeModel; + QMutex m_lock; +} ; + + + +fxMixer::fxMixer() : + journallingObject(), + model( NULL ) +{ + for( int i = 0; i < NUM_FX_CHANNELS+1; ++i ) + { + m_fxChannels[i] = new fxChannel( this ); + } +} + + + + +fxMixer::~fxMixer() +{ +} + + + + +void fxMixer::mixToChannel( const surroundSampleFrame * _buf, fx_ch_t _ch ) +{ + surroundSampleFrame * buf = m_fxChannels[_ch]->m_buffer; + for( f_cnt_t f = 0; f < engine::getMixer()->framesPerPeriod(); ++f ) + { + buf[f][0] += _buf[f][0]; + buf[f][1] += _buf[f][1]; + } + m_fxChannels[_ch]->m_used = TRUE; +} + + + +void fxMixer::processChannel( fx_ch_t _ch ) +{ + if( m_fxChannels[_ch]->m_used || _ch == 0 ) + { + const fpp_t f = engine::getMixer()->framesPerPeriod(); + m_fxChannels[_ch]->m_fxChain.startRunning(); + m_fxChannels[_ch]->m_fxChain.processAudioBuffer( + m_fxChannels[_ch]->m_buffer, f ); + m_fxChannels[_ch]->m_peakLeft = engine::getMixer()->peakValueLeft( m_fxChannels[_ch]->m_buffer, f ) * m_fxChannels[_ch]->m_volumeModel.value(); + m_fxChannels[_ch]->m_peakRight = engine::getMixer()->peakValueRight( m_fxChannels[_ch]->m_buffer, f ) * m_fxChannels[_ch]->m_volumeModel.value(); + } + else + { + m_fxChannels[_ch]->m_peakLeft = + m_fxChannels[_ch]->m_peakRight = 0.0f; + } +} + + + + +void fxMixer::prepareMasterMix( void ) +{ + engine::getMixer()->clearAudioBuffer( m_fxChannels[0]->m_buffer, + engine::getMixer()->framesPerPeriod() ); +} + + + + +const surroundSampleFrame * fxMixer::masterMix( void ) +{ + surroundSampleFrame * buf = m_fxChannels[0]->m_buffer; + for( int i = 1; i < NUM_FX_CHANNELS+1; ++i ) + { + if( m_fxChannels[i]->m_used ) + { + surroundSampleFrame * _buf = m_fxChannels[i]->m_buffer; + for( f_cnt_t f = 0; f < + engine::getMixer()->framesPerPeriod(); ++f ) + { + buf[f][0] += _buf[f][0]*m_fxChannels[i]->m_volumeModel.value(); + buf[f][1] += _buf[f][1]*m_fxChannels[i]->m_volumeModel.value(); + } + engine::getMixer()->clearAudioBuffer( _buf, + engine::getMixer()->framesPerPeriod() ); + m_fxChannels[i]->m_used = FALSE; + } + } + processChannel( 0 ); + for( f_cnt_t f = 0; f < engine::getMixer()->framesPerPeriod(); ++f ) + { + buf[f][0] *= m_fxChannels[0]->m_volumeModel.value(); + buf[f][1] *= m_fxChannels[0]->m_volumeModel.value(); + } + m_fxChannels[0]->m_peakLeft *= engine::getMixer()->masterGain(); + m_fxChannels[0]->m_peakRight *= engine::getMixer()->masterGain(); + return( buf ); +} + + + + +void fxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) +{ +/* m_chordsEnabledModel.saveSettings( _doc, _this, "chord-enabled" ); + m_chordsModel.saveSettings( _doc, _this, "chord" ); + m_chordRangeModel.saveSettings( _doc, _this, "chordrange" );*/ +} + + + + +void fxMixer::loadSettings( const QDomElement & _this ) +{ +/* m_chordsEnabledModel.loadSettings( _this, "chord-enabled" ); + m_chordsModel.loadSettings( _this, "chord" ); + m_chordRangeModel.loadSettings( _this, "chordrange" );*/ +} + + +class fxLine : public QWidget +{ +public: + fxLine( QWidget * _parent, fxMixerView * _mv ) : + QWidget( _parent ), + m_mv( _mv ) + { + setFixedSize( 32, 200 ); + setAttribute( Qt::WA_OpaquePaintEvent, TRUE ); + } + + virtual void setName( const QString & _name ) + { + m_name = _name; + } + virtual void paintEvent( QPaintEvent * ) + { + QPainter p( this ); + p.fillRect( rect(), QColor( 72, 76, 88 ) ); +/* p.setPen( QColor( 144, 152, 176 ) ); + p.drawLine( 0, 0, width()-1, 0 ); + p.drawLine( 0, 0, 0, height()-1 ); + p.setPen( QColor( 36, 38, 44 ) ); + p.drawLine( 0, height()-1, width()-1, height()-1 ); + p.drawLine( width()-1, 0, width()-1, height()-1 );*/ + p.setPen( QColor( 40, 42, 48 ) ); + p.drawRect( 0, 0, width()-2, height()-2 ); + p.setPen( QColor( 108, 114, 132 ) ); + p.drawRect( 1, 1, width()-2, height()-2 ); + p.setPen( QColor( 20, 24, 32 ) ); + p.drawRect( 0, 0, width()-1, height()-1 ); + + p.rotate( -90 ); + p.setPen( m_mv->currentFxLine() == this ? + QColor( 0, 255, 0 ) : Qt::white ); + p.setFont( pointSizeF( font(), 7.5f ) ); + p.drawText( -70, 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; + +} ; + + + + +fxMixerView::fxMixerView() : + QWidget(), + modelView( NULL ) +{ + fxMixer * m = engine::getFxMixer(); + + QPalette pal = palette(); + pal.setColor( QPalette::Background, QColor( 72, 76, 88 ) ); + setPalette( pal ); + setAutoFillBackground( TRUE ); + +// setFixedHeight( 250+216 ); + setWindowTitle( tr( "FX-Mixer" ) ); +// setWindowIcon( embed::getIconPixmap( "fxmixer" ) ); + engine::getMainWindow()->workspace()->addSubWindow( this ); + parentWidget()->setAttribute( Qt::WA_DeleteOnClose, FALSE ); + QVBoxLayout * ml = new QVBoxLayout( this ); + ml->setMargin( 0 ); + ml->setSpacing( 0 ); + + QScrollArea * a = new QScrollArea( this ); + a->setFrameShape( QFrame::NoFrame ); + a->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + a->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + a->setFixedHeight( 216 ); + ml->addWidget( a ); + + m_fxRackArea = new QWidget( this ); + m_fxRackArea->setFixedHeight( 250 ); + ml->addWidget( m_fxRackArea ); + ml->addStretch(); + + QWidget * base = new QWidget; + QHBoxLayout * bl = new QHBoxLayout( base ); + bl->setSpacing( 0 ); + bl->setMargin( 0 ); + a->setWidget( base ); + + base->setFixedSize( (NUM_FX_CHANNELS+1)*33+6+10, 200 ); + pal = base->palette(); + pal.setColor( QPalette::Background, QColor( 72, 76, 88 ) ); + base->setPalette( pal ); + base->setAutoFillBackground( TRUE ); + + bl->addSpacing( 6 ); + for( int i = 0; i < NUM_FX_CHANNELS+1; ++i ) + { + fxChannelView * cv = &m_fxChannelViews[i]; + cv->m_fxLine = new fxLine( base, this ); + bl->addWidget( cv->m_fxLine ); + if( i == 0 ) + { + bl->addSpacing( 10 ); + cv->m_fxLine->setName( tr( "Master" ).arg( i ) ); + } + else + { + bl->addSpacing( 1 ); + cv->m_fxLine->setName( tr( "FX %1" ).arg( i ) ); + } + lcdSpinBox * l = new lcdSpinBox( 2, cv->m_fxLine ); + l->model()->setRange( i, i ); + l->model()->setValue( i ); + l->update(); + cv->m_fader = new fader( &m->m_fxChannels[i]->m_volumeModel, + cv->m_fxLine ); + cv->m_fader->move( 15-cv->m_fader->width()/2, 80 ); + cv->m_rackView = new effectRackView( + &m->m_fxChannels[i]->m_fxChain, m_fxRackArea ); + cv->m_rackView->setFixedSize( 250, 250 ); + } + + show(); + setCurrentFxLine( m_fxChannelViews[0].m_fxLine ); + + QTimer * t = new QTimer( this ); + connect( t, SIGNAL( timeout() ), this, SLOT( updateFaders() ) ); + t->start( 50 ); +} + + + + +fxMixerView::~fxMixerView() +{ +} + + + + +void fxMixerView::setCurrentFxLine( fxLine * _line ) +{ + m_currentFxLine = _line; + for( int i = 0; i < NUM_FX_CHANNELS+1; ++i ) + { + if( m_fxChannelViews[i].m_fxLine == _line ) + { + m_fxChannelViews[i].m_rackView->show(); + } + else + { + m_fxChannelViews[i].m_rackView->hide(); + } + m_fxChannelViews[i].m_fxLine->update(); + } +} + + + + +void fxMixerView::updateFaders( void ) +{ + fxMixer * m = engine::getFxMixer(); + for( int i = 0; i < NUM_FX_CHANNELS+1; ++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.1; + if( m->m_fxChannels[i]->m_peakLeft > opl ) + { + m_fxChannelViews[i].m_fader->setPeak_L( + m->m_fxChannels[i]->m_peakLeft ); + } + else + { + 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 ); + } + else + { + m_fxChannelViews[i].m_fader->setPeak_R( opr/fall_off ); + } + } +} + + + +#include "fx_mixer.moc" + +#endif diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index 75bb64f9e..ae7b06d37 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -31,6 +31,7 @@ #include #include "mixer.h" +#include "fx_mixer.h" #include "play_handle.h" #include "song.h" #include "templates.h" @@ -495,6 +496,7 @@ const surroundSampleFrame * mixer::renderNextBuffer( void ) //printf("---------------------------next period\n"); // if( criticalXRuns() == FALSE ) { + engine::getFxMixer()->prepareMasterMix(); engine::getSong()->processNextBuffer(); lockPlayHandles(); @@ -605,6 +607,14 @@ if( COND_NPH ) } } } + for( int i = 1; i < NUM_FX_CHANNELS+1; ++i ) + { + engine::getFxMixer()->processChannel( i ); + } + const surroundSampleFrame * buf = + engine::getFxMixer()->masterMix(); + memcpy( m_writeBuf, buf, m_framesPerPeriod * + sizeof( surroundSampleFrame ) ); } unlock(); @@ -647,26 +657,6 @@ void mixer::clear( void ) -void FASTCALL mixer::clearAudioBuffer( sampleFrame * _ab, - const f_cnt_t _frames, - const f_cnt_t _offset ) -{ - memset( _ab+_offset, 0, sizeof( *_ab ) * _frames ); -} - - - -#ifndef DISABLE_SURROUND -void FASTCALL mixer::clearAudioBuffer( surroundSampleFrame * _ab, - const f_cnt_t _frames, - const f_cnt_t _offset ) -{ - memset( _ab+_offset, 0, sizeof( *_ab ) * _frames ); -} -#endif - - - void FASTCALL mixer::bufferToPort( const sampleFrame * _buf, const fpp_t _frames, const f_cnt_t _offset, @@ -721,6 +711,59 @@ void FASTCALL mixer::bufferToPort( const sampleFrame * _buf, +void FASTCALL mixer::clearAudioBuffer( sampleFrame * _ab, + const f_cnt_t _frames, + const f_cnt_t _offset ) +{ + memset( _ab+_offset, 0, sizeof( *_ab ) * _frames ); +} + + + +#ifndef DISABLE_SURROUND +void FASTCALL mixer::clearAudioBuffer( surroundSampleFrame * _ab, + const f_cnt_t _frames, + const f_cnt_t _offset ) +{ + memset( _ab+_offset, 0, sizeof( *_ab ) * _frames ); +} +#endif + + + + +float mixer::peakValueLeft( surroundSampleFrame * _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]; + } + } + return( p ); +} + + + + +float mixer::peakValueRight( surroundSampleFrame * _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]; + } + } + return( p ); +} + + + + void mixer::setHighQuality( bool _hq_on ) { // don't delete the audio-device @@ -954,8 +997,7 @@ midiClient * mixer::tryMIDIClients( void ) -void mixer::processBuffer( const surroundSampleFrame * _buf, - fx_ch_t/* _fx_chnl */ ) +void mixer::processBuffer( const surroundSampleFrame * _buf, fx_ch_t _fx_chnl ) { // TODO: process according effect-channel @@ -968,22 +1010,7 @@ void mixer::processBuffer( const surroundSampleFrame * _buf, m_newBuffer[chnl] = TRUE; } }*/ - - static QMutex m; - m.lock(); - for( fpp_t frame = 0; frame < m_framesPerPeriod; ++frame ) - { - for( ch_cnt_t chnl = 0; chnl < m_audioDev->channels(); ++chnl ) - { - m_writeBuf[frame][chnl] += _buf[frame][chnl]; -/* - if( m_scaleClip ) - { - scaleClip( frame, chnl ); - }*/ - } - } - m.unlock(); + engine::getFxMixer()->mixToChannel( _buf, _fx_chnl ); } diff --git a/src/tracks/instrument_track.cpp b/src/tracks/instrument_track.cpp index 9f822f834..e94b3cb3d 100644 --- a/src/tracks/instrument_track.cpp +++ b/src/tracks/instrument_track.cpp @@ -108,7 +108,7 @@ instrumentTrack::instrumentTrack( trackContainer * _tc ) : m_volumeModel( DEFAULT_VOLUME, MIN_VOLUME, MAX_VOLUME, 1.0f, this ), m_surroundAreaModel( this, this ), - m_effectChannelModel( 1, 1, NUM_FX_CHANNELS, 1, this ), + m_effectChannelModel( 0, 0, NUM_FX_CHANNELS, 1, this ), m_instrument( NULL ), m_soundShaping( this ), m_arpeggiator( this ), @@ -189,6 +189,7 @@ void instrumentTrack::processAudioBuffer( sampleFrame * _buf, } volumeVector v = m_surroundAreaModel.getVolumeVector( v_scale ); + m_audioPort.setNextFxChannel( m_effectChannelModel.value() ); engine::getMixer()->bufferToPort( _buf, ( _n != NULL ) ? tMin( _n->framesLeftForCurrentPeriod(), _frames ) : @@ -1162,8 +1163,8 @@ void instrumentTrackWindow::modelChanged( void ) this, SLOT( updateInstrumentView() ), Qt::QueuedConnection ); m_volumeKnob->setModel( &m_track->m_volumeModel ); - m_volumeKnob->setModel( &m_track->m_volumeModel ); m_surroundArea->setModel( &m_track->m_surroundAreaModel ); + m_effectChannelNumber->setModel( &m_track->m_effectChannelModel ); m_pianoView->setModel( &m_track->m_piano ); m_ssView->setModel( &m_track->m_soundShaping ); diff --git a/src/widgets/fader.cpp b/src/widgets/fader.cpp new file mode 100644 index 000000000..c0b882cb1 --- /dev/null +++ b/src/widgets/fader.cpp @@ -0,0 +1,220 @@ +/* + * fader.cpp - fader-widget used in mixer - partly taken from Hydrogen + * + * Copyright (c) 2008 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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. + * + */ + +/* + * Hydrogen + * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net] + * + * http://www.hydrogen-music.org + * + * 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; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include +#include +#include + +#include "fader.h" +#include "embed.h" + + +fader::fader( floatModel * _model, QWidget * _parent ) : + QWidget( _parent ), + floatModelView( _model ), + m_model( _model ), + m_fPeakValue_L( 0.0 ), + m_fPeakValue_R( 0.0 ), + m_fMinPeak( 0.01f ), + m_fMaxPeak( 1.1 ), + m_back( embed::getIconPixmap( "fader_background" ) ), + m_leds( embed::getIconPixmap( "fader_leds" ) ), + m_knob( embed::getIconPixmap( "fader_knob" ) ) +{ + setAttribute( Qt::WA_NoBackground ); + setMinimumSize( 23, 116 ); + setMaximumSize( 23, 116); + resize( 23, 116 ); + setModel( _model ); +} + + + +fader::~fader() +{ +} + + + +void fader::mouseMoveEvent( QMouseEvent *ev ) +{ + float fVal = (float)( height() - ev->y() ) / (float)height(); + fVal = fVal * ( m_model->maxValue() - m_model->minValue() ); + + fVal = fVal + m_model->minValue(); + + m_model->setValue( fVal ); +} + + + +void fader::mousePressEvent(QMouseEvent *ev) +{ + mouseMoveEvent( ev ); +} + + + +void fader::wheelEvent ( QWheelEvent *ev ) +{ + ev->accept(); + + if ( ev->delta() > 0 ) + { + m_model->incValue( 5 ); + } + else + { + m_model->incValue( -5 ); + } +} + + + +/// +/// Set peak value (0.0 .. 1.0) +/// +void fader::setPeak_L( float fPeak ) +{ + if ( fPeak < m_fMinPeak ) { + fPeak = m_fMinPeak; + } + else if ( fPeak > m_fMaxPeak ) { + fPeak = m_fMaxPeak; + } + + if ( m_fPeakValue_L != fPeak) { + m_fPeakValue_L = fPeak; + update(); + } +} + + + + +/// +/// Set peak value (0.0 .. 1.0) +/// +void fader::setPeak_R( float fPeak ) +{ + if ( fPeak < m_fMinPeak ) { + fPeak = m_fMinPeak; + } + else if ( fPeak > m_fMaxPeak ) { + fPeak = m_fMaxPeak; + } + + if ( m_fPeakValue_R != fPeak ) { + m_fPeakValue_R = fPeak; + update(); + } +} + + + +void fader::paintEvent( QPaintEvent * ev) +{ + QPainter painter(this); + + // background +// painter.drawPixmap( rect(), m_back, QRect( 0, 0, 23, 116 ) ); + painter.drawPixmap( ev->rect(), m_back, ev->rect() ); + + + // peak leds + //float fRange = abs( m_fMaxPeak ) + abs( m_fMinPeak ); + + float realPeak_L = m_fPeakValue_L - m_fMinPeak; + //int peak_L = 116 - ( realPeak_L / fRange ) * 116.0; + int peak_L = 116 - ( realPeak_L / ( m_fMaxPeak - m_fMinPeak ) ) * 116.0; + + if ( peak_L > 116 ) { + peak_L = 116; + } + painter.drawPixmap( QRect( 0, peak_L, 11, 116 - peak_L ), m_leds, QRect( 0, peak_L, 11, 116 - peak_L ) ); + + + float realPeak_R = m_fPeakValue_R - m_fMinPeak; + int peak_R = 116 - ( realPeak_R / ( m_fMaxPeak - m_fMinPeak ) ) * 116.0; + if ( peak_R > 116 ) { + peak_R = 116; + } + painter.drawPixmap( QRect( 11, peak_R, 11, 116 - peak_R ), m_leds, QRect( 11, peak_R, 11, 116 - peak_R ) ); + + // knob + static const uint knob_height = 29; + static const uint knob_width = 15; + + float fRange = m_model->maxValue() - m_model->minValue(); + + float realVal = m_model->value() - m_model->minValue(); + +// uint knob_y = (uint)( 116.0 - ( 86.0 * ( m_model->value() / fRange ) ) ); + uint knob_y = (uint)( 116.0 - ( 86.0 * ( realVal / fRange ) ) ); + + + painter.drawPixmap( QRect( 4, knob_y - knob_height, knob_width, knob_height), m_knob, QRect( 0, 0, knob_width, knob_height ) ); +} + + + +void fader::setMaxPeak( float fMax ) +{ + m_fMaxPeak = fMax; +} + + + +void fader::setMinPeak( float fMin ) +{ + m_fMinPeak = fMin; +} + + + +#include "fader.moc" +