1 /*
2      File: CAVolumeCurve.cpp
3  Abstract: CAVolumeCurve.h
4   Version: 1.1
5 
6  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
7  Inc. ("Apple") in consideration of your agreement to the following
8  terms, and your use, installation, modification or redistribution of
9  this Apple software constitutes acceptance of these terms.  If you do
10  not agree with these terms, please do not use, install, modify or
11  redistribute this Apple software.
12 
13  In consideration of your agreement to abide by the following terms, and
14  subject to these terms, Apple grants you a personal, non-exclusive
15  license, under Apple's copyrights in this original Apple software (the
16  "Apple Software"), to use, reproduce, modify and redistribute the Apple
17  Software, with or without modifications, in source and/or binary forms;
18  provided that if you redistribute the Apple Software in its entirety and
19  without modifications, you must retain this notice and the following
20  text and disclaimers in all such redistributions of the Apple Software.
21  Neither the name, trademarks, service marks or logos of Apple Inc. may
22  be used to endorse or promote products derived from the Apple Software
23  without specific prior written permission from Apple.  Except as
24  expressly stated in this notice, no other rights or licenses, express or
25  implied, are granted by Apple herein, including but not limited to any
26  patent rights that may be infringed by your derivative works or by other
27  works in which the Apple Software may be incorporated.
28 
29  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
30  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34 
35  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42  POSSIBILITY OF SUCH DAMAGE.
43 
44  Copyright (C) 2014 Apple Inc. All Rights Reserved.
45 
46 */
47 //=============================================================================
48 //	Includes
49 //=============================================================================
50 
51 #include "CAVolumeCurve.h"
52 #include "CADebugMacros.h"
53 #include <math.h>
54 
55 //=============================================================================
56 //	CAVolumeCurve
57 //=============================================================================
58 
CAVolumeCurve()59 CAVolumeCurve::CAVolumeCurve()
60 :
61 	mTag(0),
62 	mCurveMap(),
63 	mIsApplyingTransferFunction(true),
64 	mTransferFunction(kPow2Over1Curve),
65 	mRawToScalarExponentNumerator(2.0f),
66 	mRawToScalarExponentDenominator(1.0f)
67 {
68 }
69 
~CAVolumeCurve()70 CAVolumeCurve::~CAVolumeCurve()
71 {
72 }
73 
GetMinimumRaw() const74 SInt32	CAVolumeCurve::GetMinimumRaw() const
75 {
76 	SInt32 theAnswer = 0;
77 
78 	if(!mCurveMap.empty())
79 	{
80 		CurveMap::const_iterator theIterator = mCurveMap.begin();
81 		theAnswer = theIterator->first.mMinimum;
82 	}
83 
84 	return theAnswer;
85 }
86 
GetMaximumRaw() const87 SInt32	CAVolumeCurve::GetMaximumRaw() const
88 {
89 	SInt32 theAnswer = 0;
90 
91 	if(!mCurveMap.empty())
92 	{
93 		CurveMap::const_iterator theIterator = mCurveMap.begin();
94 		std::advance(theIterator, static_cast<int>(mCurveMap.size() - 1));
95 		theAnswer = theIterator->first.mMaximum;
96 	}
97 
98 	return theAnswer;
99 }
100 
GetMinimumDB() const101 Float32	CAVolumeCurve::GetMinimumDB() const
102 {
103 	Float32 theAnswer = 0;
104 
105 	if(!mCurveMap.empty())
106 	{
107 		CurveMap::const_iterator theIterator = mCurveMap.begin();
108 		theAnswer = theIterator->second.mMinimum;
109 	}
110 
111 	return theAnswer;
112 }
113 
GetMaximumDB() const114 Float32	CAVolumeCurve::GetMaximumDB() const
115 {
116 	Float32 theAnswer = 0;
117 
118 	if(!mCurveMap.empty())
119 	{
120 		CurveMap::const_iterator theIterator = mCurveMap.begin();
121 		std::advance(theIterator, static_cast<int>(mCurveMap.size() - 1));
122 		theAnswer = theIterator->second.mMaximum;
123 	}
124 
125 	return theAnswer;
126 }
127 
SetTransferFunction(UInt32 inTransferFunction)128 void	CAVolumeCurve::SetTransferFunction(UInt32 inTransferFunction)
129 {
130 	mTransferFunction = inTransferFunction;
131 
132 	//	figure out the co-efficients
133 	switch(inTransferFunction)
134 	{
135 		case kLinearCurve:
136 			mIsApplyingTransferFunction = false;
137 			mRawToScalarExponentNumerator = 1.0f;
138 			mRawToScalarExponentDenominator = 1.0f;
139 			break;
140 
141 		case kPow1Over3Curve:
142 			mIsApplyingTransferFunction = true;
143 			mRawToScalarExponentNumerator = 1.0f;
144 			mRawToScalarExponentDenominator = 3.0f;
145 			break;
146 
147 		case kPow1Over2Curve:
148 			mIsApplyingTransferFunction = true;
149 			mRawToScalarExponentNumerator = 1.0f;
150 			mRawToScalarExponentDenominator = 2.0f;
151 			break;
152 
153 		case kPow3Over4Curve:
154 			mIsApplyingTransferFunction = true;
155 			mRawToScalarExponentNumerator = 3.0f;
156 			mRawToScalarExponentDenominator = 4.0f;
157 			break;
158 
159 		case kPow3Over2Curve:
160 			mIsApplyingTransferFunction = true;
161 			mRawToScalarExponentNumerator = 3.0f;
162 			mRawToScalarExponentDenominator = 2.0f;
163 			break;
164 
165 		case kPow2Over1Curve:
166 			mIsApplyingTransferFunction = true;
167 			mRawToScalarExponentNumerator = 2.0f;
168 			mRawToScalarExponentDenominator = 1.0f;
169 			break;
170 
171 		case kPow3Over1Curve:
172 			mIsApplyingTransferFunction = true;
173 			mRawToScalarExponentNumerator = 3.0f;
174 			mRawToScalarExponentDenominator = 1.0f;
175 			break;
176 
177 		case kPow4Over1Curve:
178 			mIsApplyingTransferFunction = true;
179 			mRawToScalarExponentNumerator = 4.0f;
180 			mRawToScalarExponentDenominator = 1.0f;
181 			break;
182 
183 		case kPow5Over1Curve:
184 			mIsApplyingTransferFunction = true;
185 			mRawToScalarExponentNumerator = 5.0f;
186 			mRawToScalarExponentDenominator = 1.0f;
187 			break;
188 
189 		case kPow6Over1Curve:
190 			mIsApplyingTransferFunction = true;
191 			mRawToScalarExponentNumerator = 6.0f;
192 			mRawToScalarExponentDenominator = 1.0f;
193 			break;
194 
195 		case kPow7Over1Curve:
196 			mIsApplyingTransferFunction = true;
197 			mRawToScalarExponentNumerator = 7.0f;
198 			mRawToScalarExponentDenominator = 1.0f;
199 			break;
200 
201 		case kPow8Over1Curve:
202 			mIsApplyingTransferFunction = true;
203 			mRawToScalarExponentNumerator = 8.0f;
204 			mRawToScalarExponentDenominator = 1.0f;
205 			break;
206 
207 		case kPow9Over1Curve:
208 			mIsApplyingTransferFunction = true;
209 			mRawToScalarExponentNumerator = 9.0f;
210 			mRawToScalarExponentDenominator = 1.0f;
211 			break;
212 
213 		case kPow10Over1Curve:
214 			mIsApplyingTransferFunction = true;
215 			mRawToScalarExponentNumerator = 10.0f;
216 			mRawToScalarExponentDenominator = 1.0f;
217 			break;
218 
219 		case kPow11Over1Curve:
220 			mIsApplyingTransferFunction = true;
221 			mRawToScalarExponentNumerator = 11.0f;
222 			mRawToScalarExponentDenominator = 1.0f;
223 			break;
224 
225 		case kPow12Over1Curve:
226 			mIsApplyingTransferFunction = true;
227 			mRawToScalarExponentNumerator = 12.0f;
228 			mRawToScalarExponentDenominator = 1.0f;
229 			break;
230 
231 		default:
232 			mIsApplyingTransferFunction = true;
233 			mRawToScalarExponentNumerator = 2.0f;
234 			mRawToScalarExponentDenominator = 1.0f;
235 			break;
236 	};
237 }
238 
AddRange(SInt32 inMinRaw,SInt32 inMaxRaw,Float32 inMinDB,Float32 inMaxDB)239 void	CAVolumeCurve::AddRange(SInt32 inMinRaw, SInt32 inMaxRaw, Float32 inMinDB, Float32 inMaxDB)
240 {
241 	CARawPoint theRaw(inMinRaw, inMaxRaw);
242 	CADBPoint theDB(inMinDB, inMaxDB);
243 
244 	bool isOverlapped = false;
245 	bool isDone = false;
246 	CurveMap::iterator theIterator = mCurveMap.begin();
247 	while((theIterator != mCurveMap.end()) && !isOverlapped && !isDone)
248 	{
249 		isOverlapped = CARawPoint::Overlap(theRaw, theIterator->first);
250 		isDone = theRaw >= theIterator->first;
251 
252 		if(!isOverlapped && !isDone)
253 		{
254 			std::advance(theIterator, 1);
255 		}
256 	}
257 
258 	if(!isOverlapped)
259 	{
260 		mCurveMap.insert(CurveMap::value_type(theRaw, theDB));
261 	}
262 	else
263 	{
264 		DebugMessage("CAVolumeCurve::AddRange: new point overlaps");
265 	}
266 }
267 
ResetRange()268 void	CAVolumeCurve::ResetRange()
269 {
270 	mCurveMap.clear();
271 }
272 
CheckForContinuity() const273 bool	CAVolumeCurve::CheckForContinuity() const
274 {
275 	bool theAnswer = true;
276 
277 	CurveMap::const_iterator theIterator = mCurveMap.begin();
278 	if(theIterator != mCurveMap.end())
279 	{
280 		SInt32 theRaw = theIterator->first.mMinimum;
281 		Float32 theDB = theIterator->second.mMinimum;
282 		do
283 		{
284 			SInt32 theRawMin = theIterator->first.mMinimum;
285 			SInt32 theRawMax = theIterator->first.mMaximum;
286 			SInt32 theRawRange = theRawMax - theRawMin;
287 
288 			Float32 theDBMin = theIterator->second.mMinimum;
289 			Float32 theDBMax = theIterator->second.mMaximum;
290 			Float32 theDBRange = theDBMax - theDBMin;
291 
292 			theAnswer = theRaw == theRawMin;
293 			theAnswer = theAnswer && (theDB == theDBMin);
294 
295 			theRaw += theRawRange;
296 			theDB += theDBRange;
297 
298 			std::advance(theIterator, 1);
299 		}
300 		while((theIterator != mCurveMap.end()) && theAnswer);
301 	}
302 
303 	return theAnswer;
304 }
305 
ConvertDBToRaw(Float32 inDB) const306 SInt32	CAVolumeCurve::ConvertDBToRaw(Float32 inDB) const
307 {
308 	//	clamp the value to the dB range
309 	Float32 theOverallDBMin = GetMinimumDB();
310 	Float32 theOverallDBMax = GetMaximumDB();
311 
312 	if(inDB < theOverallDBMin) inDB = theOverallDBMin;
313 	if(inDB > theOverallDBMax) inDB = theOverallDBMax;
314 
315 	//	get the first entry in the curve map;
316 	CurveMap::const_iterator theIterator = mCurveMap.begin();
317 
318 	//	initialize the answer to the minimum raw of the first item in the curve map
319 	SInt32 theAnswer = theIterator->first.mMinimum;
320 
321 	//	iterate through the curve map until we run out of dB
322 	bool isDone = false;
323 	while(!isDone && (theIterator != mCurveMap.end()))
324 	{
325 		SInt32 theRawMin = theIterator->first.mMinimum;
326 		SInt32 theRawMax = theIterator->first.mMaximum;
327 		SInt32 theRawRange = theRawMax - theRawMin;
328 
329 		Float32 theDBMin = theIterator->second.mMinimum;
330 		Float32 theDBMax = theIterator->second.mMaximum;
331 		Float32 theDBRange = theDBMax - theDBMin;
332 
333 		Float32 theDBPerRaw = theDBRange / static_cast<Float32>(theRawRange);
334 
335 		//	figure out how many steps we are into this entry in the curve map
336 		if(inDB > theDBMax)
337 		{
338 			//	we're past the end of this one, so add in the whole range for this entry
339 			theAnswer += theRawRange;
340 		}
341 		else
342 		{
343 			//	it's somewhere within the current entry
344 			//	figure out how many steps it is
345 			Float32 theNumberRawSteps = inDB - theDBMin;
346 			theNumberRawSteps /= theDBPerRaw;
347 
348 			//	only move in whole steps
349 			theNumberRawSteps = roundf(theNumberRawSteps);
350 
351 			//	add this many steps to the answer
352 			theAnswer += static_cast<SInt32>(theNumberRawSteps);
353 
354 			//	mark that we are done
355 			isDone = true;
356 		}
357 
358 		//	go to the next entry in the curve map
359 		std::advance(theIterator, 1);
360 	}
361 
362 	return theAnswer;
363 }
364 
ConvertRawToDB(SInt32 inRaw) const365 Float32	CAVolumeCurve::ConvertRawToDB(SInt32 inRaw) const
366 {
367 	Float32 theAnswer = 0;
368 
369 	//	clamp the raw value
370 	SInt32 theOverallRawMin = GetMinimumRaw();
371 	SInt32 theOverallRawMax = GetMaximumRaw();
372 
373 	if(inRaw < theOverallRawMin) inRaw = theOverallRawMin;
374 	if(inRaw > theOverallRawMax) inRaw = theOverallRawMax;
375 
376 	//	figure out how many raw steps need to be taken from the first one
377 	SInt32 theNumberRawSteps = inRaw - theOverallRawMin;
378 
379 	//	get the first item in the curve map
380 	CurveMap::const_iterator theIterator = mCurveMap.begin();
381 
382 	//	initialize the answer to the minimum dB of the first item in the curve map
383 	theAnswer = theIterator->second.mMinimum;
384 
385 	//	iterate through the curve map until we run out of steps
386 	while((theNumberRawSteps > 0) && (theIterator != mCurveMap.end()))
387 	{
388 		//	compute some values
389 		SInt32 theRawMin = theIterator->first.mMinimum;
390 		SInt32 theRawMax = theIterator->first.mMaximum;
391 		SInt32 theRawRange = theRawMax - theRawMin;
392 
393 		Float32 theDBMin = theIterator->second.mMinimum;
394 		Float32 theDBMax = theIterator->second.mMaximum;
395 		Float32 theDBRange = theDBMax - theDBMin;
396 
397 		Float32 theDBPerRaw = theDBRange / static_cast<Float32>(theRawRange);
398 
399 		//	there might be more steps than the current map entry accounts for
400 		SInt32 theRawStepsToAdd = std::min(theRawRange, theNumberRawSteps);
401 
402 		//	add this many steps worth of db to the answer;
403 		theAnswer += theRawStepsToAdd * theDBPerRaw;
404 
405 		//	figure out how many steps are left
406 		theNumberRawSteps -= theRawStepsToAdd;
407 
408 		//	go to the next map entry
409 		std::advance(theIterator, 1);
410 	}
411 
412 	return theAnswer;
413 }
414 
ConvertRawToScalar(SInt32 inRaw) const415 Float32	CAVolumeCurve::ConvertRawToScalar(SInt32 inRaw) const
416 {
417 	//	get some important values
418 	Float32	theDBMin = GetMinimumDB();
419 	Float32	theDBMax = GetMaximumDB();
420 	Float32	theDBRange = theDBMax - theDBMin;
421 	SInt32	theRawMin = GetMinimumRaw();
422 	SInt32	theRawMax = GetMaximumRaw();
423 	SInt32	theRawRange = theRawMax - theRawMin;
424 
425 	//	range the raw value
426 	if(inRaw < theRawMin) inRaw = theRawMin;
427 	if(inRaw > theRawMax) inRaw = theRawMax;
428 
429 	//	calculate the distance in the range inRaw is
430 	Float32 theAnswer = static_cast<Float32>(inRaw - theRawMin) / static_cast<Float32>(theRawRange);
431 
432 	//	only apply a curve to the scalar values if the dB range is greater than 30
433 	if(mIsApplyingTransferFunction && (theDBRange > 30.0f))
434 	{
435 		theAnswer = powf(theAnswer, mRawToScalarExponentNumerator / mRawToScalarExponentDenominator);
436 	}
437 
438 	return theAnswer;
439 }
440 
ConvertDBToScalar(Float32 inDB) const441 Float32	CAVolumeCurve::ConvertDBToScalar(Float32 inDB) const
442 {
443 	SInt32 theRawValue = ConvertDBToRaw(inDB);
444 	Float32 theAnswer = ConvertRawToScalar(theRawValue);
445 	return theAnswer;
446 }
447 
ConvertScalarToRaw(Float32 inScalar) const448 SInt32	CAVolumeCurve::ConvertScalarToRaw(Float32 inScalar) const
449 {
450 	//	range the scalar value
451 	inScalar = std::min(1.0f, std::max(0.0f, inScalar));
452 
453 	//	get some important values
454 	Float32	theDBMin = GetMinimumDB();
455 	Float32	theDBMax = GetMaximumDB();
456 	Float32	theDBRange = theDBMax - theDBMin;
457 	SInt32	theRawMin = GetMinimumRaw();
458 	SInt32	theRawMax = GetMaximumRaw();
459 	SInt32	theRawRange = theRawMax - theRawMin;
460 
461 	//	have to undo the curve if the dB range is greater than 30
462 	if(mIsApplyingTransferFunction && (theDBRange > 30.0f))
463 	{
464 		inScalar = powf(inScalar, mRawToScalarExponentDenominator / mRawToScalarExponentNumerator);
465 	}
466 
467 	//	now we can figure out how many raw steps this is
468 	Float32 theNumberRawSteps = inScalar * static_cast<Float32>(theRawRange);
469 	theNumberRawSteps = roundf(theNumberRawSteps);
470 
471 	//	the answer is the minimum raw value plus the number of raw steps
472 	SInt32 theAnswer = theRawMin + static_cast<SInt32>(theNumberRawSteps);
473 
474 	return theAnswer;
475 }
476 
ConvertScalarToDB(Float32 inScalar) const477 Float32	CAVolumeCurve::ConvertScalarToDB(Float32 inScalar) const
478 {
479 	SInt32 theRawValue = ConvertScalarToRaw(inScalar);
480 	Float32 theAnswer = ConvertRawToDB(theRawValue);
481 	return theAnswer;
482 }
483