From: Mark G. <fat...@nt...> - 2008-03-31 21:32:26
|
Hi, This is a patch based on trunk SVN 85. It fixes one problem, and changes the behaviour of one feature. I can split it up into bugfix as one diff and new feature as another diff if you like. Bugfix: ===== As discussed on the Hydrogen forum, track outputs were re-initialised every time a new note was played, meaning that successive hits of the same sample cut each other off, leading to a staccato-like sound which was very different to what came out of the main output. I have changed the behaviour to implement a new buffer for each track output which is only re-initialised once every time, in the same way the main output is. The way I've created the buffer is the same way it was done before, which was admitted in the comment as a hack but I don't know of a better way. New Feature: ========== Track outputs were pre-fader meaning the only way to adjust relative volumes of instruments was to adjust the gains of the individual layers. The change is to add an option in the preferences dialog to allow track outputs to be post-fader in which case fader, LPF, all gains, and ADSR are applied to each track output. This option is also saved in Preferences. I've been running this for a few weeks quite intensively and it has been stable so far, but I'm a little nervous about it as I'm no C++ expert. I'm happy to discuss it and try alternative approaches. Index: hydrogen-trunk/gui/src/PreferencesDialog.cpp =================================================================== --- hydrogen-trunk/gui/src/PreferencesDialog.cpp (revision 85) +++ hydrogen-trunk/gui/src/PreferencesDialog.cpp (working copy) @@ -121,6 +121,7 @@ // JACK //trackOutsCheckBox->setChecked( pPref->m_bJackTrackOuts ); connectDefaultsCheckBox->setChecked( pPref->m_bJackConnectDefaults ); + postFaderTrackOutsCheckBox->setChecked( pPref->m_bPostFaderTrackOuts ); //~ JACK @@ -276,6 +277,7 @@ // JACK //pPref->m_bJackTrackOuts = trackOutsCheckBox->isChecked(); pPref->m_bJackConnectDefaults = connectDefaultsCheckBox->isChecked(); + pPref->m_bPostFaderTrackOuts = postFaderTrackOutsCheckBox->isChecked(); //~ JACK pPref->m_nBufferSize = bufferSizeSpinBox->value(); @@ -403,6 +405,7 @@ bufferSizeSpinBox->setEnabled( false ); sampleRateComboBox->setEnabled( false ); connectDefaultsCheckBox->setEnabled( false ); + postFaderTrackOutsCheckBox->setEnabled( false ); } else if ( driverComboBox->currentText() == "OSS" ) { // OSS info += trUtf8("<b>Open Sound System</b><br>Simple audio driver [/dev/dsp]"); @@ -414,6 +417,7 @@ bufferSizeSpinBox->setEnabled(true); sampleRateComboBox->setEnabled(true); connectDefaultsCheckBox->setEnabled(false); + postFaderTrackOutsCheckBox->setEnabled( false ); } else if ( driverComboBox->currentText() == "JACK" ) { // JACK info += trUtf8("<b>Jack Audio Connection Kit Driver</b><br>Low latency audio driver"); @@ -425,6 +429,7 @@ bufferSizeSpinBox->setEnabled(false); sampleRateComboBox->setEnabled(false); connectDefaultsCheckBox->setEnabled(true); + postFaderTrackOutsCheckBox->setEnabled( true ); } else if ( driverComboBox->currentText() == "ALSA" ) { // ALSA info += trUtf8("<b>ALSA Driver</b><br>"); @@ -436,6 +441,7 @@ bufferSizeSpinBox->setEnabled(true); sampleRateComboBox->setEnabled(true); connectDefaultsCheckBox->setEnabled(false); + postFaderTrackOutsCheckBox->setEnabled( false ); } else if ( driverComboBox->currentText() == "PortAudio" ) { info += trUtf8( "<b>PortAudio Driver</b><br>" ); @@ -447,6 +453,7 @@ bufferSizeSpinBox->setEnabled(true); sampleRateComboBox->setEnabled(true); connectDefaultsCheckBox->setEnabled(false); + postFaderTrackOutsCheckBox->setEnabled( false ); } else if ( driverComboBox->currentText() == "CoreAudio" ) { info += trUtf8( "<b>CoreAudio Driver</b><br>" ); @@ -458,6 +465,7 @@ bufferSizeSpinBox->setEnabled(true); sampleRateComboBox->setEnabled(true); connectDefaultsCheckBox->setEnabled(false); + postFaderTrackOutsCheckBox->setEnabled( false ); } else { std::string selectedDriver = (driverComboBox->currentText()).toStdString(); Index: hydrogen-trunk/gui/src/UI/PreferencesDialog_UI.ui =================================================================== --- hydrogen-trunk/gui/src/UI/PreferencesDialog_UI.ui (revision 85) +++ hydrogen-trunk/gui/src/UI/PreferencesDialog_UI.ui (working copy) @@ -435,6 +435,22 @@ </item> </layout> </widget> + <widget class="QCheckBox" name="postFaderTrackOutsCheckBox" > + <property name="geometry" > + <rect> + <x>10</x> + <y>170</y> + <width>191</width> + <height>22</height> + </rect> + </property> + <property name="text" > + <string>Post-Fader Track Outputs</string> + </property> + <property name="shortcut" > + <string>Alt+F</string> + </property> + </widget> </widget> <widget class="QWidget" name="tab" > <attribute name="title" > @@ -446,7 +462,7 @@ <x>10</x> <y>10</y> <width>451</width> - <height>107</height> + <height>114</height> </rect> </property> <layout class="QGridLayout" > @@ -811,7 +827,6 @@ </widget> </widget> <layoutdefault spacing="6" margin="11" /> - <includes/> <resources/> <connections/> </ui> Index: hydrogen-trunk/hydrogen.kdevelop =================================================================== --- hydrogen-trunk/hydrogen.kdevelop (revision 85) +++ hydrogen-trunk/hydrogen.kdevelop (working copy) @@ -134,8 +134,7 @@ </kdevtrollproject> <kdevcppsupport> <references> - <pcs>Qt4</pcs> - <pcs>automatic_%2Fhome%2Fcomix%2Fprogetti%2Fhydrogen_svn%2Ftrunk</pcs> + <pcs>automatic_%2Fhome%2Fbob%2Fsrc%2Fhydrogen-trunk</pcs> </references> <codecompletion> <includeGlobalFunctions>true</includeGlobalFunctions> @@ -181,7 +180,7 @@ <includestyle>4</includestyle> <designerintegration>ExternalDesigner</designerintegration> <qmake>/usr/bin/qmake</qmake> - <designer></designer> + <designer>/usr/bin/designer-qt4</designer> <designerpluginpaths/> </qt> <splitheadersource> Index: hydrogen-trunk/libs/hydrogen/include/hydrogen/Preferences.h =================================================================== --- hydrogen-trunk/libs/hydrogen/include/hydrogen/Preferences.h (revision 85) +++ hydrogen-trunk/libs/hydrogen/include/hydrogen/Preferences.h (working copy) @@ -161,6 +161,7 @@ std::string m_sJackPortName2; bool m_bJackTransportMode; bool m_bJackConnectDefaults; + bool m_bPostFaderTrackOuts; /// Returns an instance of PreferencesMng class Index: hydrogen-trunk/libs/hydrogen/include/hydrogen/sampler/Sampler.h =================================================================== --- hydrogen-trunk/libs/hydrogen/include/hydrogen/sampler/Sampler.h (revision 85) +++ hydrogen-trunk/libs/hydrogen/include/hydrogen/sampler/Sampler.h (working copy) @@ -51,6 +51,9 @@ float *__main_out_L; ///< sampler main out (left channel) float *__main_out_R; ///< sampler main out (right channel) + float *__track_out_L[MAX_INSTRUMENTS]; + float *__track_out_R[MAX_INSTRUMENTS]; + Sampler(); ~Sampler(); @@ -73,7 +76,9 @@ void set_audio_output( AudioOutput* audio_output ); + void makeTrackOutputQueues( ); + private: std::vector<Note*> __playing_notes_queue; static Sampler* __instance; @@ -82,7 +87,6 @@ /// Instrument used for the preview feature. Instrument *__preview_instrument; - unsigned __render_note( Note* pNote, unsigned nBufferSize, Song* pSong ); int __render_note_no_resample( @@ -92,7 +96,8 @@ int nInitialSilence, float cost_L, float cost_R, - float cost_track, + float cost_track_L, + float cost_track_R, float fSendFXLevel_L, float fSendFXLevel_R, Song* pSong @@ -105,7 +110,8 @@ int nInitialSilence, float cost_L, float cost_R, - float cost_track, + float cost_track_L, + float cost_track_R, float fLayerPitch, float fSendFXLevel_L, float fSendFXLevel_R, Index: hydrogen-trunk/libs/hydrogen/include/hydrogen/IO/JackOutput.h =================================================================== --- hydrogen-trunk/libs/hydrogen/include/hydrogen/IO/JackOutput.h (revision 85) +++ hydrogen-trunk/libs/hydrogen/include/hydrogen/IO/JackOutput.h (working copy) @@ -58,6 +58,7 @@ void deactivate(); unsigned getBufferSize(); unsigned getSampleRate(); + int getNumTracks(); jack_transport_state_t getTransportState() { return m_JackTransportState; Index: hydrogen-trunk/libs/hydrogen/src/preferences.cpp =================================================================== --- hydrogen-trunk/libs/hydrogen/src/preferences.cpp (revision 85) +++ hydrogen-trunk/libs/hydrogen/src/preferences.cpp (working copy) @@ -243,6 +243,7 @@ } //m_bJackTrackOuts = LocalFileMng::readXmlBool( jackDriverNode, "jack_track_outs", m_bJackTrackOuts ); m_bJackConnectDefaults = LocalFileMng::readXmlBool( jackDriverNode, "jack_connect_defaults", m_bJackConnectDefaults ); + m_bPostFaderTrackOuts = LocalFileMng::readXmlBool( jackDriverNode, "track_out_post_fader", m_bPostFaderTrackOuts ); } @@ -454,6 +455,12 @@ } LocalFileMng::writeXmlString( &jackDriverNode, "jack_connect_defaults", jackConnectDefaultsString ); + std::string postFaderTrackOutsString = "false"; + if ( m_bPostFaderTrackOuts ) { + postFaderTrackOutsString = "true"; + } + LocalFileMng::writeXmlString( &jackDriverNode, "track_out_post_fader", postFaderTrackOutsString ); + // jack track outs //string jackTrackOutsString = "false"; //if (m_bJackTrackOuts) { Index: hydrogen-trunk/libs/hydrogen/src/hydrogen.cpp =================================================================== --- hydrogen-trunk/libs/hydrogen/src/hydrogen.cpp (revision 85) +++ hydrogen-trunk/libs/hydrogen/src/hydrogen.cpp (working copy) @@ -872,6 +872,9 @@ if ( m_pAudioDriver->get_class_name() == "JackOutput" ) { static_cast< JackOutput* >( m_pAudioDriver )->makeTrackOutputs( m_pSong ); } + + AudioEngine::get_instance()->get_sampler()->makeTrackOutputQueues(); + #endif } Index: hydrogen-trunk/libs/hydrogen/src/sampler/sampler.cpp =================================================================== --- hydrogen-trunk/libs/hydrogen/src/sampler/sampler.cpp (revision 85) +++ hydrogen-trunk/libs/hydrogen/src/sampler/sampler.cpp (working copy) @@ -94,6 +94,14 @@ memset( __main_out_R, 0, nFrames * sizeof( float ) ); +#ifdef JACK_SUPPORT + if ( __audio_output->has_track_outs() ) { + for(int nTrack = 0; nTrack < ( ( JackOutput* )__audio_output )->getNumTracks( ); nTrack++) { + memset( __track_out_L[nTrack], 0, ( ( JackOutput* )__audio_output )->getBufferSize( ) * sizeof( float ) ); + memset( __track_out_R[nTrack], 0, ( ( JackOutput* )__audio_output )->getBufferSize( ) * sizeof( float ) ); + } + } +#endif // JACK_SUPPORT // Max notes limit int m_nMaxNotes = Preferences::getInstance()->m_nMaxNotes; @@ -226,14 +234,18 @@ float cost_L = 1.0f; float cost_R = 1.0f; - float cost_track = 1.0f; + float cost_track_L = 1.0f; + float cost_track_R = 1.0f; float fSendFXLevel_L = 1.0f; float fSendFXLevel_R = 1.0f; if ( pInstr->is_muted() || pSong->__is_muted ) { // is instrument muted? cost_L = 0.0; cost_R = 0.0; - + if ( Preferences::getInstance()->m_bPostFaderTrackOuts ) { + cost_track_L = 0.0; + cost_track_R = 0.0; + } fSendFXLevel_L = 0.0f; fSendFXLevel_R = 0.0f; } else { // Precompute some values... @@ -245,6 +257,9 @@ fSendFXLevel_L = cost_L; cost_L = cost_L * pInstr->get_volume(); // instrument volume + if ( Preferences::getInstance()->m_bPostFaderTrackOuts ) { + cost_track_L = cost_L * 2; + } cost_L = cost_L * pSong->get_volume(); // song volume cost_L = cost_L * 2; // max pan is 0.5 @@ -257,13 +272,19 @@ fSendFXLevel_R = cost_R; cost_R = cost_R * pInstr->get_volume(); // instrument volume + if ( Preferences::getInstance()->m_bPostFaderTrackOuts ) { + cost_track_R = cost_R * 2; + } cost_R = cost_R * pSong->get_volume(); // song pan cost_R = cost_R * 2; // max pan is 0.5 } + if ( !Preferences::getInstance()->m_bPostFaderTrackOuts ) { // direct track outputs only use velocity - cost_track = cost_track * pNote->get_velocity(); - cost_track = cost_track * fLayerGain; + cost_track_L = cost_track_L * pNote->get_velocity(); + cost_track_L = cost_track_L * fLayerGain; + cost_track_R = cost_track_L; + } // Se non devo fare resample (drumkit) posso evitare di utilizzare i float e gestire il tutto in // maniera ottimizzata @@ -277,9 +298,9 @@ //_INFOLOG( "total pitch: " + to_string( fTotalPitch ) ); if ( fTotalPitch == 0.0 && pSample->get_sample_rate() == __audio_output->getSampleRate() ) { // NO RESAMPLE - return __render_note_no_resample( pSample, pNote, nBufferSize, nInitialSilence, cost_L, cost_R, cost_track, fSendFXLevel_L, fSendFXLevel_R, pSong ); + return __render_note_no_resample( pSample, pNote, nBufferSize, nInitialSilence, cost_L, cost_R, cost_track_L, cost_track_R, fSendFXLevel_L, fSendFXLevel_R, pSong ); } else { // RESAMPLE - return __render_note_resample( pSample, pNote, nBufferSize, nInitialSilence, cost_L, cost_R, cost_track, fLayerPitch, fSendFXLevel_L, fSendFXLevel_R, pSong ); + return __render_note_resample( pSample, pNote, nBufferSize, nInitialSilence, cost_L, cost_R, cost_track_L, cost_track_R, fLayerPitch, fSendFXLevel_L, fSendFXLevel_R, pSong ); } } @@ -293,7 +314,8 @@ int nInitialSilence, float cost_L, float cost_R, - float cost_track, + float cost_track_L, + float cost_track_R, float fSendFXLevel_L, float fSendFXLevel_R, Song* pSong @@ -344,21 +366,9 @@ } fADSRValue = pNote->m_adsr.get_value( 1 ); - fVal_L = pSample_data_L[ nSamplePos ] * cost_L * fADSRValue; - fVal_R = pSample_data_R[ nSamplePos ] * cost_R * fADSRValue; + fVal_L = pSample_data_L[ nSamplePos ] * fADSRValue; + fVal_R = pSample_data_R[ nSamplePos ] * fADSRValue; - if ( __audio_output->has_track_outs() ) { - // hack hack hack: cast to JackOutput -#ifdef JACK_SUPPORT - float* track_out_L = ( ( JackOutput* )__audio_output )->getTrackOut_L( nInstrument ); - float* track_out_R = ( ( JackOutput* )__audio_output )->getTrackOut_L( nInstrument ); - assert( track_out_L ); - assert( track_out_R ); - track_out_L[nBufferPos] = pSample_data_L[nSamplePos] * cost_track * fADSRValue; - track_out_R[nBufferPos] = pSample_data_R[nSamplePos] * cost_track * fADSRValue; -#endif - } - // Low pass resonant filter if ( bUseLPF ) { pNote->m_fBandPassFilterBuffer_L = fResonance * pNote->m_fBandPassFilterBuffer_L + fCutoff * ( fVal_L - pNote->m_fLowPassFilterBuffer_L ); @@ -370,6 +380,21 @@ fVal_R = pNote->m_fLowPassFilterBuffer_R; } + if ( __audio_output->has_track_outs() ) { +#ifdef JACK_SUPPORT + assert( __track_out_L[ nInstrument ] ); + assert( __track_out_R[ nInstrument ] ); + // NOTE: LPF is not applied at this point! +// __track_out_L[ nInstrument ][nBufferPos] += (pSample_data_L[ nSamplePos ] * cost_track_L * fADSRValue); +// __track_out_R[ nInstrument ][nBufferPos] += (pSample_data_R[ nSamplePos ] * cost_track_R * fADSRValue); + __track_out_L[ nInstrument ][nBufferPos] += fVal_L * cost_track_L; + __track_out_R[ nInstrument ][nBufferPos] += fVal_R * cost_track_R; +#endif + } + + fVal_L = fVal_L * cost_L; + fVal_R = fVal_R * cost_R; + // update instr peak if ( fVal_L > fInstrPeak_L ) { fInstrPeak_L = fVal_L; @@ -431,7 +456,8 @@ int nInitialSilence, float cost_L, float cost_R, - float cost_track, + float cost_track_L, + float cost_track_R, float fLayerPitch, float fSendFXLevel_L, float fSendFXLevel_R, @@ -500,22 +526,10 @@ fVal_R = linear_interpolation( pSample_data_R[nSamplePos], pSample_data_R[nSamplePos + 1], fDiff ); } - if ( __audio_output->has_track_outs() ) { -#ifdef JACK_SUPPORT - // hack hack hack: cast to JackOutput - float* track_out_L = ( ( JackOutput* )__audio_output )->getTrackOut_L( nInstrument ); - float* track_out_R = ( ( JackOutput* )__audio_output )->getTrackOut_L( nInstrument ); - assert( track_out_L ); - assert( track_out_R ); - track_out_L[nBufferPos] = fVal_L * cost_track * fADSRValue; - track_out_R[nBufferPos] = fVal_R * cost_track * fADSRValue; -#endif - } - // ADSR envelope fADSRValue = pNote->m_adsr.get_value( fStep ); - fVal_L = fVal_L * cost_L * fADSRValue; - fVal_R = fVal_R * cost_R * fADSRValue; + fVal_L = fVal_L * fADSRValue; + fVal_R = fVal_R * fADSRValue; // Low pass resonant filter if ( bUseLPF ) { @@ -528,6 +542,20 @@ fVal_R = pNote->m_fLowPassFilterBuffer_R; } + if ( __audio_output->has_track_outs() ) { +#ifdef JACK_SUPPORT + assert( __track_out_L[ nInstrument ] ); + assert( __track_out_R[ nInstrument ] ); +// __track_out_L[ nInstrument ][nBufferPos] += (tVal_L * cost_track_L * fADSRValue); +// __track_out_R[ nInstrument ][nBufferPos] += (tVal_R * cost_track_R * fADSRValue); + __track_out_L[ nInstrument ][nBufferPos] += (fVal_L * cost_track_L); + __track_out_R[ nInstrument ][nBufferPos] += (fVal_R * cost_track_R); +#endif + } + + fVal_L = fVal_L * cost_L; + fVal_R = fVal_R * cost_R; + // update instr peak if ( fVal_L > fInstrPeak_L ) { fInstrPeak_L = fVal_L; @@ -664,10 +692,20 @@ void Sampler::set_audio_output( AudioOutput* audio_output ) { __audio_output = audio_output; + } +void Sampler::makeTrackOutputQueues( ) +{ +#ifdef JACK_SUPPORT + if ( __audio_output->has_track_outs() ) { + for (int nTrack = 0; nTrack < ( ( JackOutput* )__audio_output )->getNumTracks( ); nTrack++) { + __track_out_L[nTrack] = ( ( JackOutput* )__audio_output )->getTrackOut_L( nTrack ); + __track_out_R[nTrack] = ( ( JackOutput* )__audio_output )->getTrackOut_R( nTrack ); + } + } +#endif // JACK_SUPPORT +} - -}; - +} Index: hydrogen-trunk/libs/hydrogen/src/IO/jack_output.cpp =================================================================== --- hydrogen-trunk/libs/hydrogen/src/IO/jack_output.cpp (revision 85) +++ hydrogen-trunk/libs/hydrogen/src/IO/jack_output.cpp (working copy) @@ -561,7 +561,12 @@ } } +int JackOutput::getNumTracks() +{ + return track_port_count; +} + }; #endif // JACK_SUPPORT |