Commit b38e94c9 authored by Dipl.-Ing. Jonas Stienen's avatar Dipl.-Ing. Jonas Stienen
Browse files

Adding turbine feature

parent 85ab6f58
#include <ITAAudiofileWriter.h>
#include <ITAStringUtils.h>
#include <ITAStopWatch.h>
#include <ITAConstants.h>
#include <ITASampleBuffer.h>
#include <ITAStopWatch.h>
#include <ITAStringUtils.h>
#include <VistaTools/VistaRandomNumberGenerator.h>
#include <DspFilters/Butterworth.h>
#include <algorithm>
#include <atomic>
#include <iostream>
......@@ -19,36 +21,56 @@ const int g_iIIRFilterOrder = 1;
const int g_iChannels = 1;
/**
* Manual implementation of the "forced flame" pure data patch from Andy Farnell's Designing Sound
*/
* Manual implementation of the "jet engine" pure data patch (combining "forced flame" and "turbine") from Andy Farnell's Designing Sound
*/
namespace ITADSP
{
namespace PD
{
class CForcedFlame
class CJetEngine
{
public:
inline CForcedFlame( float fInletInit = 0.1f )
inline CJetEngine( float RPMInit = 500.0f)
{
m_vfTurbineModeFrequencies = { 3097.0f, 4495.0f, 5588.0f, 7414.0f, 11000.0f };
m_vfTurbineModeAmplitudes = { 0.25f, 0.25f, 1.0f, 0.4f, 0.4f };
m_vfTurbineModePhaseShift = { 0, 0, 0, 0, 0 };
m_vfRPMRange = { 500.0f, 6000.0f };
const float fCenterFrequency1 = 8000.0f;
const float fQ1 = 0.5f;
m_oBandPassFilter1.setup( g_iIIRFilterOrder, float( g_dSampleRate ), fCenterFrequency1, fCenterFrequency1 / fQ1 );
m_oForcedFlameBP1.setup( g_iIIRFilterOrder, float( g_dSampleRate ), fCenterFrequency1, fCenterFrequency1 / fQ1 );
const float fCutoffFrequency1 = 120.0f;
m_oHighPassFilter1.setup( g_iIIRFilterOrder, float( g_dSampleRate ), fCutoffFrequency1 );
m_oForcedFlameHP1.setup( g_iIIRFilterOrder, float( g_dSampleRate ), fCutoffFrequency1 );
const float fLPCutoffFrequency1 = 11000.0f;
m_oJetEngineLP1.setup( g_iIIRFilterOrder, float( g_dSampleRate ), fLPCutoffFrequency1 );
SetInlet( fInletInit );
SetRPM( RPMInit );
};
inline virtual ~CForcedFlame() {};
virtual void SetInlet( float fInletValue );
inline virtual ~CJetEngine() {};
virtual void SetRPM( float fInletValue );
virtual void Process( float* pfOutputBuffer, int iNumSamples );
protected:
void UpdateForcedFlame( float fRPM );
void UpdateTurbine( float fRPM );
private:
VistaRandomNumberGenerator oRNG;
SimpleFilter< Butterworth::BandPass< g_iIIRFilterOrder >, g_iChannels > m_oBandPassFilter1, m_oBandPassFilter2, m_oBandPassFilter3;
SimpleFilter< Butterworth::HighPass< g_iIIRFilterOrder >, g_iChannels > m_oHighPassFilter1;
SimpleFilter< Butterworth::BandPass< g_iIIRFilterOrder >, g_iChannels > m_oForcedFlameBP1, m_oForcedFlameBP2, m_oForcedFlameBP3;
SimpleFilter< Butterworth::HighPass< g_iIIRFilterOrder >, g_iChannels > m_oForcedFlameHP1;
SimpleFilter< Butterworth::LowPass< g_iIIRFilterOrder >, g_iChannels > m_oJetEngineLP1;
std::vector< float > m_vfTurbineModeFrequencies, m_vfRPMRange, m_vfTurbineModePhaseShift, m_vfTurbineModeAmplitudes;
float m_fTempSample;
};
}
}
......@@ -57,49 +79,114 @@ int main( int, char** )
{
ITASampleBuffer oOutputBuffer( g_iOutputLengthSamples );
ITADSP::PD::CForcedFlame oPatch( 0.5f );
float fRPM = 2500.f;
ITADSP::PD::CJetEngine oPatch( fRPM );
oPatch.Process( oOutputBuffer.GetData(), g_iOutputLengthSamples );
string sFilePath = "ITADSP_pd_forced_flame_out.wav";
string sFilePath = "ITADSP_pd_jet_engine_out.wav";
writeAudiofile( sFilePath, &oOutputBuffer, g_dSampleRate, ITAQuantization::ITA_FLOAT );
cout << "Exported result to " << sFilePath << endl;
return 255;
}
void ITADSP::PD::CForcedFlame::Process( float* pfOutputBuffer, int iNumSamples )
void ITADSP::PD::CJetEngine::SetRPM( float fRPM )
{
UpdateForcedFlame( fRPM );
UpdateTurbine( fRPM );
}
void ITADSP::PD::CJetEngine::UpdateForcedFlame( float fRPM )
{
assert( m_vfRPMRange.size() == 2 );
float fValidRPM = std::min( std::max( m_vfRPMRange[ 0 ], fRPM ), m_vfRPMRange[ 1 ] );
// Normalize input
const float fNormalizedFlameControl = std::max( 0.1f, fValidRPM / m_vfRPMRange[ 1 ] );
const float fCenterFrequency2 = fNormalizedFlameControl * fNormalizedFlameControl * 150.0f;
m_oForcedFlameBP2.setup( g_iIIRFilterOrder, float( g_dSampleRate ), fCenterFrequency2, fCenterFrequency2 / 1.0f );
const float fCenterFrequency3 = fNormalizedFlameControl * 12000.0f;
const float fQ3 = 0.6f;
m_oForcedFlameBP3.setup( g_iIIRFilterOrder, float( g_dSampleRate ), fCenterFrequency3, fCenterFrequency3 / fQ3 );
}
void ITADSP::PD::CJetEngine::UpdateTurbine( float fRPM )
{
assert( m_vfRPMRange.size() == 2 );
float fValidRPM = std::min( std::max( m_vfRPMRange[ 0 ], fRPM ), m_vfRPMRange[ 1 ] );
// Normalize input
const float fNormalizedTurbineControl = std::max( 0.1f, fValidRPM / m_vfRPMRange[ 1 ] );
for( auto& v : m_vfTurbineModeFrequencies )
v *= fNormalizedTurbineControl;
}
void ITADSP::PD::CJetEngine::Process( float* pfOutputBuffer, int iNumSamples )
{
float* pfTempSampleAlias = &m_fTempSample; // We need this ref pointer for Dsp functions, but its ugly we dont use it apart from that
for( int n = 0; n < iNumSamples; n++ )
{
float* pfCurrentSample( &(pfOutputBuffer[ n ]) );
float& fCurrentSample( pfOutputBuffer[ n ] );
*pfCurrentSample = oRNG.GenerateFloat( -1.0f, 1.0f ); // noise~
// Forced flame
m_oBandPassFilter1.process( 1, &pfCurrentSample ); // bd~ 8000 0.5
m_fTempSample = oRNG.GenerateFloat( -1.0f, 1.0f ); // noise~
m_oBandPassFilter2.process( 1, &pfCurrentSample ); // vcf~ 0 1 (real part only = band pass)
m_oForcedFlameBP1.process( 1, &pfTempSampleAlias ); // bd~ 8000 0.5
m_oHighPassFilter1.process( 1, &pfCurrentSample ); // hip~ 120
m_oForcedFlameBP2.process( 1, &pfTempSampleAlias ); // vcf~ 0 1 (real part only = band pass)
*pfCurrentSample *= 120.0f; // *~ 120
m_oForcedFlameHP1.process( 1, &pfTempSampleAlias ); // hip~ 120
*pfCurrentSample = ( *pfCurrentSample < -1.0f ) ? -1 : ( ( *pfCurrentSample > 1 ) ? 1 : *pfCurrentSample ); // clip~ -1 1
m_fTempSample *= 120.0f; // *~ 120
m_oBandPassFilter3.process( 1, &pfCurrentSample ); // vcf~ 0 0.6 (real part only = band pass)
}
}
m_fTempSample = ( m_fTempSample < -1.0f ) ? -1 : ( ( m_fTempSample > 1 ) ? 1 : m_fTempSample ); // clip~ -1 1
void ITADSP::PD::CForcedFlame::SetInlet( float fInlet )
{
if( fInlet < 0.1f )
fInlet = 0.1f;
else if( fInlet > 1.0f )
fInlet = 1.0f;
m_oForcedFlameBP3.process( 1, &pfTempSampleAlias ); // vcf~ 0 0.6 (real part only = band pass)
const float fCenterFrequency2 = fInlet * fInlet * 150.0f;
m_oBandPassFilter2.setup( g_iIIRFilterOrder, float( g_dSampleRate ), fCenterFrequency2, fCenterFrequency2 / 1.0f );
fCurrentSample = m_fTempSample; // override buffer
const float fCenterFrequency3 = fInlet * 12000.0f;
const float fQ3 = 0.6f;
m_oBandPassFilter3.setup( g_iIIRFilterOrder, float( g_dSampleRate ), fCenterFrequency3, fCenterFrequency3 / fQ3 );
// Turbine (adds to output buffer)
m_fTempSample = 0.0f;
assert( m_vfTurbineModeFrequencies.size() == m_vfTurbineModePhaseShift.size() );
for( int i = 0; i < m_vfTurbineModeFrequencies.size(); i++ )
{
const float& fFrequency( m_vfTurbineModeFrequencies[ i ] );
const float& fPhaseShift( m_vfTurbineModePhaseShift[ i ] );
const float& fAmplitude( m_vfTurbineModeAmplitudes[ i ] );
const int iPeriodLengthSamples = ( int ) round( g_dSampleRate / fFrequency );
const float t = fmodf( ITAConstants::TWO_PI_F_L / float( iPeriodLengthSamples ) * float( n ), ITAConstants::TWO_PI_F );
m_fTempSample += fAmplitude * sin( t + fPhaseShift ); // all osc~ and *~
}
m_fTempSample = ( m_fTempSample < -0.9f ) ? -0.9f : ( ( m_fTempSample > 0.9f ) ? 0.9f : m_fTempSample ); // clip~ -0.9 0.9
// Jet engine
fCurrentSample += m_fTempSample * 0.1f; // combine turbine and flame with factor *~ 0.5 from jet engine patch
m_oJetEngineLP1.process( 1, &pfTempSampleAlias ); // ~lop 11000
fCurrentSample *= 0.2f; // *~0.2
}
// Update phases for turbine
for( int i = 0; i < m_vfTurbineModeFrequencies.size(); i++ )
{
const float& fFrequency( m_vfTurbineModeFrequencies[ i ] );
float& fPhaseShift( m_vfTurbineModePhaseShift[ i ] );
const int iPeriodLengthSamples = ( int ) round( g_dSampleRate / fFrequency );
fPhaseShift = fmodf( ITAConstants::TWO_PI_F_L / float( iPeriodLengthSamples ) * iNumSamples + fPhaseShift, ITAConstants::TWO_PI_F );
}
}
ita_read_wav( 'ITADSP_pd_forced_flame_out.wav' )
ITADSP_pd_forced_flame_out.pt
ITADSP_pd_forced_flame_out.play
\ No newline at end of file
jet_engine = ita_read( 'ITADSP_pd_jet_engine_out.wav' )
jet_engine.pt
jet_engine.play
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment