/*============================================================================= CAVolumeCurve.cpp $Log: CAVolumeCurve.cpp,v $ Revision 1.6 2004/05/21 00:11:30 jcm10 add support for the driver to say that a level control shouldn't use the volume curve Revision 1.5 2003/07/26 00:19:17 jcm10 use the right max to clamp the input in ConvertRawToScalar() Revision 1.4 2002/12/17 20:01:54 jcm10 round when converting to raw to make the UI better Revision 1.3 2002/11/08 04:04:10 jcm10 stop using interpolation in the conversions so that everything clamps onto actual steps in the ranges Revision 1.2 2002/11/07 21:14:14 jcm10 fix some naming in CAVolumeCurve to better match the usage in the HAL API Revision 1.1 2002/10/31 01:04:43 jcm10 moved here from AudioHardware/Utility Revision 1.3 2002/06/26 21:37:09 jcm10 take out the old code Revision 1.2 2002/06/25 23:33:39 jcm10 make the curve for percent conversions a bit more shallow Revision 1.1 2002/05/21 00:55:49 jcm10 new sample rate properties and new volume control behavior Revision 0.0 Sun May 19 2002 19:26:33 US/Pacific moorf Created $NoKeywords: $ =============================================================================*/ //============================================================================= // Includes //============================================================================= #include "CAVolumeCurve.h" #include "CADebugMacros.h" #include //============================================================================= // CAVolumeCurve //============================================================================= CAVolumeCurve::CAVolumeCurve() : mCurveMap(), mIsApplyingCurve(true) { } CAVolumeCurve::~CAVolumeCurve() { } SInt32 CAVolumeCurve::GetMinimumRaw() const { SInt32 theAnswer = 0; if(!mCurveMap.empty()) { CurveMap::const_iterator theIterator = mCurveMap.begin(); theAnswer = theIterator->first.mMinimum; } return theAnswer; } SInt32 CAVolumeCurve::GetMaximumRaw() const { SInt32 theAnswer = 0; if(!mCurveMap.empty()) { CurveMap::const_iterator theIterator = mCurveMap.begin(); std::advance(theIterator, mCurveMap.size() - 1); theAnswer = theIterator->first.mMaximum; } return theAnswer; } Float64 CAVolumeCurve::GetMinimumDB() const { Float64 theAnswer = 0; if(!mCurveMap.empty()) { CurveMap::const_iterator theIterator = mCurveMap.begin(); theAnswer = theIterator->second.mMinimum; } return theAnswer; } Float64 CAVolumeCurve::GetMaximumDB() const { Float64 theAnswer = 0; if(!mCurveMap.empty()) { CurveMap::const_iterator theIterator = mCurveMap.begin(); std::advance(theIterator, mCurveMap.size() - 1); theAnswer = theIterator->second.mMaximum; } return theAnswer; } void CAVolumeCurve::AddRange(SInt32 inMinRaw, SInt32 inMaxRaw, Float64 inMinDB, Float64 inMaxDB) { CARawPoint theRaw(inMinRaw, inMaxRaw); CADBPoint theDB(inMinDB, inMaxDB); bool isOverlapped = false; bool isDone = false; CurveMap::iterator theIterator = mCurveMap.begin(); while((theIterator != mCurveMap.end()) && !isOverlapped && !isDone) { isOverlapped = CARawPoint::Overlap(theRaw, theIterator->first); isDone = theRaw >= theIterator->first; if(!isOverlapped && !isDone) { std::advance(theIterator, 1); } } if(!isOverlapped) { mCurveMap.insert(CurveMap::value_type(theRaw, theDB)); } else { DebugMessage("CAVolumeCurve::AddRange: new point overlaps"); } } void CAVolumeCurve::ResetRange() { mCurveMap.clear(); } bool CAVolumeCurve::CheckForContinuity() const { bool theAnswer = true; CurveMap::const_iterator theIterator = mCurveMap.begin(); if(theIterator != mCurveMap.end()) { SInt32 theRaw = theIterator->first.mMinimum; Float64 theDB = theIterator->second.mMinimum; do { SInt32 theRawMin = theIterator->first.mMinimum; SInt32 theRawMax = theIterator->first.mMaximum; SInt32 theRawRange = theRawMax - theRawMin; Float64 theDBMin = theIterator->second.mMinimum; Float64 theDBMax = theIterator->second.mMaximum; Float64 theDBRange = theDBMax - theDBMin; theAnswer = theRaw == theRawMin; theAnswer = theDB == theDBMin; theRaw += theRawRange; theDB += theDBRange; std::advance(theIterator, 1); } while((theIterator != mCurveMap.end()) && theAnswer); } return theAnswer; } SInt32 CAVolumeCurve::ConvertDBToRaw(Float64 inDB) const { // clamp the value to the dB range Float64 theOverallDBMin = GetMinimumDB(); Float64 theOverallDBMax = GetMaximumDB(); if(inDB < theOverallDBMin) inDB = theOverallDBMin; if(inDB > theOverallDBMax) inDB = theOverallDBMax; // get the first entry in the curve map; CurveMap::const_iterator theIterator = mCurveMap.begin(); // initialize the answer to the minimum raw of the first item in the curve map SInt32 theAnswer = theIterator->first.mMinimum; // iterate through the curve map until we run out of dB bool isDone = false; while(!isDone && (theIterator != mCurveMap.end())) { SInt32 theRawMin = theIterator->first.mMinimum; SInt32 theRawMax = theIterator->first.mMaximum; SInt32 theRawRange = theRawMax - theRawMin; Float64 theDBMin = theIterator->second.mMinimum; Float64 theDBMax = theIterator->second.mMaximum; Float64 theDBRange = theDBMax - theDBMin; Float64 theDBPerRaw = theDBRange / static_cast(theRawRange); // figure out how many steps we are into this entry in the curve map if(inDB > theDBMax) { // we're past the end of this one, so add in the whole range for this entry theAnswer += theRawRange; } else { // it's somewhere within the current entry // figure out how many steps it is Float64 theNumberRawSteps = inDB - theDBMin; theNumberRawSteps /= theDBPerRaw; // only move in whole steps theNumberRawSteps = round(theNumberRawSteps); // add this many steps to the answer theAnswer += static_cast(theNumberRawSteps); // mark that we are done isDone = true; } // go to the next entry in the curve map std::advance(theIterator, 1); } return theAnswer; } Float64 CAVolumeCurve::ConvertRawToDB(SInt32 inRaw) const { Float64 theAnswer = 0; // clamp the raw value SInt32 theOverallRawMin = GetMinimumRaw(); SInt32 theOverallRawMax = GetMaximumRaw(); if(inRaw < theOverallRawMin) inRaw = theOverallRawMin; if(inRaw > theOverallRawMax) inRaw = theOverallRawMax; // figure out how many raw steps need to be taken from the first one SInt32 theNumberRawSteps = inRaw - theOverallRawMin; // get the first item in the curve map CurveMap::const_iterator theIterator = mCurveMap.begin(); // initialize the answer to the minimum dB of the first item in the curve map theAnswer = theIterator->second.mMinimum; // iterate through the curve map until we run out of steps while((theNumberRawSteps > 0) && (theIterator != mCurveMap.end())) { // compute some values SInt32 theRawMin = theIterator->first.mMinimum; SInt32 theRawMax = theIterator->first.mMaximum; SInt32 theRawRange = theRawMax - theRawMin; Float64 theDBMin = theIterator->second.mMinimum; Float64 theDBMax = theIterator->second.mMaximum; Float64 theDBRange = theDBMax - theDBMin; Float64 theDBPerRaw = theDBRange / static_cast(theRawRange); // there might be more steps than the current map entry accounts for SInt32 theRawStepsToAdd = std::min(theRawRange, theNumberRawSteps); // add this many steps worth of db to the answer; theAnswer += theRawStepsToAdd * theDBPerRaw; // figure out how many steps are left theNumberRawSteps -= theRawStepsToAdd; // go to the next map entry std::advance(theIterator, 1); } return theAnswer; } Float64 CAVolumeCurve::ConvertRawToScalar(SInt32 inRaw) const { // get some important values Float64 theDBMin = GetMinimumDB(); Float64 theDBMax = GetMaximumDB(); Float64 theDBRange = theDBMax - theDBMin; SInt32 theRawMin = GetMinimumRaw(); SInt32 theRawMax = GetMaximumRaw(); SInt32 theRawRange = theRawMax - theRawMin; // range the raw value if(inRaw < theRawMin) inRaw = theRawMin; if(inRaw > theRawMax) inRaw = theRawMax; // calculate the distance in the range inRaw is Float64 theAnswer = static_cast(inRaw - theRawMin) / static_cast(theRawRange); // only apply a curve to the scalar values if the dB range is greater than 30 if(mIsApplyingCurve && (theDBRange > 30.0)) { theAnswer = pow(theAnswer, 2.0 / 1.0); } return theAnswer; } Float64 CAVolumeCurve::ConvertDBToScalar(Float64 inDB) const { SInt32 theRawValue = ConvertDBToRaw(inDB); Float64 theAnswer = ConvertRawToScalar(theRawValue); return theAnswer; } SInt32 CAVolumeCurve::ConvertScalarToRaw(Float64 inScalar) const { // range the scalar value inScalar = std::min(1.0, std::max(0.0, inScalar)); // get some important values Float64 theDBMin = GetMinimumDB(); Float64 theDBMax = GetMaximumDB(); Float64 theDBRange = theDBMax - theDBMin; SInt32 theRawMin = GetMinimumRaw(); SInt32 theRawMax = GetMaximumRaw(); SInt32 theRawRange = theRawMax - theRawMin; // have to undo the curve if the dB range is greater than 30 if(mIsApplyingCurve && (theDBRange > 30.0)) { inScalar = pow(inScalar, 1.0 / 2.0); } // now we can figure out how many raw steps this is Float64 theNumberRawSteps = inScalar * static_cast(theRawRange); theNumberRawSteps = round(theNumberRawSteps); // the answer is the minimum raw value plus the number of raw steps SInt32 theAnswer = theRawMin + static_cast(theNumberRawSteps); return theAnswer; } Float64 CAVolumeCurve::ConvertScalarToDB(Float64 inScalar) const { SInt32 theRawValue = ConvertScalarToRaw(inScalar); Float64 theAnswer = ConvertRawToDB(theRawValue); return theAnswer; }