Example #1
0
size_t SourceFile::read( Buffer *buffer )
{
	CI_ASSERT( buffer->getNumChannels() == getNumChannels() );
	CI_ASSERT( mReadPos < mNumFrames );

	size_t numRead;

	if( mConverter ) {
		size_t sourceBufFrames = size_t( buffer->getNumFrames() * (float)getSampleRateNative() / (float)getSampleRate() );
		size_t numFramesNeeded = std::min( mFileNumFrames - mReadPos, std::min( getMaxFramesPerRead(), sourceBufFrames ) );

		mConverterReadBuffer.setNumFrames( numFramesNeeded );
		performRead( &mConverterReadBuffer, 0, numFramesNeeded );
		pair<size_t, size_t> count = mConverter->convert( &mConverterReadBuffer, buffer );
		numRead = count.second;
	}
	else {
		size_t numFramesNeeded = std::min( mNumFrames - mReadPos, std::min( getMaxFramesPerRead(), buffer->getNumFrames() ) );
		numRead = performRead( buffer, 0, numFramesNeeded );
	}

	mReadPos += numRead;
	return numRead;
}
Example #2
0
void SourceFile::setupSampleRateConversion()
{
	size_t nativeSampleRate = getSampleRateNative();
	size_t outputSampleRate = getSampleRate();
	if( ! outputSampleRate ) {
		outputSampleRate = nativeSampleRate;
		setSampleRate( nativeSampleRate );
	}
	else if( outputSampleRate != nativeSampleRate ) {
		mNumFrames = (size_t)std::ceil( (float)mFileNumFrames * (float)outputSampleRate / (float)nativeSampleRate );

		if( ! supportsConversion() ) {
			size_t numChannels = getNumChannels();
			mConverter = audio::dsp::Converter::create( nativeSampleRate, outputSampleRate, numChannels, numChannels, getMaxFramesPerRead() );
			mConverterReadBuffer.setSize( getMaxFramesPerRead(), numChannels );
		}
	}
	else {
		mNumFrames = mFileNumFrames;
		mConverter.reset();
	}
}
Example #3
0
BufferRef SourceFile::loadBuffer()
{
	seek( 0 );

	BufferRef result = make_shared<Buffer>( mNumFrames, getNumChannels() );

	if( mConverter ) {
		// TODO: need BufferView's in order to reduce number of copies
		Buffer converterDestBuffer( mConverter->getDestMaxFramesPerBlock(), getNumChannels() );
		size_t readCount = 0;
		while( true ) {
			size_t framesNeeded = min( getMaxFramesPerRead(), mFileNumFrames - readCount );
			if( framesNeeded == 0 )
				break;

			// make sourceBuffer num frames match outNumFrames so that Converter doesn't think it has more
			if( framesNeeded != mConverterReadBuffer.getNumFrames() )
				mConverterReadBuffer.setNumFrames( framesNeeded );

			size_t outNumFrames = performRead( &mConverterReadBuffer, 0, framesNeeded );
			CI_ASSERT( outNumFrames == framesNeeded );

			pair<size_t, size_t> count = mConverter->convert( &mConverterReadBuffer, &converterDestBuffer );
			result->copyOffset( converterDestBuffer, count.second, mReadPos, 0 );

			readCount += outNumFrames;
			mReadPos += count.second;
		}
	}
	else {
		size_t readCount = performRead( result.get(), 0, mNumFrames );
		mReadPos = readCount;
	}

	return result;
}
// TODO: test setting MF_LOW_LATENCY attribute
void SourceFileMediaFoundation::initReader()
{
	CI_ASSERT( mDataSource );
	mFramesRemainingInReadBuffer = 0;

	::IMFAttributes *attributes;
	HRESULT hr = ::MFCreateAttributes( &attributes, 1 );
	CI_ASSERT( hr == S_OK );
	auto attributesPtr = ci::msw::makeComUnique( attributes );

	::IMFSourceReader *sourceReader;

	if( mDataSource->isFilePath() ) {
		hr = ::MFCreateSourceReaderFromURL( mDataSource->getFilePath().wstring().c_str(), attributesPtr.get(), &sourceReader );
		if( hr != S_OK ) {
			string errorString = string( "SourceFileMediaFoundation: Failed to create SourceReader from URL: " ) +  mDataSource->getFilePath().string(); 
			if( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
				errorString += ", file not found.";
			throw AudioFileExc( errorString );
		}
	}
	else {
		mComIStream = ci::msw::makeComUnique( new ci::msw::ComIStream( mDataSource->createStream() ) );
		::IMFByteStream *byteStream;
		hr = ::MFCreateMFByteStreamOnStream( mComIStream.get(), &byteStream );
		CI_ASSERT( hr == S_OK );
		mByteStream = ci::msw::makeComUnique( byteStream );

		hr = ::MFCreateSourceReaderFromByteStream( byteStream, attributesPtr.get(), &sourceReader );
		if( hr != S_OK )
			throw AudioFileExc( "SourceFileMediaFoundation: Failed to create SourceReader from resource." );
	}

	mSourceReader = ci::msw::makeComUnique( sourceReader );

	// get files native format
	::IMFMediaType *nativeType;
	::WAVEFORMATEX *nativeFormat;
	UINT32 formatSize;
	hr = mSourceReader->GetNativeMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &nativeType );
	CI_ASSERT( hr == S_OK );
	auto nativeTypeUnique = ci::msw::makeComUnique( nativeType );

	hr = ::MFCreateWaveFormatExFromMFMediaType( nativeType, &nativeFormat, &formatSize );
	CI_ASSERT( hr == S_OK );

	mNumChannels = nativeFormat->nChannels;
	mSampleRate = nativeFormat->nSamplesPerSec;

	GUID outputSubType = MFAudioFormat_PCM; // default to PCM 16-bit int, upgrade if we can.
	mSampleType = SampleType::INT_16;

	if( nativeFormat->wBitsPerSample == 32 ) {
		mSampleType = SampleType::FLOAT_32;
		outputSubType = MFAudioFormat_Float;
	}
	else if( nativeFormat->wBitsPerSample == 24 ) {
		mSampleType = SampleType::INT_24;
		if( mNumChannels > 1 )
			mBitConverterBuffer.setSize( getMaxFramesPerRead(), mNumChannels );
	}

	::CoTaskMemFree( nativeFormat );

	mBytesPerSample = getBytesPerSample( mSampleType );
	mReadBuffer.setSize( getMaxFramesPerRead(), mNumChannels );

	// set output type, which loads the proper decoder:
	::IMFMediaType *outputType;
	hr = ::MFCreateMediaType( &outputType );
	auto outputTypeRef = ci::msw::makeComUnique( outputType );
	hr = outputTypeRef->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Audio );
	CI_ASSERT( hr == S_OK );
	hr = outputTypeRef->SetGUID( MF_MT_SUBTYPE, outputSubType );
	CI_ASSERT( hr == S_OK );

	hr = mSourceReader->SetCurrentMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, outputTypeRef.get() );
	CI_ASSERT( hr == S_OK );

	// after the decoder is loaded, we have to now get the 'complete' output type before retrieving its format
	::IMFMediaType *completeOutputType;
	hr = mSourceReader->GetCurrentMediaType( MF_SOURCE_READER_FIRST_AUDIO_STREAM, &completeOutputType );
	CI_ASSERT( hr == S_OK );

	::WAVEFORMATEX *format;
	hr = ::MFCreateWaveFormatExFromMFMediaType( completeOutputType, &format, &formatSize );
	CI_ASSERT( hr == S_OK );
	::CoTaskMemFree( format );

	// get seconds:
	::PROPVARIANT durationProp;
	hr = mSourceReader->GetPresentationAttribute( MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &durationProp );
	CI_ASSERT( hr == S_OK );
	LONGLONG duration = durationProp.uhVal.QuadPart;
	
	mSeconds = nanoSecondsToSeconds( duration );
	mNumFrames = mFileNumFrames = size_t( mSeconds * (double)mSampleRate );

	::PROPVARIANT seekProp;
	hr = mSourceReader->GetPresentationAttribute( MF_SOURCE_READER_MEDIASOURCE, MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS, &seekProp );
	CI_ASSERT( hr == S_OK );
	ULONG flags = seekProp.ulVal;
	mCanSeek = ( ( flags & MFMEDIASOURCE_CAN_SEEK ) == MFMEDIASOURCE_CAN_SEEK );
}