/* TempoController.c */
/*****************************************************************************/
/*                                                                           */
/*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
/*    Copyright (C) 1994  Thomas R. Lawrence                                 */
/*                                                                           */
/*    This program is free software; you can redistribute it and/or modify   */
/*    it under the terms of the GNU General Public License as published by   */
/*    the Free Software Foundation; either version 2 of the License, or      */
/*    (at your option) any later version.                                    */
/*                                                                           */
/*    This program is distributed in the hope that it will be useful,        */
/*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
/*    GNU General Public License for more details.                           */
/*                                                                           */
/*    You should have received a copy of the GNU General Public License      */
/*    along with this program; if not, write to the Free Software            */
/*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                           */
/*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
/*                                                                           */
/*****************************************************************************/

#include "MiscInfo.h"
#include "Audit.h"
#include "Debug.h"
#include "Definitions.h"

#include "TempoController.h"
#include "Memory.h"
#include "LinearTransition.h"
#include "PlayTrackInfoThang.h"


#define MINBPM (Double2LargeBCD(1))


struct TempoControlRec
	{
		LargeBCDType						DefaultBeatsPerMinute;
		LargeBCDType						CurrentBeatsPerMinute;
		struct LinearTransRec*	BeatsPerMinuteChange;
		long										BeatsPerMinuteChangeCountdown;
	};


/* create a new tempo control record */
TempoControlRec*				NewTempoControl(LargeBCDType DefaultBeatsPerMinute)
	{
		TempoControlRec*			Tempo;

		Tempo = (TempoControlRec*)AllocPtrCanFail(sizeof(TempoControlRec),"TempoControlRec");
		if (Tempo == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		Tempo->DefaultBeatsPerMinute = DefaultBeatsPerMinute;
		Tempo->CurrentBeatsPerMinute = Tempo->DefaultBeatsPerMinute;
		Tempo->BeatsPerMinuteChange = NewLinearTransition(0,0,1);
		if (Tempo->BeatsPerMinuteChange == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)Tempo);
				goto FailurePoint1;
			}
		Tempo->BeatsPerMinuteChangeCountdown = 0;
		return Tempo;
	}


/* dispose of a tempo control record */
void										DisposeTempoControl(TempoControlRec* Tempo)
	{
		CheckPtrExistence(Tempo);
		DisposeLinearTransition(Tempo->BeatsPerMinuteChange);
		ReleasePtr((char*)Tempo);
	}


/* update the tempo control & return the new value */
LargeBCDType						TempoControlUpdate(TempoControlRec* Tempo, long NumTicks)
	{
		CheckPtrExistence(Tempo);
		if (NumTicks < Tempo->BeatsPerMinuteChangeCountdown)
			{
				Tempo->CurrentBeatsPerMinute = LinearTransitionUpdateMultiple(
					Tempo->BeatsPerMinuteChange,NumTicks);
				Tempo->BeatsPerMinuteChangeCountdown -= NumTicks;
			}
		else if (Tempo->BeatsPerMinuteChangeCountdown > 0)
			{
				Tempo->CurrentBeatsPerMinute = LinearTransitionUpdateMultiple(
					Tempo->BeatsPerMinuteChange,Tempo->BeatsPerMinuteChangeCountdown);
				Tempo->BeatsPerMinuteChangeCountdown = 0;
			}
		return Tempo->CurrentBeatsPerMinute;
	}


/* reset tempo to default value */
void										TempoControlRestoreDefault(TempoControlRec* Tempo)
	{
		CheckPtrExistence(Tempo);
		Tempo->CurrentBeatsPerMinute = Tempo->DefaultBeatsPerMinute;
		Tempo->BeatsPerMinuteChangeCountdown = 0;
	}


/* set the tempo to the specified number of beats per minute */
void										TempoControlSetBeatsPerMinute(TempoControlRec* Tempo,
													LargeBCDType NewBeatsPerMinute)
	{
		CheckPtrExistence(Tempo);
		Tempo->CurrentBeatsPerMinute = NewBeatsPerMinute;
		if (Tempo->CurrentBeatsPerMinute < MINBPM)
			{
				Tempo->CurrentBeatsPerMinute = MINBPM;
			}
		Tempo->BeatsPerMinuteChangeCountdown = 0;
	}


/* adjust the tempo by adding the specified value to it */
void										TempoControlAdjustBeatsPerMinute(TempoControlRec* Tempo,
													LargeBCDType IncrementBeatsPerMinute)
	{
		CheckPtrExistence(Tempo);
		Tempo->CurrentBeatsPerMinute += IncrementBeatsPerMinute;
		if (Tempo->CurrentBeatsPerMinute < MINBPM)
			{
				Tempo->CurrentBeatsPerMinute = MINBPM;
			}
		Tempo->BeatsPerMinuteChangeCountdown = 0;
	}


/* sweep the tempo to a new value */
void										TempoControlSweepToNewValue(TempoControlRec* Tempo,
													LargeBCDType NewBPM, SmallExtBCDType NumBeatsToReach)
	{
		CheckPtrExistence(Tempo);
		if (NewBPM < MINBPM)
			{
				NewBPM = MINBPM;
			}
		if (NumBeatsToReach < 0)
			{
				NumBeatsToReach = 0;
			}
		Tempo->BeatsPerMinuteChangeCountdown = SmallExtBCD2Double(NumBeatsToReach)
			* (DURATIONUPDATECLOCKRESOLUTION / 4);
		RefillLinearTransition(Tempo->BeatsPerMinuteChange,Tempo->CurrentBeatsPerMinute,
			NewBPM,Tempo->BeatsPerMinuteChangeCountdown);
	}


/* sweep the tempo to a new value relative to the current value */
void										TempoControlSweepToAdjustedValue(TempoControlRec* Tempo,
													LargeBCDType AdjustBPM, SmallExtBCDType NumBeatsToReach)
	{
		CheckPtrExistence(Tempo);
		TempoControlSweepToNewValue(Tempo,Tempo->CurrentBeatsPerMinute + AdjustBPM,
			NumBeatsToReach);
	}
