diff --git a/include/ITAAudioSample.h b/include/ITAAudioSample.h index 4cf548fa36fad2c5450577be490575da9328cfd0..838a82cc6d1e4cbdb4bd26e976582b7513e947a4 100644 --- a/include/ITAAudioSample.h +++ b/include/ITAAudioSample.h @@ -37,6 +37,12 @@ public: * Requires initialization or load to be used, otherwise methods will throw ITAException. */ CITAAudioSample( const float fSampleRate = 44100.0f ); + + //! Create audio sample from file + /** + * Requires initialization or load to be used, otherwise methods will throw ITAException. + */ + CITAAudioSample( const std::string& sFilePath ); //! Create (empty) audio sample with given parameters /** @@ -87,6 +93,9 @@ public: //! Load audio sample from file and convert sample rate, if necessary void LoadWithSampleTypeConversion( const std::string& sFilePath ); + //! Load audio sample from file and convert sample rate, if necessary + void LoadWithSampleTypeConversion( const CITAAudioSample& asSource ); + private: //! Disable this Init method from sample buffer diff --git a/src/ITAAudioSample.cpp b/src/ITAAudioSample.cpp index 6a313e961de41feaa46c37ec13e283af25f00f8b..93a93843faf91aff4dd24fca47e24a4988c9b5d6 100644 --- a/src/ITAAudioSample.cpp +++ b/src/ITAAudioSample.cpp @@ -1,6 +1,7 @@ #include #include +#include #include CITAAudioSample::CITAAudioSample( const float fSampleRate ) @@ -26,6 +27,12 @@ CITAAudioSample::CITAAudioSample( const CITAAudioSample& sfSource ) ITASampleFrame::write( sfSource, sfSource.GetLength() ); } +CITAAudioSample::CITAAudioSample( const std::string& sFilePath ) + :CITAAudioSample() +{ + Load( sFilePath ); +} + void CITAAudioSample::Init( const int iNumChannels, const int iLength, const float fSampleRate, const bool bZeroInit /*= true */ ) { m_fSampleRate = fSampleRate; @@ -45,12 +52,25 @@ void CITAAudioSample::LoadWithSampleTypeConversion( const std::string& sFilePath Load( sfRaw, GetSampleRate() ); } +void CITAAudioSample::LoadWithSampleTypeConversion( const CITAAudioSample& asSource ) +{ + ITASampleFrame* psfSource = ( ITASampleFrame* ) &asSource; + Load( *psfSource, asSource.GetSampleRate() ); +} + void CITAAudioSample::Load( const CITAAudioSample& oSource ) { Load( oSource, oSource.GetSampleRate() ); } +void CITAAudioSample::Load( const std::string& sFilePath ) +{ + double dSampleRate; + ITASampleFrame::Load( sFilePath, dSampleRate ); + m_fSampleRate = float( dSampleRate ); +} + float CITAAudioSample::GetSampleRate() const { return m_fSampleRate; @@ -70,26 +90,33 @@ void CITAAudioSample::Load( const ITASampleFrame& sfSource, const float fSourceS } const float fSRCRation = GetSampleRate() / fSourceSampleRate; - const int iTargetLength = (int) ceil( sfSource.GetLength() * fSRCRation ); // @tbd test! - Init( sfSource.GetNumChannels(), iTargetLength, m_fSampleRate ); + const float fTargetLengthSamples = sfSource.GetLength() * fSRCRation; + int iTargetLengthSamples = ( int ) floor( fTargetLengthSamples ); + const float fTargetLengthSubsampleFraction = fTargetLengthSamples - float( iTargetLengthSamples ); + // Account for exact match, then the "last" sample is not resampled anymore and can be skipped + if( fTargetLengthSubsampleFraction == 0.0f ) + iTargetLengthSamples--; + Init( sfSource.GetNumChannels(), iTargetLengthSamples, m_fSampleRate ); const int iConverter = SRC_SINC_MEDIUM_QUALITY; for( int i = 0; i < sfSource.GetNumChannels(); i++ ) { int iSRCError; - SRC_STATE* pSRCStace = src_new( iConverter, sfSource.GetNumChannels(), &iSRCError ); + SRC_STATE* pSRCStace = src_new( iConverter, 1, &iSRCError ); // single channel conversion, samplerate expets interleaved. this is not compatible with ITASampleBuffer/Frame if( pSRCStace == nullptr ) ITA_EXCEPT_INVALID_PARAMETER( "Could not create sample rate converter, samplerate error was: " + std::string( src_strerror( iSRCError ) ) ); - + SRC_DATA oSRCData; oSRCData.data_in = sfSource[ i ].GetData(); oSRCData.data_out = ( *this )[ i ].GetData(); - oSRCData.input_frames = 1; - oSRCData.output_frames = 1; + oSRCData.input_frames = sfSource.GetLength(); // In samplerate, one frame is "one sample" of multi channel audio + oSRCData.output_frames = iTargetLengthSamples; oSRCData.src_ratio = fSRCRation; if( ( iSRCError = src_process( pSRCStace, &oSRCData ) ) != 0 ) ITA_EXCEPT_INVALID_PARAMETER( "Could not convert sample rate: " + std::string( src_strerror( iSRCError ) ) ); + assert( oSRCData.output_frames_gen == iTargetLengthSamples ); + src_delete( pSRCStace ); } -} \ No newline at end of file +} diff --git a/tests/ITABaseAudioSampleTest.cpp b/tests/ITABaseAudioSampleTest.cpp index af1e55df2243e26b2a3650cbef9b06e34e0f3fb6..4a69ec72dd2289d395dfce5bc39e43b797c304ca 100644 --- a/tests/ITABaseAudioSampleTest.cpp +++ b/tests/ITABaseAudioSampleTest.cpp @@ -7,6 +7,20 @@ using namespace std; int main( int, char** ) { + try + { + CITAAudioSample asTTS48kHz( "tts.wav" ); + CITAAudioSample asTTS44kHz; + asTTS44kHz.LoadWithSampleTypeConversion( asTTS48kHz ); + asTTS44kHz.Store( "tts_44khz.wav" ); + + cout << "Converted files." << endl; + } + catch( ITAException& err ) + { + cerr << "File test error: " << err << endl; + } + try { CITAAudioSample as44kfs; @@ -18,13 +32,15 @@ int main( int, char** ) as44kfs[ 0 ][ 0 ] = 1.0f; as44kfs[ 0 ][ as44kfs.GetLength() - 1 ] = -1.0f; - as48kfs.Load( as44kfs ); - as96kfs.Load( as48kfs ); - as128kfs.Load( as96kfs ); + as48kfs.LoadWithSampleTypeConversion( as44kfs ); + as96kfs.LoadWithSampleTypeConversion( as48kfs ); + as128kfs.LoadWithSampleTypeConversion( as96kfs ); + + cout << "Converted." << endl; } catch( ITAException& err ) { - cerr << "Error: " << err << endl; + cerr << "Generic test error: " << err << endl; } return 0;