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
-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://ad.doubleclick.net/clk;164216239;13503038;w?http://sf.net/marketplace
_______________________________________________
Hydrogen-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/hydrogen-devel