ita_convoi.cpp 9.64 KB
Newer Older
1 2 3 4 5
/*
 * ----------------------------------------------------------------
 *
 *		ITA core libs
 *		(c) Copyright Institute of Technical Acoustics (ITA)
6
 *		RWTH Aachen University, Germany, 2015-2019
7 8 9 10 11 12 13 14 15 16 17
 *
 * ----------------------------------------------------------------
 *				    ____  __________  _______
 *				   //  / //__   ___/ //  _   |
 *				  //  /    //  /    //  /_|  |
 *				 //  /    //  /    //  ___   |
 *				//__/    //__/    //__/   |__|
 *
 * ----------------------------------------------------------------
 *
 */
18 19 20
#include <ITAUPConvolution.h>
#include <ITAUPFilter.h>
#include <ITAUPFilterPool.h>
21 22 23 24 25

#include <ITAFileDataSource.h>
#include <ITAPortaudioInterface.h>
#include <ITAAudiofileReader.h>
#include <ITAStreamPatchBay.h>
26
#include <ITADataSourceRealization.h>
27 28 29 30 31
#include <ITAStreamMultiplier1N.h>
#include <ITASampleFrame.h>

#include <ITAException.h>

32
#include <cassert>
33 34 35
#include <string>
#include <iostream>
#include <sstream>
36 37

#ifdef WIN32
38
#include <conio.h>
39 40 41
#else
#include <ncurses.h>
#endif
42 43 44 45

#include <VistaTools/VistaFileSystemFile.h>


46
class ITAStreamConvolver : public ITADatasourceRealizationEventHandler, public ITAUPConvolution
47 48 49
{
public:
	inline ITAStreamConvolver( const double dSampleRate, const int iBlockLength, const int iFilterLength )
50
		: ITAUPConvolution( iBlockLength, iFilterLength )
51 52 53 54 55 56 57 58 59 60 61 62
		, pdsInput( NULL )
	{
		m_pdsOutput = new ITADatasourceRealization( 1, dSampleRate, iBlockLength );
		m_pdsOutput->SetStreamEventHandler( this );
	};

	inline void HandleProcessStream( ITADatasourceRealization*, const ITAStreamInfo* pStreamInfo )
	{
		if( !pdsInput )
			ITA_EXCEPT1( MODAL_EXCEPTION, "Input data source not defined yet" );

		const float* pfInputData = pdsInput->GetBlockPointer( 0, pStreamInfo );
63
		Process( pfInputData, GetBlocklength(), m_pdsOutput->GetWritePointer( 0 ), GetBlocklength(), ITABase::MixingMethod::OVERWRITE );
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
		m_pdsOutput->IncrementWritePointer();
	};

	void HandlePostIncrementBlockPointer( ITADatasourceRealization* )
	{
		pdsInput->IncrementBlockPointer();
	};

	ITADatasource* GetOutputDataSource()
	{
		return m_pdsOutput;
	};

	ITADatasource* pdsInput;

private:
	ITADatasourceRealization* m_pdsOutput;
};

83 84
std::string ita_convoi_syntax();
std::string ita_convoi_commands();
85
void ita_convio_exchange_channel( ITAStreamConvolver*, const ITASampleFrame&, const int iChannelIndex );
86
void ita_convio_dirac_channel( ITAStreamConvolver* );
87 88 89 90 91

int main( int argc, char* argv[] )
{
	if( argc < 3 )
	{
92
		std::cout << ita_convoi_syntax() <<  std::endl;
93 94 95 96
		return 255;
	}

	// Input wav file
97
	std::string sInputFilePath = std::string( argv[ 1 ] );
98 99 100
	VistaFileSystemFile oInputFile( sInputFilePath );
	if( !oInputFile.Exists() || !oInputFile.IsFile() )
	{
101
		std::cerr << "Input file '" << oInputFile.GetName() << "' not found or invalid" <<  std::endl;
102 103 104 105 106 107 108 109 110 111
		return 255;
	}

	ITAAudiofileReader* pSample = NULL;
	try
	{
		pSample = ITAAudiofileReader::create( oInputFile.GetName() );
	}
	catch( ITAException& e )
	{
112
		std::cerr << "Could not read input file: " << e <<  std::endl;
113 114 115 116 117 118 119
		return 255;
	}
	
	double dSamplingRate = pSample->getSamplerate();
	delete pSample;

	// IR filter file
120
	std::string sIRFilePath = std::string( argv[ 2 ] );
121 122 123
	VistaFileSystemFile oIRFile( sIRFilePath );
	if( !oIRFile.Exists() || !oIRFile.IsFile() )
	{
124
		std::cerr << "IR filter file '" << oIRFile.GetName() << "' not found or invalid" <<  std::endl;
125 126 127 128 129 130 131 132 133 134
		return 255;
	}

	ITAAudiofileReader* pIRAudioFile = NULL;
	try
	{
		pIRAudioFile = ITAAudiofileReader::create( oIRFile.GetName() );
	}
	catch( ITAException& e )
	{
135
		std::cerr << "Could not read IR filter file: " << e <<  std::endl;
136 137 138 139 140 141 142 143 144 145
		return 255;
	}
	double dSamplingRateFilter = pIRAudioFile->getSamplerate();
	int iFilterChannels = pIRAudioFile->getNumberOfChannels();
	int iFilterLength = pIRAudioFile->getLength();
	delete pIRAudioFile;

	// Require matching sampling rates
	if( dSamplingRate != dSamplingRateFilter )
	{
146
		std::cerr << "Sampling rate of input file (" << dSamplingRate << ") and IR filter (" << dSamplingRateFilter << ") does not match" <<  std::endl;
147 148 149 150 151 152
		return 255;
	}

	int iBlockLength = ITAPortaudioInterface::GetPreferredBufferSize();
	if( argc > 3 )
	{
153
		iBlockLength = atoi( argv[ 3 ] );
154 155 156
	}

	// Start audio streaming
157
	ITAPortaudioInterface::ITA_PA_ERRORCODE iError;
158 159 160
	ITAPortaudioInterface oITAPA( dSamplingRate, iBlockLength );
	int iPortaudioDeviceID = oITAPA.GetDefaultOutputDevice();
	if( argc > 4 )
161
		iPortaudioDeviceID = atoi( argv[ 4 ] );
162

163
	std::cout << "Attempting to initialize Portaudio device with ID " << iPortaudioDeviceID << std::endl;
164 165
	if( ( iError = oITAPA.Initialize( iPortaudioDeviceID ) ) != ITAPortaudioInterface::ITA_PA_NO_ERROR )
	{
166
		std::cerr << "Could not initialize Portaudio, encountered error: " << ITAPortaudioInterface::GetErrorCodeString( iError ) <<  std::endl;
167 168
		return 255;
	}
169 170 171 172 173 174

	std::cout << "Starting audio streaming on device '" << oITAPA.GetDeviceName(iPortaudioDeviceID) << "'"
		<< " with " << dSamplingRate / 1.0e3 << " kHz and a block size of " << iBlockLength << " samples on "
		<< oITAPA.GetNumOutputChannels( iPortaudioDeviceID ) << " output channels." << std::endl;
	std::cout << "Your FIR filter has " << iFilterChannels << " channel" << ( iFilterChannels > 1 ? "s" : "" ) << " and a length of " << iFilterLength << " taps / samples"
		<< " (" << double( iFilterLength ) / dSamplingRate << " seconds)." << std::endl;
175
	
176 177 178 179 180 181
	// Streaming chaing
	ITAStreamConvolver oConvolver( dSamplingRate, iBlockLength, iFilterLength );

	ITAFileDatasource oSample( oInputFile.GetName(), iBlockLength, true );
	if( oSample.GetNumberOfChannels() != 1 )
	{
182
		std::cout << "Warning ... input file has multiple channels, mixing to mono." <<  std::endl;
183 184 185 186 187 188 189 190 191 192 193
	}

	ITAStreamPatchbay oPatchBay( dSamplingRate, iBlockLength );
	int iInputID = oPatchBay.AddInput( &oSample );
	int iOutputID = oPatchBay.AddOutput( 1 );
	oConvolver.pdsInput = oPatchBay.GetOutputDatasource( iOutputID );
	
	for( int n = 0; n < int( oSample.GetNumberOfChannels() ); n++ ) // Downmix
		oPatchBay.ConnectChannels( iInputID, n, iOutputID, 0 );

	ITAStreamMultiplier1N oMultiplier( oConvolver.GetOutputDataSource(), oITAPA.GetNumOutputChannels( iPortaudioDeviceID ) );
194 195 196 197 198 199
	
	if( ( iError = oITAPA.SetPlaybackDatasource( &oMultiplier ) ) != ITAPortaudioInterface::ITA_PA_NO_ERROR )
	{
		std::cerr << "Could not set streaming playback for Portaudio, encountered error: " << ITAPortaudioInterface::GetErrorCodeString( iError ) <<  std::endl;
		return 255;
	}
200

201 202 203 204 205 206 207 208 209 210 211 212
	if( ( iError = oITAPA.Open() ) != ITAPortaudioInterface::ITA_PA_NO_ERROR )
	{
		std::cerr << "Could not open streaming over Portaudio, encountered error: " << ITAPortaudioInterface::GetErrorCodeString( iError ) <<  std::endl;
		return 255;
	}

	if( ( iError = oITAPA.Start() ) != ITAPortaudioInterface::ITA_PA_NO_ERROR )
	{
		std::cerr << "Could not start streaming, encountered error: " << ITAPortaudioInterface::GetErrorCodeString( iError ) <<  std::endl;
		return 255;
	}
	
213 214 215 216 217 218

	int iCurrentIRChannelIndex = 0;
	ITASampleFrame sfIR( oIRFile.GetName() );
	ita_convio_exchange_channel( &oConvolver, sfIR, iCurrentIRChannelIndex );

	// Start user interaction
219
	std::cout << ita_convoi_commands() <<  std::endl;
220 221 222 223 224 225 226 227
	int iKey;
	while( ( iKey = _getch() ) != 'q' )
	{
		switch( iKey )
		{
		case( 'm' ) :
		{
			// Toggle
228 229
			oMultiplier.SetMuted( !oMultiplier.IsMuted() );
			if( oMultiplier.IsMuted() )
230 231 232
				std::cout << "Output is now muted" <<  std::endl;
			else
				std::cout << "Output is loud" <<  std::endl;
233 234 235 236
			break;
		}
		case( 'n' ) :
		{
237 238 239
			// Switch to next (or only) channel
			iCurrentIRChannelIndex = ( iCurrentIRChannelIndex + 1 ) % iFilterChannels;
			ita_convio_exchange_channel( &oConvolver, sfIR, iCurrentIRChannelIndex );
240 241
			break;
		}
242 243 244 245 246 247 248 249 250 251 252 253 254 255
		case( 'd' ) :
		{
			ita_convio_dirac_channel( &oConvolver );
			break;
		}
		case( 48 + 1 ) :
		case( 48 + 2 ) :
		case( 48 + 3 ) :
		case( 48 + 4 ) :
		case( 48 + 5 ) :
		case( 48 + 6 ) :
		case( 48 + 7 ) :
		case( 48 + 8 ) :
		case( 48 + 9 ) :
256 257
		{
			// Switch to next channel
258 259
			int iRequestedChannelIndex = iKey - 1 - 48;
			if( iFilterChannels > iRequestedChannelIndex )
260
			{
261
				iCurrentIRChannelIndex = iRequestedChannelIndex;
262 263 264 265
				ita_convio_exchange_channel( &oConvolver, sfIR, iCurrentIRChannelIndex );
			}
			else
			{
266
				std::cerr << "Requested channel is out of range, maximum IR channel number is " << iFilterChannels <<  std::endl;
267 268 269 270 271
			}
			break;
		}
		default:
		{
272 273
			std::cerr << "Unrecognized key command '" << char( iKey ) << "'" <<  std::endl;
			std::cout << ita_convoi_commands() <<  std::endl;
274 275 276 277 278 279
			break;
		}
		}
	}

	oITAPA.Stop();
280 281
	oITAPA.Close();
	oITAPA.Finalize();
282 283 284 285
	
	return 0;
}

286
std::string ita_convoi_syntax()
287
{
288 289
	std::stringstream ss;
	ss << "Syntax: ita_convoi SAMPLE_INPUT_WAV_MONO IR_FILTER_WAV_MULTICHANNEL [BLOCKLENGTH] [PORTAUDIO_DEVICE]" <<  std::endl;
290 291 292
	return ss.str();
}

293
std::string ita_convoi_commands()
294
{
295 296 297 298 299 300
	std::stringstream ss;
	ss << "Commands:\t'q' quit" <<  std::endl;
	ss << "\t\t'm' toggle mute" <<  std::endl;
	ss << "\t\t'n' switch to next IR channel" <<  std::endl;
	ss << "\t\t'd' exchange a Dirac filter" <<  std::endl;
	ss << "\t\t1-9 exchange IR filter to given channel, if available" <<  std::endl;
301 302 303 304 305 306
	return ss.str();
}

void ita_convio_exchange_channel( ITAStreamConvolver* pSC, const ITASampleFrame& sfIR, const int iChannelIndex )
{
	assert( sfIR.channels() > iChannelIndex );
307 308 309 310
	ITAUPFilter* pFilter = pSC->RequestFilter();
	pFilter->Load( sfIR[ iChannelIndex ].GetData(), sfIR.GetLength() );
	pSC->ExchangeFilter( pFilter );
	pSC->ReleaseFilter( pFilter );
311 312 313 314 315 316
	std::cout << "Exchanged filter, now streaming channel " << iChannelIndex + 1 <<  std::endl;
}


void ita_convio_dirac_channel( ITAStreamConvolver* pSC )
{
317
	ITAUPFilter* pFilter = pSC->RequestFilter();
318
	pFilter->identity();
319 320
	pSC->ExchangeFilter( pFilter );
	pSC->ReleaseFilter( pFilter );
321
	std::cout << "Exchanged Dirac filter" <<  std::endl;
322
}