/* * Implements properties: * "Speed" - cause the sound to play faster or slower * "Pitch" - raise or lower the pitch of the sound * * Pitch changing is implemented by combining speed changing and rate changing * (via resampling). The resampler needs to be changed later than the speed * changer; this class just controls parameters on the other two real filters. */ #include "global.h" #include "RageSoundReader_PitchChange.h" #include "RageSoundReader_SpeedChange.h" #include "RageSoundReader_Resample_Good.h" #include "RageLog.h" RageSoundReader_PitchChange::RageSoundReader_PitchChange( RageSoundReader *pSource ): RageSoundReader_Filter( NULL ) { m_pSpeedChange = new RageSoundReader_SpeedChange( pSource ); m_pResample = new RageSoundReader_Resample_Good( m_pSpeedChange, m_pSpeedChange->GetSampleRate() ); m_pSource = m_pResample; m_fSpeedRatio = 1.0f; m_fPitchRatio = 1.0f; m_fLastSetSpeedRatio = m_fSpeedRatio; m_fLastSetPitchRatio = m_fPitchRatio; } RageSoundReader_PitchChange::RageSoundReader_PitchChange( const RageSoundReader_PitchChange &cpy ): RageSoundReader_Filter( cpy ) { /* The source tree has already been copied. Our source is m_pResample; its source * is m_pSpeedChange (and its source is a copy of the pSource we were initialized * with). */ m_pResample = dynamic_cast( &*m_pSource ); m_pSpeedChange = dynamic_cast( m_pResample->GetSource() ); m_fSpeedRatio = cpy.m_fSpeedRatio; m_fPitchRatio = cpy.m_fPitchRatio; m_fLastSetSpeedRatio = cpy.m_fLastSetSpeedRatio; m_fLastSetPitchRatio = cpy.m_fLastSetPitchRatio; } int RageSoundReader_PitchChange::Read( float *pBuf, int iFrames ) { /* m_pSpeedChange->NextReadWillStep is true if speed changes will be applied * immediately on the next Read(). When this is true, apply the ratio to the * resampler and the speed changer simultaneously, so they take effect as * closely together as possible. */ if( (m_fLastSetSpeedRatio != m_fSpeedRatio || m_fLastSetPitchRatio != m_fPitchRatio) && m_pSpeedChange->NextReadWillStep() ) { float fRate = GetStreamToSourceRatio(); /* This is the simple way: */ // m_pResample->SetRate( m_fPitchRatio ); // m_pSpeedChange->SetSpeedRatio( m_fSpeedRatio / m_fPitchRatio ); /* However, the resampler has a limited granularity due to internal fixed- * point math, and the actual ratio will be slightly different than what * we tell it to use. The actual ratio used is fActualPitchRatio. */ m_pResample->SetRate( m_fPitchRatio ); float fActualPitchRatio = m_pResample->GetRate(); float fRequestedSpeedRatio = m_fSpeedRatio / fActualPitchRatio; m_pSpeedChange->SetSpeedRatio( fRequestedSpeedRatio ); m_fLastSetSpeedRatio = m_fSpeedRatio; m_fLastSetPitchRatio = m_fPitchRatio; /* If we just applied a new speed and it caused the ratio to change, return * no data, so the caller can see the new ratio. */ if( fRate != GetStreamToSourceRatio() ) return 0; } return RageSoundReader_Filter::Read( pBuf, iFrames ); } bool RageSoundReader_PitchChange::SetProperty( const RString &sProperty, float fValue ) { if( sProperty == "Rate" ) { /* Don't propagate this. m_pResample will take it, but it's under * our control. */ return false; } if( sProperty == "Speed" ) { SetSpeedRatio( fValue ); return true; } if( sProperty == "Pitch" ) { SetPitchRatio( fValue ); return true; } return RageSoundReader_Filter::SetProperty( sProperty, fValue ); } /* * Copyright (c) 2006 Glenn Maynard * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons to * whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */